Provide monitor information. (#161359)

Fixes https://github.com/flutter/flutter/issues/144230
This commit is contained in:
Robert Ancell 2025-01-13 10:20:40 +13:00 committed by GitHub
parent bd1ebf2e14
commit d14140f854
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
17 changed files with 367 additions and 101 deletions

View File

@ -44348,6 +44348,9 @@ ORIGIN: ../../../flutter/shell/platform/linux/fl_binary_messenger_test.cc + ../.
ORIGIN: ../../../flutter/shell/platform/linux/fl_dart_project.cc + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/shell/platform/linux/fl_dart_project_private.h + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/shell/platform/linux/fl_dart_project_test.cc + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/shell/platform/linux/fl_display_monitor.cc + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/shell/platform/linux/fl_display_monitor.h + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/shell/platform/linux/fl_display_monitor_test.cc + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/shell/platform/linux/fl_engine.cc + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/shell/platform/linux/fl_engine_private.h + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/shell/platform/linux/fl_engine_test.cc + ../../../flutter/LICENSE
@ -47317,6 +47320,9 @@ FILE: ../../../flutter/shell/platform/linux/fl_binary_messenger_test.cc
FILE: ../../../flutter/shell/platform/linux/fl_dart_project.cc
FILE: ../../../flutter/shell/platform/linux/fl_dart_project_private.h
FILE: ../../../flutter/shell/platform/linux/fl_dart_project_test.cc
FILE: ../../../flutter/shell/platform/linux/fl_display_monitor.cc
FILE: ../../../flutter/shell/platform/linux/fl_display_monitor.h
FILE: ../../../flutter/shell/platform/linux/fl_display_monitor_test.cc
FILE: ../../../flutter/shell/platform/linux/fl_engine.cc
FILE: ../../../flutter/shell/platform/linux/fl_engine_private.h
FILE: ../../../flutter/shell/platform/linux/fl_engine_test.cc

View File

@ -107,6 +107,7 @@ source_set("flutter_linux_sources") {
"fl_binary_codec.cc",
"fl_binary_messenger.cc",
"fl_dart_project.cc",
"fl_display_monitor.cc",
"fl_engine.cc",
"fl_event_channel.cc",
"fl_framebuffer.cc",
@ -216,6 +217,7 @@ executable("flutter_linux_unittests") {
"fl_binary_codec_test.cc",
"fl_binary_messenger_test.cc",
"fl_dart_project_test.cc",
"fl_display_monitor_test.cc",
"fl_engine_test.cc",
"fl_event_channel_test.cc",
"fl_framebuffer_test.cc",

View File

@ -0,0 +1,120 @@
// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "flutter/shell/platform/linux/fl_display_monitor.h"
#include "flutter/shell/platform/linux/fl_engine_private.h"
struct _FlDisplayMonitor {
GObject parent_instance;
// Engine being updated.
GWeakRef engine;
// Display being monitored.
GdkDisplay* display;
// Mapping of GdkMonitor to display IDs.
GHashTable* display_ids_by_monitor;
// Next ID to assign to a new monitor.
FlutterEngineDisplayId next_display_id;
};
G_DEFINE_TYPE(FlDisplayMonitor, fl_display_monitor, G_TYPE_OBJECT)
// Send the current monitor state to the engine.
static void notify_display_update(FlDisplayMonitor* self) {
g_autoptr(FlEngine) engine = FL_ENGINE(g_weak_ref_get(&self->engine));
if (engine == nullptr) {
return;
}
int n_monitors = gdk_display_get_n_monitors(self->display);
g_autofree FlutterEngineDisplay* displays =
g_new0(FlutterEngineDisplay, n_monitors);
for (int i = 0; i < n_monitors; i++) {
FlutterEngineDisplay* display = &displays[i];
GdkMonitor* monitor = gdk_display_get_monitor(self->display, i);
FlutterEngineDisplayId display_id = GPOINTER_TO_INT(
g_hash_table_lookup(self->display_ids_by_monitor, monitor));
if (display_id == 0) {
display_id = self->next_display_id;
g_hash_table_insert(self->display_ids_by_monitor, g_object_ref(monitor),
GINT_TO_POINTER(display_id));
self->next_display_id++;
}
GdkRectangle geometry;
gdk_monitor_get_geometry(monitor, &geometry);
display->struct_size = sizeof(FlutterEngineDisplay);
display->display_id = display_id;
display->single_display = false;
display->refresh_rate = gdk_monitor_get_refresh_rate(monitor) / 1000.0;
display->width = geometry.width;
display->height = geometry.height;
display->device_pixel_ratio = gdk_monitor_get_scale_factor(monitor);
}
fl_engine_notify_display_update(engine, displays, n_monitors);
}
static void monitor_added_cb(FlDisplayMonitor* self, GdkMonitor* monitor) {
notify_display_update(self);
}
static void monitor_removed_cb(FlDisplayMonitor* self, GdkMonitor* monitor) {
g_hash_table_remove(self->display_ids_by_monitor, monitor);
notify_display_update(self);
}
static void fl_display_monitor_dispose(GObject* object) {
FlDisplayMonitor* self = FL_DISPLAY_MONITOR(object);
g_weak_ref_clear(&self->engine);
g_clear_object(&self->display);
g_clear_pointer(&self->display_ids_by_monitor, g_hash_table_unref);
G_OBJECT_CLASS(fl_display_monitor_parent_class)->dispose(object);
}
static void fl_display_monitor_class_init(FlDisplayMonitorClass* klass) {
GObjectClass* object_class = G_OBJECT_CLASS(klass);
object_class->dispose = fl_display_monitor_dispose;
}
static void fl_display_monitor_init(FlDisplayMonitor* self) {
self->display_ids_by_monitor = g_hash_table_new_full(
g_direct_hash, g_direct_equal, g_object_unref, nullptr);
self->next_display_id = 1;
}
FlDisplayMonitor* fl_display_monitor_new(FlEngine* engine,
GdkDisplay* display) {
FlDisplayMonitor* self =
FL_DISPLAY_MONITOR(g_object_new(fl_display_monitor_get_type(), nullptr));
g_weak_ref_init(&self->engine, engine);
self->display = GDK_DISPLAY(g_object_ref(display));
return self;
}
void fl_display_monitor_start(FlDisplayMonitor* self) {
g_return_if_fail(FL_IS_DISPLAY_MONITOR(self));
g_signal_connect_object(self->display, "monitor-added",
G_CALLBACK(monitor_added_cb), self,
G_CONNECT_SWAPPED);
g_signal_connect_object(self->display, "monitor-removed",
G_CALLBACK(monitor_removed_cb), self,
G_CONNECT_SWAPPED);
notify_display_update(self);
}
FlutterEngineDisplayId fl_display_monitor_get_display_id(FlDisplayMonitor* self,
GdkMonitor* monitor) {
g_return_val_if_fail(FL_IS_DISPLAY_MONITOR(self), 0);
return GPOINTER_TO_INT(
g_hash_table_lookup(self->display_ids_by_monitor, monitor));
}

View File

@ -0,0 +1,56 @@
// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef FLUTTER_SHELL_PLATFORM_LINUX_FL_DISPLAY_MONITOR_H_
#define FLUTTER_SHELL_PLATFORM_LINUX_FL_DISPLAY_MONITOR_H_
#include <gdk/gdk.h>
#include "flutter/shell/platform/embedder/embedder.h"
#include "flutter/shell/platform/linux/public/flutter_linux/fl_engine.h"
G_BEGIN_DECLS
G_DECLARE_FINAL_TYPE(FlDisplayMonitor,
fl_display_monitor,
FL,
DISPLAY_MONITOR,
GObject);
/**
* fl_display_monitor_new:
* @engine: engine to update.
* @display: display to monitor.
*
* Creates a new object to keep the engine updated with the currently used
* displays. In GDK, a display is called a "monitor".
*
* Returns: a new #FlDisplayMontior.
*/
FlDisplayMonitor* fl_display_monitor_new(FlEngine* engine, GdkDisplay* display);
/**
* fl_display_monitor_start:
* @monitor: an #FlDisplayMonitor.
*
* Start monitoring for display changes.
*/
void fl_display_monitor_start(FlDisplayMonitor* monitor);
/**
* fl_display_monitor_get_display_id:
* @monitor: an #FlDisplayMonitor.
* @gdk_monitor: GDK monitor to get display ID for.
*
* Get the ID Flutter is using for a given monitor.
*
* Returns: an ID or 0 if unknown.
*/
FlutterEngineDisplayId fl_display_monitor_get_display_id(
FlDisplayMonitor* monitor,
GdkMonitor* gdk_monitor);
G_END_DECLS
#endif // FLUTTER_SHELL_PLATFORM_LINUX_FL_DISPLAY_MONITOR_H_

View File

@ -0,0 +1,36 @@
// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "flutter/shell/platform/linux/fl_display_monitor.h"
#include "flutter/shell/platform/embedder/test_utils/proc_table_replacement.h"
#include "flutter/shell/platform/linux/fl_engine_private.h"
#include "gtest/gtest.h"
TEST(FlDisplayMonitorTest, Test) {
g_autoptr(FlDartProject) project = fl_dart_project_new();
g_autoptr(FlEngine) engine = fl_engine_new(project);
g_autoptr(GError) error = nullptr;
EXPECT_TRUE(fl_engine_start(engine, &error));
EXPECT_EQ(error, nullptr);
bool called = false;
fl_engine_get_embedder_api(engine)->NotifyDisplayUpdate = MOCK_ENGINE_PROC(
NotifyDisplayUpdate,
([&called](auto engine, FlutterEngineDisplaysUpdateType update_type,
const FlutterEngineDisplay* displays, size_t displays_length) {
called = true;
EXPECT_EQ(displays_length, 1u);
return kSuccess;
}));
g_autoptr(FlDisplayMonitor) monitor =
fl_display_monitor_new(engine, gdk_display_get_default());
EXPECT_FALSE(called);
fl_display_monitor_start(monitor);
EXPECT_TRUE(called);
}

View File

@ -12,6 +12,7 @@
#include "flutter/shell/platform/embedder/embedder.h"
#include "flutter/shell/platform/linux/fl_binary_messenger_private.h"
#include "flutter/shell/platform/linux/fl_dart_project_private.h"
#include "flutter/shell/platform/linux/fl_display_monitor.h"
#include "flutter/shell/platform/linux/fl_engine_private.h"
#include "flutter/shell/platform/linux/fl_pixel_buffer_texture_private.h"
#include "flutter/shell/platform/linux/fl_platform_handler.h"
@ -41,6 +42,9 @@ struct _FlEngine {
// The project this engine is running.
FlDartProject* project;
// Watches for monitors changes to update engine.
FlDisplayMonitor* display_monitor;
// Renders the Flutter app.
FlRenderer* renderer;
@ -532,6 +536,11 @@ FlRenderer* fl_engine_get_renderer(FlEngine* self) {
return self->renderer;
}
FlDisplayMonitor* fl_engine_get_display_monitor(FlEngine* self) {
g_return_val_if_fail(FL_IS_ENGINE(self), nullptr);
return self->display_monitor;
}
gboolean fl_engine_start(FlEngine* self, GError** error) {
g_return_val_if_fail(FL_IS_ENGINE(self), FALSE);
@ -642,23 +651,9 @@ gboolean fl_engine_start(FlEngine* self, GError** error) {
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;
result = self->embedder_api.NotifyDisplayUpdate(
self->engine, kFlutterEngineDisplaysUpdateTypeStartup, &display, 1);
if (result != kSuccess) {
g_warning("Failed to notify display update to Flutter engine: %d", result);
}
self->display_monitor =
fl_display_monitor_new(self, gdk_display_get_default());
fl_display_monitor_start(self->display_monitor);
return TRUE;
}
@ -667,6 +662,19 @@ FlutterEngineProcTable* fl_engine_get_embedder_api(FlEngine* self) {
return &(self->embedder_api);
}
void fl_engine_notify_display_update(FlEngine* self,
const FlutterEngineDisplay* displays,
size_t displays_length) {
g_return_if_fail(FL_IS_ENGINE(self));
FlutterEngineResult result = self->embedder_api.NotifyDisplayUpdate(
self->engine, kFlutterEngineDisplaysUpdateTypeStartup, displays,
displays_length);
if (result != kSuccess) {
g_warning("Failed to notify display update to Flutter engine: %d", result);
}
}
FlutterViewId fl_engine_add_view(FlEngine* self,
size_t width,
size_t height,
@ -681,11 +689,16 @@ FlutterViewId fl_engine_add_view(FlEngine* self,
FlutterViewId view_id = self->next_view_id;
self->next_view_id++;
// We don't know which display this view will open on, so set to zero and this
// will be updated in a following FlutterWindowMetricsEvent
FlutterEngineDisplayId display_id = 0;
FlutterWindowMetricsEvent metrics;
metrics.struct_size = sizeof(FlutterWindowMetricsEvent);
metrics.width = width;
metrics.height = height;
metrics.pixel_ratio = pixel_ratio;
metrics.display_id = display_id;
metrics.view_id = view_id;
FlutterAddViewInfo info;
info.struct_size = sizeof(FlutterAddViewInfo);
@ -881,6 +894,7 @@ GBytes* fl_engine_send_platform_message_finish(FlEngine* self,
}
void fl_engine_send_window_metrics_event(FlEngine* self,
FlutterEngineDisplayId display_id,
FlutterViewId view_id,
size_t width,
size_t height,
@ -896,6 +910,7 @@ void fl_engine_send_window_metrics_event(FlEngine* self,
event.width = width;
event.height = height;
event.pixel_ratio = pixel_ratio;
event.display_id = display_id;
event.view_id = view_id;
self->embedder_api.SendWindowMetricsEvent(self->engine, &event);
}

View File

@ -8,6 +8,7 @@
#include <glib-object.h>
#include "flutter/shell/platform/embedder/embedder.h"
#include "flutter/shell/platform/linux/fl_display_monitor.h"
#include "flutter/shell/platform/linux/fl_mouse_cursor_handler.h"
#include "flutter/shell/platform/linux/fl_renderer.h"
#include "flutter/shell/platform/linux/fl_task_runner.h"
@ -81,6 +82,16 @@ FlEngine* fl_engine_new_with_renderer(FlDartProject* project,
*/
FlRenderer* fl_engine_get_renderer(FlEngine* engine);
/**
* fl_engine_get_display_monitor:
* @engine: an #FlEngine.
*
* Gets the display monitor used by this engine.
*
* Returns: an #FlDisplayMonitor.
*/
FlDisplayMonitor* fl_engine_get_display_monitor(FlEngine* engine);
/**
* fl_engine_start:
* @engine: an #FlEngine.
@ -103,6 +114,18 @@ gboolean fl_engine_start(FlEngine* engine, GError** error);
*/
FlutterEngineProcTable* fl_engine_get_embedder_api(FlEngine* engine);
/**
* fl_engine_notify_display_update:
* @engine: an #FlEngine.
* @displays: displays present on the system.
* @displays_length: length of @displays.
*
* Notify the current displays that are in the system.
*/
void fl_engine_notify_display_update(FlEngine* engine,
const FlutterEngineDisplay* displays,
size_t displays_length);
/**
* fl_engine_add_view:
* @engine: an #FlEngine.
@ -213,6 +236,7 @@ void fl_engine_set_update_semantics_handler(
/**
* fl_engine_send_window_metrics_event:
* @engine: an #FlEngine.
* @display_id: the display this view is rendering on.
* @view_id: the view that the event occured on.
* @width: width of the window in pixels.
* @height: height of the window in pixels.
@ -221,6 +245,7 @@ void fl_engine_set_update_semantics_handler(
* Sends a window metrics event to the engine.
*/
void fl_engine_send_window_metrics_event(FlEngine* engine,
FlutterEngineDisplayId display_id,
FlutterViewId view_id,
size_t width,
size_t height,

View File

@ -14,6 +14,63 @@
// MOCK_ENGINE_PROC is leaky by design
// NOLINTBEGIN(clang-analyzer-core.StackAddressEscape)
// Checks notifying display updates works.
TEST(FlEngineTest, NotifyDisplayUpdate) {
g_autoptr(FlDartProject) project = fl_dart_project_new();
g_autoptr(FlEngine) engine = fl_engine_new(project);
g_autoptr(GError) error = nullptr;
EXPECT_TRUE(fl_engine_start(engine, &error));
EXPECT_EQ(error, nullptr);
bool called = false;
fl_engine_get_embedder_api(engine)->NotifyDisplayUpdate = MOCK_ENGINE_PROC(
NotifyDisplayUpdate,
([&called](auto engine, FlutterEngineDisplaysUpdateType update_type,
const FlutterEngineDisplay* displays, size_t displays_length) {
called = true;
EXPECT_EQ(update_type, kFlutterEngineDisplaysUpdateTypeStartup);
EXPECT_EQ(displays_length, 2u);
EXPECT_EQ(displays[0].display_id, 1u);
EXPECT_EQ(displays[0].refresh_rate, 60);
EXPECT_EQ(displays[0].width, 1024u);
EXPECT_EQ(displays[0].height, 768u);
EXPECT_EQ(displays[0].device_pixel_ratio, 1.0);
EXPECT_EQ(displays[1].display_id, 2u);
EXPECT_EQ(displays[1].refresh_rate, 120);
EXPECT_EQ(displays[1].width, 3840u);
EXPECT_EQ(displays[1].height, 2160u);
EXPECT_EQ(displays[1].device_pixel_ratio, 2.0);
return kSuccess;
}));
FlutterEngineDisplay displays[2] = {
{
.struct_size = sizeof(FlutterEngineDisplay),
.display_id = 1,
.single_display = false,
.refresh_rate = 60.0,
.width = 1024,
.height = 768,
.device_pixel_ratio = 1.0,
},
{
.struct_size = sizeof(FlutterEngineDisplay),
.display_id = 2,
.single_display = false,
.refresh_rate = 120.0,
.width = 3840,
.height = 2160,
.device_pixel_ratio = 2.0,
}};
fl_engine_notify_display_update(engine, displays, 2);
EXPECT_TRUE(called);
}
// Checks sending window metrics events works.
TEST(FlEngineTest, WindowMetrics) {
g_autoptr(FlDartProject) project = fl_dart_project_new();
@ -28,6 +85,7 @@ TEST(FlEngineTest, WindowMetrics) {
SendWindowMetricsEvent,
([&called](auto engine, const FlutterWindowMetricsEvent* event) {
called = true;
EXPECT_EQ(event->display_id, 99u);
EXPECT_EQ(event->view_id, 1);
EXPECT_EQ(event->width, static_cast<size_t>(3840));
EXPECT_EQ(event->height, static_cast<size_t>(2160));
@ -36,7 +94,7 @@ TEST(FlEngineTest, WindowMetrics) {
return kSuccess;
}));
fl_engine_send_window_metrics_event(engine, 1, 3840, 2160, 2.0);
fl_engine_send_window_metrics_event(engine, 99, 1, 3840, 2160, 2.0);
EXPECT_TRUE(called);
}

View File

@ -381,11 +381,6 @@ void fl_renderer_clear_current(FlRenderer* self) {
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);

View File

@ -55,16 +55,6 @@ struct _FlRendererClass {
* @renderer: an #FlRenderer.
*/
void (*clear_current)(FlRenderer* renderer);
/**
* 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);
};
/**
@ -235,15 +225,6 @@ void fl_renderer_render(FlRenderer* renderer,
*/
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_

View File

@ -39,24 +39,6 @@ static void fl_renderer_gdk_clear_current(FlRenderer* renderer) {
gdk_gl_context_clear_current();
}
// Implements FlRenderer::get_refresh_rate.
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);
@ -74,7 +56,6 @@ static void fl_renderer_gdk_class_init(FlRendererGdkClass* klass) {
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) {}

View File

@ -19,18 +19,11 @@ static void fl_renderer_headless_make_resource_current(FlRenderer* renderer) {}
// 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) {}

View File

@ -93,20 +93,6 @@ TEST(FlRendererTest, RestoresGLState) {
EXPECT_EQ(texture_2d_binding, kFakeTextureName);
}
static constexpr double kExpectedRefreshRate = 120.0;
static gdouble renderer_get_refresh_rate(FlRenderer* renderer) {
return kExpectedRefreshRate;
}
TEST(FlRendererTest, RefreshRate) {
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);
}
TEST(FlRendererTest, BlitFramebuffer) {
::testing::NiceMock<flutter::testing::MockEpoxy> epoxy;

View File

@ -221,8 +221,21 @@ static void handle_geometry_changed(FlView* self) {
GtkAllocation allocation;
gtk_widget_get_allocation(GTK_WIDGET(self), &allocation);
gint scale_factor = gtk_widget_get_scale_factor(GTK_WIDGET(self));
// Note we can't detect if a window is moved between monitors - this
// information is provided by Wayland but GTK only notifies us if the scale
// has changed, so moving between two monitors of the same scale doesn't
// provide any information.
GdkWindow* window =
gtk_widget_get_window(gtk_widget_get_toplevel(GTK_WIDGET(self)));
GdkMonitor* monitor = gdk_display_get_monitor_at_window(
gtk_widget_get_display(GTK_WIDGET(self)), window);
fl_engine_send_window_metrics_event(
self->engine, self->view_id, allocation.width * scale_factor,
self->engine,
fl_display_monitor_get_display_id(
fl_engine_get_display_monitor(self->engine), monitor),
self->view_id, allocation.width * scale_factor,
allocation.height * scale_factor, scale_factor);
// Make sure the view has been realized and its size has been allocated before

View File

@ -6,7 +6,6 @@
struct _FlMockRenderer {
FlRenderer parent_instance;
FlMockRendererGetRefreshRate get_refresh_rate;
};
struct _FlMockRenderable {
@ -33,22 +32,11 @@ static void fl_mock_renderer_make_resource_current(FlRenderer* renderer) {}
// 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) {}
@ -70,11 +58,9 @@ static void fl_mock_renderable_class_init(FlMockRenderableClass* klass) {}
static void fl_mock_renderable_init(FlMockRenderable* self) {}
// Creates a stub renderer
FlMockRenderer* fl_mock_renderer_new(
FlMockRendererGetRefreshRate get_refresh_rate) {
FlMockRenderer* fl_mock_renderer_new() {
FlMockRenderer* self =
FL_MOCK_RENDERER(g_object_new(fl_mock_renderer_get_type(), nullptr));
self->get_refresh_rate = get_refresh_rate;
return self;
}

View File

@ -24,8 +24,7 @@ G_DECLARE_FINAL_TYPE(FlMockRenderable,
typedef gdouble (*FlMockRendererGetRefreshRate)(FlRenderer* renderer);
FlMockRenderer* fl_mock_renderer_new(
FlMockRendererGetRefreshRate get_refresh_rate = nullptr);
FlMockRenderer* fl_mock_renderer_new();
FlMockRenderable* fl_mock_renderable_new();

View File

@ -12,12 +12,26 @@ MockWindow::MockWindow() {
mock = this;
}
GdkDisplay* gdk_display_get_default() {
return GDK_DISPLAY(g_object_new(gdk_display_get_type(), nullptr));
}
void gdk_display_beep(GdkDisplay* display) {}
GdkWindowState gdk_window_get_state(GdkWindow* window) {
return mock->gdk_window_get_state(window);
}
GdkDisplay* gdk_window_get_display(GdkWindow* window) {
return nullptr;
return GDK_DISPLAY(g_object_new(gdk_display_get_type(), nullptr));
}
int gdk_display_get_n_monitors(GdkDisplay* display) {
return 1;
}
GdkMonitor* gdk_display_get_monitor(GdkDisplay* display, int n) {
return GDK_MONITOR(g_object_new(gdk_monitor_get_type(), nullptr));
}
GdkMonitor* gdk_display_get_monitor_at_window(GdkDisplay* display,