diff --git a/lib/ui/BUILD.gn b/lib/ui/BUILD.gn index eadf0718656f9..596dac782b3d4 100644 --- a/lib/ui/BUILD.gn +++ b/lib/ui/BUILD.gn @@ -163,6 +163,7 @@ source_set("ui") { public_deps = [ "//flutter/flow", + "//flutter/shell/common:display", "//flutter/shell/common:platform_message_handler", "//flutter/third_party/txt", ] diff --git a/lib/ui/fixtures/ui_test.dart b/lib/ui/fixtures/ui_test.dart index 36f09bf397811..6658948e422f2 100644 --- a/lib/ui/fixtures/ui_test.dart +++ b/lib/ui/fixtures/ui_test.dart @@ -6,7 +6,7 @@ import 'dart:async'; import 'dart:typed_data'; import 'dart:ui'; import 'dart:isolate'; -import 'dart:ffi'; +import 'dart:ffi' hide Size; void main() {} @@ -444,7 +444,7 @@ void hooksTests() async { window.onMetricsChanged!(); _callHook( '_updateWindowMetrics', - 20, + 21, 0, // window Id 0.1234, // device pixel ratio 0.0, // width @@ -465,6 +465,7 @@ void hooksTests() async { [], // display features bounds [], // display features types [], // display features states + 0, // Display ID ); expectIdentical(originalZone, callbackZone); @@ -540,7 +541,7 @@ void hooksTests() async { await test('View padding/insets/viewPadding/systemGestureInsets', () { _callHook( '_updateWindowMetrics', - 20, + 21, 0, // window Id 1.0, // devicePixelRatio 800.0, // width @@ -561,6 +562,7 @@ void hooksTests() async { [], // display features bounds [], // display features types [], // display features states + 0, // Display ID ); expectEquals(window.viewInsets.bottom, 0.0); @@ -570,7 +572,7 @@ void hooksTests() async { _callHook( '_updateWindowMetrics', - 20, + 21, 0, // window Id 1.0, // devicePixelRatio 800.0, // width @@ -591,6 +593,7 @@ void hooksTests() async { [], // display features bounds [], // display features types [], // display features states + 0, // Display ID ); expectEquals(window.viewInsets.bottom, 400.0); @@ -602,7 +605,7 @@ void hooksTests() async { await test('Window physical touch slop', () { _callHook( '_updateWindowMetrics', - 20, + 21, 0, // window Id 1.0, // devicePixelRatio 800.0, // width @@ -623,6 +626,7 @@ void hooksTests() async { [], // display features bounds [], // display features types [], // display features states + 0, // Display ID ); expectEquals(window.gestureSettings, @@ -630,7 +634,7 @@ void hooksTests() async { _callHook( '_updateWindowMetrics', - 20, + 21, 0, // window Id 1.0, // devicePixelRatio 800.0, // width @@ -651,6 +655,7 @@ void hooksTests() async { [], // display features bounds [], // display features types [], // display features states + 0, // Display ID ); expectEquals(window.gestureSettings, @@ -658,7 +663,7 @@ void hooksTests() async { _callHook( '_updateWindowMetrics', - 20, + 21, 0, // window Id 1.0, // devicePixelRatio 800.0, // width @@ -679,6 +684,7 @@ void hooksTests() async { [], // display features bounds [], // display features types [], // display features states + 0, // Display ID ); expectEquals(window.gestureSettings, @@ -881,6 +887,28 @@ void hooksTests() async { expectEquals(frameNumber, 2); }); + await test('_updateDisplays preserves callback zone', () { + late Zone innerZone; + late Zone runZone; + late Display display; + + runZoned(() { + innerZone = Zone.current; + window.onMetricsChanged = () { + runZone = Zone.current; + display = PlatformDispatcher.instance.displays.first; + }; + }); + + _callHook('_updateDisplays', 5, [0], [800], [600], [1.5], [65]); + expectNotEquals(runZone, null); + expectIdentical(runZone, innerZone); + expectEquals(display.id, 0); + expectEquals(display.size, const Size(800, 600)); + expectEquals(display.devicePixelRatio, 1.5); + expectEquals(display.refreshRate, 65); + }); + await test('_futureize handles callbacker sync error', () async { String? callbacker(void Function(Object? arg) cb) { return 'failure'; @@ -1043,4 +1071,5 @@ external void _callHook( Object? arg18, Object? arg19, Object? arg20, + Object? arg21, ]); diff --git a/lib/ui/hooks.dart b/lib/ui/hooks.dart index f92d287cf0f14..5d5f129021412 100644 --- a/lib/ui/hooks.dart +++ b/lib/ui/hooks.dart @@ -4,6 +4,32 @@ part of dart.ui; +@pragma('vm:entry-point') +void _updateDisplays( + List ids, + List widths, + List heights, + List devicePixelRatios, + List refreshRates, +) { + assert(ids.length == widths.length); + assert(ids.length == heights.length); + assert(ids.length == devicePixelRatios.length); + assert(ids.length == refreshRates.length); + final List displays = []; + for (int index = 0; index < ids.length; index += 1) { + final int displayId = ids[index]; + displays.add(Display._( + id: displayId, + size: Size(widths[index], heights[index]), + devicePixelRatio: devicePixelRatios[index], + refreshRate: refreshRates[index], + )); + } + + PlatformDispatcher.instance._updateDisplays(displays); +} + @pragma('vm:entry-point') void _updateWindowMetrics( Object id, @@ -26,6 +52,7 @@ void _updateWindowMetrics( List displayFeaturesBounds, List displayFeaturesType, List displayFeaturesState, + int displayId, ) { PlatformDispatcher.instance._updateWindowMetrics( id, @@ -48,6 +75,7 @@ void _updateWindowMetrics( displayFeaturesBounds, displayFeaturesType, displayFeaturesState, + displayId, ); } diff --git a/lib/ui/platform_dispatcher.dart b/lib/ui/platform_dispatcher.dart index 329a054828156..4bfdd52288f10 100644 --- a/lib/ui/platform_dispatcher.dart +++ b/lib/ui/platform_dispatcher.dart @@ -149,6 +149,26 @@ class PlatformDispatcher { _onPlatformConfigurationChangedZone = Zone.current; } + /// The current list of displays. + /// + /// If any of their configurations change, [onMetricsChanged] will be called. + /// + /// To get the display for a [FlutterView], use [FlutterView.display]. + /// + /// Platforms may limit what information is available to the application with + /// regard to secondary displays and/or displays that do not have an active + /// application window. + /// + /// Presently, on Android and Web this collection will only contain the + /// display that the current window is on. On iOS, it will only contains the + /// main display on the phone or tablet. On Desktop, it will contain only + /// a main display with a valid refresh rate but invalid size and device + /// pixel ratio values. + // TODO(dnfield): Update these docs when https://github.com/flutter/flutter/issues/125939 + // and https://github.com/flutter/flutter/issues/125938 are resolved. + Iterable get displays => _displays.values; + final Map _displays = {}; + /// The current list of views, including top level platform windows used by /// the application. /// @@ -215,6 +235,17 @@ class PlatformDispatcher { _onMetricsChangedZone = Zone.current; } + // Called from the engine, via hooks.dart. + // + // Updates the available displays. + void _updateDisplays(List displays) { + _displays.clear(); + for (final Display display in displays) { + _displays[display.id] = display; + } + _invoke(onMetricsChanged, _onMetricsChangedZone); + } + // Called from the engine, via hooks.dart // // Updates the metrics of the window with the given id. @@ -239,6 +270,7 @@ class PlatformDispatcher { List displayFeaturesBounds, List displayFeaturesType, List displayFeaturesState, + int displayId, ) { final _ViewConfiguration previousConfiguration = _viewConfigurations[id] ?? const _ViewConfiguration(); @@ -283,6 +315,7 @@ class PlatformDispatcher { state: displayFeaturesState, devicePixelRatio: devicePixelRatio, ), + displayId: displayId, ); _invoke(onMetricsChanged, _onMetricsChangedZone); } @@ -1318,6 +1351,7 @@ class _ViewConfiguration { this.padding = ViewPadding.zero, this.gestureSettings = const GestureSettings(), this.displayFeatures = const [], + this.displayId = 0, }); /// Copy this configuration with some fields replaced. @@ -1332,6 +1366,7 @@ class _ViewConfiguration { ViewPadding? padding, GestureSettings? gestureSettings, List? displayFeatures, + int? displayId, }) { return _ViewConfiguration( view: view ?? this.view, @@ -1344,11 +1379,16 @@ class _ViewConfiguration { padding: padding ?? this.padding, gestureSettings: gestureSettings ?? this.gestureSettings, displayFeatures: displayFeatures ?? this.displayFeatures, + displayId: displayId ?? this.displayId, ); } final FlutterView? view; + /// The identifier for a display for this view, in + /// [PlatformDispatcher._displays]. + final int displayId; + /// The pixel density of the output surface. final double devicePixelRatio; diff --git a/lib/ui/window.dart b/lib/ui/window.dart index 19f40c6e1ee6a..c3c107c7683da 100644 --- a/lib/ui/window.dart +++ b/lib/ui/window.dart @@ -3,6 +3,40 @@ // found in the LICENSE file. part of dart.ui; +/// A configurable display that a [FlutterView] renders on. +/// +/// Use [FlutterView.display] to get the current display for that view. +class Display { + const Display._({ + required this.id, + required this.devicePixelRatio, + required this.size, + required this.refreshRate, + }); + + /// A unique identifier for this display. + /// + /// This identifier is unique among a list of displays the Flutter framework + /// is aware of, and is not derived from any platform specific identifiers for + /// displays. + final int id; + + /// The device pixel ratio of this display. + /// + /// This value is the same as the value of [FlutterView.devicePixelRatio] for + /// all view objects attached to this display. + final double devicePixelRatio; + + /// The physical size of this display. + final Size size; + + /// The refresh rate in FPS of this display. + final double refreshRate; + + @override + String toString() => 'Display(id: $id, size: $size, devicePixelRatio: $devicePixelRatio, refreshRate: $refreshRate)'; +} + /// A view into which a Flutter [Scene] is drawn. /// /// Each [FlutterView] has its own layer tree that is rendered @@ -67,6 +101,12 @@ class FlutterView { return platformDispatcher._viewConfigurations[viewId]!; } + /// The [Display] this view is drawn in. + Display get display { + assert(platformDispatcher._displays.containsKey(_viewConfiguration.displayId)); + return platformDispatcher._displays[_viewConfiguration.displayId]!; + } + /// The number of device pixels for each logical pixel for the screen this /// view is displayed on. /// @@ -92,6 +132,8 @@ class FlutterView { /// /// * [WidgetsBindingObserver], for a mechanism at the widgets layer to /// observe when this value changes. + /// * [Display.devicePixelRatio], which reports the DPR of the display. + /// The value here is equal to the value on the [display.devicePixelRatio]. double get devicePixelRatio => _viewConfiguration.devicePixelRatio; /// The dimensions and location of the rectangle into which the scene rendered diff --git a/lib/ui/window/platform_configuration.cc b/lib/ui/window/platform_configuration.cc index 0c6af5fac1e14..b1cd11fad30e7 100644 --- a/lib/ui/window/platform_configuration.cc +++ b/lib/ui/window/platform_configuration.cc @@ -43,6 +43,9 @@ void PlatformConfiguration::DidCreateIsolate() { on_error_.Set(tonic::DartState::Current(), Dart_GetField(library, tonic::ToDart("_onError"))); + update_displays_.Set( + tonic::DartState::Current(), + Dart_GetField(library, tonic::ToDart("_updateDisplays"))); update_locales_.Set(tonic::DartState::Current(), Dart_GetField(library, tonic::ToDart("_updateLocales"))); update_user_settings_data_.Set( @@ -78,7 +81,38 @@ void PlatformConfiguration::DidCreateIsolate() { // See: https://github.com/flutter/flutter/issues/120306 windows_.emplace(kImplicitViewId, std::make_unique( - kImplicitViewId, ViewportMetrics{1.0, 0.0, 0.0, -1})); + kImplicitViewId, ViewportMetrics{1.0, 0.0, 0.0, -1, 0})); +} + +void PlatformConfiguration::UpdateDisplays( + const std::vector& displays) { + std::vector ids; + std::vector widths; + std::vector heights; + std::vector device_pixel_ratios; + std::vector refresh_rates; + for (const auto& display : displays) { + ids.push_back(display.id); + widths.push_back(display.width); + heights.push_back(display.height); + device_pixel_ratios.push_back(display.pixel_ratio); + refresh_rates.push_back(display.refresh_rate); + } + std::shared_ptr dart_state = + update_displays_.dart_state().lock(); + if (!dart_state) { + return; + } + tonic::DartState::Scope scope(dart_state); + tonic::CheckAndHandleError(tonic::DartInvoke( + update_displays_.Get(), + { + tonic::ToDart>(ids), + tonic::ToDart>(widths), + tonic::ToDart>(heights), + tonic::ToDart>(device_pixel_ratios), + tonic::ToDart>(refresh_rates), + })); } void PlatformConfiguration::UpdateLocales( diff --git a/lib/ui/window/platform_configuration.h b/lib/ui/window/platform_configuration.h index 56189f4354bc6..c03f38194bf50 100644 --- a/lib/ui/window/platform_configuration.h +++ b/lib/ui/window/platform_configuration.h @@ -17,6 +17,7 @@ #include "flutter/lib/ui/window/pointer_data_packet.h" #include "flutter/lib/ui/window/viewport_metrics.h" #include "flutter/lib/ui/window/window.h" +#include "flutter/shell/common/display.h" #include "third_party/tonic/dart_persistent_value.h" #include "third_party/tonic/typed_data/dart_byte_data.h" @@ -268,10 +269,14 @@ class PlatformConfiguration final { void DidCreateIsolate(); //---------------------------------------------------------------------------- - /// @brief Update the specified locale data in the framework. + /// @brief Update the specified display data in the framework. /// - /// @deprecated The persistent isolate data must be used for this purpose - /// instead. + /// @param[in] displays The display data to send to Dart. + /// + void UpdateDisplays(const std::vector& displays); + + //---------------------------------------------------------------------------- + /// @brief Update the specified locale data in the framework. /// /// @param[in] locale_data The locale data. This should consist of groups of /// 4 strings, each group representing a single locale. @@ -281,9 +286,6 @@ class PlatformConfiguration final { //---------------------------------------------------------------------------- /// @brief Update the user settings data in the framework. /// - /// @deprecated The persistent isolate data must be used for this purpose - /// instead. - /// /// @param[in] data The user settings data. /// void UpdateUserSettingsData(const std::string& data); @@ -291,9 +293,6 @@ class PlatformConfiguration final { //---------------------------------------------------------------------------- /// @brief Updates the lifecycle state data in the framework. /// - /// @deprecated The persistent isolate data must be used for this purpose - /// instead. - /// /// @param[in] data The lifecycle state data. /// void UpdateInitialLifecycleState(const std::string& data); @@ -443,6 +442,7 @@ class PlatformConfiguration final { private: PlatformConfigurationClient* client_; tonic::DartPersistentValue on_error_; + tonic::DartPersistentValue update_displays_; tonic::DartPersistentValue update_locales_; tonic::DartPersistentValue update_user_settings_data_; tonic::DartPersistentValue update_initial_lifecycle_state_; diff --git a/lib/ui/window/platform_configuration_unittests.cc b/lib/ui/window/platform_configuration_unittests.cc index 9b971947f5e84..e89a5cc7d381c 100644 --- a/lib/ui/window/platform_configuration_unittests.cc +++ b/lib/ui/window/platform_configuration_unittests.cc @@ -73,7 +73,7 @@ TEST_F(ShellTest, PlatformConfigurationWindowMetricsUpdate) { ASSERT_NE(configuration->get_window(0), nullptr); configuration->get_window(0)->UpdateWindowMetrics( - ViewportMetrics{2.0, 10.0, 20.0, 22}); + ViewportMetrics{2.0, 10.0, 20.0, 22, 0}); ASSERT_EQ( configuration->get_window(0)->viewport_metrics().device_pixel_ratio, 2.0); diff --git a/lib/ui/window/viewport_metrics.cc b/lib/ui/window/viewport_metrics.cc index 1d61a6fc552a3..b4bdb2bbe4151 100644 --- a/lib/ui/window/viewport_metrics.cc +++ b/lib/ui/window/viewport_metrics.cc @@ -13,11 +13,13 @@ ViewportMetrics::ViewportMetrics() = default; ViewportMetrics::ViewportMetrics(double p_device_pixel_ratio, double p_physical_width, double p_physical_height, - double p_physical_touch_slop) + double p_physical_touch_slop, + size_t p_display_id) : device_pixel_ratio(p_device_pixel_ratio), physical_width(p_physical_width), physical_height(p_physical_height), - physical_touch_slop(p_physical_touch_slop) {} + physical_touch_slop(p_physical_touch_slop), + display_id(p_display_id) {} ViewportMetrics::ViewportMetrics( double p_device_pixel_ratio, @@ -38,7 +40,8 @@ ViewportMetrics::ViewportMetrics( double p_physical_touch_slop, const std::vector& p_physical_display_features_bounds, const std::vector& p_physical_display_features_type, - const std::vector& p_physical_display_features_state) + const std::vector& p_physical_display_features_state, + size_t p_display_id) : device_pixel_ratio(p_device_pixel_ratio), physical_width(p_physical_width), physical_height(p_physical_height), @@ -59,7 +62,8 @@ ViewportMetrics::ViewportMetrics( physical_touch_slop(p_physical_touch_slop), physical_display_features_bounds(p_physical_display_features_bounds), physical_display_features_type(p_physical_display_features_type), - physical_display_features_state(p_physical_display_features_state) {} + physical_display_features_state(p_physical_display_features_state), + display_id(p_display_id) {} bool operator==(const ViewportMetrics& a, const ViewportMetrics& b) { return a.device_pixel_ratio == b.device_pixel_ratio && @@ -85,7 +89,9 @@ bool operator==(const ViewportMetrics& a, const ViewportMetrics& b) { a.physical_display_features_bounds == b.physical_display_features_bounds && a.physical_display_features_type == b.physical_display_features_type && - a.physical_display_features_state == b.physical_display_features_state; + a.physical_display_features_state == + b.physical_display_features_state && + a.display_id == b.display_id; } std::ostream& operator<<(std::ostream& os, const ViewportMetrics& a) { @@ -101,7 +107,8 @@ std::ostream& operator<<(std::ostream& os, const ViewportMetrics& a) { << a.physical_system_gesture_inset_right << "R " << a.physical_system_gesture_inset_bottom << "B " << a.physical_system_gesture_inset_left << "L] " - << "Display Features: " << a.physical_display_features_type.size(); + << "Display Features: " << a.physical_display_features_type.size() << " " + << "Display ID: " << a.display_id; return os; } diff --git a/lib/ui/window/viewport_metrics.h b/lib/ui/window/viewport_metrics.h index 414a8b132d56d..13a0dee9439ea 100644 --- a/lib/ui/window/viewport_metrics.h +++ b/lib/ui/window/viewport_metrics.h @@ -15,7 +15,8 @@ struct ViewportMetrics { ViewportMetrics(double p_device_pixel_ratio, double p_physical_width, double p_physical_height, - double p_physical_touch_slop); + double p_physical_touch_slop, + size_t display_id); ViewportMetrics(double p_device_pixel_ratio, double p_physical_width, double p_physical_height, @@ -34,7 +35,8 @@ struct ViewportMetrics { double p_physical_touch_slop, const std::vector& p_physical_display_features_bounds, const std::vector& p_physical_display_features_type, - const std::vector& p_physical_display_features_state); + const std::vector& p_physical_display_features_state, + size_t p_display_id); double device_pixel_ratio = 1.0; double physical_width = 0; @@ -55,6 +57,7 @@ struct ViewportMetrics { std::vector physical_display_features_bounds; std::vector physical_display_features_type; std::vector physical_display_features_state; + size_t display_id = 0; }; bool operator==(const ViewportMetrics& a, const ViewportMetrics& b); diff --git a/lib/ui/window/window.cc b/lib/ui/window/window.cc index 80db1dc846efe..f3866f2ebc56a 100644 --- a/lib/ui/window/window.cc +++ b/lib/ui/window/window.cc @@ -52,6 +52,7 @@ void Window::UpdateWindowMetrics(const ViewportMetrics& metrics) { tonic::ToDart(metrics.physical_display_features_bounds), tonic::ToDart(metrics.physical_display_features_type), tonic::ToDart(metrics.physical_display_features_state), + tonic::ToDart(metrics.display_id), })); } diff --git a/lib/web_ui/lib/platform_dispatcher.dart b/lib/web_ui/lib/platform_dispatcher.dart index 75bc01ab38b61..2546404fbacce 100644 --- a/lib/web_ui/lib/platform_dispatcher.dart +++ b/lib/web_ui/lib/platform_dispatcher.dart @@ -29,6 +29,8 @@ abstract class PlatformDispatcher { VoidCallback? get onPlatformConfigurationChanged; set onPlatformConfigurationChanged(VoidCallback? callback); + Iterable get displays; + Iterable get views; FlutterView? get implicitView; diff --git a/lib/web_ui/lib/src/engine/dom.dart b/lib/web_ui/lib/src/engine/dom.dart index ca7056d05672b..90de2bda8d364 100644 --- a/lib/web_ui/lib/src/engine/dom.dart +++ b/lib/web_ui/lib/src/engine/dom.dart @@ -2822,6 +2822,9 @@ class DomScreen {} extension DomScreenExtension on DomScreen { external DomScreenOrientation? get orientation; + + external double get width; + external double get height; } @JS() diff --git a/lib/web_ui/lib/src/engine/platform_dispatcher.dart b/lib/web_ui/lib/src/engine/platform_dispatcher.dart index ad127afb6c184..b377363d4ac6b 100644 --- a/lib/web_ui/lib/src/engine/platform_dispatcher.dart +++ b/lib/web_ui/lib/src/engine/platform_dispatcher.dart @@ -134,6 +134,9 @@ class EnginePlatformDispatcher extends ui.PlatformDispatcher { _onPlatformConfigurationChanged, _onPlatformConfigurationChangedZone); } + @override + Iterable get displays => []; + /// The current list of windows. @override Iterable get views => viewData.values; diff --git a/lib/web_ui/lib/src/engine/window.dart b/lib/web_ui/lib/src/engine/window.dart index 218ddfea88077..903f6135a458e 100644 --- a/lib/web_ui/lib/src/engine/window.dart +++ b/lib/web_ui/lib/src/engine/window.dart @@ -45,6 +45,24 @@ set customUrlStrategy(ui_web.UrlStrategy? strategy) { _customUrlStrategy = strategy; } +class EngineFlutterDisplay extends ui.Display { + EngineFlutterDisplay({ + required this.id, + required this.devicePixelRatio, + required this.size, + required this.refreshRate, + }); + + @override + final int id; + @override + final double devicePixelRatio; + @override + final ui.Size size; + @override + final double refreshRate; +} + /// The Web implementation of [ui.SingletonFlutterWindow]. class EngineFlutterWindow extends ui.SingletonFlutterWindow { EngineFlutterWindow(this.viewId, this.platformDispatcher) { @@ -62,6 +80,16 @@ class EngineFlutterWindow extends ui.SingletonFlutterWindow { }); } + @override + ui.Display get display { + return EngineFlutterDisplay( + id: 0, + size: ui.Size(domWindow.screen?.width ?? 0, domWindow.screen?.height ?? 0), + devicePixelRatio: domWindow.devicePixelRatio, + refreshRate: 60, + ); + } + @override final Object viewId; diff --git a/lib/web_ui/lib/window.dart b/lib/web_ui/lib/window.dart index b53592964dd99..2cb8750205711 100644 --- a/lib/web_ui/lib/window.dart +++ b/lib/web_ui/lib/window.dart @@ -4,6 +4,13 @@ part of ui; +abstract class Display { + int get id; + double get devicePixelRatio; + Size get size; + double get refreshRate; +} + abstract class FlutterView { PlatformDispatcher get platformDispatcher; Object get viewId; @@ -16,6 +23,7 @@ abstract class FlutterView { ViewPadding get padding; GestureSettings get gestureSettings; List get displayFeatures; + Display get display; void render(Scene scene) => platformDispatcher.render(scene, this); void updateSemantics(SemanticsUpdate update) => platformDispatcher.updateSemantics(update); } diff --git a/runtime/BUILD.gn b/runtime/BUILD.gn index 48ef5882eca4b..70e9172d51dd3 100644 --- a/runtime/BUILD.gn +++ b/runtime/BUILD.gn @@ -104,6 +104,7 @@ source_set("runtime") { "//flutter/flow", "//flutter/fml", "//flutter/lib/io", + "//flutter/shell/common:display", "//flutter/third_party/tonic", "//flutter/third_party/txt", "//third_party/dart/runtime:dart_api", diff --git a/runtime/platform_data.h b/runtime/platform_data.h index 8b11b34ef6585..b6d4c65f94d19 100644 --- a/runtime/platform_data.h +++ b/runtime/platform_data.h @@ -10,6 +10,7 @@ #include #include "flutter/lib/ui/window/viewport_metrics.h" +#include "flutter/shell/common/display.h" namespace flutter { @@ -42,6 +43,7 @@ struct PlatformData { bool semantics_enabled = false; bool assistive_technology_enabled = false; int32_t accessibility_feature_flags_ = 0; + std::vector displays; }; } // namespace flutter diff --git a/runtime/runtime_controller.cc b/runtime/runtime_controller.cc index e384f96ec3aee..5ffd02d099139 100644 --- a/runtime/runtime_controller.cc +++ b/runtime/runtime_controller.cc @@ -121,7 +121,8 @@ bool RuntimeController::FlushRuntimeStateToIsolate() { SetAccessibilityFeatures( platform_data_.accessibility_feature_flags_) && SetUserSettingsData(platform_data_.user_settings_data) && - SetInitialLifecycleState(platform_data_.lifecycle_state); + SetInitialLifecycleState(platform_data_.lifecycle_state) && + SetDisplays(platform_data_.displays); } bool RuntimeController::SetViewportMetrics(const ViewportMetrics& metrics) { @@ -511,6 +512,17 @@ void RuntimeController::RequestDartDeferredLibrary(intptr_t loading_unit_id) { return client_.RequestDartDeferredLibrary(loading_unit_id); } +bool RuntimeController::SetDisplays(const std::vector& displays) { + TRACE_EVENT0("flutter", "SetDisplays"); + platform_data_.displays = displays; + + if (auto* platform_configuration = GetPlatformConfigurationIfAvailable()) { + platform_configuration->UpdateDisplays(displays); + return true; + } + return false; +} + RuntimeController::Locale::Locale(std::string language_code_, std::string country_code_, std::string script_code_, diff --git a/runtime/runtime_controller.h b/runtime/runtime_controller.h index b3fc828f8264c..1df085262f605 100644 --- a/runtime/runtime_controller.h +++ b/runtime/runtime_controller.h @@ -174,6 +174,14 @@ class RuntimeController : public PlatformConfigurationClient { /// bool SetViewportMetrics(const ViewportMetrics& metrics); + //---------------------------------------------------------------------------- + /// @brief Forward the specified display metrics to the running isolate. + /// If the isolate is not running, these metrics will be saved and + /// flushed to the isolate when it starts. + /// + /// @param[in] displays The available displays. + bool SetDisplays(const std::vector& displays); + //---------------------------------------------------------------------------- /// @brief Forward the specified locale data to the running isolate. If /// the isolate is not running, this data will be saved and diff --git a/shell/common/BUILD.gn b/shell/common/BUILD.gn index 1bff582d2b41f..19009938162fc 100644 --- a/shell/common/BUILD.gn +++ b/shell/common/BUILD.gn @@ -65,6 +65,18 @@ source_set("platform_message_handler") { deps = [ "//flutter/fml:fml" ] } +source_set("display") { + sources = [ + "display.cc", + "display.h", + "variable_refresh_rate_display.cc", + "variable_refresh_rate_display.h", + "variable_refresh_rate_reporter.h", + ] + public_configs = [ "//flutter:config" ] + deps = [ "//flutter/fml:fml" ] +} + source_set("common") { sources = [ "animator.cc", @@ -73,8 +85,6 @@ source_set("common") { "canvas_spy.h", "context_options.cc", "context_options.h", - "display.cc", - "display.h", "display_manager.cc", "display_manager.h", "dl_op_spy.cc", @@ -110,9 +120,6 @@ source_set("common") { "switches.h", "thread_host.cc", "thread_host.h", - "variable_refresh_rate_display.cc", - "variable_refresh_rate_display.h", - "variable_refresh_rate_reporter.h", "vsync_waiter.cc", "vsync_waiter.h", "vsync_waiter_fallback.cc", @@ -122,6 +129,7 @@ source_set("common") { public_configs = [ "//flutter:config" ] public_deps = [ + ":display", ":platform_message_handler", "//flutter/shell/version", "//flutter/third_party/tonic", diff --git a/shell/common/display.h b/shell/common/display.h index a4cd5aa0863e0..361e45bfc1207 100644 --- a/shell/common/display.h +++ b/shell/common/display.h @@ -14,11 +14,21 @@ namespace flutter { /// Unique ID per display that is stable until the Flutter application restarts. /// See also: `flutter::Display` -typedef uint64_t DisplayId; +typedef size_t DisplayId; /// To be used when the display refresh rate is unknown. static constexpr double kUnknownDisplayRefreshRate = 0; +/// The POD of a |Display|. This data is for a point in time and suitable +/// for copying. +struct DisplayData { + DisplayId id; + double width; + double height; + double pixel_ratio; + double refresh_rate; +}; + /// Display refers to a graphics hardware system consisting of a framebuffer, /// typically a monitor or a screen. This class holds the various display /// settings. @@ -26,18 +36,17 @@ class Display { public: //------------------------------------------------------------------------------ /// @brief Construct a new Display object in case where the display id of the - /// display is known. In cases where there is more than one display, every - /// display is expected to have a display id. - /// - Display(DisplayId display_id, double refresh_rate) - : display_id_(display_id), refresh_rate_(refresh_rate) {} - - //------------------------------------------------------------------------------ - /// @brief Construct a new Display object when there is only a single display. - /// When there are multiple displays, every display must have a display id. - /// - explicit Display(double refresh_rate) - : display_id_({}), refresh_rate_(refresh_rate) {} + /// display is known. + Display(DisplayId display_id, + double refresh_rate, + double width, + double height, + double device_pixel_ratio) + : display_id_(display_id), + refresh_rate_(refresh_rate), + width_(width), + height_(height), + device_pixel_ratio_(device_pixel_ratio) {} virtual ~Display() = default; @@ -46,11 +55,33 @@ class Display { virtual double GetRefreshRate() const; /// Returns the `DisplayId` of the display. - std::optional GetDisplayId() const { return display_id_; } + DisplayId GetDisplayId() const { return display_id_; } + + /// The width of the display in physical pixels. + virtual double GetWidth() const { return width_; } + + /// The height of the display in physical pixels. + virtual double GetHeight() const { return height_; } + + /// The device pixel ratio of the display. + virtual double GetDevicePixelRatio() const { return device_pixel_ratio_; } + + DisplayData GetDisplayData() { + return DisplayData{ + .id = GetDisplayId(), + .width = GetWidth(), + .height = GetHeight(), + .pixel_ratio = GetDevicePixelRatio(), + .refresh_rate = GetRefreshRate(), + }; + } private: - std::optional display_id_; + DisplayId display_id_; double refresh_rate_; + double width_; + double height_; + double device_pixel_ratio_; FML_DISALLOW_COPY_AND_ASSIGN(Display); }; diff --git a/shell/common/display_manager.cc b/shell/common/display_manager.cc index 75211308de727..e9382028dc626 100644 --- a/shell/common/display_manager.cc +++ b/shell/common/display_manager.cc @@ -23,28 +23,10 @@ double DisplayManager::GetMainDisplayRefreshRate() const { } void DisplayManager::HandleDisplayUpdates( - DisplayUpdateType update_type, std::vector> displays) { + FML_DCHECK(!displays.empty()); std::scoped_lock lock(displays_mutex_); - CheckDisplayConfiguration(displays); - switch (update_type) { - case DisplayUpdateType::kStartup: - FML_CHECK(displays_.empty()); - displays_ = std::move(displays); - return; - default: - FML_CHECK(false) << "Unknown DisplayUpdateType."; - } -} - -void DisplayManager::CheckDisplayConfiguration( - const std::vector>& displays) const { - FML_CHECK(!displays.empty()); - if (displays.size() > 1) { - for (auto& display : displays) { - FML_CHECK(display->GetDisplayId().has_value()); - } - } + displays_ = std::move(displays); } } // namespace flutter diff --git a/shell/common/display_manager.h b/shell/common/display_manager.h index 774373d54e70b..ae3e9d0a5607e 100644 --- a/shell/common/display_manager.h +++ b/shell/common/display_manager.h @@ -12,17 +12,6 @@ namespace flutter { -/// The update type parameter that is passed to -/// `DisplayManager::HandleDisplayUpdates`. -enum class DisplayUpdateType { - /// `flutter::Display`s that were active during start-up. A display is - /// considered active if: - /// 1. The frame buffer hardware is connected. - /// 2. The display is drawable, e.g. it isn't being mirrored from another - /// connected display or sleeping. - kStartup -}; - /// Manages lifecycle of the connected displays. This class is thread-safe. class DisplayManager { public: @@ -39,20 +28,12 @@ class DisplayManager { double GetMainDisplayRefreshRate() const; /// Handles the display updates. - void HandleDisplayUpdates(DisplayUpdateType update_type, - std::vector> displays); + void HandleDisplayUpdates(std::vector> displays); private: /// Guards `displays_` vector. mutable std::mutex displays_mutex_; std::vector> displays_; - - /// Checks that the provided display configuration is valid. Currently this - /// ensures that all the displays have an id in the case there are multiple - /// displays. In case where there is a single display, it is valid for the - /// display to not have an id. - void CheckDisplayConfiguration( - const std::vector>& displays) const; }; } // namespace flutter diff --git a/shell/common/engine.cc b/shell/common/engine.cc index e63a19352f095..9107be5bacb92 100644 --- a/shell/common/engine.cc +++ b/shell/common/engine.cc @@ -583,4 +583,9 @@ const std::weak_ptr Engine::GetVsyncWaiter() const { return animator_->GetVsyncWaiter(); } +void Engine::SetDisplays(const std::vector& displays) { + runtime_controller_->SetDisplays(displays); + ScheduleFrame(); +} + } // namespace flutter diff --git a/shell/common/engine.h b/shell/common/engine.h index 04bbae3e7c1b9..d4b4340658eae 100644 --- a/shell/common/engine.h +++ b/shell/common/engine.h @@ -685,6 +685,14 @@ class Engine final : public RuntimeDelegate, PointerDataDispatcher::Delegate { /// void SetViewportMetrics(const ViewportMetrics& metrics); + //---------------------------------------------------------------------------- + /// @brief Updates the display metrics for the currently running Flutter + /// application. + /// + /// @param[in] displays A complete list of displays + /// + void SetDisplays(const std::vector& displays); + //---------------------------------------------------------------------------- /// @brief Notifies the engine that the embedder has sent it a message. /// This call originates in the platform view and has been diff --git a/shell/common/shell.cc b/shell/common/shell.cc index 214b3b384569c..3fb24ae421a8b 100644 --- a/shell/common/shell.cc +++ b/shell/common/shell.cc @@ -2071,9 +2071,23 @@ void Shell::SetGpuAvailability(GpuAvailability availability) { } } -void Shell::OnDisplayUpdates(DisplayUpdateType update_type, - std::vector> displays) { - display_manager_->HandleDisplayUpdates(update_type, std::move(displays)); +void Shell::OnDisplayUpdates(std::vector> displays) { + FML_DCHECK(is_setup_); + FML_DCHECK(task_runners_.GetPlatformTaskRunner()->RunsTasksOnCurrentThread()); + + std::vector display_data; + for (const auto& display : displays) { + display_data.push_back(display->GetDisplayData()); + } + task_runners_.GetUITaskRunner()->PostTask( + [engine = engine_->GetWeakPtr(), + display_data = std::move(display_data)]() { + if (engine) { + engine->SetDisplays(display_data); + } + }); + + display_manager_->HandleDisplayUpdates(std::move(displays)); } fml::TimePoint Shell::GetCurrentTimePoint() { @@ -2086,6 +2100,9 @@ Shell::GetPlatformMessageHandler() const { } const std::weak_ptr Shell::GetVsyncWaiter() const { + if (!engine_) { + return {}; + } return engine_->GetVsyncWaiter(); } diff --git a/shell/common/shell.h b/shell/common/shell.h index 2ecf71dac6559..b0c474c722281 100644 --- a/shell/common/shell.h +++ b/shell/common/shell.h @@ -374,8 +374,7 @@ class Shell final : public PlatformView::Delegate, //---------------------------------------------------------------------------- /// @brief Notifies the display manager of the updates. /// - void OnDisplayUpdates(DisplayUpdateType update_type, - std::vector> displays); + void OnDisplayUpdates(std::vector> displays); //---------------------------------------------------------------------------- /// @brief Queries the `DisplayManager` for the main display refresh rate. diff --git a/shell/common/shell_test.cc b/shell/common/shell_test.cc index 6cef594385a6c..a85a58e15b7f5 100644 --- a/shell/common/shell_test.cc +++ b/shell/common/shell_test.cc @@ -129,7 +129,8 @@ void ShellTest::SetViewportMetrics(Shell* shell, double width, double height) { 22, // physical touch slop std::vector(), // display features bounds std::vector(), // display features type - std::vector() // display features state + std::vector(), // display features state + 0 // Display ID }; // Set viewport to nonempty, and call Animator::BeginFrame to make the layer // tree pipeline nonempty. Without either of this, the layer tree below @@ -168,7 +169,7 @@ void ShellTest::PumpOneFrame(Shell* shell, double width, double height, LayerTreeBuilder builder) { - PumpOneFrame(shell, {1.0, width, height, 22}, std::move(builder)); + PumpOneFrame(shell, {1.0, width, height, 22, 0}, std::move(builder)); } void ShellTest::PumpOneFrame(Shell* shell, diff --git a/shell/common/shell_unittests.cc b/shell/common/shell_unittests.cc index 60c119ac967ec..2584e6cff0e12 100644 --- a/shell/common/shell_unittests.cc +++ b/shell/common/shell_unittests.cc @@ -1450,7 +1450,7 @@ TEST_F(ShellTest, WaitForFirstFrameZeroSizeFrame) { configuration.SetEntrypoint("emptyMain"); RunEngine(shell.get(), std::move(configuration)); - PumpOneFrame(shell.get(), {1.0, 0.0, 0.0, 22}); + PumpOneFrame(shell.get(), {1.0, 0.0, 0.0, 22, 0}); fml::Status result = shell->WaitForFirstFrame(fml::TimeDelta::Zero()); ASSERT_FALSE(result.ok()); ASSERT_EQ(result.code(), fml::StatusCode::kDeadlineExceeded); @@ -1584,7 +1584,7 @@ TEST_F(ShellTest, MultipleFluttersSetResourceCacheBytes) { RunEngine(shell.get(), std::move(configuration)); PostSync(shell->GetTaskRunners().GetPlatformTaskRunner(), [&shell]() { - shell->GetPlatformView()->SetViewportMetrics({1.0, 100, 100, 22}); + shell->GetPlatformView()->SetViewportMetrics({1.0, 100, 100, 22, 0}); }); // first cache bytes @@ -1613,7 +1613,7 @@ TEST_F(ShellTest, MultipleFluttersSetResourceCacheBytes) { PostSync(second_shell->GetTaskRunners().GetPlatformTaskRunner(), [&second_shell]() { second_shell->GetPlatformView()->SetViewportMetrics( - {1.0, 100, 100, 22}); + {1.0, 100, 100, 22, 0}); }); // first cache bytes + second cache bytes EXPECT_EQ(GetRasterizerResourceCacheBytesSync(*shell), @@ -1622,7 +1622,7 @@ TEST_F(ShellTest, MultipleFluttersSetResourceCacheBytes) { PostSync(second_shell->GetTaskRunners().GetPlatformTaskRunner(), [&second_shell]() { second_shell->GetPlatformView()->SetViewportMetrics( - {1.0, 100, 300, 22}); + {1.0, 100, 300, 22, 0}); }); // first cache bytes + second cache bytes EXPECT_EQ(GetRasterizerResourceCacheBytesSync(*shell), @@ -1630,18 +1630,20 @@ TEST_F(ShellTest, MultipleFluttersSetResourceCacheBytes) { std::unique_ptr third_shell = shell_spawn_callback(); PlatformViewNotifyCreated(third_shell.get()); - PostSync( - third_shell->GetTaskRunners().GetPlatformTaskRunner(), [&third_shell]() { - third_shell->GetPlatformView()->SetViewportMetrics({1.0, 400, 100, 22}); - }); + PostSync(third_shell->GetTaskRunners().GetPlatformTaskRunner(), + [&third_shell]() { + third_shell->GetPlatformView()->SetViewportMetrics( + {1.0, 400, 100, 22, 0}); + }); // first cache bytes + second cache bytes + third cache bytes EXPECT_EQ(GetRasterizerResourceCacheBytesSync(*shell), static_cast(3840000U)); - PostSync( - third_shell->GetTaskRunners().GetPlatformTaskRunner(), [&third_shell]() { - third_shell->GetPlatformView()->SetViewportMetrics({1.0, 800, 100, 22}); - }); + PostSync(third_shell->GetTaskRunners().GetPlatformTaskRunner(), + [&third_shell]() { + third_shell->GetPlatformView()->SetViewportMetrics( + {1.0, 800, 100, 22, 0}); + }); // max bytes threshold EXPECT_EQ(GetRasterizerResourceCacheBytesSync(*shell), static_cast(4000000U)); @@ -1653,7 +1655,7 @@ TEST_F(ShellTest, MultipleFluttersSetResourceCacheBytes) { PostSync(second_shell->GetTaskRunners().GetPlatformTaskRunner(), [&second_shell]() { second_shell->GetPlatformView()->SetViewportMetrics( - {1.0, 100, 100, 22}); + {1.0, 100, 100, 22, 0}); }); // first cache bytes + second cache bytes EXPECT_EQ(GetRasterizerResourceCacheBytesSync(*shell), @@ -1697,7 +1699,7 @@ TEST_F(ShellTest, SetResourceCacheSize) { fml::TaskRunner::RunNowOrPostTask( shell->GetTaskRunners().GetPlatformTaskRunner(), [&shell]() { - shell->GetPlatformView()->SetViewportMetrics({1.0, 400, 200, 22}); + shell->GetPlatformView()->SetViewportMetrics({1.0, 400, 200, 22, 0}); }); PumpOneFrame(shell.get()); @@ -1717,7 +1719,7 @@ TEST_F(ShellTest, SetResourceCacheSize) { fml::TaskRunner::RunNowOrPostTask( shell->GetTaskRunners().GetPlatformTaskRunner(), [&shell]() { - shell->GetPlatformView()->SetViewportMetrics({1.0, 800, 400, 22}); + shell->GetPlatformView()->SetViewportMetrics({1.0, 800, 400, 22, 0}); }); PumpOneFrame(shell.get()); @@ -1734,7 +1736,7 @@ TEST_F(ShellTest, SetResourceCacheSizeEarly) { fml::TaskRunner::RunNowOrPostTask( shell->GetTaskRunners().GetPlatformTaskRunner(), [&shell]() { - shell->GetPlatformView()->SetViewportMetrics({1.0, 400, 200, 22}); + shell->GetPlatformView()->SetViewportMetrics({1.0, 400, 200, 22, 0}); }); PumpOneFrame(shell.get()); @@ -1761,7 +1763,7 @@ TEST_F(ShellTest, SetResourceCacheSizeNotifiesDart) { fml::TaskRunner::RunNowOrPostTask( shell->GetTaskRunners().GetPlatformTaskRunner(), [&shell]() { - shell->GetPlatformView()->SetViewportMetrics({1.0, 400, 200, 22}); + shell->GetPlatformView()->SetViewportMetrics({1.0, 400, 200, 22, 0}); }); PumpOneFrame(shell.get()); @@ -2625,7 +2627,7 @@ TEST_F(ShellTest, DISABLED_DiscardLayerTreeOnResize) { [&shell, &expected_size]() { shell->GetPlatformView()->SetViewportMetrics( {1.0, static_cast(expected_size.width()), - static_cast(expected_size.height()), 22}); + static_cast(expected_size.height()), 22, 0}); }); auto configuration = RunConfiguration::InferFromSettings(settings); @@ -2698,7 +2700,7 @@ TEST_F(ShellTest, DISABLED_DiscardResubmittedLayerTreeOnResize) { [&shell, &origin_size]() { shell->GetPlatformView()->SetViewportMetrics( {1.0, static_cast(origin_size.width()), - static_cast(origin_size.height()), 22}); + static_cast(origin_size.height()), 22, 0}); }); auto configuration = RunConfiguration::InferFromSettings(settings); @@ -2717,7 +2719,7 @@ TEST_F(ShellTest, DISABLED_DiscardResubmittedLayerTreeOnResize) { [&shell, &new_size, &resize_latch]() { shell->GetPlatformView()->SetViewportMetrics( {1.0, static_cast(new_size.width()), - static_cast(new_size.height()), 22}); + static_cast(new_size.height()), 22, 0}); resize_latch.Signal(); }); @@ -2780,13 +2782,14 @@ TEST_F(ShellTest, IgnoresInvalidMetrics) { RunEngine(shell.get(), std::move(configuration)); task_runner->PostTask([&]() { - shell->GetPlatformView()->SetViewportMetrics({0.0, 400, 200, 22}); + shell->GetPlatformView()->SetViewportMetrics({0.0, 400, 200, 22, 0}); task_runner->PostTask([&]() { - shell->GetPlatformView()->SetViewportMetrics({0.8, 0.0, 200, 22}); + shell->GetPlatformView()->SetViewportMetrics({0.8, 0.0, 200, 22, 0}); task_runner->PostTask([&]() { - shell->GetPlatformView()->SetViewportMetrics({0.8, 400, 0.0, 22}); + shell->GetPlatformView()->SetViewportMetrics({0.8, 400, 0.0, 22, 0}); task_runner->PostTask([&]() { - shell->GetPlatformView()->SetViewportMetrics({0.8, 400, 200.0, 22}); + shell->GetPlatformView()->SetViewportMetrics( + {0.8, 400, 200.0, 22, 0}); }); }); }); @@ -2798,7 +2801,7 @@ TEST_F(ShellTest, IgnoresInvalidMetrics) { latch.Reset(); task_runner->PostTask([&]() { - shell->GetPlatformView()->SetViewportMetrics({1.2, 600, 300, 22}); + shell->GetPlatformView()->SetViewportMetrics({1.2, 600, 300, 22, 0}); }); latch.Wait(); ASSERT_EQ(last_device_pixel_ratio, 1.2); diff --git a/shell/common/variable_refresh_rate_display.cc b/shell/common/variable_refresh_rate_display.cc index d51af1a08a724..138fc0edb6ad5 100644 --- a/shell/common/variable_refresh_rate_display.cc +++ b/shell/common/variable_refresh_rate_display.cc @@ -18,13 +18,15 @@ namespace flutter { VariableRefreshRateDisplay::VariableRefreshRateDisplay( DisplayId display_id, - const std::weak_ptr& refresh_rate_reporter) - : Display(display_id, GetInitialRefreshRate(refresh_rate_reporter)), - refresh_rate_reporter_(refresh_rate_reporter) {} - -VariableRefreshRateDisplay::VariableRefreshRateDisplay( - const std::weak_ptr& refresh_rate_reporter) - : Display(GetInitialRefreshRate(refresh_rate_reporter)), + const std::weak_ptr& refresh_rate_reporter, + double width, + double height, + double device_pixel_ratio) + : Display(display_id, + GetInitialRefreshRate(refresh_rate_reporter), + width, + height, + device_pixel_ratio), refresh_rate_reporter_(refresh_rate_reporter) {} double VariableRefreshRateDisplay::GetRefreshRate() const { diff --git a/shell/common/variable_refresh_rate_display.h b/shell/common/variable_refresh_rate_display.h index 38cb979c36926..e0dcd681bf571 100644 --- a/shell/common/variable_refresh_rate_display.h +++ b/shell/common/variable_refresh_rate_display.h @@ -18,9 +18,10 @@ class VariableRefreshRateDisplay : public Display { public: explicit VariableRefreshRateDisplay( DisplayId display_id, - const std::weak_ptr& refresh_rate_reporter); - explicit VariableRefreshRateDisplay( - const std::weak_ptr& refresh_rate_reporter); + const std::weak_ptr& refresh_rate_reporter, + double width, + double height, + double device_pixel_ratio); ~VariableRefreshRateDisplay() = default; // |Display| diff --git a/shell/common/variable_refresh_rate_display_unittests.cc b/shell/common/variable_refresh_rate_display_unittests.cc index e65641264d8e1..d9725cf815daf 100644 --- a/shell/common/variable_refresh_rate_display_unittests.cc +++ b/shell/common/variable_refresh_rate_display_unittests.cc @@ -13,14 +13,16 @@ namespace testing { TEST(VariableRefreshRateDisplayTest, ReportCorrectInitialRefreshRate) { auto refresh_rate_reporter = std::make_shared(60); auto display = flutter::VariableRefreshRateDisplay( - std::weak_ptr(refresh_rate_reporter)); + 0, std::weak_ptr(refresh_rate_reporter), 600, + 800, 60); ASSERT_EQ(display.GetRefreshRate(), 60); } TEST(VariableRefreshRateDisplayTest, ReportCorrectRefreshRateWhenUpdated) { auto refresh_rate_reporter = std::make_shared(60); auto display = flutter::VariableRefreshRateDisplay( - std::weak_ptr(refresh_rate_reporter)); + 0, std::weak_ptr(refresh_rate_reporter), 600, + 800, 60); refresh_rate_reporter->UpdateRefreshRate(30); ASSERT_EQ(display.GetRefreshRate(), 30); } @@ -29,7 +31,8 @@ TEST(VariableRefreshRateDisplayTest, Report0IfReporterSharedPointerIsDestroyedAfterDisplayCreation) { auto refresh_rate_reporter = std::make_shared(60); auto display = flutter::VariableRefreshRateDisplay( - std::weak_ptr(refresh_rate_reporter)); + 0, std::weak_ptr(refresh_rate_reporter), 600, + 800, 60); refresh_rate_reporter.reset(); ASSERT_EQ(display.GetRefreshRate(), 0); } @@ -39,7 +42,8 @@ TEST(VariableRefreshRateDisplayTest, auto refresh_rate_reporter = std::make_shared(60); refresh_rate_reporter.reset(); auto display = flutter::VariableRefreshRateDisplay( - std::weak_ptr(refresh_rate_reporter)); + 0, std::weak_ptr(refresh_rate_reporter), 600, + 800, 60); ASSERT_EQ(display.GetRefreshRate(), 0); } diff --git a/shell/platform/android/android_context_gl_unittests.cc b/shell/platform/android/android_context_gl_unittests.cc index bf1c7a78407ec..200b7eb8ff6a5 100644 --- a/shell/platform/android/android_context_gl_unittests.cc +++ b/shell/platform/android/android_context_gl_unittests.cc @@ -58,6 +58,9 @@ class MockPlatformViewAndroidJNI : public PlatformViewAndroidJNI { std::unique_ptr>( std::vector supported_locales_data)); MOCK_METHOD0(GetDisplayRefreshRate, double()); + MOCK_METHOD0(GetDisplayWidth, double()); + MOCK_METHOD0(GetDisplayHeight, double()); + MOCK_METHOD0(GetDisplayDensity, double()); MOCK_METHOD1(RequestDartDeferredLibrary, bool(int loading_unit_id)); }; diff --git a/shell/platform/android/android_display.cc b/shell/platform/android/android_display.cc index edd378bff15d6..9547341957b67 100644 --- a/shell/platform/android/android_display.cc +++ b/shell/platform/android/android_display.cc @@ -3,17 +3,32 @@ // found in the LICENSE file. #include "flutter/shell/platform/android/android_display.h" -#include "android_display.h" namespace flutter { AndroidDisplay::AndroidDisplay( std::shared_ptr jni_facade) - : Display(jni_facade->GetDisplayRefreshRate()), + : Display(0, + jni_facade->GetDisplayRefreshRate(), + jni_facade->GetDisplayWidth(), + jni_facade->GetDisplayHeight(), + jni_facade->GetDisplayDensity()), jni_facade_(std::move(jni_facade)) {} double AndroidDisplay::GetRefreshRate() const { return jni_facade_->GetDisplayRefreshRate(); } +double AndroidDisplay::GetWidth() const { + return jni_facade_->GetDisplayWidth(); +} + +double AndroidDisplay::GetHeight() const { + return jni_facade_->GetDisplayHeight(); +} + +double AndroidDisplay::GetDevicePixelRatio() const { + return jni_facade_->GetDisplayDensity(); +} + } // namespace flutter diff --git a/shell/platform/android/android_display.h b/shell/platform/android/android_display.h index 524779af2f067..495077b1b19bf 100644 --- a/shell/platform/android/android_display.h +++ b/shell/platform/android/android_display.h @@ -16,12 +16,21 @@ namespace flutter { /// A |Display| that listens to refresh rate changes. class AndroidDisplay : public Display { public: - explicit AndroidDisplay(std::shared_ptr jni_facade); + AndroidDisplay(std::shared_ptr jni_facade); ~AndroidDisplay() = default; // |Display| double GetRefreshRate() const override; + // |Display| + virtual double GetWidth() const override; + + // |Display| + virtual double GetHeight() const override; + + // |Display| + virtual double GetDevicePixelRatio() const override; + private: std::shared_ptr jni_facade_; diff --git a/shell/platform/android/android_shell_holder.cc b/shell/platform/android/android_shell_holder.cc index 9a11a97c4513b..becc6861a6060 100644 --- a/shell/platform/android/android_shell_holder.cc +++ b/shell/platform/android/android_shell_holder.cc @@ -117,10 +117,6 @@ AndroidShellHolder::AndroidShellHolder( shell.GetSettings().msaa_samples // msaa sample count ); weak_platform_view = platform_view_android->GetWeakPtr(); - std::vector> displays; - displays.push_back(std::make_unique(jni_facade)); - shell.OnDisplayUpdates(DisplayUpdateType::kStartup, - std::move(displays)); return platform_view_android; }; @@ -248,10 +244,6 @@ std::unique_ptr AndroidShellHolder::Spawn( android_context // Android context ); weak_platform_view = platform_view_android->GetWeakPtr(); - std::vector> displays; - displays.push_back(std::make_unique(jni_facade)); - shell.OnDisplayUpdates(DisplayUpdateType::kStartup, - std::move(displays)); return platform_view_android; }; @@ -291,6 +283,7 @@ void AndroidShellHolder::Launch( if (!config) { return; } + UpdateDisplayMetrics(); shell_->RunEngine(std::move(config.value())); } @@ -348,4 +341,10 @@ std::optional AndroidShellHolder::BuildRunConfiguration( return config; } +void AndroidShellHolder::UpdateDisplayMetrics() { + std::vector> displays; + displays.push_back(std::make_unique(jni_facade_)); + shell_->OnDisplayUpdates(std::move(displays)); +} + } // namespace flutter diff --git a/shell/platform/android/android_shell_holder.h b/shell/platform/android/android_shell_holder.h index 06866862f6bf5..30530a896204b 100644 --- a/shell/platform/android/android_shell_holder.h +++ b/shell/platform/android/android_shell_holder.h @@ -103,6 +103,8 @@ class AndroidShellHolder { return shell_->GetPlatformMessageHandler(); } + void UpdateDisplayMetrics(); + private: const flutter::Settings settings_; const std::shared_ptr jni_facade_; diff --git a/shell/platform/android/android_shell_holder_unittests.cc b/shell/platform/android/android_shell_holder_unittests.cc index 85cb352e005f9..fa138c920371b 100644 --- a/shell/platform/android/android_shell_holder_unittests.cc +++ b/shell/platform/android/android_shell_holder_unittests.cc @@ -50,6 +50,9 @@ class MockPlatformViewAndroidJNI : public PlatformViewAndroidJNI { std::unique_ptr>( std::vector supported_locales_data)); MOCK_METHOD0(GetDisplayRefreshRate, double()); + MOCK_METHOD0(GetDisplayWidth, double()); + MOCK_METHOD0(GetDisplayHeight, double()); + MOCK_METHOD0(GetDisplayDensity, double()); MOCK_METHOD1(RequestDartDeferredLibrary, bool(int loading_unit_id)); }; diff --git a/shell/platform/android/io/flutter/embedding/android/FlutterView.java b/shell/platform/android/io/flutter/embedding/android/FlutterView.java index 9bd0022fe356f..45fae9f96456c 100644 --- a/shell/platform/android/io/flutter/embedding/android/FlutterView.java +++ b/shell/platform/android/io/flutter/embedding/android/FlutterView.java @@ -451,6 +451,8 @@ protected void onConfigurationChanged(@NonNull Configuration newConfig) { Log.v(TAG, "Configuration changed. Sending locales and user settings to Flutter."); localizationPlugin.sendLocalesToFlutter(newConfig); sendUserSettingsToFlutter(); + + ViewUtils.calculateMaximumDisplayMetrics(getContext(), flutterEngine); } } diff --git a/shell/platform/android/io/flutter/embedding/engine/FlutterEngine.java b/shell/platform/android/io/flutter/embedding/engine/FlutterEngine.java index bc2902c0281a9..a3a463be04553 100644 --- a/shell/platform/android/io/flutter/embedding/engine/FlutterEngine.java +++ b/shell/platform/android/io/flutter/embedding/engine/FlutterEngine.java @@ -38,6 +38,7 @@ import io.flutter.embedding.engine.systemchannels.TextInputChannel; import io.flutter.plugin.localization.LocalizationPlugin; import io.flutter.plugin.platform.PlatformViewsController; +import io.flutter.util.ViewUtils; import java.util.HashSet; import java.util.List; import java.util.Set; @@ -76,7 +77,7 @@ * {@link DartExecutor} is run. Each Isolate is a self-contained Dart environment and cannot * communicate with each other except via Isolate ports. */ -public class FlutterEngine { +public class FlutterEngine implements ViewUtils.DisplayUpdater { private static final String TAG = "FlutterEngine"; @NonNull private final FlutterJNI flutterJNI; @@ -379,6 +380,8 @@ public FlutterEngine( if (automaticallyRegisterPlugins && flutterLoader.automaticallyRegisterPlugins()) { GeneratedPluginRegister.registerGeneratedPlugins(this); } + + ViewUtils.calculateMaximumDisplayMetrics(context, this); } private void attachToJni() { @@ -645,4 +648,9 @@ public interface EngineLifecycleListener { */ void onEngineWillDestroy(); } + + @Override + public void updateDisplayMetrics(float width, float height, float density) { + flutterJNI.updateDisplayMetrics(0 /* display ID */, width, height, density); + } } diff --git a/shell/platform/android/io/flutter/embedding/engine/FlutterJNI.java b/shell/platform/android/io/flutter/embedding/engine/FlutterJNI.java index 3a20807bdf1f0..4471d73f0f098 100644 --- a/shell/platform/android/io/flutter/embedding/engine/FlutterJNI.java +++ b/shell/platform/android/io/flutter/embedding/engine/FlutterJNI.java @@ -214,6 +214,10 @@ public void init( */ private static float refreshRateFPS = 60.0f; + private static float displayWidth = -1.0f; + private static float displayHeight = -1.0f; + private static float displayDensity = -1.0f; + // This is set from native code via JNI. @Nullable private static String vmServiceUri; @@ -274,6 +278,18 @@ public void setRefreshRateFPS(float refreshRateFPS) { updateRefreshRate(); } + public void updateDisplayMetrics(int displayId, float width, float height, float density) { + FlutterJNI.displayWidth = width; + FlutterJNI.displayHeight = height; + FlutterJNI.displayDensity = density; + if (!FlutterJNI.loadLibraryCalled) { + return; + } + nativeUpdateDisplayMetrics(nativeShellHolderId); + } + + private native void nativeUpdateDisplayMetrics(long nativeShellHolderId); + public void updateRefreshRate() { if (!FlutterJNI.loadLibraryCalled) { return; diff --git a/shell/platform/android/io/flutter/util/ViewUtils.java b/shell/platform/android/io/flutter/util/ViewUtils.java index 5baaca2ba549d..7915f28ff427d 100644 --- a/shell/platform/android/io/flutter/util/ViewUtils.java +++ b/shell/platform/android/io/flutter/util/ViewUtils.java @@ -12,8 +12,32 @@ import android.view.ViewGroup; import androidx.annotation.NonNull; import androidx.annotation.Nullable; +import androidx.window.layout.WindowMetrics; +import androidx.window.layout.WindowMetricsCalculator; public final class ViewUtils { + public interface DisplayUpdater { + /** Publishes display metrics to Dart code in Flutter. */ + public void updateDisplayMetrics(float width, float height, float density); + } + + /** + * Calculates the maximum display metrics for the given context, and pushes the metric data to the + * updater. + */ + public static void calculateMaximumDisplayMetrics( + @Nullable Context context, @NonNull DisplayUpdater updater) { + Activity activity = getActivity(context); + if (activity != null) { + WindowMetrics metrics = + WindowMetricsCalculator.getOrCreate().computeMaximumWindowMetrics(activity); + float width = metrics.getBounds().width(); + float height = metrics.getBounds().height(); + float density = context.getResources().getDisplayMetrics().density; + updater.updateDisplayMetrics(width, height, density); + } + } + /** * Retrieves the {@link Activity} from a given {@link Context}. * diff --git a/shell/platform/android/jni/jni_mock.h b/shell/platform/android/jni/jni_mock.h index aaa52b80d7bd4..b2966e6e67156 100644 --- a/shell/platform/android/jni/jni_mock.h +++ b/shell/platform/android/jni/jni_mock.h @@ -98,6 +98,9 @@ class JNIMock final : public PlatformViewAndroidJNI { (override)); MOCK_METHOD(double, GetDisplayRefreshRate, (), (override)); + MOCK_METHOD(double, GetDisplayWidth, (), (override)); + MOCK_METHOD(double, GetDisplayHeight, (), (override)); + MOCK_METHOD(double, GetDisplayDensity, (), (override)); MOCK_METHOD(bool, RequestDartDeferredLibrary, diff --git a/shell/platform/android/jni/platform_view_android_jni.h b/shell/platform/android/jni/platform_view_android_jni.h index 8e38a385fe771..35e949f0bc9dd 100644 --- a/shell/platform/android/jni/platform_view_android_jni.h +++ b/shell/platform/android/jni/platform_view_android_jni.h @@ -195,6 +195,12 @@ class PlatformViewAndroidJNI { virtual double GetDisplayRefreshRate() = 0; + virtual double GetDisplayWidth() = 0; + + virtual double GetDisplayHeight() = 0; + + virtual double GetDisplayDensity() = 0; + virtual bool RequestDartDeferredLibrary(int loading_unit_id) = 0; }; diff --git a/shell/platform/android/platform_view_android_jni_impl.cc b/shell/platform/android/platform_view_android_jni_impl.cc index 031ec1057eb22..c4580bc0562d7 100644 --- a/shell/platform/android/platform_view_android_jni_impl.cc +++ b/shell/platform/android/platform_view_android_jni_impl.cc @@ -335,11 +335,18 @@ static void SetViewportMetrics(JNIEnv* env, displayFeaturesBounds, displayFeaturesType, displayFeaturesState, + 0, // Display ID }; ANDROID_SHELL_HOLDER->GetPlatformView()->SetViewportMetrics(metrics); } +static void UpdateDisplayMetrics(JNIEnv* env, + jobject jcaller, + jlong shell_holder) { + ANDROID_SHELL_HOLDER->UpdateDisplayMetrics(); +} + static jobject GetBitmap(JNIEnv* env, jobject jcaller, jlong shell_holder) { auto screenshot = ANDROID_SHELL_HOLDER->Screenshot( Rasterizer::ScreenshotType::UncompressedImage, false); @@ -795,6 +802,11 @@ bool RegisterApi(JNIEnv* env) { .signature = "(ILjava/lang/String;Z)V", .fnPtr = reinterpret_cast(&DeferredComponentInstallFailure), }, + { + .name = "nativeUpdateDisplayMetrics", + .signature = "(J)V", + .fnPtr = reinterpret_cast(&UpdateDisplayMetrics), + }, }; if (env->RegisterNatives(g_flutter_jni_class->obj(), flutter_jni_methods, @@ -1622,6 +1634,60 @@ double PlatformViewAndroidJNIImpl::GetDisplayRefreshRate() { return static_cast(env->GetStaticFloatField(clazz.obj(), fid)); } +double PlatformViewAndroidJNIImpl::GetDisplayWidth() { + JNIEnv* env = fml::jni::AttachCurrentThread(); + + auto java_object = java_object_.get(env); + if (java_object.is_null()) { + return -1; + } + + fml::jni::ScopedJavaLocalRef clazz( + env, env->GetObjectClass(java_object.obj())); + if (clazz.is_null()) { + return -1; + } + + jfieldID fid = env->GetStaticFieldID(clazz.obj(), "displayWidth", "F"); + return static_cast(env->GetStaticFloatField(clazz.obj(), fid)); +} + +double PlatformViewAndroidJNIImpl::GetDisplayHeight() { + JNIEnv* env = fml::jni::AttachCurrentThread(); + + auto java_object = java_object_.get(env); + if (java_object.is_null()) { + return -1; + } + + fml::jni::ScopedJavaLocalRef clazz( + env, env->GetObjectClass(java_object.obj())); + if (clazz.is_null()) { + return -1; + } + + jfieldID fid = env->GetStaticFieldID(clazz.obj(), "displayHeight", "F"); + return static_cast(env->GetStaticFloatField(clazz.obj(), fid)); +} + +double PlatformViewAndroidJNIImpl::GetDisplayDensity() { + JNIEnv* env = fml::jni::AttachCurrentThread(); + + auto java_object = java_object_.get(env); + if (java_object.is_null()) { + return -1; + } + + fml::jni::ScopedJavaLocalRef clazz( + env, env->GetObjectClass(java_object.obj())); + if (clazz.is_null()) { + return -1; + } + + jfieldID fid = env->GetStaticFieldID(clazz.obj(), "displayDensity", "F"); + return static_cast(env->GetStaticFloatField(clazz.obj(), fid)); +} + bool PlatformViewAndroidJNIImpl::RequestDartDeferredLibrary( int loading_unit_id) { JNIEnv* env = fml::jni::AttachCurrentThread(); diff --git a/shell/platform/android/platform_view_android_jni_impl.h b/shell/platform/android/platform_view_android_jni_impl.h index b3df5358291fa..6b1e283e812d0 100644 --- a/shell/platform/android/platform_view_android_jni_impl.h +++ b/shell/platform/android/platform_view_android_jni_impl.h @@ -82,6 +82,12 @@ class PlatformViewAndroidJNIImpl final : public PlatformViewAndroidJNI { double GetDisplayRefreshRate() override; + double GetDisplayWidth() override; + + double GetDisplayHeight() override; + + double GetDisplayDensity() override; + bool RequestDartDeferredLibrary(int loading_unit_id) override; private: diff --git a/shell/platform/android/test/io/flutter/embedding/android/FlutterViewTest.java b/shell/platform/android/test/io/flutter/embedding/android/FlutterViewTest.java index 41337f0c30b3d..5371e05b58bbf 100644 --- a/shell/platform/android/test/io/flutter/embedding/android/FlutterViewTest.java +++ b/shell/platform/android/test/io/flutter/embedding/android/FlutterViewTest.java @@ -227,6 +227,20 @@ public void onConfigurationChanged_fizzlesWhenNullEngine() { verify(flutterEngine, times(2)).getSettingsChannel(); } + @Test + public void onConfigurationChanged_notifiesEngineOfDisplaySize() { + FlutterView flutterView = new FlutterView(Robolectric.setupActivity(Activity.class)); + FlutterEngine flutterEngine = spy(new FlutterEngine(ctx, mockFlutterLoader, mockFlutterJni)); + + Configuration configuration = ctx.getResources().getConfiguration(); + + flutterView.attachToFlutterEngine(flutterEngine); + flutterView.onConfigurationChanged(configuration); + + verify(flutterEngine, times(1)) + .updateDisplayMetrics(any(Float.class), any(Float.class), any(Float.class)); + } + // TODO(mattcarroll): turn this into an e2e test. GitHub #42990 @Test public void itSendsLightPlatformBrightnessToFlutter() { diff --git a/shell/platform/android/test/io/flutter/embedding/engine/FlutterEngineTest.java b/shell/platform/android/test/io/flutter/embedding/engine/FlutterEngineTest.java index cc7a8e5fee999..c1b049b89ecd5 100644 --- a/shell/platform/android/test/io/flutter/embedding/engine/FlutterEngineTest.java +++ b/shell/platform/android/test/io/flutter/embedding/engine/FlutterEngineTest.java @@ -15,6 +15,7 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +import android.app.Activity; import android.content.Context; import android.content.pm.PackageManager.NameNotFoundException; import android.content.res.Configuration; @@ -42,6 +43,8 @@ import org.mockito.MockitoAnnotations; import org.mockito.invocation.InvocationOnMock; import org.mockito.stubbing.Answer; +import org.robolectric.Robolectric; +import org.robolectric.android.controller.ActivityController; import org.robolectric.annotation.Config; import org.robolectric.shadows.ShadowLog; @@ -98,6 +101,21 @@ public void itAutomaticallyRegistersPluginsByDefault() { assertEquals(flutterEngine, registeredEngines.get(0)); } + @Test + public void itUpdatesDisplayMetricsOnConstructionWithActivityContext() { + // Needs an activity. ApplicationContext won't work for this. + ActivityController activityController = Robolectric.buildActivity(Activity.class); + FlutterJNI mockFlutterJNI = mock(FlutterJNI.class); + when(mockFlutterJNI.isAttached()).thenReturn(true); + + FlutterLoader mockFlutterLoader = mock(FlutterLoader.class); + FlutterEngine flutterEngine = + new FlutterEngine(activityController.get(), mockFlutterLoader, mockFlutterJNI); + + verify(mockFlutterJNI, times(1)) + .updateDisplayMetrics(eq(0), any(Float.class), any(Float.class), any(Float.class)); + } + @Test public void itSendLocalesOnEngineInit() { FlutterJNI mockFlutterJNI = mock(FlutterJNI.class); diff --git a/shell/platform/darwin/ios/framework/Source/FlutterEngine.mm b/shell/platform/darwin/ios/framework/Source/FlutterEngine.mm index 10b87e099a50b..a124d2b0296a4 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterEngine.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterEngine.mm @@ -395,6 +395,7 @@ - (void)setViewController:(FlutterViewController*)viewController { viewController ? [viewController getWeakPtr] : fml::WeakPtr(); self.iosPlatformView->SetOwnerViewController(_viewController); [self maybeSetupPlatformViewChannels]; + [self updateDisplays]; _textInputPlugin.get().viewController = viewController; _undoManagerPlugin.get().viewController = viewController; @@ -732,7 +733,7 @@ - (void)setupShell:(std::unique_ptr)shell _shell = std::move(shell); [self setupChannels]; [self onLocaleUpdated:nil]; - [self initializeDisplays]; + [self updateDisplays]; _publisher.reset([[FlutterDartVMServicePublisher alloc] initWithEnableVMServicePublication:doesVMServicePublication]); [self maybeSetupPlatformViewChannels]; @@ -873,12 +874,19 @@ - (BOOL)createShell:(NSString*)entrypoint return _shell != nullptr; } -- (void)initializeDisplays { - auto vsync_waiter = std::shared_ptr(_shell->GetVsyncWaiter().lock()); +- (void)updateDisplays { + if (!_shell) { + // Tests may do this. + return; + } + auto vsync_waiter = _shell->GetVsyncWaiter().lock(); auto vsync_waiter_ios = std::static_pointer_cast(vsync_waiter); std::vector> displays; - displays.push_back(std::make_unique(vsync_waiter_ios)); - _shell->OnDisplayUpdates(flutter::DisplayUpdateType::kStartup, std::move(displays)); + auto screen_size = UIScreen.mainScreen.nativeBounds.size; + auto scale = UIScreen.mainScreen.scale; + displays.push_back(std::make_unique( + 0, vsync_waiter_ios, screen_size.width, screen_size.height, scale)); + _shell->OnDisplayUpdates(std::move(displays)); } - (BOOL)run { diff --git a/shell/platform/darwin/ios/framework/Source/FlutterEngineTest.mm b/shell/platform/darwin/ios/framework/Source/FlutterEngineTest.mm index b4dfb9502f511..2d55b14826692 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterEngineTest.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterEngineTest.mm @@ -331,4 +331,14 @@ - (void)testFlutterTextInputViewDidResignFirstResponderWillCallTextInputClientCo OCMVerify([mockBinaryMessenger sendOnChannel:@"flutter/textinput" message:encodedMethodCall]); } +- (void)testFlutterEngineUpdatesDisplays { + FlutterEngine* engine = [[FlutterEngine alloc] init]; + id mockEngine = OCMPartialMock(engine); + + [engine run]; + OCMVerify(times(1), [mockEngine updateDisplays]); + engine.viewController = nil; + OCMVerify(times(2), [mockEngine updateDisplays]); +} + @end diff --git a/shell/platform/darwin/ios/framework/Source/FlutterEngine_Test.h b/shell/platform/darwin/ios/framework/Source/FlutterEngine_Test.h index 1346c9aed6d99..7e4ebc62ae885 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterEngine_Test.h +++ b/shell/platform/darwin/ios/framework/Source/FlutterEngine_Test.h @@ -30,4 +30,5 @@ class ThreadHost; initialRoute:(/*nullable*/ NSString*)initialRoute entrypointArgs:(/*nullable*/ NSArray*)entrypointArgs; - (const flutter::ThreadHost&)threadHost; +- (void)updateDisplays; @end diff --git a/shell/platform/embedder/embedder.cc b/shell/platform/embedder/embedder.cc index 6e59644216064..876e8f3529351 100644 --- a/shell/platform/embedder/embedder.cc +++ b/shell/platform/embedder/embedder.cc @@ -2998,17 +2998,13 @@ FlutterEngineResult FlutterEngineNotifyDisplayUpdate( case kFlutterEngineDisplaysUpdateTypeStartup: { std::vector> displays; for (size_t i = 0; i < display_count; i++) { - if (embedder_displays[i].single_display) { - displays.push_back(std::make_unique( - embedder_displays[i].refresh_rate)); - } else { - displays.push_back(std::make_unique( - embedder_displays[i].display_id, - embedder_displays[i].refresh_rate)); - } + displays.push_back(std::make_unique( + 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)); } - engine->GetShell().OnDisplayUpdates(flutter::DisplayUpdateType::kStartup, - std::move(displays)); + engine->GetShell().OnDisplayUpdates(std::move(displays)); return kSuccess; } default: diff --git a/shell/platform/fuchsia/flutter/flatland_platform_view.cc b/shell/platform/fuchsia/flutter/flatland_platform_view.cc index e6cd12805bf1d..946ec867d5e52 100644 --- a/shell/platform/fuchsia/flutter/flatland_platform_view.cc +++ b/shell/platform/fuchsia/flutter/flatland_platform_view.cc @@ -105,6 +105,7 @@ void FlatlandPlatformView::OnGetLayout( {}, // p_physical_display_features_bounds {}, // p_physical_display_features_type {}, // p_physical_display_features_state + 0, // p_display_id }); parent_viewport_watcher_->GetLayout( diff --git a/shell/platform/fuchsia/flutter/gfx_platform_view.cc b/shell/platform/fuchsia/flutter/gfx_platform_view.cc index ed9eeece151a0..fc96059c2a5e1 100644 --- a/shell/platform/fuchsia/flutter/gfx_platform_view.cc +++ b/shell/platform/fuchsia/flutter/gfx_platform_view.cc @@ -231,6 +231,7 @@ void GfxPlatformView::OnScenicEvent( {}, // p_physical_display_features_bounds {}, // p_physical_display_features_type {}, // p_physical_display_features_state + 0, // pdisplay_id }); } } diff --git a/shell/platform/fuchsia/flutter/platform_view_unittest.cc b/shell/platform/fuchsia/flutter/platform_view_unittest.cc index 624ad33b51699..fc883b5c8b22c 100644 --- a/shell/platform/fuchsia/flutter/platform_view_unittest.cc +++ b/shell/platform/fuchsia/flutter/platform_view_unittest.cc @@ -636,7 +636,7 @@ TEST_F(PlatformViewTests, SetViewportMetrics) { delegate.metrics(), flutter::ViewportMetrics( valid_pixel_ratio, std::round(valid_pixel_ratio * valid_max_bound), - std::round(valid_pixel_ratio * valid_max_bound), -1.0)); + std::round(valid_pixel_ratio * valid_max_bound), -1.0, 0)); } // This test makes sure that the PlatformView correctly registers semantics @@ -1447,7 +1447,8 @@ TEST_F(PlatformViewTests, TouchSourceLogicalToPhysicalConversion) { }))); session_listener->OnScenicEvent(std::move(scenic_events)); RunLoopUntilIdle(); - EXPECT_EQ(delegate.metrics(), flutter::ViewportMetrics(2.f, 40.f, 40.f, -1)); + EXPECT_EQ(delegate.metrics(), + flutter::ViewportMetrics(2.f, 40.f, 40.f, -1, 0)); // Inject std::vector events = diff --git a/shell/platform/fuchsia/flutter/tests/flatland_platform_view_unittest.cc b/shell/platform/fuchsia/flutter/tests/flatland_platform_view_unittest.cc index c1ffbcd96ab66..a7ef860f95b66 100644 --- a/shell/platform/fuchsia/flutter/tests/flatland_platform_view_unittest.cc +++ b/shell/platform/fuchsia/flutter/tests/flatland_platform_view_unittest.cc @@ -660,7 +660,7 @@ TEST_F(FlatlandPlatformViewTests, SetViewportMetrics) { RunLoopUntilIdle(); EXPECT_EQ(delegate.metrics(), flutter::ViewportMetrics(kDPR, std::round(width * kDPR), - std::round(height * kDPR), -1.0)); + std::round(height * kDPR), -1.0, 0)); } // This test makes sure that the PlatformView correctly registers semantics @@ -1496,7 +1496,8 @@ TEST_F(FlatlandPlatformViewTests, TouchSourceLogicalToPhysicalConversion) { viewport_watcher.SetLayout(width, height); RunLoopUntilIdle(); - EXPECT_EQ(delegate.metrics(), flutter::ViewportMetrics(1, width, height, -1)); + EXPECT_EQ(delegate.metrics(), + flutter::ViewportMetrics(1, width, height, -1, 0)); // Inject std::vector events = diff --git a/shell/testing/tester_main.cc b/shell/testing/tester_main.cc index 7fb8fa1d523d3..c1058543b068e 100644 --- a/shell/testing/tester_main.cc +++ b/shell/testing/tester_main.cc @@ -339,10 +339,20 @@ int RunTester(const flutter::Settings& settings, } }); + auto device_pixel_ratio = 3.0; + auto physical_width = 2400.0; // 800 at 3x resolution. + auto physical_height = 1800.0; // 600 at 3x resolution. + + std::vector> displays; + displays.push_back(std::make_unique( + 0, 60, physical_width, physical_height, device_pixel_ratio)); + shell->OnDisplayUpdates(std::move(displays)); + flutter::ViewportMetrics metrics{}; - metrics.device_pixel_ratio = 3.0; - metrics.physical_width = 2400.0; // 800 at 3x resolution. - metrics.physical_height = 1800.0; // 600 at 3x resolution. + metrics.device_pixel_ratio = device_pixel_ratio; + metrics.physical_width = physical_width; + metrics.physical_height = physical_height; + metrics.display_id = 0; shell->GetPlatformView()->SetViewportMetrics(metrics); // Run the message loop and wait for the script to do its thing. diff --git a/testing/dart/window_test.dart b/testing/dart/window_test.dart index 018c72a575154..f51c6501cf29c 100644 --- a/testing/dart/window_test.dart +++ b/testing/dart/window_test.dart @@ -79,4 +79,14 @@ void main() { final Locale? result = PlatformDispatcher.instance.computePlatformResolvedLocale(supportedLocales); expect(result, null); }); + + test('Display is configured for the implicitView', () { + final FlutterView implicitView = PlatformDispatcher.instance.implicitView!; + final Display display = implicitView.display; + + expect(display.id, 0); + expect(display.devicePixelRatio, implicitView.devicePixelRatio); + expect(display.refreshRate, 60); + expect(display.size, implicitView.physicalSize); + }); } diff --git a/testing/scenario_app/lib/main.dart b/testing/scenario_app/lib/main.dart index d45760c62d9c9..06567dba20894 100644 --- a/testing/scenario_app/lib/main.dart +++ b/testing/scenario_app/lib/main.dart @@ -24,6 +24,15 @@ void main() { ..onPointerDataPacket = _onPointerDataPacket ..scheduleFrame(); + final FlutterView view = PlatformDispatcher.instance.implicitView!; + // Asserting that this is greater than zero since this app runs on different + // platforms with different sizes. If it is greater than zero, it has been + // initialized to some meaningful value at least. + assert( + view.display.size > Offset.zero, + 'Expected ${view.display} to be initialized.', + ); + final ByteData data = ByteData(1); data.setUint8(0, 1); PlatformDispatcher.instance.sendPlatformMessage('waiting_for_status', data, null);