mirror of
https://github.com/flutter/flutter.git
synced 2026-02-20 02:29:02 +08:00
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:
parent
f15262c9f7
commit
0accf67215
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
@ -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",
|
||||
|
||||
104
engine/src/flutter/shell/platform/windows/display_monitor.cc
Normal file
104
engine/src/flutter/shell/platform/windows/display_monitor.cc
Normal 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
|
||||
49
engine/src/flutter/shell/platform/windows/display_monitor.h
Normal file
49
engine/src/flutter/shell/platform/windows/display_monitor.h
Normal 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_
|
||||
@ -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
|
||||
@ -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.
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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);
|
||||
};
|
||||
|
||||
|
||||
@ -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,
|
||||
([¬ify_display_update_called, engine_instance = engine.get()](
|
||||
([¬ify_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;
|
||||
}));
|
||||
|
||||
@ -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);
|
||||
};
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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);
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user