Add Linux platform support for fetching refresh rate on startup. (#52934)
This patch addresses the missing implementation of
`platformDispatcher.views` on Linux. It checks the refresh rate of the
renderer's window and returns the value. Without this implementation,
`WidgetsBinding.instance.platformDispatcher.views.first.display.size`
would throw an exception on Linux, preventing safe usage.
Related: https://github.com/flutter/flutter/issues/144230
diff --git a/shell/platform/linux/fl_engine.cc b/shell/platform/linux/fl_engine.cc
index f2c8040..98d4389 100644
--- a/shell/platform/linux/fl_engine.cc
+++ b/shell/platform/linux/fl_engine.cc
@@ -573,6 +573,26 @@
g_warning("Failed to enable accessibility features on Flutter engine");
}
+ gdouble refresh_rate = fl_renderer_get_refresh_rate(self->renderer);
+ // FlutterEngineDisplay::refresh_rate expects 0 if the refresh rate is
+ // unknown.
+ if (refresh_rate <= 0.0) {
+ refresh_rate = 0.0;
+ }
+ FlutterEngineDisplay display = {};
+ display.struct_size = sizeof(FlutterEngineDisplay);
+ display.display_id = 0;
+ display.single_display = true;
+ display.refresh_rate = refresh_rate;
+
+ std::vector displays = {display};
+ result = self->embedder_api.NotifyDisplayUpdate(
+ self->engine, kFlutterEngineDisplaysUpdateTypeStartup, displays.data(),
+ displays.size());
+ if (result != kSuccess) {
+ g_warning("Failed to notify display update to Flutter engine: %d", result);
+ }
+
return TRUE;
}
diff --git a/shell/platform/linux/fl_renderer.cc b/shell/platform/linux/fl_renderer.cc
index 595c315..a2163aa 100644
--- a/shell/platform/linux/fl_renderer.cc
+++ b/shell/platform/linux/fl_renderer.cc
@@ -153,6 +153,11 @@
FL_RENDERER_GET_CLASS(self)->clear_current(self);
}
+gdouble fl_renderer_get_refresh_rate(FlRenderer* self) {
+ g_return_val_if_fail(FL_IS_RENDERER(self), -1.0);
+ return FL_RENDERER_GET_CLASS(self)->get_refresh_rate(self);
+}
+
guint32 fl_renderer_get_fbo(FlRenderer* self) {
g_return_val_if_fail(FL_IS_RENDERER(self), 0);
diff --git a/shell/platform/linux/fl_renderer.h b/shell/platform/linux/fl_renderer.h
index e2145fd..39222ef 100644
--- a/shell/platform/linux/fl_renderer.h
+++ b/shell/platform/linux/fl_renderer.h
@@ -80,6 +80,16 @@
*/
gboolean (*collect_backing_store)(FlRenderer* renderer,
const FlutterBackingStore* backing_store);
+
+ /**
+ * Virtual method called when Flutter wants to get the refresh rate of the
+ * renderer.
+ * @renderer: an #FlRenderer.
+ *
+ * Returns: The refresh rate of the display in Hz. If the refresh rate is
+ * not available, returns -1.0.
+ */
+ gdouble (*get_refresh_rate)(FlRenderer* renderer);
};
/**
@@ -223,6 +233,15 @@
*/
void fl_renderer_cleanup(FlRenderer* renderer);
+/**
+ * fl_renderer_get_refresh_rate:
+ * @renderer: an #FlRenderer.
+ *
+ * Returns: The refresh rate of the display in Hz. If the refresh rate is
+ * not available, returns -1.0.
+ */
+gdouble fl_renderer_get_refresh_rate(FlRenderer* renderer);
+
G_END_DECLS
#endif // FLUTTER_SHELL_PLATFORM_LINUX_FL_RENDERER_H_
diff --git a/shell/platform/linux/fl_renderer_gdk.cc b/shell/platform/linux/fl_renderer_gdk.cc
index f5d4990..dec1b74 100644
--- a/shell/platform/linux/fl_renderer_gdk.cc
+++ b/shell/platform/linux/fl_renderer_gdk.cc
@@ -36,6 +36,23 @@
gdk_gl_context_clear_current();
}
+static gdouble fl_renderer_gdk_get_refresh_rate(FlRenderer* renderer) {
+ FlRendererGdk* self = FL_RENDERER_GDK(renderer);
+ GdkDisplay* display = gdk_window_get_display(self->window);
+ GdkMonitor* monitor =
+ gdk_display_get_monitor_at_window(display, self->window);
+ if (monitor == nullptr) {
+ return -1.0;
+ }
+
+ int refresh_rate = gdk_monitor_get_refresh_rate(monitor);
+ if (refresh_rate <= 0) {
+ return -1.0;
+ }
+ // the return value is in milli-hertz, convert to hertz
+ return static_cast<gdouble>(refresh_rate) / 1000.0;
+}
+
static void fl_renderer_gdk_dispose(GObject* object) {
FlRendererGdk* self = FL_RENDERER_GDK(object);
@@ -52,6 +69,7 @@
FL_RENDERER_CLASS(klass)->make_resource_current =
fl_renderer_gdk_make_resource_current;
FL_RENDERER_CLASS(klass)->clear_current = fl_renderer_gdk_clear_current;
+ FL_RENDERER_CLASS(klass)->get_refresh_rate = fl_renderer_gdk_get_refresh_rate;
}
static void fl_renderer_gdk_init(FlRendererGdk* self) {}
diff --git a/shell/platform/linux/fl_renderer_headless.cc b/shell/platform/linux/fl_renderer_headless.cc
index 1736e14..c94cd1f 100644
--- a/shell/platform/linux/fl_renderer_headless.cc
+++ b/shell/platform/linux/fl_renderer_headless.cc
@@ -19,11 +19,18 @@
// Implements FlRenderer::clear_current.
static void fl_renderer_headless_clear_current(FlRenderer* renderer) {}
+// Implements FlRenderer::get_refresh_rate.
+static gdouble fl_renderer_headless_get_refresh_rate(FlRenderer* renderer) {
+ return -1.0;
+}
+
static void fl_renderer_headless_class_init(FlRendererHeadlessClass* klass) {
FL_RENDERER_CLASS(klass)->make_current = fl_renderer_headless_make_current;
FL_RENDERER_CLASS(klass)->make_resource_current =
fl_renderer_headless_make_resource_current;
FL_RENDERER_CLASS(klass)->clear_current = fl_renderer_headless_clear_current;
+ FL_RENDERER_CLASS(klass)->get_refresh_rate =
+ fl_renderer_headless_get_refresh_rate;
}
static void fl_renderer_headless_init(FlRendererHeadless* self) {}
diff --git a/shell/platform/linux/fl_renderer_test.cc b/shell/platform/linux/fl_renderer_test.cc
index 7c01204..a44e546 100644
--- a/shell/platform/linux/fl_renderer_test.cc
+++ b/shell/platform/linux/fl_renderer_test.cc
@@ -51,3 +51,19 @@
g_object_ref_sink(view);
}
+
+static constexpr double kExpectedRefreshRate = 120.0;
+static gdouble renderer_get_refresh_rate(FlRenderer* renderer) {
+ return kExpectedRefreshRate;
+}
+
+TEST(FlRendererTest, RefreshRate) {
+ flutter::testing::fl_ensure_gtk_init();
+ g_autoptr(FlDartProject) project = fl_dart_project_new();
+ g_autoptr(FlMockRenderer) renderer =
+ fl_mock_renderer_new(&renderer_get_refresh_rate);
+
+ gdouble result_refresh_rate =
+ fl_renderer_get_refresh_rate(FL_RENDERER(renderer));
+ EXPECT_DOUBLE_EQ(result_refresh_rate, kExpectedRefreshRate);
+}
diff --git a/shell/platform/linux/testing/mock_engine.cc b/shell/platform/linux/testing/mock_engine.cc
index eac0cb3..040e0eb 100644
--- a/shell/platform/linux/testing/mock_engine.cc
+++ b/shell/platform/linux/testing/mock_engine.cc
@@ -513,6 +513,14 @@
return kSuccess;
}
+FlutterEngineResult FlutterEngineNotifyDisplayUpdate(
+ FLUTTER_API_SYMBOL(FlutterEngine) engine,
+ FlutterEngineDisplaysUpdateType update_type,
+ const FlutterEngineDisplay* displays,
+ size_t display_count) {
+ return kSuccess;
+}
+
} // namespace
FlutterEngineResult FlutterEngineGetProcAddresses(
@@ -552,5 +560,6 @@
table->UnregisterExternalTexture = &FlutterEngineUnregisterExternalTexture;
table->UpdateAccessibilityFeatures =
&FlutterEngineUpdateAccessibilityFeatures;
+ table->NotifyDisplayUpdate = &FlutterEngineNotifyDisplayUpdate;
return kSuccess;
}
diff --git a/shell/platform/linux/testing/mock_renderer.cc b/shell/platform/linux/testing/mock_renderer.cc
index 91d87b4..c70980b 100644
--- a/shell/platform/linux/testing/mock_renderer.cc
+++ b/shell/platform/linux/testing/mock_renderer.cc
@@ -6,6 +6,7 @@
struct _FlMockRenderer {
FlRenderer parent_instance;
+ FlMockRendererGetRefreshRate get_refresh_rate;
};
G_DEFINE_TYPE(FlMockRenderer, fl_mock_renderer, fl_renderer_get_type())
@@ -19,16 +20,31 @@
// Implements FlRenderer::clear_current.
static void fl_mock_renderer_clear_current(FlRenderer* renderer) {}
+// Implements FlRenderer::get_refresh_rate.
+static gdouble fl_mock_renderer_default_get_refresh_rate(FlRenderer* renderer) {
+ FlMockRenderer* self = FL_MOCK_RENDERER(renderer);
+ if (self->get_refresh_rate != nullptr) {
+ return self->get_refresh_rate(renderer);
+ }
+ return -1.0;
+}
+
static void fl_mock_renderer_class_init(FlMockRendererClass* klass) {
FL_RENDERER_CLASS(klass)->make_current = fl_mock_renderer_make_current;
FL_RENDERER_CLASS(klass)->make_resource_current =
fl_mock_renderer_make_resource_current;
FL_RENDERER_CLASS(klass)->clear_current = fl_mock_renderer_clear_current;
+ FL_RENDERER_CLASS(klass)->get_refresh_rate =
+ fl_mock_renderer_default_get_refresh_rate;
}
static void fl_mock_renderer_init(FlMockRenderer* self) {}
// Creates a stub renderer
-FlMockRenderer* fl_mock_renderer_new() {
- return FL_MOCK_RENDERER(g_object_new(fl_mock_renderer_get_type(), nullptr));
+FlMockRenderer* fl_mock_renderer_new(
+ FlMockRendererGetRefreshRate get_refresh_rate) {
+ FlMockRenderer* fl_mock_renderer = FL_MOCK_RENDERER(
+ g_object_new_valist(fl_mock_renderer_get_type(), nullptr, nullptr));
+ fl_mock_renderer->get_refresh_rate = get_refresh_rate;
+ return fl_mock_renderer;
}
diff --git a/shell/platform/linux/testing/mock_renderer.h b/shell/platform/linux/testing/mock_renderer.h
index 5369a37..2dbbe08 100644
--- a/shell/platform/linux/testing/mock_renderer.h
+++ b/shell/platform/linux/testing/mock_renderer.h
@@ -15,7 +15,10 @@
MOCK_RENDERER,
FlRenderer)
-FlMockRenderer* fl_mock_renderer_new();
+typedef gdouble (*FlMockRendererGetRefreshRate)(FlRenderer* renderer);
+
+FlMockRenderer* fl_mock_renderer_new(
+ FlMockRendererGetRefreshRate get_refresh_rate = nullptr);
G_END_DECLS