Provide monitor list, display size, refresh rate, and more for Windows (#164460)

This PR enhances the Windows implementation by adding support for
retrieving display properties, available displays, refresh rate, dpi,
size, and more.

Fixes: #160660, #125939

## Pre-launch Checklist

- [X] I read the [Contributor Guide] and followed the process outlined
there for submitting PRs.
- [X] I read the [Tree Hygiene] wiki page, which explains my
responsibilities.
- [X] I read and followed the [Flutter Style Guide], including [Features
we expect every widget to implement].
- [X] I signed the [CLA].
- [X] I listed at least one issue that this PR fixes in the description
above.
- [X] I updated/added relevant documentation (doc comments with `///`).
- [X] I added new tests to check the change I am making, or this PR is
[test-exempt].
- [X] I followed the [breaking change policy] and added [Data Driven
Fixes] where supported.
- [X] All existing and new tests are passing.

<!-- Links -->
[Contributor Guide]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#overview
[Tree Hygiene]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md
[test-exempt]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#tests
[Flutter Style Guide]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md
[Features we expect every widget to implement]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md#features-we-expect-every-widget-to-implement
[CLA]: https://cla.developers.google.com/
[flutter/tests]: https://github.com/flutter/tests
[breaking change policy]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#handling-breaking-changes
[Discord]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Chat.md
[Data Driven Fixes]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Data-driven-Fixes.md

---------

Co-authored-by: Matthew Kosarek <matt.kosarek@canonical.com>
This commit is contained in:
Jon Ihlas 2025-08-07 17:20:42 +02:00 committed by GitHub
parent f15262c9f7
commit 0accf67215
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
15 changed files with 481 additions and 26 deletions

View File

@ -421,6 +421,7 @@
../../../flutter/shell/platform/windows/fixtures
../../../flutter/shell/platform/windows/flutter_project_bundle_unittests.cc
../../../flutter/shell/platform/windows/flutter_window_unittests.cc
../../../flutter/shell/platform/windows/display_monitor_unittests.cc
../../../flutter/shell/platform/windows/flutter_windows_engine_unittests.cc
../../../flutter/shell/platform/windows/flutter_windows_texture_registrar_unittests.cc
../../../flutter/shell/platform/windows/flutter_windows_unittests.cc

View File

@ -54075,6 +54075,8 @@ ORIGIN: ../../../flutter/shell/platform/windows/flutter_project_bundle.h + ../..
ORIGIN: ../../../flutter/shell/platform/windows/flutter_window.cc + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/shell/platform/windows/flutter_window.h + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/shell/platform/windows/flutter_windows.cc + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/shell/platform/windows/display_monitor.cc + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/shell/platform/windows/display_monitor.h + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/shell/platform/windows/flutter_windows_engine.cc + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/shell/platform/windows/flutter_windows_engine.h + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/shell/platform/windows/flutter_windows_internal.h + ../../../flutter/LICENSE
@ -57165,6 +57167,8 @@ FILE: ../../../flutter/shell/platform/windows/flutter_project_bundle.h
FILE: ../../../flutter/shell/platform/windows/flutter_window.cc
FILE: ../../../flutter/shell/platform/windows/flutter_window.h
FILE: ../../../flutter/shell/platform/windows/flutter_windows.cc
FILE: ../../../flutter/shell/platform/windows/display_monitor.cc
FILE: ../../../flutter/shell/platform/windows/display_monitor.h
FILE: ../../../flutter/shell/platform/windows/flutter_windows_engine.cc
FILE: ../../../flutter/shell/platform/windows/flutter_windows_engine.h
FILE: ../../../flutter/shell/platform/windows/flutter_windows_internal.h

View File

@ -24,7 +24,6 @@ double DisplayManager::GetMainDisplayRefreshRate() const {
void DisplayManager::HandleDisplayUpdates(
std::vector<std::unique_ptr<Display>> displays) {
FML_DCHECK(!displays.empty());
std::scoped_lock lock(displays_mutex_);
displays_ = std::move(displays);
}

View File

@ -52,6 +52,8 @@ source_set("flutter_windows_source") {
"cursor_handler.h",
"direct_manipulation.cc",
"direct_manipulation.h",
"display_monitor.cc",
"display_monitor.h",
"dpi_utils.cc",
"dpi_utils.h",
"egl/context.cc",
@ -206,6 +208,7 @@ executable("flutter_windows_unittests") {
"compositor_software_unittests.cc",
"cursor_handler_unittests.cc",
"direct_manipulation_unittests.cc",
"display_monitor_unittests.cc",
"dpi_utils_unittests.cc",
"flutter_project_bundle_unittests.cc",
"flutter_window_unittests.cc",

View File

@ -0,0 +1,104 @@
// 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 "display_monitor.h"
#include <windows.h>
#include "flutter/shell/platform/windows/dpi_utils.h"
#include "flutter/shell/platform/windows/flutter_windows_engine.h"
namespace flutter {
namespace {
// Data structure to pass to the display enumeration callback.
struct MonitorEnumState {
const DisplayMonitor* display_monitor;
std::vector<FlutterEngineDisplay>* displays;
};
} // namespace
DisplayMonitor::DisplayMonitor(FlutterWindowsEngine* engine)
: engine_(engine), win32_(engine->windows_proc_table()) {}
DisplayMonitor::~DisplayMonitor() {}
BOOL CALLBACK DisplayMonitor::EnumMonitorCallback(HMONITOR monitor,
HDC hdc,
LPRECT rect,
LPARAM data) {
MonitorEnumState* state = reinterpret_cast<MonitorEnumState*>(data);
const DisplayMonitor* self = state->display_monitor;
std::vector<FlutterEngineDisplay>* displays = state->displays;
MONITORINFOEXW monitor_info = {};
monitor_info.cbSize = sizeof(monitor_info);
if (self->win32_->GetMonitorInfoW(monitor, &monitor_info) == 0) {
// Return TRUE to continue enumeration and skip this monitor.
// Returning FALSE would stop the entire enumeration process,
// potentially missing other valid monitors.
return TRUE;
}
DEVMODEW dev_mode = {};
dev_mode.dmSize = sizeof(dev_mode);
if (!self->win32_->EnumDisplaySettingsW(monitor_info.szDevice,
ENUM_CURRENT_SETTINGS, &dev_mode)) {
// Return TRUE to continue enumeration and skip this monitor.
// Returning FALSE would stop the entire enumeration process,
// potentially missing other valid monitors.
return TRUE;
}
UINT dpi = GetDpiForMonitor(monitor);
FlutterEngineDisplay display = {};
display.struct_size = sizeof(FlutterEngineDisplay);
display.display_id = reinterpret_cast<FlutterEngineDisplayId>(monitor);
display.single_display = false;
display.refresh_rate = dev_mode.dmDisplayFrequency;
display.width = monitor_info.rcMonitor.right - monitor_info.rcMonitor.left;
display.height = monitor_info.rcMonitor.bottom - monitor_info.rcMonitor.top;
display.device_pixel_ratio =
static_cast<double>(dpi) / static_cast<double>(kDefaultDpi);
displays->push_back(display);
return TRUE;
}
void DisplayMonitor::UpdateDisplays() {
auto displays = GetDisplays();
engine_->UpdateDisplay(displays);
}
bool DisplayMonitor::HandleWindowMessage(HWND hwnd,
UINT message,
WPARAM wparam,
LPARAM lparam,
LRESULT* result) {
switch (message) {
case WM_DISPLAYCHANGE:
case WM_DPICHANGED:
UpdateDisplays();
break;
}
return false;
}
std::vector<FlutterEngineDisplay> DisplayMonitor::GetDisplays() const {
std::vector<FlutterEngineDisplay> displays;
MonitorEnumState state = {this, &displays};
win32_->EnumDisplayMonitors(nullptr, nullptr, EnumMonitorCallback,
reinterpret_cast<LPARAM>(&state));
if (displays.size() == 1) {
displays[0].single_display = true;
}
return displays;
}
} // namespace flutter

View File

@ -0,0 +1,49 @@
// 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_WINDOWS_DISPLAY_MONITOR_H_
#define FLUTTER_SHELL_PLATFORM_WINDOWS_DISPLAY_MONITOR_H_
#include <windows.h>
#include <memory>
#include <vector>
#include "flutter/shell/platform/embedder/embedder.h"
#include "flutter/shell/platform/windows/windows_proc_table.h"
namespace flutter {
class FlutterWindowsEngine;
class DisplayMonitor {
public:
explicit DisplayMonitor(FlutterWindowsEngine* engine);
~DisplayMonitor();
// Updates the display information and notifies the engine
void UpdateDisplays();
// Handles Windows messages related to display changes
// Returns true if the message was handled and should not be further processed
bool HandleWindowMessage(HWND hwnd,
UINT message,
WPARAM wparam,
LPARAM lparam,
LRESULT* result);
// Get the display information for all displays
std::vector<FlutterEngineDisplay> GetDisplays() const;
private:
// Called by EnumDisplayMonitors once for each display.
static BOOL CALLBACK EnumMonitorCallback(HMONITOR monitor,
HDC hdc,
LPRECT rect,
LPARAM data);
FlutterWindowsEngine* engine_;
std::shared_ptr<WindowsProcTable> win32_;
};
} // namespace flutter
#endif // FLUTTER_SHELL_PLATFORM_WINDOWS_DISPLAY_MONITOR_H_

View File

@ -0,0 +1,123 @@
// 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 <cstring>
#include "flutter/shell/platform/windows/display_monitor.h"
#include <string>
#include "flutter/shell/platform/windows/testing/flutter_windows_engine_builder.h"
#include "flutter/shell/platform/windows/testing/windows_test.h"
#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include "shell/platform/windows/testing/mock_windows_proc_table.h"
// Mock Windows API functions to avoid hardware dependencies
#define MOCK_WINDOWS_API
namespace flutter {
namespace testing {
using ::testing::_;
using ::testing::AllOf;
using ::testing::AtLeast;
using ::testing::DoAll;
using ::testing::Field;
using ::testing::NiceMock;
using ::testing::Return;
using ::testing::SetArgPointee;
using ::testing::StrEq;
class DisplayMonitorTest : public WindowsTest {};
// Test that the display monitor correctly handles multiple monitors
TEST_F(DisplayMonitorTest, MultipleMonitors) {
auto mock_windows_proc_table =
std::make_shared<NiceMock<MockWindowsProcTable>>();
FlutterWindowsEngineBuilder builder(GetContext());
builder.SetWindowsProcTable(mock_windows_proc_table);
std::unique_ptr<FlutterWindowsEngine> engine = builder.Build();
HMONITOR mock_monitor1 = reinterpret_cast<HMONITOR>(123);
HMONITOR mock_monitor2 = reinterpret_cast<HMONITOR>(456);
MONITORINFOEXW monitor_info1 = {};
monitor_info1.cbSize = sizeof(MONITORINFOEXW);
monitor_info1.rcMonitor = {0, 0, 1920, 1080};
monitor_info1.rcWork = {0, 0, 1920, 1080};
monitor_info1.dwFlags = MONITORINFOF_PRIMARY;
wcscpy_s(monitor_info1.szDevice, L"\\\\.\\DISPLAY1");
MONITORINFOEXW monitor_info2 = {};
monitor_info2.cbSize = sizeof(MONITORINFOEXW);
monitor_info2.rcMonitor = {1920, 0, 1920 + 2560, 1440};
monitor_info2.rcWork = {1920, 0, 1920 + 2560, 1440};
monitor_info2.dwFlags = 0;
wcscpy_s(monitor_info2.szDevice, L"\\\\.\\DISPLAY2");
EXPECT_CALL(*mock_windows_proc_table, GetMonitorInfoW(mock_monitor1, _))
.WillOnce(DoAll(SetArgPointee<1>(monitor_info1), Return(TRUE)));
EXPECT_CALL(*mock_windows_proc_table, GetMonitorInfoW(mock_monitor2, _))
.WillOnce(DoAll(SetArgPointee<1>(monitor_info2), Return(TRUE)));
EXPECT_CALL(*mock_windows_proc_table,
EnumDisplayMonitors(nullptr, nullptr, _, _))
.WillOnce([&](HDC hdc, LPCRECT lprcClip, MONITORENUMPROC lpfnEnum,
LPARAM dwData) {
lpfnEnum(mock_monitor1, nullptr, &monitor_info1.rcMonitor, dwData);
lpfnEnum(mock_monitor2, nullptr, &monitor_info2.rcMonitor, dwData);
return TRUE;
});
// Set up GetDpiForMonitor to return different DPI values
EXPECT_CALL(*mock_windows_proc_table, GetDpiForMonitor(mock_monitor1, _))
.WillRepeatedly(Return(96)); // Default/Standard DPI
EXPECT_CALL(*mock_windows_proc_table, GetDpiForMonitor(mock_monitor2, _))
.WillRepeatedly(Return(144)); // High DPI
EXPECT_CALL(*mock_windows_proc_table, EnumDisplaySettings(_, _, _))
.WillRepeatedly(Return(TRUE));
// Create the display monitor with the mock engine
auto display_monitor = std::make_unique<DisplayMonitor>(engine.get());
display_monitor->UpdateDisplays();
}
// Test that the display monitor correctly handles a display change message
TEST_F(DisplayMonitorTest, HandleDisplayChangeMessage) {
// Create a mock Windows proc table
auto mock_windows_proc_table =
std::make_shared<NiceMock<MockWindowsProcTable>>();
// Create a mock engine
FlutterWindowsEngineBuilder builder(GetContext());
builder.SetWindowsProcTable(mock_windows_proc_table);
std::unique_ptr<FlutterWindowsEngine> engine = builder.Build();
EXPECT_CALL(*mock_windows_proc_table, EnumDisplayMonitors(_, _, _, _))
.WillRepeatedly(Return(TRUE));
// Create the display monitor with the mock engine
auto display_monitor = std::make_unique<DisplayMonitor>(engine.get());
// Test handling a display change message
HWND dummy_hwnd = reinterpret_cast<HWND>(1);
LRESULT result = 0;
// Verify that WM_DISPLAYCHANGE is handled
EXPECT_FALSE(display_monitor->HandleWindowMessage(
dummy_hwnd, WM_DISPLAYCHANGE, 0, 0, &result));
// Verify that WM_DPICHANGED is handled
EXPECT_FALSE(display_monitor->HandleWindowMessage(dummy_hwnd, WM_DPICHANGED,
0, 0, &result));
// Verify that other messages are not handled
EXPECT_FALSE(display_monitor->HandleWindowMessage(dummy_hwnd, WM_PAINT, 0, 0,
&result));
}
} // namespace testing
} // namespace flutter

View File

@ -10,8 +10,6 @@ namespace flutter {
namespace {
constexpr UINT kDefaultDpi = 96;
// This is the MDT_EFFECTIVE_DPI value from MONITOR_DPI_TYPE, an enum declared
// in ShellScalingApi.h. Replicating here to avoid importing the library
// directly.

View File

@ -9,6 +9,8 @@
namespace flutter {
constexpr UINT kDefaultDpi = 96;
/// Returns the DPI for |hwnd|. Supports all DPI awareness modes, and is
/// backward compatible down to Windows Vista. If |hwnd| is nullptr, returns the
/// DPI for the primary monitor. If Per-Monitor DPI awareness is not available,

View File

@ -21,6 +21,7 @@
#include "flutter/shell/platform/windows/accessibility_bridge_windows.h"
#include "flutter/shell/platform/windows/compositor_opengl.h"
#include "flutter/shell/platform/windows/compositor_software.h"
#include "flutter/shell/platform/windows/display_monitor.h"
#include "flutter/shell/platform/windows/flutter_windows_view.h"
#include "flutter/shell/platform/windows/keyboard_key_channel_handler.h"
#include "flutter/shell/platform/windows/system_utils.h"
@ -199,12 +200,22 @@ FlutterWindowsEngine::FlutterWindowsEngine(
egl_manager_ = egl::Manager::Create(
static_cast<egl::GpuPreference>(project_->gpu_preference()));
window_proc_delegate_manager_ = std::make_unique<WindowProcDelegateManager>();
display_monitor_ = std::make_unique<DisplayMonitor>(this);
window_proc_delegate_manager_->RegisterTopLevelWindowProcDelegate(
[](HWND hwnd, UINT msg, WPARAM wpar, LPARAM lpar, void* user_data,
LRESULT* result) {
BASE_DCHECK(user_data);
FlutterWindowsEngine* that =
static_cast<FlutterWindowsEngine*>(user_data);
BASE_DCHECK(that->display_monitor_);
if (that->display_monitor_->HandleWindowMessage(hwnd, msg, wpar, lpar,
result)) {
return true;
}
BASE_DCHECK(that->lifecycle_manager_);
bool handled =
that->lifecycle_manager_->WindowProc(hwnd, msg, wpar, lpar, result);
@ -486,18 +497,7 @@ bool FlutterWindowsEngine::Run(std::string_view entrypoint) {
return false;
}
// Configure device frame rate displayed via devtools.
FlutterEngineDisplay display = {};
display.struct_size = sizeof(FlutterEngineDisplay);
display.display_id = 0;
display.single_display = true;
display.refresh_rate =
1.0 / (static_cast<double>(FrameInterval().count()) / 1000000000.0);
std::vector<FlutterEngineDisplay> displays = {display};
embedder_api_.NotifyDisplayUpdate(engine_,
kFlutterEngineDisplaysUpdateTypeStartup,
displays.data(), displays.size());
display_monitor_->UpdateDisplays();
SendSystemLocales();
@ -707,6 +707,15 @@ void FlutterWindowsEngine::AddPluginRegistrarDestructionCallback(
plugin_registrar_destruction_callbacks_[callback] = registrar;
}
void FlutterWindowsEngine::UpdateDisplay(
const std::vector<FlutterEngineDisplay>& displays) {
if (engine_) {
embedder_api_.NotifyDisplayUpdate(engine_,
kFlutterEngineDisplaysUpdateTypeStartup,
displays.data(), displays.size());
}
}
void FlutterWindowsEngine::SendWindowMetricsEvent(
const FlutterWindowMetricsEvent& event) {
if (engine_) {
@ -1043,8 +1052,11 @@ void FlutterWindowsEngine::OnQuit(std::optional<HWND> hwnd,
}
void FlutterWindowsEngine::OnDwmCompositionChanged() {
std::shared_lock read_lock(views_mutex_);
if (display_monitor_) {
display_monitor_->UpdateDisplays();
}
std::shared_lock read_lock(views_mutex_);
for (auto iterator = views_.begin(); iterator != views_.end(); iterator++) {
iterator->second->OnDwmCompositionChanged();
}
@ -1113,4 +1125,17 @@ bool FlutterWindowsEngine::Present(const FlutterPresentViewInfo* info) {
return compositor_->Present(view, info->layers, info->layers_count);
}
bool FlutterWindowsEngine::HandleDisplayMonitorMessage(HWND hwnd,
UINT message,
WPARAM wparam,
LPARAM lparam,
LRESULT* result) {
if (!display_monitor_) {
return false;
}
return display_monitor_->HandleWindowMessage(hwnd, message, wparam, lparam,
result);
}
} // namespace flutter

View File

@ -56,6 +56,7 @@ namespace flutter {
constexpr FlutterViewId kImplicitViewId = 0;
class FlutterWindowsView;
class DisplayMonitor;
// Update the thread priority for the Windows engine.
static void WindowsPlatformThreadPrioritySetter(
@ -159,6 +160,11 @@ class FlutterWindowsEngine {
return message_dispatcher_.get();
}
DisplayMonitor* display_monitor() { return display_monitor_.get(); }
// Notifies the engine about a display update.
void UpdateDisplay(const std::vector<FlutterEngineDisplay>& displays);
TaskRunner* task_runner() { return task_runner_.get(); }
BinaryMessenger* messenger_wrapper() { return messenger_wrapper_.get(); }
@ -418,6 +424,9 @@ class FlutterWindowsEngine {
// a view to the engine or after removing a view from the engine.
mutable std::shared_mutex views_mutex_;
// The display monitor.
std::unique_ptr<DisplayMonitor> display_monitor_;
// Task runner for tasks posted from the engine.
std::unique_ptr<TaskRunner> task_runner_;
@ -510,6 +519,13 @@ class FlutterWindowsEngine {
std::unique_ptr<PlatformViewPlugin> platform_view_plugin_;
// Handles display-related window messages.
bool HandleDisplayMonitorMessage(HWND hwnd,
UINT message,
WPARAM wparam,
LPARAM lparam,
LRESULT* result);
FML_DISALLOW_COPY_AND_ASSIGN(FlutterWindowsEngine);
};

View File

@ -43,8 +43,11 @@ void PumpMessage() {
namespace flutter {
namespace testing {
using ::testing::_;
using ::testing::DoAll;
using ::testing::NiceMock;
using ::testing::Return;
using ::testing::SetArgPointee;
class FlutterWindowsEngineTest : public WindowsTest {};
@ -66,6 +69,57 @@ TEST_F(FlutterWindowsEngineTest, RunDoesExpectedInitialization) {
builder.AddDartEntrypointArgument("arg1");
builder.AddDartEntrypointArgument("arg2");
auto windows_proc_table = std::make_shared<MockWindowsProcTable>();
HMONITOR mock_monitor = reinterpret_cast<HMONITOR>(1);
MONITORINFOEXW monitor_info = {};
monitor_info.cbSize = sizeof(MONITORINFOEXW);
monitor_info.rcMonitor = {0, 0, 1920, 1080};
monitor_info.rcWork = {0, 0, 1920, 1080};
monitor_info.dwFlags = MONITORINFOF_PRIMARY;
wcscpy_s(monitor_info.szDevice, L"\\\\.\\DISPLAY1");
EXPECT_CALL(*windows_proc_table, GetMonitorInfoW(mock_monitor, _))
.WillRepeatedly(DoAll(SetArgPointee<1>(monitor_info), Return(TRUE)));
EXPECT_CALL(*windows_proc_table, EnumDisplayMonitors(nullptr, nullptr, _, _))
.WillRepeatedly([&](HDC hdc, LPCRECT lprcClip, MONITORENUMPROC lpfnEnum,
LPARAM dwData) {
lpfnEnum(mock_monitor, nullptr, &monitor_info.rcMonitor, dwData);
return TRUE;
});
EXPECT_CALL(*windows_proc_table, GetDpiForMonitor(mock_monitor, _))
.WillRepeatedly(Return(96));
// Mock locale information
EXPECT_CALL(*windows_proc_table, GetThreadPreferredUILanguages(_, _, _, _))
.WillRepeatedly(
[](DWORD flags, PULONG count, PZZWSTR languages, PULONG length) {
// We need to mock the locale information twice because the first
// call is to get the size and the second call is to fill the
// buffer.
if (languages == nullptr) {
// First call is to get the size
*count = 1; // One language
*length = 10; // "fr-FR\0\0" (double null-terminated)
return TRUE;
} else {
// Second call is to fill the buffer
*count = 1;
// Fill with "fr-FR\0\0" (double null-terminated)
wchar_t* lang_buffer = languages;
wcscpy(lang_buffer, L"fr-FR");
// Move past the first null terminator to add the second
lang_buffer += wcslen(L"fr-FR") + 1;
*lang_buffer = L'\0';
return TRUE;
}
});
builder.SetWindowsProcTable(windows_proc_table);
std::unique_ptr<FlutterWindowsEngine> engine = builder.Build();
EngineModifier modifier(engine.get());
@ -141,23 +195,15 @@ TEST_F(FlutterWindowsEngineTest, RunDoesExpectedInitialization) {
// And it should send display info.
bool notify_display_update_called = false;
modifier.SetFrameInterval(16600000); // 60 fps.
modifier.embedder_api().NotifyDisplayUpdate = MOCK_ENGINE_PROC(
NotifyDisplayUpdate,
([&notify_display_update_called, engine_instance = engine.get()](
([&notify_display_update_called](
FLUTTER_API_SYMBOL(FlutterEngine) raw_engine,
const FlutterEngineDisplaysUpdateType update_type,
const FlutterEngineDisplay* embedder_displays,
size_t display_count) {
EXPECT_EQ(update_type, kFlutterEngineDisplaysUpdateTypeStartup);
EXPECT_EQ(display_count, 1);
FlutterEngineDisplay display = embedder_displays[0];
EXPECT_EQ(display.display_id, 0);
EXPECT_EQ(display.single_display, true);
EXPECT_EQ(std::floor(display.refresh_rate), 60.0);
notify_display_update_called = true;
return kSuccess;
}));

View File

@ -41,6 +41,24 @@ class MockWindowsProcTable : public WindowsProcTable {
MOCK_METHOD(HCURSOR, SetCursor, (HCURSOR cursor), (const, override));
MOCK_METHOD(BOOL,
EnumDisplaySettings,
(LPCWSTR lpszDeviceName, DWORD iModeNum, DEVMODEW* lpDevMode),
(const, override));
MOCK_METHOD(BOOL,
GetMonitorInfo,
(HMONITOR hMonitor, LPMONITORINFO lpmi),
(const, override));
MOCK_METHOD(
BOOL,
EnumDisplayMonitors,
(HDC hdc, LPCRECT lprcClip, MONITORENUMPROC lpfnEnum, LPARAM dwData),
(const, override));
MOCK_METHOD(UINT, GetDpiForMonitor, (HMONITOR, UINT), ());
private:
FML_DISALLOW_COPY_AND_ASSIGN(MockWindowsProcTable);
};

View File

@ -120,4 +120,33 @@ BOOL WindowsProcTable::AdjustWindowRectExForDpi(LPRECT lpRect,
dwExStyle, dpi);
}
int WindowsProcTable::GetSystemMetrics(int nIndex) const {
return ::GetSystemMetrics(nIndex);
}
BOOL WindowsProcTable::EnumDisplayDevices(LPCWSTR lpDevice,
DWORD iDevNum,
PDISPLAY_DEVICE lpDisplayDevice,
DWORD dwFlags) const {
return ::EnumDisplayDevices(lpDevice, iDevNum, lpDisplayDevice, dwFlags);
}
BOOL WindowsProcTable::EnumDisplaySettings(LPCWSTR lpszDeviceName,
DWORD iModeNum,
DEVMODEW* lpDevMode) const {
return ::EnumDisplaySettingsW(lpszDeviceName, iModeNum, lpDevMode);
}
BOOL WindowsProcTable::GetMonitorInfo(HMONITOR hMonitor,
LPMONITORINFO lpmi) const {
return ::GetMonitorInfoW(hMonitor, lpmi);
}
BOOL WindowsProcTable::EnumDisplayMonitors(HDC hdc,
LPCRECT lprcClip,
MONITORENUMPROC lpfnEnum,
LPARAM dwData) const {
return ::EnumDisplayMonitors(hdc, lprcClip, lpfnEnum, dwData);
}
} // namespace flutter

View File

@ -123,6 +123,44 @@ class WindowsProcTable {
DWORD dwExStyle,
UINT dpi) const;
// Get the system metrics.
//
// See:
// https://learn.microsoft.com/windows/win32/api/winuser/nf-winuser-getsystemmetrics
virtual int GetSystemMetrics(int nIndex) const;
// Enumerate display devices.
//
// See:
// https://learn.microsoft.com/windows/win32/api/winuser/nf-winuser-enumdisplaydevicesw
virtual BOOL EnumDisplayDevices(LPCWSTR lpDevice,
DWORD iDevNum,
PDISPLAY_DEVICE lpDisplayDevice,
DWORD dwFlags) const;
// Enumerate display settings.
//
// See:
// https://learn.microsoft.com/windows/win32/api/winuser/nf-winuser-enumdisplaysettingsw
virtual BOOL EnumDisplaySettings(LPCWSTR lpszDeviceName,
DWORD iModeNum,
DEVMODEW* lpDevMode) const;
// Get monitor info.
//
// See:
// https://learn.microsoft.com/windows/win32/api/winuser/nf-winuser-getmonitorinfow
virtual BOOL GetMonitorInfo(HMONITOR hMonitor, LPMONITORINFO lpmi) const;
// Enumerate display monitors.
//
// See:
// https://learn.microsoft.com/windows/win32/api/winuser/nf-winuser-enumdisplaymonitors
virtual BOOL EnumDisplayMonitors(HDC hdc,
LPCRECT lprcClip,
MONITORENUMPROC lpfnEnum,
LPARAM dwData) const;
private:
using GetPointerType_ = BOOL __stdcall(UINT32 pointerId,
POINTER_INPUT_TYPE* pointerType);