From e2f4825d02664085fc321fa00e6a675c2b3caf98 Mon Sep 17 00:00:00 2001 From: schectman Date: Mon, 10 Jul 2023 11:37:38 -0400 Subject: [PATCH 01/33] Begin lifecycle additions --- .../platform/windows/flutter_windows_engine.cc | 7 +++---- .../flutter_windows_engine_unittests.cc | 18 ++++++++++++++++++ .../windows/windows_lifecycle_manager.cc | 10 ++++++++++ .../windows/windows_lifecycle_manager.h | 18 ++++++++++++++++++ 4 files changed, 49 insertions(+), 4 deletions(-) diff --git a/shell/platform/windows/flutter_windows_engine.cc b/shell/platform/windows/flutter_windows_engine.cc index 68cccd27ab19f..c4cf94e528322 100644 --- a/shell/platform/windows/flutter_windows_engine.cc +++ b/shell/platform/windows/flutter_windows_engine.cc @@ -584,10 +584,9 @@ void FlutterWindowsEngine::SetNextFrameCallback(fml::closure callback) { } void FlutterWindowsEngine::SetLifecycleState(flutter::AppLifecycleState state) { - const char* state_name = flutter::AppLifecycleStateToString(state); - SendPlatformMessage("flutter/lifecycle", - reinterpret_cast(state_name), - strlen(state_name), nullptr, nullptr); + if (lifecycle_manager_) { + lifecycle_manager_->SetLifecycleState(state); + } } void FlutterWindowsEngine::SendSystemLocales() { diff --git a/shell/platform/windows/flutter_windows_engine_unittests.cc b/shell/platform/windows/flutter_windows_engine_unittests.cc index 80fb26f6cd3b3..3e5a4ed2a4f52 100644 --- a/shell/platform/windows/flutter_windows_engine_unittests.cc +++ b/shell/platform/windows/flutter_windows_engine_unittests.cc @@ -668,6 +668,7 @@ class MockWindowsLifecycleManager : public WindowsLifecycleManager { UINT)); MOCK_METHOD4(DispatchMessage, void(HWND, UINT, WPARAM, LPARAM)); MOCK_METHOD0(IsLastWindowOfProcess, bool(void)); + MOCK_METHOD1(SetLifecycleState, void(AppLifecycleState)); }; TEST_F(FlutterWindowsEngineTest, TestExit) { @@ -895,5 +896,22 @@ TEST_F(FlutterWindowsEngineTest, EnableApplicationLifecycle) { 0); } +TEST_F(FlutterWindowsEngineTest, AppStartsInResumedState) { + FlutterWindowsEngineBuilder builder{GetContext()}; + + auto window_binding_handler = + std::make_unique<::testing::NiceMock>(); + MockFlutterWindowsView view(std::move(window_binding_handler)); + view.SetEngine(builder.Build()); + FlutterWindowsEngine* engine = view.GetEngine(); + + EngineModifier modifier(engine); + modifier.embedder_api().RunsAOTCompiledDartCode = []() { return false; }; + auto handler = std::make_unique(engine); + EXPECT_CALL(*handler, SetLifecycleState(AppLifecycleState::kResumed)).Times(1); + modifier.SetLifecycleManager(std::move(handler)); + engine->Run(); +} + } // namespace testing } // namespace flutter diff --git a/shell/platform/windows/windows_lifecycle_manager.cc b/shell/platform/windows/windows_lifecycle_manager.cc index f8bf5de37f7bf..32f591c0404e9 100644 --- a/shell/platform/windows/windows_lifecycle_manager.cc +++ b/shell/platform/windows/windows_lifecycle_manager.cc @@ -163,4 +163,14 @@ void WindowsLifecycleManager::BeginProcessingClose() { process_close_ = true; } +void WindowsLifecycleManager::SetLifecycleState(flutter::AppLifecycleState state) { + state_ = state; + if (engine_) { + const char* state_name = flutter::AppLifecycleStateToString(state); + engine_->SendPlatformMessage("flutter/lifecycle", + reinterpret_cast(state_name), + strlen(state_name), nullptr, nullptr); + } +} + } // namespace flutter diff --git a/shell/platform/windows/windows_lifecycle_manager.h b/shell/platform/windows/windows_lifecycle_manager.h index b08ec57a4dbd8..2fe44280b4113 100644 --- a/shell/platform/windows/windows_lifecycle_manager.h +++ b/shell/platform/windows/windows_lifecycle_manager.h @@ -9,12 +9,20 @@ #include #include +#include #include +#include "flutter/shell/platform/common/app_lifecycle_state.h" + namespace flutter { class FlutterWindowsEngine; +struct WindowState { + bool is_visible; + bool is_focused; +}; + /// A manager for lifecycle events of the top-level window. /// /// Currently handles the following events: @@ -40,6 +48,8 @@ class WindowsLifecycleManager { // Signal to start consuming WM_CLOSE messages. void BeginProcessingClose(); + virtual void SetLifecycleState(flutter::AppLifecycleState state); + protected: // Check the number of top-level windows associated with this process, and // return true only if there are 1 or fewer. @@ -51,11 +61,19 @@ class WindowsLifecycleManager { LPARAM lparam); private: + + FlutterWindowsEngine* engine_; std::map, int> sent_close_messages_; bool process_close_; + + std::map window_states_; + + std::mutex state_update_lock_; + + flutter::AppLifecycleState state_; }; } // namespace flutter From 030975b79e61842b12fbb1a9844adf2984ae2f0c Mon Sep 17 00:00:00 2001 From: schectman Date: Mon, 10 Jul 2023 13:30:47 -0400 Subject: [PATCH 02/33] Add state transitions --- .../windows/windows_lifecycle_manager.cc | 38 ++++++++++++++++++- .../windows/windows_lifecycle_manager.h | 19 +++++++--- 2 files changed, 50 insertions(+), 7 deletions(-) diff --git a/shell/platform/windows/windows_lifecycle_manager.cc b/shell/platform/windows/windows_lifecycle_manager.cc index 32f591c0404e9..919226c21e275 100644 --- a/shell/platform/windows/windows_lifecycle_manager.cc +++ b/shell/platform/windows/windows_lifecycle_manager.cc @@ -163,14 +163,48 @@ void WindowsLifecycleManager::BeginProcessingClose() { process_close_ = true; } -void WindowsLifecycleManager::SetLifecycleState(flutter::AppLifecycleState state) { +void WindowsLifecycleManager::SetLifecycleState(AppLifecycleState state) { state_ = state; if (engine_) { - const char* state_name = flutter::AppLifecycleStateToString(state); + const char* state_name = AppLifecycleStateToString(state); engine_->SendPlatformMessage("flutter/lifecycle", reinterpret_cast(state_name), strlen(state_name), nullptr, nullptr); } } +void WindowsLifecycleManager::OnWindowStateEvent(HWND hwnd, WindowStateEvent event) { + std::lock_guard guard(state_update_lock_); + switch (event) { + case SHOW: { + bool first_shown_window = visible_windows_.empty(); + auto pair = visible_windows_.insert(hwnd); + if (first_shown_window && pair.second && state_ == AppLifecycleState::kHidden) { + SetLifecycleState(AppLifecycleState::kInactive); + } + break; + } + case HIDE: { + if (visible_windows_.erase(hwnd) && visible_windows_.empty() && (state_ == AppLifecycleState::kResumed || state_ == AppLifecycleState::kInactive)) { + SetLifecycleState(AppLifecycleState::kHidden); + } + break; + } + case FOCUS: { + bool first_focused_window = focused_windows_.empty(); + auto pair = focused_windows_.insert(hwnd); + if (first_focused_window && pair.second && state_ == AppLifecycleState::kInactive) { + SetLifecycleState(AppLifecycleState::kResumed); + } + break; + } + case UNFOCUS: { + if (focused_windows_.erase(hwnd) && focused_windows_.empty() && state_ == AppLifecycleState::kResumed) { + SetLifecycleState(AppLifecycleState::kInactive); + } + break; + } + } +} + } // namespace flutter diff --git a/shell/platform/windows/windows_lifecycle_manager.h b/shell/platform/windows/windows_lifecycle_manager.h index 2fe44280b4113..f00669f15ae85 100644 --- a/shell/platform/windows/windows_lifecycle_manager.h +++ b/shell/platform/windows/windows_lifecycle_manager.h @@ -11,6 +11,7 @@ #include #include #include +#include #include "flutter/shell/platform/common/app_lifecycle_state.h" @@ -18,9 +19,11 @@ namespace flutter { class FlutterWindowsEngine; -struct WindowState { - bool is_visible; - bool is_focused; +enum WindowStateEvent { + SHOW, + HIDE, + FOCUS, + UNFOCUS, }; /// A manager for lifecycle events of the top-level window. @@ -48,7 +51,11 @@ class WindowsLifecycleManager { // Signal to start consuming WM_CLOSE messages. void BeginProcessingClose(); - virtual void SetLifecycleState(flutter::AppLifecycleState state); + virtual void SetLifecycleState(AppLifecycleState state); + + virtual void OnWindowStateEvent(HWND hwnd, WindowStateEvent event); + + AppLifecycleState GetLifecycleState() { return state_; } protected: // Check the number of top-level windows associated with this process, and @@ -69,7 +76,9 @@ class WindowsLifecycleManager { bool process_close_; - std::map window_states_; + std::set visible_windows_; + + std::set focused_windows_; std::mutex state_update_lock_; From be44662a809f1b90bac822cced891d4176318085 Mon Sep 17 00:00:00 2001 From: schectman Date: Mon, 10 Jul 2023 13:34:04 -0400 Subject: [PATCH 03/33] Add WindowsLifecycleManager unit test file --- ci/licenses_golden/licenses_flutter | 1 + shell/platform/windows/BUILD.gn | 1 + shell/platform/windows/windows_lifecycle_manager_unittests.cc | 3 +++ 3 files changed, 5 insertions(+) create mode 100644 shell/platform/windows/windows_lifecycle_manager_unittests.cc diff --git a/ci/licenses_golden/licenses_flutter b/ci/licenses_golden/licenses_flutter index 8d1d6795f8fd5..fcf0dcd392b44 100644 --- a/ci/licenses_golden/licenses_flutter +++ b/ci/licenses_golden/licenses_flutter @@ -5953,6 +5953,7 @@ FILE: ../../../flutter/shell/platform/windows/window_proc_delegate_manager.h FILE: ../../../flutter/shell/platform/windows/window_state.h FILE: ../../../flutter/shell/platform/windows/windows_lifecycle_manager.cc FILE: ../../../flutter/shell/platform/windows/windows_lifecycle_manager.h +FILE: ../../../flutter/shell/platform/windows/windows_lifecycle_manager_unittests.cc FILE: ../../../flutter/shell/platform/windows/windows_proc_table.cc FILE: ../../../flutter/shell/platform/windows/windows_proc_table.h FILE: ../../../flutter/shell/platform/windows/windowsx_shim.h diff --git a/shell/platform/windows/BUILD.gn b/shell/platform/windows/BUILD.gn index 5ee5b431991c8..10369da9c097e 100644 --- a/shell/platform/windows/BUILD.gn +++ b/shell/platform/windows/BUILD.gn @@ -221,6 +221,7 @@ executable("flutter_windows_unittests") { "text_input_plugin_unittest.cc", "window_proc_delegate_manager_unittests.cc", "window_unittests.cc", + "windows_lifecycle_manager_unittests.cc", ] configs += diff --git a/shell/platform/windows/windows_lifecycle_manager_unittests.cc b/shell/platform/windows/windows_lifecycle_manager_unittests.cc new file mode 100644 index 0000000000000..e7217c7d7b3fa --- /dev/null +++ b/shell/platform/windows/windows_lifecycle_manager_unittests.cc @@ -0,0 +1,3 @@ +// 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. From 8ab87dfa5a0c6695728c9fa492225894e3b369f9 Mon Sep 17 00:00:00 2001 From: schectman Date: Mon, 10 Jul 2023 13:50:08 -0400 Subject: [PATCH 04/33] Test state transitions --- .../windows/windows_lifecycle_manager.cc | 5 ++ .../windows/windows_lifecycle_manager.h | 1 + .../windows_lifecycle_manager_unittests.cc | 58 +++++++++++++++++++ 3 files changed, 64 insertions(+) diff --git a/shell/platform/windows/windows_lifecycle_manager.cc b/shell/platform/windows/windows_lifecycle_manager.cc index 919226c21e275..252ad7ee425af 100644 --- a/shell/platform/windows/windows_lifecycle_manager.cc +++ b/shell/platform/windows/windows_lifecycle_manager.cc @@ -204,6 +204,11 @@ void WindowsLifecycleManager::OnWindowStateEvent(HWND hwnd, WindowStateEvent eve } break; } + case DESTROY: { + focused_windows_.erase(hwnd); + visible_windows_.erase(hwnd); + break; + } } } diff --git a/shell/platform/windows/windows_lifecycle_manager.h b/shell/platform/windows/windows_lifecycle_manager.h index f00669f15ae85..93590ae79cbb5 100644 --- a/shell/platform/windows/windows_lifecycle_manager.h +++ b/shell/platform/windows/windows_lifecycle_manager.h @@ -24,6 +24,7 @@ enum WindowStateEvent { HIDE, FOCUS, UNFOCUS, + DESTROY, }; /// A manager for lifecycle events of the top-level window. diff --git a/shell/platform/windows/windows_lifecycle_manager_unittests.cc b/shell/platform/windows/windows_lifecycle_manager_unittests.cc index e7217c7d7b3fa..513e6af5001b1 100644 --- a/shell/platform/windows/windows_lifecycle_manager_unittests.cc +++ b/shell/platform/windows/windows_lifecycle_manager_unittests.cc @@ -1,3 +1,61 @@ // 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/windows/windows_lifecycle_manager.h" + +#include "flutter/shell/platform/windows/testing/windows_test.h" +#include "gtest/gtest.h" + +namespace flutter{ +namespace testing{ + +class WindowsLifecycleManagerTest : public WindowsTest {}; + +TEST_F(WindowsLifecycleManagerTest, StateTransitions) { + WindowsLifecycleManager manager(nullptr); + HWND win1 = (HWND)1; + HWND win2 = (HWND)2; + + // Hidden to inactive upon window shown. + manager.SetLifecycleState(AppLifecycleState::kHidden); + manager.OnWindowStateEvent(win1, SHOW); + EXPECT_EQ(manager.GetLifecycleState(), AppLifecycleState::kInactive); + + // Showing a second window does not change state. + manager.OnWindowStateEvent(win2, SHOW); + EXPECT_EQ(manager.GetLifecycleState(), AppLifecycleState::kInactive); + + // Inactive to resumed upon window focus. + manager.OnWindowStateEvent(win2, FOCUS); + EXPECT_EQ(manager.GetLifecycleState(), AppLifecycleState::kResumed); + + // Showing a second window does not change state. + manager.OnWindowStateEvent(win1, FOCUS); + EXPECT_EQ(manager.GetLifecycleState(), AppLifecycleState::kResumed); + + // Unfocusing one window does not change state while another is focused. + manager.OnWindowStateEvent(win1, UNFOCUS); + EXPECT_EQ(manager.GetLifecycleState(), AppLifecycleState::kResumed); + + // Unfocusing final remaining focused window transitions to inactive. + manager.OnWindowStateEvent(win2, UNFOCUS); + EXPECT_EQ(manager.GetLifecycleState(), AppLifecycleState::kInactive); + + // Hiding one of two visible windows does not change state. + manager.OnWindowStateEvent(win2, HIDE); + EXPECT_EQ(manager.GetLifecycleState(), AppLifecycleState::kInactive); + + // Hiding only visible window transitions to hidden. + manager.OnWindowStateEvent(win1, HIDE); + EXPECT_EQ(manager.GetLifecycleState(), AppLifecycleState::kHidden); + + // Transition directly from resumed to hidden when the window is hidden. + manager.OnWindowStateEvent(win1, SHOW); + manager.OnWindowStateEvent(win1, FOCUS); + manager.OnWindowStateEvent(win1, HIDE); + EXPECT_EQ(manager.GetLifecycleState(), AppLifecycleState::kHidden); +} + +} +} From baadbb1142f7ef57fef0ba4909a29757a2e39f91 Mon Sep 17 00:00:00 2001 From: schectman Date: Mon, 10 Jul 2023 15:13:13 -0400 Subject: [PATCH 05/33] Capture FlutterWindow messages --- shell/platform/windows/flutter_window.cc | 23 ++++++++++++- shell/platform/windows/flutter_window.h | 9 ++++++ .../windows/flutter_windows_engine.cc | 4 +++ .../platform/windows/flutter_windows_engine.h | 4 +++ .../platform/windows/flutter_windows_view.cc | 6 ++++ shell/platform/windows/flutter_windows_view.h | 3 ++ shell/platform/windows/testing/mock_window.h | 2 ++ .../mock_window_binding_handler_delegate.h | 2 ++ shell/platform/windows/window.cc | 5 +++ shell/platform/windows/window.h | 4 +++ .../windows/window_binding_handler_delegate.h | 5 +++ .../windows/windows_lifecycle_manager.cc | 32 ++++++++++++++++--- .../windows/windows_lifecycle_manager.h | 1 - 13 files changed, 93 insertions(+), 7 deletions(-) diff --git a/shell/platform/windows/flutter_window.cc b/shell/platform/windows/flutter_window.cc index 6dba8cadc368b..8ab8ecd5ef3cf 100644 --- a/shell/platform/windows/flutter_window.cc +++ b/shell/platform/windows/flutter_window.cc @@ -69,7 +69,9 @@ static HCURSOR GetCursorByName(const std::string& cursor_name) { } // namespace FlutterWindow::FlutterWindow(int width, int height) - : binding_handler_delegate_(nullptr) { + : binding_handler_delegate_(nullptr), + restored_(false), + focused_(false) { Window::InitializeChild("FLUTTERVIEW", width, height); current_cursor_ = ::LoadCursor(nullptr, IDC_ARROW); } @@ -79,6 +81,12 @@ FlutterWindow::~FlutterWindow() {} void FlutterWindow::SetView(WindowBindingHandlerDelegate* window) { binding_handler_delegate_ = window; direct_manipulation_owner_->SetBindingHandlerDelegate(window); + if (restored_) { + binding_handler_delegate_->OnWindowStateEvent(GetWindowHandle(), SHOW); + } + if (focused_) { + binding_handler_delegate_->OnWindowStateEvent(GetWindowHandle(), FOCUS); + } } WindowsRenderTarget FlutterWindow::GetRenderTarget() { @@ -328,4 +336,17 @@ bool FlutterWindow::NeedsVSync() { return true; } +void FlutterWindow::OnWindowStateEvent(WindowStateEvent event) { + switch (event) { + case SHOW: restored_ = true; break; + case HIDE: restored_ = focused_ = false; break; + case FOCUS: restored_ = focused_ = true; break; + case UNFOCUS: focused_ = false; break; + } + HWND hwnd = GetWindowHandle(); + if (hwnd && binding_handler_delegate_) { + binding_handler_delegate_->OnWindowStateEvent(hwnd, event); + } +} + } // namespace flutter diff --git a/shell/platform/windows/flutter_window.h b/shell/platform/windows/flutter_window.h index 5725af5de1a6a..c485ebd0505ff 100644 --- a/shell/platform/windows/flutter_window.h +++ b/shell/platform/windows/flutter_window.h @@ -162,6 +162,9 @@ class FlutterWindow : public Window, public WindowBindingHandler { // |Window| ui::AXFragmentRootDelegateWin* GetAxFragmentRootDelegate() override; + // |Window| + void OnWindowStateEvent(WindowStateEvent event) override; + private: // A pointer to a FlutterWindowsView that can be used to update engine // windowing and input state. @@ -174,6 +177,12 @@ class FlutterWindow : public Window, public WindowBindingHandler { RECT cursor_rect_; FML_DISALLOW_COPY_AND_ASSIGN(FlutterWindow); + + // The window receives resize and focus messages before its view is set, so + // these values cache the state of the window in the meantime so that the + // proper application lifecycle state can be updated once the view is set. + bool restored_; + bool focused_; }; } // namespace flutter diff --git a/shell/platform/windows/flutter_windows_engine.cc b/shell/platform/windows/flutter_windows_engine.cc index c4cf94e528322..1fe2d53e6c3c3 100644 --- a/shell/platform/windows/flutter_windows_engine.cc +++ b/shell/platform/windows/flutter_windows_engine.cc @@ -795,4 +795,8 @@ void FlutterWindowsEngine::OnApplicationLifecycleEnabled() { lifecycle_manager_->BeginProcessingClose(); } +void FlutterWindowsEngine::OnWindowStateEvent(HWND hwnd, WindowStateEvent event) { + lifecycle_manager_->OnWindowStateEvent(hwnd, event); +} + } // namespace flutter diff --git a/shell/platform/windows/flutter_windows_engine.h b/shell/platform/windows/flutter_windows_engine.h index 3e5a7343287b6..a79393819470b 100644 --- a/shell/platform/windows/flutter_windows_engine.h +++ b/shell/platform/windows/flutter_windows_engine.h @@ -262,6 +262,10 @@ class FlutterWindowsEngine { // Registers the top level handler for the WM_CLOSE window message. void OnApplicationLifecycleEnabled(); + // Called when a Window receives an event that may alter the application + // lifecycle state. + void OnWindowStateEvent(HWND hwnd, WindowStateEvent event); + protected: // Creates the keyboard key handler. // diff --git a/shell/platform/windows/flutter_windows_view.cc b/shell/platform/windows/flutter_windows_view.cc index 131cd2a52a82c..d668d2f447b6c 100644 --- a/shell/platform/windows/flutter_windows_view.cc +++ b/shell/platform/windows/flutter_windows_view.cc @@ -673,4 +673,10 @@ void FlutterWindowsView::OnDwmCompositionChanged() { } } +void FlutterWindowsView::OnWindowStateEvent(HWND hwnd, WindowStateEvent event) { + if (engine_) { + engine_->OnWindowStateEvent(hwnd, event); + } +} + } // namespace flutter diff --git a/shell/platform/windows/flutter_windows_view.h b/shell/platform/windows/flutter_windows_view.h index 8dc31fafee39c..c536856947f18 100644 --- a/shell/platform/windows/flutter_windows_view.h +++ b/shell/platform/windows/flutter_windows_view.h @@ -211,6 +211,9 @@ class FlutterWindowsView : public WindowBindingHandlerDelegate, return accessibility_bridge_; } + // |WindowBidningHandlerDelegate| + void OnWindowStateEvent(HWND hwnd, WindowStateEvent event) override; + protected: virtual void NotifyWinEventWrapper(ui::AXPlatformNodeWin* node, ax::mojom::Event event); diff --git a/shell/platform/windows/testing/mock_window.h b/shell/platform/windows/testing/mock_window.h index 53567b723dafc..4294476d91750 100644 --- a/shell/platform/windows/testing/mock_window.h +++ b/shell/platform/windows/testing/mock_window.h @@ -66,6 +66,8 @@ class MockWindow : public Window { MOCK_METHOD3(OnGetObject, LRESULT(UINT, WPARAM, LPARAM)); + MOCK_METHOD1(OnWindowStateEvent, void(WindowStateEvent)); + void CallOnImeComposition(UINT const message, WPARAM const wparam, LPARAM const lparam); diff --git a/shell/platform/windows/testing/mock_window_binding_handler_delegate.h b/shell/platform/windows/testing/mock_window_binding_handler_delegate.h index 35147d4e09200..fb58b354cb72d 100644 --- a/shell/platform/windows/testing/mock_window_binding_handler_delegate.h +++ b/shell/platform/windows/testing/mock_window_binding_handler_delegate.h @@ -61,6 +61,8 @@ class MockWindowBindingHandlerDelegate : public WindowBindingHandlerDelegate { MOCK_METHOD0(GetAxFragmentRootDelegate, ui::AXFragmentRootDelegateWin*()); + MOCK_METHOD2(OnWindowStateEvent, void(HWND, WindowStateEvent)); + private: FML_DISALLOW_COPY_AND_ASSIGN(MockWindowBindingHandlerDelegate); }; diff --git a/shell/platform/windows/window.cc b/shell/platform/windows/window.cc index ceb9cb8379ff9..b0680bce42ea1 100644 --- a/shell/platform/windows/window.cc +++ b/shell/platform/windows/window.cc @@ -352,6 +352,8 @@ Window::HandleMessage(UINT const message, current_width_ = width; current_height_ = height; HandleResize(width, height); + + OnWindowStateEvent(width == 0 && height == 0 ? HIDE : SHOW); break; case WM_PAINT: OnPaint(); @@ -430,9 +432,11 @@ Window::HandleMessage(UINT const message, break; } case WM_SETFOCUS: + OnWindowStateEvent(FOCUS); ::CreateCaret(window_handle_, nullptr, 1, 1); break; case WM_KILLFOCUS: + OnWindowStateEvent(UNFOCUS); ::DestroyCaret(); break; case WM_LBUTTONDOWN: @@ -612,6 +616,7 @@ void Window::UpdateScrollOffsetMultiplier() { void Window::Destroy() { if (window_handle_) { + OnWindowStateEvent(HIDE); text_input_manager_->SetWindowHandle(nullptr); DestroyWindow(window_handle_); window_handle_ = nullptr; diff --git a/shell/platform/windows/window.h b/shell/platform/windows/window.h index 632e140979575..b3605cd7b19d5 100644 --- a/shell/platform/windows/window.h +++ b/shell/platform/windows/window.h @@ -18,6 +18,7 @@ #include "flutter/shell/platform/windows/keyboard_manager.h" #include "flutter/shell/platform/windows/sequential_id_generator.h" #include "flutter/shell/platform/windows/text_input_manager.h" +#include "flutter/shell/platform/windows/windows_lifecycle_manager.h" #include "flutter/shell/platform/windows/windows_proc_table.h" #include "flutter/shell/platform/windows/windowsx_shim.h" #include "flutter/third_party/accessibility/ax/platform/ax_fragment_root_delegate_win.h" @@ -223,6 +224,9 @@ class Window : public KeyboardManager::WindowDelegate { // Called to obtain a pointer to the fragment root delegate. virtual ui::AXFragmentRootDelegateWin* GetAxFragmentRootDelegate() = 0; + // Called on a resize or focus event. + virtual void OnWindowStateEvent(WindowStateEvent event) = 0; + protected: // Win32's DefWindowProc. // diff --git a/shell/platform/windows/window_binding_handler_delegate.h b/shell/platform/windows/window_binding_handler_delegate.h index 1812107f2dfa9..27c5687adf168 100644 --- a/shell/platform/windows/window_binding_handler_delegate.h +++ b/shell/platform/windows/window_binding_handler_delegate.h @@ -9,6 +9,7 @@ #include "flutter/shell/platform/common/geometry.h" #include "flutter/shell/platform/embedder/embedder.h" +#include "flutter/shell/platform/windows/windows_lifecycle_manager.h" #include "flutter/third_party/accessibility/ax/platform/ax_fragment_root_delegate_win.h" #include "flutter/third_party/accessibility/gfx/native_widget_types.h" @@ -144,6 +145,10 @@ class WindowBindingHandlerDelegate { // MSAA, UIA elements do not explicitly store or enumerate their // children and parents, so a method such as this is required. virtual ui::AXFragmentRootDelegateWin* GetAxFragmentRootDelegate() = 0; + + // Called when a window receives an event that may alter application lifecycle + // state. + virtual void OnWindowStateEvent(HWND hwnd, WindowStateEvent event) = 0; }; } // namespace flutter diff --git a/shell/platform/windows/windows_lifecycle_manager.cc b/shell/platform/windows/windows_lifecycle_manager.cc index 252ad7ee425af..cf05dbff86444 100644 --- a/shell/platform/windows/windows_lifecycle_manager.cc +++ b/shell/platform/windows/windows_lifecycle_manager.cc @@ -75,6 +75,25 @@ bool WindowsLifecycleManager::WindowProc(HWND hwnd, case WM_DWMCOMPOSITIONCHANGED: engine_->OnDwmCompositionChanged(); break; + + case WM_SIZE: + if (wpar == SIZE_MAXIMIZED || wpar == SIZE_RESTORED) { + OnWindowStateEvent(hwnd, SHOW); + } + else if (wpar == SIZE_MINIMIZED) { + OnWindowStateEvent(hwnd, HIDE); + } + break; + + case WM_SHOWWINDOW: + if (!wpar) { + OnWindowStateEvent(hwnd, HIDE); + } + break; + + case WM_DESTROY: + OnWindowStateEvent(hwnd, HIDE); + break; } return false; } @@ -164,6 +183,9 @@ void WindowsLifecycleManager::BeginProcessingClose() { } void WindowsLifecycleManager::SetLifecycleState(AppLifecycleState state) { + if (state_ == state) { + return; + } state_ = state; if (engine_) { const char* state_name = AppLifecycleStateToString(state); @@ -174,6 +196,11 @@ void WindowsLifecycleManager::SetLifecycleState(AppLifecycleState state) { } void WindowsLifecycleManager::OnWindowStateEvent(HWND hwnd, WindowStateEvent event) { + // Synthesize an unfocus event when a focused window is hidden. + if (event == HIDE && focused_windows_.find(hwnd) != focused_windows_.end()) { + OnWindowStateEvent(hwnd, UNFOCUS); + } + std::lock_guard guard(state_update_lock_); switch (event) { case SHOW: { @@ -204,11 +231,6 @@ void WindowsLifecycleManager::OnWindowStateEvent(HWND hwnd, WindowStateEvent eve } break; } - case DESTROY: { - focused_windows_.erase(hwnd); - visible_windows_.erase(hwnd); - break; - } } } diff --git a/shell/platform/windows/windows_lifecycle_manager.h b/shell/platform/windows/windows_lifecycle_manager.h index 93590ae79cbb5..f00669f15ae85 100644 --- a/shell/platform/windows/windows_lifecycle_manager.h +++ b/shell/platform/windows/windows_lifecycle_manager.h @@ -24,7 +24,6 @@ enum WindowStateEvent { HIDE, FOCUS, UNFOCUS, - DESTROY, }; /// A manager for lifecycle events of the top-level window. From 4612d909e264ed73d80033d6d9926b8c29e8a402 Mon Sep 17 00:00:00 2001 From: schectman Date: Mon, 10 Jul 2023 15:40:38 -0400 Subject: [PATCH 06/33] Test top level messages --- .../platform/windows/flutter_windows_engine.h | 2 ++ .../flutter_windows_engine_unittests.cc | 23 +++++++++++++++++++ .../windows/windows_lifecycle_manager.cc | 4 +++- 3 files changed, 28 insertions(+), 1 deletion(-) diff --git a/shell/platform/windows/flutter_windows_engine.h b/shell/platform/windows/flutter_windows_engine.h index a79393819470b..1ab71d20ddec3 100644 --- a/shell/platform/windows/flutter_windows_engine.h +++ b/shell/platform/windows/flutter_windows_engine.h @@ -266,6 +266,8 @@ class FlutterWindowsEngine { // lifecycle state. void OnWindowStateEvent(HWND hwnd, WindowStateEvent event); + WindowsLifecycleManager* GetLifecycleManager() { return lifecycle_manager_.get(); } + protected: // Creates the keyboard key handler. // diff --git a/shell/platform/windows/flutter_windows_engine_unittests.cc b/shell/platform/windows/flutter_windows_engine_unittests.cc index 3e5a4ed2a4f52..ea9d4779c1c84 100644 --- a/shell/platform/windows/flutter_windows_engine_unittests.cc +++ b/shell/platform/windows/flutter_windows_engine_unittests.cc @@ -913,5 +913,28 @@ TEST_F(FlutterWindowsEngineTest, AppStartsInResumedState) { engine->Run(); } +TEST_F(FlutterWindowsEngineTest, LifecycleStateTransition) { + FlutterWindowsEngineBuilder builder{GetContext()}; + + auto window_binding_handler = + std::make_unique<::testing::NiceMock>(); + MockFlutterWindowsView view(std::move(window_binding_handler)); + view.SetEngine(builder.Build()); + FlutterWindowsEngine* engine = view.GetEngine(); + + EngineModifier modifier(engine); + modifier.embedder_api().RunsAOTCompiledDartCode = []() { return false; }; + engine->Run(); + + engine->window_proc_delegate_manager()->OnTopLevelWindowProc((HWND)1, WM_SIZE, SIZE_RESTORED, 0); + EXPECT_EQ(engine->GetLifecycleManager()->GetLifecycleState(), AppLifecycleState::kResumed); + + engine->window_proc_delegate_manager()->OnTopLevelWindowProc((HWND)1, WM_SIZE, SIZE_MINIMIZED, 0); + EXPECT_EQ(engine->GetLifecycleManager()->GetLifecycleState(), AppLifecycleState::kHidden); + + engine->window_proc_delegate_manager()->OnTopLevelWindowProc((HWND)1, WM_SIZE, SIZE_RESTORED, 0); + EXPECT_EQ(engine->GetLifecycleManager()->GetLifecycleState(), AppLifecycleState::kInactive); +} + } // namespace testing } // namespace flutter diff --git a/shell/platform/windows/windows_lifecycle_manager.cc b/shell/platform/windows/windows_lifecycle_manager.cc index cf05dbff86444..9d5225ac36f9e 100644 --- a/shell/platform/windows/windows_lifecycle_manager.cc +++ b/shell/platform/windows/windows_lifecycle_manager.cc @@ -212,7 +212,9 @@ void WindowsLifecycleManager::OnWindowStateEvent(HWND hwnd, WindowStateEvent eve break; } case HIDE: { - if (visible_windows_.erase(hwnd) && visible_windows_.empty() && (state_ == AppLifecycleState::kResumed || state_ == AppLifecycleState::kInactive)) { + bool present = visible_windows_.erase(hwnd); + bool empty = visible_windows_.empty(); + if (present && empty && (state_ == AppLifecycleState::kResumed || state_ == AppLifecycleState::kInactive)) { SetLifecycleState(AppLifecycleState::kHidden); } break; From 958fe90c7fd5c2065a058fa8265fb82feb45a0cf Mon Sep 17 00:00:00 2001 From: schectman Date: Mon, 10 Jul 2023 15:49:11 -0400 Subject: [PATCH 07/33] Testing FlutterWindow --- .../windows/flutter_window_unittests.cc | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/shell/platform/windows/flutter_window_unittests.cc b/shell/platform/windows/flutter_window_unittests.cc index e8ec2820666b1..9e28507dc00b2 100644 --- a/shell/platform/windows/flutter_window_unittests.cc +++ b/shell/platform/windows/flutter_window_unittests.cc @@ -324,5 +324,28 @@ TEST(FlutterWindowTest, AlertNode) { EXPECT_EQ(role.lVal, ROLE_SYSTEM_ALERT); } +TEST(FlutterWindowTest, LifecycleFocusMessages) { + MockFlutterWindow win32window; + MockWindowBindingHandlerDelegate delegate; + win32window.SetView(&delegate); + + WindowStateEvent last_event; + ON_CALL(delegate, OnWindowStateEvent).WillByDefault([&last_event](HWND hwnd, WindowStateEvent event) { + last_event = event; + }); + + win32window.InjectWindowMessage(WM_SIZE, 0, 0); + EXPECT_EQ(last_event, HIDE); + + win32window.InjectWindowMessage(WM_SIZE, 0, MAKEWORD(1, 1)); + EXPECT_EQ(last_event, SHOW); + + win32window.InjectWindowMessage(WM_SETFOCUS, 0, 0);; + EXPECT_EQ(last_event, FOCUS); + + win32window.InjectWindowMessage(WM_KILLFOCUS, 0, 0); + EXPECT_EQ(last_event, UNFOCUS); +} + } // namespace testing } // namespace flutter From 1811b77b792ac32ac22164f3e2b65ad81aed15ed Mon Sep 17 00:00:00 2001 From: schectman Date: Mon, 10 Jul 2023 16:27:02 -0400 Subject: [PATCH 08/33] Test FlutterWindow messages --- shell/platform/windows/flutter_window.cc | 8 +++++--- shell/platform/windows/flutter_window_unittests.cc | 7 ++++++- shell/platform/windows/flutter_windows_view.h | 2 +- shell/platform/windows/window.cc | 1 - 4 files changed, 12 insertions(+), 6 deletions(-) diff --git a/shell/platform/windows/flutter_window.cc b/shell/platform/windows/flutter_window.cc index 8ab8ecd5ef3cf..e8aeb069766ba 100644 --- a/shell/platform/windows/flutter_window.cc +++ b/shell/platform/windows/flutter_window.cc @@ -76,16 +76,18 @@ FlutterWindow::FlutterWindow(int width, int height) current_cursor_ = ::LoadCursor(nullptr, IDC_ARROW); } -FlutterWindow::~FlutterWindow() {} +FlutterWindow::~FlutterWindow() { + OnWindowStateEvent(HIDE); +} void FlutterWindow::SetView(WindowBindingHandlerDelegate* window) { binding_handler_delegate_ = window; direct_manipulation_owner_->SetBindingHandlerDelegate(window); if (restored_) { - binding_handler_delegate_->OnWindowStateEvent(GetWindowHandle(), SHOW); + OnWindowStateEvent(SHOW); } if (focused_) { - binding_handler_delegate_->OnWindowStateEvent(GetWindowHandle(), FOCUS); + OnWindowStateEvent(FOCUS); } } diff --git a/shell/platform/windows/flutter_window_unittests.cc b/shell/platform/windows/flutter_window_unittests.cc index 9e28507dc00b2..ee6ce28df7aec 100644 --- a/shell/platform/windows/flutter_window_unittests.cc +++ b/shell/platform/windows/flutter_window_unittests.cc @@ -27,7 +27,9 @@ class MockFlutterWindow : public FlutterWindow { ON_CALL(*this, GetDpiScale()) .WillByDefault(Return(this->FlutterWindow::GetDpiScale())); } - virtual ~MockFlutterWindow() {} + virtual ~MockFlutterWindow() { + SetView(nullptr); + } // Wrapper for GetCurrentDPI() which is a protected method. UINT GetDpi() { return GetCurrentDPI(); } @@ -61,6 +63,7 @@ class MockFlutterWindow : public FlutterWindow { MOCK_METHOD1(Win32MapVkToChar, uint32_t(uint32_t)); MOCK_METHOD0(GetPlatformWindow, HWND()); MOCK_METHOD0(GetAxFragmentRootDelegate, ui::AXFragmentRootDelegateWin*()); + //MOCK_METHOD1(OnWindowStateEvent, void(WindowStateEvent)); protected: // |KeyboardManager::WindowDelegate| @@ -229,6 +232,8 @@ TEST(FlutterWindowTest, OnPointerStarSendsDeviceType) { kDefaultPointerDeviceId, WM_LBUTTONDOWN); win32window.OnPointerLeave(10.0, 10.0, kFlutterPointerDeviceKindStylus, kDefaultPointerDeviceId); + + win32window.SetView(nullptr); } // Tests that calls to OnScroll in turn calls GetScrollOffsetMultiplier diff --git a/shell/platform/windows/flutter_windows_view.h b/shell/platform/windows/flutter_windows_view.h index c536856947f18..792db3f5b9f95 100644 --- a/shell/platform/windows/flutter_windows_view.h +++ b/shell/platform/windows/flutter_windows_view.h @@ -211,7 +211,7 @@ class FlutterWindowsView : public WindowBindingHandlerDelegate, return accessibility_bridge_; } - // |WindowBidningHandlerDelegate| + // |WindowBindingHandlerDelegate| void OnWindowStateEvent(HWND hwnd, WindowStateEvent event) override; protected: diff --git a/shell/platform/windows/window.cc b/shell/platform/windows/window.cc index b0680bce42ea1..b45a16494ca40 100644 --- a/shell/platform/windows/window.cc +++ b/shell/platform/windows/window.cc @@ -616,7 +616,6 @@ void Window::UpdateScrollOffsetMultiplier() { void Window::Destroy() { if (window_handle_) { - OnWindowStateEvent(HIDE); text_input_manager_->SetWindowHandle(nullptr); DestroyWindow(window_handle_); window_handle_ = nullptr; From 8ef5e5862c6631094f8ae08d47b0c81eeb0f95be Mon Sep 17 00:00:00 2001 From: schectman Date: Mon, 10 Jul 2023 17:02:55 -0400 Subject: [PATCH 09/33] Todo for non-flutter windows --- shell/platform/windows/windows_lifecycle_manager.h | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/shell/platform/windows/windows_lifecycle_manager.h b/shell/platform/windows/windows_lifecycle_manager.h index f00669f15ae85..5760bdfd768e8 100644 --- a/shell/platform/windows/windows_lifecycle_manager.h +++ b/shell/platform/windows/windows_lifecycle_manager.h @@ -45,7 +45,12 @@ class WindowsLifecycleManager { std::optional lparam, UINT exit_code); - // Intercept top level window messages, only paying attention to WM_CLOSE. + // TODO(schectman): Provide an API function for non-Flutter windows' WndProc + // function to call in order to contribute to the application lifecycle. + // https://github.com/flutter/flutter/issues/103637 + + // Intercept top level window WM_CLOSE message and listen to events that may + // update the application lifecycle. bool WindowProc(HWND hwnd, UINT msg, WPARAM w, LPARAM l, LRESULT* result); // Signal to start consuming WM_CLOSE messages. From 777215da262d03c76aaf29a69e4e57157c687c2e Mon Sep 17 00:00:00 2001 From: schectman Date: Tue, 11 Jul 2023 12:14:50 -0400 Subject: [PATCH 10/33] Formatting --- shell/platform/windows/flutter_window.cc | 20 +++++++++++------- .../windows/flutter_window_unittests.cc | 16 +++++++------- .../windows/flutter_windows_engine.cc | 3 ++- .../platform/windows/flutter_windows_engine.h | 4 +++- .../flutter_windows_engine_unittests.cc | 21 ++++++++++++------- .../windows/windows_lifecycle_manager.cc | 19 ++++++++++------- .../windows/windows_lifecycle_manager.h | 2 -- .../windows_lifecycle_manager_unittests.cc | 8 +++---- 8 files changed, 56 insertions(+), 37 deletions(-) diff --git a/shell/platform/windows/flutter_window.cc b/shell/platform/windows/flutter_window.cc index e8aeb069766ba..8976aa080eef0 100644 --- a/shell/platform/windows/flutter_window.cc +++ b/shell/platform/windows/flutter_window.cc @@ -69,9 +69,7 @@ static HCURSOR GetCursorByName(const std::string& cursor_name) { } // namespace FlutterWindow::FlutterWindow(int width, int height) - : binding_handler_delegate_(nullptr), - restored_(false), - focused_(false) { + : binding_handler_delegate_(nullptr), restored_(false), focused_(false) { Window::InitializeChild("FLUTTERVIEW", width, height); current_cursor_ = ::LoadCursor(nullptr, IDC_ARROW); } @@ -340,10 +338,18 @@ bool FlutterWindow::NeedsVSync() { void FlutterWindow::OnWindowStateEvent(WindowStateEvent event) { switch (event) { - case SHOW: restored_ = true; break; - case HIDE: restored_ = focused_ = false; break; - case FOCUS: restored_ = focused_ = true; break; - case UNFOCUS: focused_ = false; break; + case SHOW: + restored_ = true; + break; + case HIDE: + restored_ = focused_ = false; + break; + case FOCUS: + restored_ = focused_ = true; + break; + case UNFOCUS: + focused_ = false; + break; } HWND hwnd = GetWindowHandle(); if (hwnd && binding_handler_delegate_) { diff --git a/shell/platform/windows/flutter_window_unittests.cc b/shell/platform/windows/flutter_window_unittests.cc index ee6ce28df7aec..7026fbfc74d6c 100644 --- a/shell/platform/windows/flutter_window_unittests.cc +++ b/shell/platform/windows/flutter_window_unittests.cc @@ -27,9 +27,7 @@ class MockFlutterWindow : public FlutterWindow { ON_CALL(*this, GetDpiScale()) .WillByDefault(Return(this->FlutterWindow::GetDpiScale())); } - virtual ~MockFlutterWindow() { - SetView(nullptr); - } + virtual ~MockFlutterWindow() { SetView(nullptr); } // Wrapper for GetCurrentDPI() which is a protected method. UINT GetDpi() { return GetCurrentDPI(); } @@ -63,7 +61,7 @@ class MockFlutterWindow : public FlutterWindow { MOCK_METHOD1(Win32MapVkToChar, uint32_t(uint32_t)); MOCK_METHOD0(GetPlatformWindow, HWND()); MOCK_METHOD0(GetAxFragmentRootDelegate, ui::AXFragmentRootDelegateWin*()); - //MOCK_METHOD1(OnWindowStateEvent, void(WindowStateEvent)); + // MOCK_METHOD1(OnWindowStateEvent, void(WindowStateEvent)); protected: // |KeyboardManager::WindowDelegate| @@ -335,9 +333,10 @@ TEST(FlutterWindowTest, LifecycleFocusMessages) { win32window.SetView(&delegate); WindowStateEvent last_event; - ON_CALL(delegate, OnWindowStateEvent).WillByDefault([&last_event](HWND hwnd, WindowStateEvent event) { - last_event = event; - }); + ON_CALL(delegate, OnWindowStateEvent) + .WillByDefault([&last_event](HWND hwnd, WindowStateEvent event) { + last_event = event; + }); win32window.InjectWindowMessage(WM_SIZE, 0, 0); EXPECT_EQ(last_event, HIDE); @@ -345,7 +344,8 @@ TEST(FlutterWindowTest, LifecycleFocusMessages) { win32window.InjectWindowMessage(WM_SIZE, 0, MAKEWORD(1, 1)); EXPECT_EQ(last_event, SHOW); - win32window.InjectWindowMessage(WM_SETFOCUS, 0, 0);; + win32window.InjectWindowMessage(WM_SETFOCUS, 0, 0); + ; EXPECT_EQ(last_event, FOCUS); win32window.InjectWindowMessage(WM_KILLFOCUS, 0, 0); diff --git a/shell/platform/windows/flutter_windows_engine.cc b/shell/platform/windows/flutter_windows_engine.cc index 1fe2d53e6c3c3..345236fb7590a 100644 --- a/shell/platform/windows/flutter_windows_engine.cc +++ b/shell/platform/windows/flutter_windows_engine.cc @@ -795,7 +795,8 @@ void FlutterWindowsEngine::OnApplicationLifecycleEnabled() { lifecycle_manager_->BeginProcessingClose(); } -void FlutterWindowsEngine::OnWindowStateEvent(HWND hwnd, WindowStateEvent event) { +void FlutterWindowsEngine::OnWindowStateEvent(HWND hwnd, + WindowStateEvent event) { lifecycle_manager_->OnWindowStateEvent(hwnd, event); } diff --git a/shell/platform/windows/flutter_windows_engine.h b/shell/platform/windows/flutter_windows_engine.h index 1ab71d20ddec3..8980351521d54 100644 --- a/shell/platform/windows/flutter_windows_engine.h +++ b/shell/platform/windows/flutter_windows_engine.h @@ -266,7 +266,9 @@ class FlutterWindowsEngine { // lifecycle state. void OnWindowStateEvent(HWND hwnd, WindowStateEvent event); - WindowsLifecycleManager* GetLifecycleManager() { return lifecycle_manager_.get(); } + WindowsLifecycleManager* GetLifecycleManager() { + return lifecycle_manager_.get(); + } protected: // Creates the keyboard key handler. diff --git a/shell/platform/windows/flutter_windows_engine_unittests.cc b/shell/platform/windows/flutter_windows_engine_unittests.cc index ea9d4779c1c84..5f27d4bf1b676 100644 --- a/shell/platform/windows/flutter_windows_engine_unittests.cc +++ b/shell/platform/windows/flutter_windows_engine_unittests.cc @@ -908,7 +908,8 @@ TEST_F(FlutterWindowsEngineTest, AppStartsInResumedState) { EngineModifier modifier(engine); modifier.embedder_api().RunsAOTCompiledDartCode = []() { return false; }; auto handler = std::make_unique(engine); - EXPECT_CALL(*handler, SetLifecycleState(AppLifecycleState::kResumed)).Times(1); + EXPECT_CALL(*handler, SetLifecycleState(AppLifecycleState::kResumed)) + .Times(1); modifier.SetLifecycleManager(std::move(handler)); engine->Run(); } @@ -926,14 +927,20 @@ TEST_F(FlutterWindowsEngineTest, LifecycleStateTransition) { modifier.embedder_api().RunsAOTCompiledDartCode = []() { return false; }; engine->Run(); - engine->window_proc_delegate_manager()->OnTopLevelWindowProc((HWND)1, WM_SIZE, SIZE_RESTORED, 0); - EXPECT_EQ(engine->GetLifecycleManager()->GetLifecycleState(), AppLifecycleState::kResumed); + engine->window_proc_delegate_manager()->OnTopLevelWindowProc( + (HWND)1, WM_SIZE, SIZE_RESTORED, 0); + EXPECT_EQ(engine->GetLifecycleManager()->GetLifecycleState(), + AppLifecycleState::kResumed); - engine->window_proc_delegate_manager()->OnTopLevelWindowProc((HWND)1, WM_SIZE, SIZE_MINIMIZED, 0); - EXPECT_EQ(engine->GetLifecycleManager()->GetLifecycleState(), AppLifecycleState::kHidden); + engine->window_proc_delegate_manager()->OnTopLevelWindowProc( + (HWND)1, WM_SIZE, SIZE_MINIMIZED, 0); + EXPECT_EQ(engine->GetLifecycleManager()->GetLifecycleState(), + AppLifecycleState::kHidden); - engine->window_proc_delegate_manager()->OnTopLevelWindowProc((HWND)1, WM_SIZE, SIZE_RESTORED, 0); - EXPECT_EQ(engine->GetLifecycleManager()->GetLifecycleState(), AppLifecycleState::kInactive); + engine->window_proc_delegate_manager()->OnTopLevelWindowProc( + (HWND)1, WM_SIZE, SIZE_RESTORED, 0); + EXPECT_EQ(engine->GetLifecycleManager()->GetLifecycleState(), + AppLifecycleState::kInactive); } } // namespace testing diff --git a/shell/platform/windows/windows_lifecycle_manager.cc b/shell/platform/windows/windows_lifecycle_manager.cc index 9d5225ac36f9e..f5f2f2263865a 100644 --- a/shell/platform/windows/windows_lifecycle_manager.cc +++ b/shell/platform/windows/windows_lifecycle_manager.cc @@ -79,8 +79,7 @@ bool WindowsLifecycleManager::WindowProc(HWND hwnd, case WM_SIZE: if (wpar == SIZE_MAXIMIZED || wpar == SIZE_RESTORED) { OnWindowStateEvent(hwnd, SHOW); - } - else if (wpar == SIZE_MINIMIZED) { + } else if (wpar == SIZE_MINIMIZED) { OnWindowStateEvent(hwnd, HIDE); } break; @@ -195,7 +194,8 @@ void WindowsLifecycleManager::SetLifecycleState(AppLifecycleState state) { } } -void WindowsLifecycleManager::OnWindowStateEvent(HWND hwnd, WindowStateEvent event) { +void WindowsLifecycleManager::OnWindowStateEvent(HWND hwnd, + WindowStateEvent event) { // Synthesize an unfocus event when a focused window is hidden. if (event == HIDE && focused_windows_.find(hwnd) != focused_windows_.end()) { OnWindowStateEvent(hwnd, UNFOCUS); @@ -206,7 +206,8 @@ void WindowsLifecycleManager::OnWindowStateEvent(HWND hwnd, WindowStateEvent eve case SHOW: { bool first_shown_window = visible_windows_.empty(); auto pair = visible_windows_.insert(hwnd); - if (first_shown_window && pair.second && state_ == AppLifecycleState::kHidden) { + if (first_shown_window && pair.second && + state_ == AppLifecycleState::kHidden) { SetLifecycleState(AppLifecycleState::kInactive); } break; @@ -214,7 +215,9 @@ void WindowsLifecycleManager::OnWindowStateEvent(HWND hwnd, WindowStateEvent eve case HIDE: { bool present = visible_windows_.erase(hwnd); bool empty = visible_windows_.empty(); - if (present && empty && (state_ == AppLifecycleState::kResumed || state_ == AppLifecycleState::kInactive)) { + if (present && empty && + (state_ == AppLifecycleState::kResumed || + state_ == AppLifecycleState::kInactive)) { SetLifecycleState(AppLifecycleState::kHidden); } break; @@ -222,13 +225,15 @@ void WindowsLifecycleManager::OnWindowStateEvent(HWND hwnd, WindowStateEvent eve case FOCUS: { bool first_focused_window = focused_windows_.empty(); auto pair = focused_windows_.insert(hwnd); - if (first_focused_window && pair.second && state_ == AppLifecycleState::kInactive) { + if (first_focused_window && pair.second && + state_ == AppLifecycleState::kInactive) { SetLifecycleState(AppLifecycleState::kResumed); } break; } case UNFOCUS: { - if (focused_windows_.erase(hwnd) && focused_windows_.empty() && state_ == AppLifecycleState::kResumed) { + if (focused_windows_.erase(hwnd) && focused_windows_.empty() && + state_ == AppLifecycleState::kResumed) { SetLifecycleState(AppLifecycleState::kInactive); } break; diff --git a/shell/platform/windows/windows_lifecycle_manager.h b/shell/platform/windows/windows_lifecycle_manager.h index 5760bdfd768e8..28bccc4dd4612 100644 --- a/shell/platform/windows/windows_lifecycle_manager.h +++ b/shell/platform/windows/windows_lifecycle_manager.h @@ -73,8 +73,6 @@ class WindowsLifecycleManager { LPARAM lparam); private: - - FlutterWindowsEngine* engine_; std::map, int> sent_close_messages_; diff --git a/shell/platform/windows/windows_lifecycle_manager_unittests.cc b/shell/platform/windows/windows_lifecycle_manager_unittests.cc index 513e6af5001b1..82fe191c099db 100644 --- a/shell/platform/windows/windows_lifecycle_manager_unittests.cc +++ b/shell/platform/windows/windows_lifecycle_manager_unittests.cc @@ -7,8 +7,8 @@ #include "flutter/shell/platform/windows/testing/windows_test.h" #include "gtest/gtest.h" -namespace flutter{ -namespace testing{ +namespace flutter { +namespace testing { class WindowsLifecycleManagerTest : public WindowsTest {}; @@ -57,5 +57,5 @@ TEST_F(WindowsLifecycleManagerTest, StateTransitions) { EXPECT_EQ(manager.GetLifecycleState(), AppLifecycleState::kHidden); } -} -} +} // namespace testing +} // namespace flutter From 000ff162b4ebcb51893f76c321b72aec4c675e09 Mon Sep 17 00:00:00 2001 From: schectman Date: Tue, 11 Jul 2023 12:42:39 -0400 Subject: [PATCH 11/33] LicensE --- ci/licenses_golden/licenses_flutter | 1 - 1 file changed, 1 deletion(-) diff --git a/ci/licenses_golden/licenses_flutter b/ci/licenses_golden/licenses_flutter index fcf0dcd392b44..8d1d6795f8fd5 100644 --- a/ci/licenses_golden/licenses_flutter +++ b/ci/licenses_golden/licenses_flutter @@ -5953,7 +5953,6 @@ FILE: ../../../flutter/shell/platform/windows/window_proc_delegate_manager.h FILE: ../../../flutter/shell/platform/windows/window_state.h FILE: ../../../flutter/shell/platform/windows/windows_lifecycle_manager.cc FILE: ../../../flutter/shell/platform/windows/windows_lifecycle_manager.h -FILE: ../../../flutter/shell/platform/windows/windows_lifecycle_manager_unittests.cc FILE: ../../../flutter/shell/platform/windows/windows_proc_table.cc FILE: ../../../flutter/shell/platform/windows/windows_proc_table.h FILE: ../../../flutter/shell/platform/windows/windowsx_shim.h From cbea24102974c45a602a7b31632d739b8e967f42 Mon Sep 17 00:00:00 2001 From: schectman Date: Tue, 11 Jul 2023 12:59:33 -0400 Subject: [PATCH 12/33] License --- ci/licenses_golden/excluded_files | 1 + 1 file changed, 1 insertion(+) diff --git a/ci/licenses_golden/excluded_files b/ci/licenses_golden/excluded_files index dd7e19286de0f..1d94e79447a5c 100644 --- a/ci/licenses_golden/excluded_files +++ b/ci/licenses_golden/excluded_files @@ -349,6 +349,7 @@ ../../../flutter/shell/platform/windows/text_input_plugin_unittest.cc ../../../flutter/shell/platform/windows/window_proc_delegate_manager_unittests.cc ../../../flutter/shell/platform/windows/window_unittests.cc +../../../flutter/shell/platform/windows/windows_lifecycle_manager_unittests.cc ../../../flutter/shell/profiling/sampling_profiler_unittest.cc ../../../flutter/shell/testing ../../../flutter/shell/vmservice/.dart_tool From 5b7249f9a7a0b7cfb672214d7e025696e6734016 Mon Sep 17 00:00:00 2001 From: schectman Date: Tue, 11 Jul 2023 17:19:51 -0400 Subject: [PATCH 13/33] PR feedback --- shell/platform/windows/flutter_window.cc | 20 +++++++++-------- shell/platform/windows/flutter_window.h | 4 ++-- .../windows/flutter_window_unittests.cc | 12 +++++----- shell/platform/windows/window.cc | 6 ++--- .../windows/windows_lifecycle_manager.cc | 20 ++++++++--------- .../windows/windows_lifecycle_manager.h | 10 ++++----- .../windows_lifecycle_manager_unittests.cc | 22 +++++++++---------- 7 files changed, 48 insertions(+), 46 deletions(-) diff --git a/shell/platform/windows/flutter_window.cc b/shell/platform/windows/flutter_window.cc index 8976aa080eef0..8507865515f5f 100644 --- a/shell/platform/windows/flutter_window.cc +++ b/shell/platform/windows/flutter_window.cc @@ -75,17 +75,17 @@ FlutterWindow::FlutterWindow(int width, int height) } FlutterWindow::~FlutterWindow() { - OnWindowStateEvent(HIDE); + OnWindowStateEvent(WindowStateEvent::kHide); } void FlutterWindow::SetView(WindowBindingHandlerDelegate* window) { binding_handler_delegate_ = window; direct_manipulation_owner_->SetBindingHandlerDelegate(window); if (restored_) { - OnWindowStateEvent(SHOW); + OnWindowStateEvent(WindowStateEvent::kShow); } if (focused_) { - OnWindowStateEvent(FOCUS); + OnWindowStateEvent(WindowStateEvent::kFocus); } } @@ -338,16 +338,18 @@ bool FlutterWindow::NeedsVSync() { void FlutterWindow::OnWindowStateEvent(WindowStateEvent event) { switch (event) { - case SHOW: + case WindowStateEvent::kShow: restored_ = true; break; - case HIDE: - restored_ = focused_ = false; + case WindowStateEvent::kHide: + restored_ = false; + focused_ = false; break; - case FOCUS: - restored_ = focused_ = true; + case WindowStateEvent::kFocus: + restored_ = true; + focused_ = false; break; - case UNFOCUS: + case WindowStateEvent::kUnfocus: focused_ = false; break; } diff --git a/shell/platform/windows/flutter_window.h b/shell/platform/windows/flutter_window.h index c485ebd0505ff..3b2ffb547c0c8 100644 --- a/shell/platform/windows/flutter_window.h +++ b/shell/platform/windows/flutter_window.h @@ -176,13 +176,13 @@ class FlutterWindow : public Window, public WindowBindingHandler { // The cursor rect set by Flutter. RECT cursor_rect_; - FML_DISALLOW_COPY_AND_ASSIGN(FlutterWindow); - // The window receives resize and focus messages before its view is set, so // these values cache the state of the window in the meantime so that the // proper application lifecycle state can be updated once the view is set. bool restored_; bool focused_; + + FML_DISALLOW_COPY_AND_ASSIGN(FlutterWindow); }; } // namespace flutter diff --git a/shell/platform/windows/flutter_window_unittests.cc b/shell/platform/windows/flutter_window_unittests.cc index 7026fbfc74d6c..55e306f5dfa63 100644 --- a/shell/platform/windows/flutter_window_unittests.cc +++ b/shell/platform/windows/flutter_window_unittests.cc @@ -61,7 +61,6 @@ class MockFlutterWindow : public FlutterWindow { MOCK_METHOD1(Win32MapVkToChar, uint32_t(uint32_t)); MOCK_METHOD0(GetPlatformWindow, HWND()); MOCK_METHOD0(GetAxFragmentRootDelegate, ui::AXFragmentRootDelegateWin*()); - // MOCK_METHOD1(OnWindowStateEvent, void(WindowStateEvent)); protected: // |KeyboardManager::WindowDelegate| @@ -231,6 +230,8 @@ TEST(FlutterWindowTest, OnPointerStarSendsDeviceType) { win32window.OnPointerLeave(10.0, 10.0, kFlutterPointerDeviceKindStylus, kDefaultPointerDeviceId); + // Destruction of win32window sends a HIDE update. In situ, the window is + // owned by the delegate, and so is destructed first. Not so here. win32window.SetView(nullptr); } @@ -339,17 +340,16 @@ TEST(FlutterWindowTest, LifecycleFocusMessages) { }); win32window.InjectWindowMessage(WM_SIZE, 0, 0); - EXPECT_EQ(last_event, HIDE); + EXPECT_EQ(last_event, WindowStateEvent::kHide); win32window.InjectWindowMessage(WM_SIZE, 0, MAKEWORD(1, 1)); - EXPECT_EQ(last_event, SHOW); + EXPECT_EQ(last_event, WindowStateEvent::kShow); win32window.InjectWindowMessage(WM_SETFOCUS, 0, 0); - ; - EXPECT_EQ(last_event, FOCUS); + EXPECT_EQ(last_event, WindowStateEvent::kFocus); win32window.InjectWindowMessage(WM_KILLFOCUS, 0, 0); - EXPECT_EQ(last_event, UNFOCUS); + EXPECT_EQ(last_event, WindowStateEvent::kUnfocus); } } // namespace testing diff --git a/shell/platform/windows/window.cc b/shell/platform/windows/window.cc index b45a16494ca40..6a372ce4c16a4 100644 --- a/shell/platform/windows/window.cc +++ b/shell/platform/windows/window.cc @@ -353,7 +353,7 @@ Window::HandleMessage(UINT const message, current_height_ = height; HandleResize(width, height); - OnWindowStateEvent(width == 0 && height == 0 ? HIDE : SHOW); + OnWindowStateEvent(width == 0 && height == 0 ? WindowStateEvent::kHide : WindowStateEvent::kShow); break; case WM_PAINT: OnPaint(); @@ -432,11 +432,11 @@ Window::HandleMessage(UINT const message, break; } case WM_SETFOCUS: - OnWindowStateEvent(FOCUS); + OnWindowStateEvent(WindowStateEvent::kFocus); ::CreateCaret(window_handle_, nullptr, 1, 1); break; case WM_KILLFOCUS: - OnWindowStateEvent(UNFOCUS); + OnWindowStateEvent(WindowStateEvent::kUnfocus); ::DestroyCaret(); break; case WM_LBUTTONDOWN: diff --git a/shell/platform/windows/windows_lifecycle_manager.cc b/shell/platform/windows/windows_lifecycle_manager.cc index f5f2f2263865a..0d56e5d9ae0b1 100644 --- a/shell/platform/windows/windows_lifecycle_manager.cc +++ b/shell/platform/windows/windows_lifecycle_manager.cc @@ -78,20 +78,20 @@ bool WindowsLifecycleManager::WindowProc(HWND hwnd, case WM_SIZE: if (wpar == SIZE_MAXIMIZED || wpar == SIZE_RESTORED) { - OnWindowStateEvent(hwnd, SHOW); + OnWindowStateEvent(hwnd, WindowStateEvent::kShow); } else if (wpar == SIZE_MINIMIZED) { - OnWindowStateEvent(hwnd, HIDE); + OnWindowStateEvent(hwnd, WindowStateEvent::kHide); } break; case WM_SHOWWINDOW: if (!wpar) { - OnWindowStateEvent(hwnd, HIDE); + OnWindowStateEvent(hwnd, WindowStateEvent::kHide); } break; case WM_DESTROY: - OnWindowStateEvent(hwnd, HIDE); + OnWindowStateEvent(hwnd, WindowStateEvent::kHide); break; } return false; @@ -197,13 +197,13 @@ void WindowsLifecycleManager::SetLifecycleState(AppLifecycleState state) { void WindowsLifecycleManager::OnWindowStateEvent(HWND hwnd, WindowStateEvent event) { // Synthesize an unfocus event when a focused window is hidden. - if (event == HIDE && focused_windows_.find(hwnd) != focused_windows_.end()) { - OnWindowStateEvent(hwnd, UNFOCUS); + if (event == WindowStateEvent::kHide && focused_windows_.find(hwnd) != focused_windows_.end()) { + OnWindowStateEvent(hwnd, WindowStateEvent::kUnfocus); } std::lock_guard guard(state_update_lock_); switch (event) { - case SHOW: { + case WindowStateEvent::kShow: { bool first_shown_window = visible_windows_.empty(); auto pair = visible_windows_.insert(hwnd); if (first_shown_window && pair.second && @@ -212,7 +212,7 @@ void WindowsLifecycleManager::OnWindowStateEvent(HWND hwnd, } break; } - case HIDE: { + case WindowStateEvent::kHide: { bool present = visible_windows_.erase(hwnd); bool empty = visible_windows_.empty(); if (present && empty && @@ -222,7 +222,7 @@ void WindowsLifecycleManager::OnWindowStateEvent(HWND hwnd, } break; } - case FOCUS: { + case WindowStateEvent::kFocus: { bool first_focused_window = focused_windows_.empty(); auto pair = focused_windows_.insert(hwnd); if (first_focused_window && pair.second && @@ -231,7 +231,7 @@ void WindowsLifecycleManager::OnWindowStateEvent(HWND hwnd, } break; } - case UNFOCUS: { + case WindowStateEvent::kUnfocus: { if (focused_windows_.erase(hwnd) && focused_windows_.empty() && state_ == AppLifecycleState::kResumed) { SetLifecycleState(AppLifecycleState::kInactive); diff --git a/shell/platform/windows/windows_lifecycle_manager.h b/shell/platform/windows/windows_lifecycle_manager.h index 28bccc4dd4612..565fc3fa37261 100644 --- a/shell/platform/windows/windows_lifecycle_manager.h +++ b/shell/platform/windows/windows_lifecycle_manager.h @@ -19,11 +19,11 @@ namespace flutter { class FlutterWindowsEngine; -enum WindowStateEvent { - SHOW, - HIDE, - FOCUS, - UNFOCUS, +enum class WindowStateEvent { + kShow, + kHide, + kFocus, + kUnfocus, }; /// A manager for lifecycle events of the top-level window. diff --git a/shell/platform/windows/windows_lifecycle_manager_unittests.cc b/shell/platform/windows/windows_lifecycle_manager_unittests.cc index 82fe191c099db..e731c4e9f349a 100644 --- a/shell/platform/windows/windows_lifecycle_manager_unittests.cc +++ b/shell/platform/windows/windows_lifecycle_manager_unittests.cc @@ -19,41 +19,41 @@ TEST_F(WindowsLifecycleManagerTest, StateTransitions) { // Hidden to inactive upon window shown. manager.SetLifecycleState(AppLifecycleState::kHidden); - manager.OnWindowStateEvent(win1, SHOW); + manager.OnWindowStateEvent(win1, WindowStateEvent::kShow); EXPECT_EQ(manager.GetLifecycleState(), AppLifecycleState::kInactive); // Showing a second window does not change state. - manager.OnWindowStateEvent(win2, SHOW); + manager.OnWindowStateEvent(win2, WindowStateEvent::kShow); EXPECT_EQ(manager.GetLifecycleState(), AppLifecycleState::kInactive); // Inactive to resumed upon window focus. - manager.OnWindowStateEvent(win2, FOCUS); + manager.OnWindowStateEvent(win2, WindowStateEvent::kFocus); EXPECT_EQ(manager.GetLifecycleState(), AppLifecycleState::kResumed); // Showing a second window does not change state. - manager.OnWindowStateEvent(win1, FOCUS); + manager.OnWindowStateEvent(win1, WindowStateEvent::kFocus); EXPECT_EQ(manager.GetLifecycleState(), AppLifecycleState::kResumed); // Unfocusing one window does not change state while another is focused. - manager.OnWindowStateEvent(win1, UNFOCUS); + manager.OnWindowStateEvent(win1, WindowStateEvent::kUnfocus); EXPECT_EQ(manager.GetLifecycleState(), AppLifecycleState::kResumed); // Unfocusing final remaining focused window transitions to inactive. - manager.OnWindowStateEvent(win2, UNFOCUS); + manager.OnWindowStateEvent(win2, WindowStateEvent::kUnfocus); EXPECT_EQ(manager.GetLifecycleState(), AppLifecycleState::kInactive); // Hiding one of two visible windows does not change state. - manager.OnWindowStateEvent(win2, HIDE); + manager.OnWindowStateEvent(win2, WindowStateEvent::kHide); EXPECT_EQ(manager.GetLifecycleState(), AppLifecycleState::kInactive); // Hiding only visible window transitions to hidden. - manager.OnWindowStateEvent(win1, HIDE); + manager.OnWindowStateEvent(win1, WindowStateEvent::kHide); EXPECT_EQ(manager.GetLifecycleState(), AppLifecycleState::kHidden); // Transition directly from resumed to hidden when the window is hidden. - manager.OnWindowStateEvent(win1, SHOW); - manager.OnWindowStateEvent(win1, FOCUS); - manager.OnWindowStateEvent(win1, HIDE); + manager.OnWindowStateEvent(win1, WindowStateEvent::kShow); + manager.OnWindowStateEvent(win1, WindowStateEvent::kFocus); + manager.OnWindowStateEvent(win1, WindowStateEvent::kHide); EXPECT_EQ(manager.GetLifecycleState(), AppLifecycleState::kHidden); } From db03f837f56e5a671a735feed382a7e12dbfed93 Mon Sep 17 00:00:00 2001 From: schectman Date: Tue, 11 Jul 2023 17:33:13 -0400 Subject: [PATCH 14/33] PR feedback --- shell/platform/windows/flutter_window.cc | 2 +- shell/platform/windows/flutter_window.h | 4 ++-- shell/platform/windows/window.cc | 3 ++- shell/platform/windows/windows_lifecycle_manager.cc | 3 ++- shell/platform/windows/windows_lifecycle_manager.h | 12 ++++++++++++ .../windows/windows_lifecycle_manager_unittests.cc | 4 ++-- 6 files changed, 21 insertions(+), 7 deletions(-) diff --git a/shell/platform/windows/flutter_window.cc b/shell/platform/windows/flutter_window.cc index 8507865515f5f..ad5772fd4e740 100644 --- a/shell/platform/windows/flutter_window.cc +++ b/shell/platform/windows/flutter_window.cc @@ -69,7 +69,7 @@ static HCURSOR GetCursorByName(const std::string& cursor_name) { } // namespace FlutterWindow::FlutterWindow(int width, int height) - : binding_handler_delegate_(nullptr), restored_(false), focused_(false) { + : binding_handler_delegate_(nullptr) { Window::InitializeChild("FLUTTERVIEW", width, height); current_cursor_ = ::LoadCursor(nullptr, IDC_ARROW); } diff --git a/shell/platform/windows/flutter_window.h b/shell/platform/windows/flutter_window.h index 3b2ffb547c0c8..55d9db885a4d0 100644 --- a/shell/platform/windows/flutter_window.h +++ b/shell/platform/windows/flutter_window.h @@ -179,8 +179,8 @@ class FlutterWindow : public Window, public WindowBindingHandler { // The window receives resize and focus messages before its view is set, so // these values cache the state of the window in the meantime so that the // proper application lifecycle state can be updated once the view is set. - bool restored_; - bool focused_; + bool restored_ = false; + bool focused_ = false; FML_DISALLOW_COPY_AND_ASSIGN(FlutterWindow); }; diff --git a/shell/platform/windows/window.cc b/shell/platform/windows/window.cc index 6a372ce4c16a4..a4cc4efec6754 100644 --- a/shell/platform/windows/window.cc +++ b/shell/platform/windows/window.cc @@ -353,7 +353,8 @@ Window::HandleMessage(UINT const message, current_height_ = height; HandleResize(width, height); - OnWindowStateEvent(width == 0 && height == 0 ? WindowStateEvent::kHide : WindowStateEvent::kShow); + OnWindowStateEvent(width == 0 && height == 0 ? WindowStateEvent::kHide + : WindowStateEvent::kShow); break; case WM_PAINT: OnPaint(); diff --git a/shell/platform/windows/windows_lifecycle_manager.cc b/shell/platform/windows/windows_lifecycle_manager.cc index 0d56e5d9ae0b1..7cbd74ab2d9c1 100644 --- a/shell/platform/windows/windows_lifecycle_manager.cc +++ b/shell/platform/windows/windows_lifecycle_manager.cc @@ -197,7 +197,8 @@ void WindowsLifecycleManager::SetLifecycleState(AppLifecycleState state) { void WindowsLifecycleManager::OnWindowStateEvent(HWND hwnd, WindowStateEvent event) { // Synthesize an unfocus event when a focused window is hidden. - if (event == WindowStateEvent::kHide && focused_windows_.find(hwnd) != focused_windows_.end()) { + if (event == WindowStateEvent::kHide && + focused_windows_.find(hwnd) != focused_windows_.end()) { OnWindowStateEvent(hwnd, WindowStateEvent::kUnfocus); } diff --git a/shell/platform/windows/windows_lifecycle_manager.h b/shell/platform/windows/windows_lifecycle_manager.h index 565fc3fa37261..755b34d0f701c 100644 --- a/shell/platform/windows/windows_lifecycle_manager.h +++ b/shell/platform/windows/windows_lifecycle_manager.h @@ -19,6 +19,8 @@ namespace flutter { class FlutterWindowsEngine; +/// An event representing a change in window state that may update the +// application lifecycle state. enum class WindowStateEvent { kShow, kHide, @@ -56,8 +58,18 @@ class WindowsLifecycleManager { // Signal to start consuming WM_CLOSE messages. void BeginProcessingClose(); + // Update the app lifecycle state in response to a change in window state. + // When the app lifecycle state actually changes, this sends a platform + // message to the framework notifying it of the state change. virtual void SetLifecycleState(AppLifecycleState state); + // Respond to a change in window state. Transitions as follows: + // When the only visible window is hidden, transition from resumed or + // inactive to hidden. + // When the only focused window is unfocused, transition from resumed to + // inactive. + // When a window is focused, transition from inactive to resumed. + // When a window is shown, transition from hidden to inactive. virtual void OnWindowStateEvent(HWND hwnd, WindowStateEvent event); AppLifecycleState GetLifecycleState() { return state_; } diff --git a/shell/platform/windows/windows_lifecycle_manager_unittests.cc b/shell/platform/windows/windows_lifecycle_manager_unittests.cc index e731c4e9f349a..6414d0ade78a0 100644 --- a/shell/platform/windows/windows_lifecycle_manager_unittests.cc +++ b/shell/platform/windows/windows_lifecycle_manager_unittests.cc @@ -14,8 +14,8 @@ class WindowsLifecycleManagerTest : public WindowsTest {}; TEST_F(WindowsLifecycleManagerTest, StateTransitions) { WindowsLifecycleManager manager(nullptr); - HWND win1 = (HWND)1; - HWND win2 = (HWND)2; + HWND win1 = reinterpret_cast(1); + HWND win2 = reinterpret_cast(2); // Hidden to inactive upon window shown. manager.SetLifecycleState(AppLifecycleState::kHidden); From 1d07015dd4c612f8f98bd5d7f80ddcdccdc7ebc8 Mon Sep 17 00:00:00 2001 From: Brandon DeRosier Date: Wed, 19 Jul 2023 09:57:50 -0700 Subject: [PATCH 15/33] [Impeller] Disable color attachment on clip pipelines for Metal & Vulkan. (#43781) Remove the color attachment completely in Metal and Vulkan clips. When the color write mask is set to all false, Metal is smart about not executing the fragment shader at all, but some Vulkan and GL drivers may not be. GL requires binding shader objects to draw, and so a cap check is needed here. In the future, we could add minimal no-op shaders to use with the GL clip pipeline to shave off a few flops on drivers that aren't smart about the color write mask. --- impeller/entity/contents/content_context.cc | 213 +++++++++--------- .../renderer/backend/gles/context_gles.cc | 1 + .../renderer/backend/gles/render_pass_gles.cc | 4 +- .../renderer/backend/metal/context_mtl.mm | 1 + .../backend/vulkan/capabilities_vk.cc | 6 + .../renderer/backend/vulkan/capabilities_vk.h | 3 + impeller/renderer/capabilities.cc | 17 ++ impeller/renderer/capabilities.h | 5 + 8 files changed, 146 insertions(+), 104 deletions(-) diff --git a/impeller/entity/contents/content_context.cc b/impeller/entity/contents/content_context.cc index 87939972b157e..c6ffbf9343d7f 100644 --- a/impeller/entity/contents/content_context.cc +++ b/impeller/entity/contents/content_context.cc @@ -29,102 +29,104 @@ void ContentContextOptions::ApplyToPipelineDescriptor( desc.SetSampleCount(sample_count); - ColorAttachmentDescriptor color0 = *desc.GetColorAttachmentDescriptor(0u); - color0.format = color_attachment_pixel_format; - color0.alpha_blend_op = BlendOperation::kAdd; - color0.color_blend_op = BlendOperation::kAdd; - - switch (pipeline_blend) { - case BlendMode::kClear: - color0.dst_alpha_blend_factor = BlendFactor::kZero; - color0.dst_color_blend_factor = BlendFactor::kZero; - color0.src_alpha_blend_factor = BlendFactor::kZero; - color0.src_color_blend_factor = BlendFactor::kZero; - break; - case BlendMode::kSource: - color0.blending_enabled = false; - color0.dst_alpha_blend_factor = BlendFactor::kZero; - color0.dst_color_blend_factor = BlendFactor::kZero; - color0.src_alpha_blend_factor = BlendFactor::kOne; - color0.src_color_blend_factor = BlendFactor::kOne; - break; - case BlendMode::kDestination: - color0.dst_alpha_blend_factor = BlendFactor::kOne; - color0.dst_color_blend_factor = BlendFactor::kOne; - color0.src_alpha_blend_factor = BlendFactor::kZero; - color0.src_color_blend_factor = BlendFactor::kZero; - break; - case BlendMode::kSourceOver: - color0.dst_alpha_blend_factor = BlendFactor::kOneMinusSourceAlpha; - color0.dst_color_blend_factor = BlendFactor::kOneMinusSourceAlpha; - color0.src_alpha_blend_factor = BlendFactor::kOne; - color0.src_color_blend_factor = BlendFactor::kOne; - break; - case BlendMode::kDestinationOver: - color0.dst_alpha_blend_factor = BlendFactor::kOne; - color0.dst_color_blend_factor = BlendFactor::kOne; - color0.src_alpha_blend_factor = BlendFactor::kOneMinusDestinationAlpha; - color0.src_color_blend_factor = BlendFactor::kOneMinusDestinationAlpha; - break; - case BlendMode::kSourceIn: - color0.dst_alpha_blend_factor = BlendFactor::kZero; - color0.dst_color_blend_factor = BlendFactor::kZero; - color0.src_alpha_blend_factor = BlendFactor::kDestinationAlpha; - color0.src_color_blend_factor = BlendFactor::kDestinationAlpha; - break; - case BlendMode::kDestinationIn: - color0.dst_alpha_blend_factor = BlendFactor::kSourceAlpha; - color0.dst_color_blend_factor = BlendFactor::kSourceAlpha; - color0.src_alpha_blend_factor = BlendFactor::kZero; - color0.src_color_blend_factor = BlendFactor::kZero; - break; - case BlendMode::kSourceOut: - color0.dst_alpha_blend_factor = BlendFactor::kZero; - color0.dst_color_blend_factor = BlendFactor::kZero; - color0.src_alpha_blend_factor = BlendFactor::kOneMinusDestinationAlpha; - color0.src_color_blend_factor = BlendFactor::kOneMinusDestinationAlpha; - break; - case BlendMode::kDestinationOut: - color0.dst_alpha_blend_factor = BlendFactor::kOneMinusSourceAlpha; - color0.dst_color_blend_factor = BlendFactor::kOneMinusSourceAlpha; - color0.src_alpha_blend_factor = BlendFactor::kZero; - color0.src_color_blend_factor = BlendFactor::kZero; - break; - case BlendMode::kSourceATop: - color0.dst_alpha_blend_factor = BlendFactor::kOneMinusSourceAlpha; - color0.dst_color_blend_factor = BlendFactor::kOneMinusSourceAlpha; - color0.src_alpha_blend_factor = BlendFactor::kDestinationAlpha; - color0.src_color_blend_factor = BlendFactor::kDestinationAlpha; - break; - case BlendMode::kDestinationATop: - color0.dst_alpha_blend_factor = BlendFactor::kSourceAlpha; - color0.dst_color_blend_factor = BlendFactor::kSourceAlpha; - color0.src_alpha_blend_factor = BlendFactor::kOneMinusDestinationAlpha; - color0.src_color_blend_factor = BlendFactor::kOneMinusDestinationAlpha; - break; - case BlendMode::kXor: - color0.dst_alpha_blend_factor = BlendFactor::kOneMinusSourceAlpha; - color0.dst_color_blend_factor = BlendFactor::kOneMinusSourceAlpha; - color0.src_alpha_blend_factor = BlendFactor::kOneMinusDestinationAlpha; - color0.src_color_blend_factor = BlendFactor::kOneMinusDestinationAlpha; - break; - case BlendMode::kPlus: - color0.dst_alpha_blend_factor = BlendFactor::kOne; - color0.dst_color_blend_factor = BlendFactor::kOne; - color0.src_alpha_blend_factor = BlendFactor::kOne; - color0.src_color_blend_factor = BlendFactor::kOne; - break; - case BlendMode::kModulate: - color0.dst_alpha_blend_factor = BlendFactor::kSourceAlpha; - color0.dst_color_blend_factor = BlendFactor::kSourceColor; - color0.src_alpha_blend_factor = BlendFactor::kZero; - color0.src_color_blend_factor = BlendFactor::kZero; - break; - default: - FML_UNREACHABLE(); + auto* color0_ref = desc.GetColorAttachmentDescriptor(0u); + if (color0_ref) { + ColorAttachmentDescriptor color0 = *color0_ref; + color0.format = color_attachment_pixel_format; + color0.alpha_blend_op = BlendOperation::kAdd; + color0.color_blend_op = BlendOperation::kAdd; + + switch (pipeline_blend) { + case BlendMode::kClear: + color0.dst_alpha_blend_factor = BlendFactor::kZero; + color0.dst_color_blend_factor = BlendFactor::kZero; + color0.src_alpha_blend_factor = BlendFactor::kZero; + color0.src_color_blend_factor = BlendFactor::kZero; + break; + case BlendMode::kSource: + color0.blending_enabled = false; + color0.dst_alpha_blend_factor = BlendFactor::kZero; + color0.dst_color_blend_factor = BlendFactor::kZero; + color0.src_alpha_blend_factor = BlendFactor::kOne; + color0.src_color_blend_factor = BlendFactor::kOne; + break; + case BlendMode::kDestination: + color0.dst_alpha_blend_factor = BlendFactor::kOne; + color0.dst_color_blend_factor = BlendFactor::kOne; + color0.src_alpha_blend_factor = BlendFactor::kZero; + color0.src_color_blend_factor = BlendFactor::kZero; + break; + case BlendMode::kSourceOver: + color0.dst_alpha_blend_factor = BlendFactor::kOneMinusSourceAlpha; + color0.dst_color_blend_factor = BlendFactor::kOneMinusSourceAlpha; + color0.src_alpha_blend_factor = BlendFactor::kOne; + color0.src_color_blend_factor = BlendFactor::kOne; + break; + case BlendMode::kDestinationOver: + color0.dst_alpha_blend_factor = BlendFactor::kOne; + color0.dst_color_blend_factor = BlendFactor::kOne; + color0.src_alpha_blend_factor = BlendFactor::kOneMinusDestinationAlpha; + color0.src_color_blend_factor = BlendFactor::kOneMinusDestinationAlpha; + break; + case BlendMode::kSourceIn: + color0.dst_alpha_blend_factor = BlendFactor::kZero; + color0.dst_color_blend_factor = BlendFactor::kZero; + color0.src_alpha_blend_factor = BlendFactor::kDestinationAlpha; + color0.src_color_blend_factor = BlendFactor::kDestinationAlpha; + break; + case BlendMode::kDestinationIn: + color0.dst_alpha_blend_factor = BlendFactor::kSourceAlpha; + color0.dst_color_blend_factor = BlendFactor::kSourceAlpha; + color0.src_alpha_blend_factor = BlendFactor::kZero; + color0.src_color_blend_factor = BlendFactor::kZero; + break; + case BlendMode::kSourceOut: + color0.dst_alpha_blend_factor = BlendFactor::kZero; + color0.dst_color_blend_factor = BlendFactor::kZero; + color0.src_alpha_blend_factor = BlendFactor::kOneMinusDestinationAlpha; + color0.src_color_blend_factor = BlendFactor::kOneMinusDestinationAlpha; + break; + case BlendMode::kDestinationOut: + color0.dst_alpha_blend_factor = BlendFactor::kOneMinusSourceAlpha; + color0.dst_color_blend_factor = BlendFactor::kOneMinusSourceAlpha; + color0.src_alpha_blend_factor = BlendFactor::kZero; + color0.src_color_blend_factor = BlendFactor::kZero; + break; + case BlendMode::kSourceATop: + color0.dst_alpha_blend_factor = BlendFactor::kOneMinusSourceAlpha; + color0.dst_color_blend_factor = BlendFactor::kOneMinusSourceAlpha; + color0.src_alpha_blend_factor = BlendFactor::kDestinationAlpha; + color0.src_color_blend_factor = BlendFactor::kDestinationAlpha; + break; + case BlendMode::kDestinationATop: + color0.dst_alpha_blend_factor = BlendFactor::kSourceAlpha; + color0.dst_color_blend_factor = BlendFactor::kSourceAlpha; + color0.src_alpha_blend_factor = BlendFactor::kOneMinusDestinationAlpha; + color0.src_color_blend_factor = BlendFactor::kOneMinusDestinationAlpha; + break; + case BlendMode::kXor: + color0.dst_alpha_blend_factor = BlendFactor::kOneMinusSourceAlpha; + color0.dst_color_blend_factor = BlendFactor::kOneMinusSourceAlpha; + color0.src_alpha_blend_factor = BlendFactor::kOneMinusDestinationAlpha; + color0.src_color_blend_factor = BlendFactor::kOneMinusDestinationAlpha; + break; + case BlendMode::kPlus: + color0.dst_alpha_blend_factor = BlendFactor::kOne; + color0.dst_color_blend_factor = BlendFactor::kOne; + color0.src_alpha_blend_factor = BlendFactor::kOne; + color0.src_color_blend_factor = BlendFactor::kOne; + break; + case BlendMode::kModulate: + color0.dst_alpha_blend_factor = BlendFactor::kSourceAlpha; + color0.dst_color_blend_factor = BlendFactor::kSourceColor; + color0.src_alpha_blend_factor = BlendFactor::kZero; + color0.src_color_blend_factor = BlendFactor::kZero; + break; + default: + FML_UNREACHABLE(); + } + desc.SetColorAttachmentDescriptor(0u, color0); } - desc.SetColorAttachmentDescriptor(0u, color0); - if (!has_stencil_attachment) { desc.ClearStencilAttachments(); } @@ -320,15 +322,22 @@ ContentContext::ContentContext(std::shared_ptr context) if (maybe_pipeline_desc.has_value()) { auto clip_pipeline_descriptor = maybe_pipeline_desc.value(); clip_pipeline_descriptor.SetLabel("Clip Pipeline"); + // Disable write to all color attachments. - auto color_attachments = - clip_pipeline_descriptor.GetColorAttachmentDescriptors(); - for (auto& color_attachment : color_attachments) { - color_attachment.second.write_mask = - static_cast(ColorWriteMask::kNone); + if (context_->GetCapabilities() + ->SupportsPipelinesWithNoColorAttachments()) { + clip_pipeline_descriptor.SetColorAttachmentDescriptors({}); + } else { + auto color_attachments = + clip_pipeline_descriptor.GetColorAttachmentDescriptors(); + for (auto& color_attachment : color_attachments) { + color_attachment.second.write_mask = + static_cast(ColorWriteMask::kNone); + } + clip_pipeline_descriptor.SetColorAttachmentDescriptors( + std::move(color_attachments)); } - clip_pipeline_descriptor.SetColorAttachmentDescriptors( - std::move(color_attachments)); + clip_pipelines_[default_options_] = std::make_unique(*context_, clip_pipeline_descriptor); } else { diff --git a/impeller/renderer/backend/gles/context_gles.cc b/impeller/renderer/backend/gles/context_gles.cc index 34b095d46ab48..091a2632e5a76 100644 --- a/impeller/renderer/backend/gles/context_gles.cc +++ b/impeller/renderer/backend/gles/context_gles.cc @@ -77,6 +77,7 @@ ContextGLES::ContextGLES(std::unique_ptr gl, .SetSupportsReadFromOnscreenTexture(false) .SetSupportsDecalTileMode(false) .SetSupportsMemorylessTextures(false) + .SetSupportsPipelinesWithNoColorAttachments(false) .Build(); } diff --git a/impeller/renderer/backend/gles/render_pass_gles.cc b/impeller/renderer/backend/gles/render_pass_gles.cc index 2cdadb4fc3cd7..33da6a0ad2f1f 100644 --- a/impeller/renderer/backend/gles/render_pass_gles.cc +++ b/impeller/renderer/backend/gles/render_pass_gles.cc @@ -257,8 +257,8 @@ struct RenderPassData { const auto* color_attachment = pipeline.GetDescriptor().GetLegacyCompatibleColorAttachment(); if (!color_attachment) { - VALIDATION_LOG - << "Color attachment is too complicated for a legacy renderer."; + VALIDATION_LOG << "The OpenGLES backend requires pipelines to have color " + "attachments."; return false; } diff --git a/impeller/renderer/backend/metal/context_mtl.mm b/impeller/renderer/backend/metal/context_mtl.mm index db2c1fef0cc21..604ec6120786c 100644 --- a/impeller/renderer/backend/metal/context_mtl.mm +++ b/impeller/renderer/backend/metal/context_mtl.mm @@ -65,6 +65,7 @@ static bool DeviceSupportsComputeSubgroups(id device) { .SetSupportsReadFromResolve(true) .SetSupportsReadFromOnscreenTexture(true) .SetSupportsMemorylessTextures(true) + .SetSupportsPipelinesWithNoColorAttachments(true) .Build(); } diff --git a/impeller/renderer/backend/vulkan/capabilities_vk.cc b/impeller/renderer/backend/vulkan/capabilities_vk.cc index 33e3ca7ceb748..78e6c0d0ff46f 100644 --- a/impeller/renderer/backend/vulkan/capabilities_vk.cc +++ b/impeller/renderer/backend/vulkan/capabilities_vk.cc @@ -431,6 +431,7 @@ bool CapabilitiesVK::SupportsReadFromOnscreenTexture() const { return false; } +// |Capabilities| bool CapabilitiesVK::SupportsDecalTileMode() const { return true; } @@ -440,6 +441,11 @@ bool CapabilitiesVK::SupportsMemorylessTextures() const { return supports_memoryless_textures_; } +// |Capabilities| +bool CapabilitiesVK::SupportsPipelinesWithNoColorAttachments() const { + return true; +} + // |Capabilities| PixelFormat CapabilitiesVK::GetDefaultColorFormat() const { return color_format_; diff --git a/impeller/renderer/backend/vulkan/capabilities_vk.h b/impeller/renderer/backend/vulkan/capabilities_vk.h index ffa96d46bc98e..3a6af726aee1b 100644 --- a/impeller/renderer/backend/vulkan/capabilities_vk.h +++ b/impeller/renderer/backend/vulkan/capabilities_vk.h @@ -93,6 +93,9 @@ class CapabilitiesVK final : public Capabilities, // |Capabilities| bool SupportsMemorylessTextures() const override; + // |Capabilities| + bool SupportsPipelinesWithNoColorAttachments() const override; + // |Capabilities| PixelFormat GetDefaultColorFormat() const override; diff --git a/impeller/renderer/capabilities.cc b/impeller/renderer/capabilities.cc index d0c9f62348066..680c64b5e4400 100644 --- a/impeller/renderer/capabilities.cc +++ b/impeller/renderer/capabilities.cc @@ -76,10 +76,16 @@ class StandardCapabilities final : public Capabilities { return default_stencil_format_; } + // |Capabilities| bool SupportsMemorylessTextures() const override { return supports_memoryless_textures_; } + // |Capabilities| + bool SupportsPipelinesWithNoColorAttachments() const override { + return supports_pipelines_with_no_color_attachments_; + } + private: StandardCapabilities(bool has_threading_restrictions, bool supports_offscreen_msaa, @@ -93,6 +99,7 @@ class StandardCapabilities final : public Capabilities { bool supports_read_from_resolve, bool supports_decal_tile_mode, bool supports_memoryless_textures, + bool supports_pipelines_with_no_color_attachments, PixelFormat default_color_format, PixelFormat default_stencil_format) : has_threading_restrictions_(has_threading_restrictions), @@ -108,6 +115,8 @@ class StandardCapabilities final : public Capabilities { supports_read_from_resolve_(supports_read_from_resolve), supports_decal_tile_mode_(supports_decal_tile_mode), supports_memoryless_textures_(supports_memoryless_textures), + supports_pipelines_with_no_color_attachments_( + supports_pipelines_with_no_color_attachments), default_color_format_(default_color_format), default_stencil_format_(default_stencil_format) {} @@ -125,6 +134,7 @@ class StandardCapabilities final : public Capabilities { bool supports_read_from_resolve_ = false; bool supports_decal_tile_mode_ = false; bool supports_memoryless_textures_ = false; + bool supports_pipelines_with_no_color_attachments_ = false; PixelFormat default_color_format_ = PixelFormat::kUnknown; PixelFormat default_stencil_format_ = PixelFormat::kUnknown; @@ -215,6 +225,12 @@ CapabilitiesBuilder& CapabilitiesBuilder::SetSupportsMemorylessTextures( return *this; } +CapabilitiesBuilder& +CapabilitiesBuilder::SetSupportsPipelinesWithNoColorAttachments(bool value) { + supports_pipelines_with_no_color_attachments_ = value; + return *this; +} + std::unique_ptr CapabilitiesBuilder::Build() { return std::unique_ptr(new StandardCapabilities( // has_threading_restrictions_, // @@ -229,6 +245,7 @@ std::unique_ptr CapabilitiesBuilder::Build() { supports_read_from_resolve_, // supports_decal_tile_mode_, // supports_memoryless_textures_, // + supports_pipelines_with_no_color_attachments_, // default_color_format_.value_or(PixelFormat::kUnknown), // default_stencil_format_.value_or(PixelFormat::kUnknown) // )); diff --git a/impeller/renderer/capabilities.h b/impeller/renderer/capabilities.h index 87a7f9df08892..c865a84e924c0 100644 --- a/impeller/renderer/capabilities.h +++ b/impeller/renderer/capabilities.h @@ -39,6 +39,8 @@ class Capabilities { virtual bool SupportsMemorylessTextures() const = 0; + virtual bool SupportsPipelinesWithNoColorAttachments() const = 0; + virtual PixelFormat GetDefaultColorFormat() const = 0; virtual PixelFormat GetDefaultStencilFormat() const = 0; @@ -83,6 +85,8 @@ class CapabilitiesBuilder { CapabilitiesBuilder& SetSupportsMemorylessTextures(bool value); + CapabilitiesBuilder& SetSupportsPipelinesWithNoColorAttachments(bool value); + std::unique_ptr Build(); private: @@ -98,6 +102,7 @@ class CapabilitiesBuilder { bool supports_read_from_resolve_ = false; bool supports_decal_tile_mode_ = false; bool supports_memoryless_textures_ = false; + bool supports_pipelines_with_no_color_attachments_ = false; std::optional default_color_format_ = std::nullopt; std::optional default_stencil_format_ = std::nullopt; From f50d5f43880a8f4b4cf557cb6ffd8eef3e735185 Mon Sep 17 00:00:00 2001 From: skia-flutter-autoroll Date: Wed, 19 Jul 2023 12:57:53 -0400 Subject: [PATCH 16/33] Roll Skia from 4728980564b1 to a352521a3a7c (3 revisions) (#43816) https://skia.googlesource.com/skia.git/+log/4728980564b1..a352521a3a7c 2023-07-19 nigeltao@google.com Roll third_party/wuffs to version 0.3.3 2023-07-19 johnstiles@google.com Enable inlining for runtime effects using SkRP. 2023-07-19 johnstiles@google.com Add instruction count to the top of SkRP dumps. If this roll has caused a breakage, revert this CL and stop the roller using the controls here: https://autoroll.skia.org/r/skia-flutter-autoroll Please CC bdero@google.com,brianosman@google.com,rmistry@google.com on the revert to ensure that a human is aware of the problem. To file a bug in Skia: https://bugs.chromium.org/p/skia/issues/entry To file a bug in Flutter: https://github.com/flutter/flutter/issues/new/choose To report a problem with the AutoRoller itself, please file a bug: https://bugs.chromium.org/p/skia/issues/entry?template=Autoroller+Bug Documentation for the AutoRoller is here: https://skia.googlesource.com/buildbot/+doc/main/autoroll/README.md --- DEPS | 2 +- ci/licenses_golden/licenses_skia | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/DEPS b/DEPS index 7199cde5c9bd1..c908c050bae13 100644 --- a/DEPS +++ b/DEPS @@ -18,7 +18,7 @@ vars = { 'llvm_git': 'https://llvm.googlesource.com', # OCMock is for testing only so there is no google clone 'ocmock_git': 'https://github.com/erikdoe/ocmock.git', - 'skia_revision': '4728980564b11ae9448e0b82797cc359bcd32137', + 'skia_revision': 'a352521a3a7cc574eaa0101f95f46db084dc4f88', # WARNING: DO NOT EDIT canvaskit_cipd_instance MANUALLY # See `lib/web_ui/README.md` for how to roll CanvasKit to a new version. diff --git a/ci/licenses_golden/licenses_skia b/ci/licenses_golden/licenses_skia index c1089835042ac..8fd86f1ea83b5 100644 --- a/ci/licenses_golden/licenses_skia +++ b/ci/licenses_golden/licenses_skia @@ -1,4 +1,4 @@ -Signature: b0048d208dcdc012695fae105ae68d25 +Signature: 2ef59152320cf1b480af37a37dc8e7c6 ==================================================================================================== LIBRARY: etc1 From 98b96ab5d5de8af2b3c15e4aa55b8913b4ba0f0c Mon Sep 17 00:00:00 2001 From: schectman Date: Wed, 19 Jul 2023 14:50:12 -0400 Subject: [PATCH 17/33] Move API call to engine --- .../windows/flutter_windows_engine.cc | 30 +++++++++++++++++++ .../platform/windows/flutter_windows_engine.h | 6 ++++ 2 files changed, 36 insertions(+) diff --git a/shell/platform/windows/flutter_windows_engine.cc b/shell/platform/windows/flutter_windows_engine.cc index 345236fb7590a..dac2e6d63115c 100644 --- a/shell/platform/windows/flutter_windows_engine.cc +++ b/shell/platform/windows/flutter_windows_engine.cc @@ -800,4 +800,34 @@ void FlutterWindowsEngine::OnWindowStateEvent(HWND hwnd, lifecycle_manager_->OnWindowStateEvent(hwnd, event); } +void FlutterWindowsEngine::ProcessExternalWindowMessage(HWND hwnd, + UINT message, + WPARAM wparam, + LPARAM lparam) { + if (lifecycle_manager_) { + std::optional event = std::nullopt; + + switch (message) { + case WM_SHOWWINDOW: + event = wparam ? flutter::WindowStateEvent::kShow + : flutter::WindowStateEvent::kHide; + break; + case WM_SIZE: + event = wparam == SIZE_MINIMIZED ? flutter::WindowStateEvent::kHide + : flutter::WindowStateEvent::kShow; + break; + case WM_SETFOCUS: + event = flutter::WindowStateEvent::kFocus; + break; + case WM_KILLFOCUS: + event = flutter::WindowStateEvent::kUnfocus; + break; + } + + if (event.has_value()) { + lifecycle_manager_->OnWindowStateEvent(hwnd, *event); + } + } +} + } // namespace flutter diff --git a/shell/platform/windows/flutter_windows_engine.h b/shell/platform/windows/flutter_windows_engine.h index 8980351521d54..3c76bf7dee35b 100644 --- a/shell/platform/windows/flutter_windows_engine.h +++ b/shell/platform/windows/flutter_windows_engine.h @@ -270,6 +270,12 @@ class FlutterWindowsEngine { return lifecycle_manager_.get(); } + // Handle a message from a non-Flutter window in the same application. + void ProcessExternalWindowMessage(HWND hwnd, + UINT message, + WPARAM wparam, + LPARAM lparam); + protected: // Creates the keyboard key handler. // From b457d1eeef21897ce6d36a2a19e713ee6ff06c3f Mon Sep 17 00:00:00 2001 From: schectman Date: Wed, 26 Jul 2023 08:28:26 -0400 Subject: [PATCH 18/33] Public API --- .../platform/windows/client_wrapper/flutter_engine.cc | 11 +++++++++++ .../client_wrapper/include/flutter/flutter_engine.h | 10 ++++++++++ .../testing/stub_flutter_windows_api.cc | 7 +++++++ shell/platform/windows/flutter_windows.cc | 9 +++++++++ shell/platform/windows/public/flutter_windows.h | 11 +++++++++++ 5 files changed, 48 insertions(+) diff --git a/shell/platform/windows/client_wrapper/flutter_engine.cc b/shell/platform/windows/client_wrapper/flutter_engine.cc index 00ccbbcff7cab..7e1b3672aa72a 100644 --- a/shell/platform/windows/client_wrapper/flutter_engine.cc +++ b/shell/platform/windows/client_wrapper/flutter_engine.cc @@ -100,6 +100,17 @@ void FlutterEngine::SetNextFrameCallback(std::function callback) { this); } +void FlutterEngine::ProcessExternalWindowMessage(HWND hwnd, + UINT message, + WPARAM wparam, + LPARAM lparam) { + FlutterDesktopEngineProcessExternalWindowMessage(engine_, + hwnd, + message, + wparam, + lparam); +} + FlutterDesktopEngineRef FlutterEngine::RelinquishEngine() { owns_engine_ = false; return engine_; diff --git a/shell/platform/windows/client_wrapper/include/flutter/flutter_engine.h b/shell/platform/windows/client_wrapper/include/flutter/flutter_engine.h index 63a820ce2d6bd..7c6e440b89ff2 100644 --- a/shell/platform/windows/client_wrapper/include/flutter/flutter_engine.h +++ b/shell/platform/windows/client_wrapper/include/flutter/flutter_engine.h @@ -84,6 +84,16 @@ class FlutterEngine : public PluginRegistry { // once on the platform thread. void SetNextFrameCallback(std::function callback); + // Called to pass an external window message to the engine for lifecycle + // state updates. This does not consume the window message. Non-Flutter windows + // must call this method in their WndProc in order to be included in the logic + // for application lifecycle state updates. + void ProcessExternalWindowMessage( + HWND hwnd, + UINT message, + WPARAM wparam, + LPARAM lparam); + private: // For access to RelinquishEngine. friend class FlutterViewController; diff --git a/shell/platform/windows/client_wrapper/testing/stub_flutter_windows_api.cc b/shell/platform/windows/client_wrapper/testing/stub_flutter_windows_api.cc index ed9f9d3bcb2ff..0d27153a0c32b 100644 --- a/shell/platform/windows/client_wrapper/testing/stub_flutter_windows_api.cc +++ b/shell/platform/windows/client_wrapper/testing/stub_flutter_windows_api.cc @@ -162,6 +162,13 @@ IDXGIAdapter* FlutterDesktopViewGetGraphicsAdapter(FlutterDesktopViewRef view) { return nullptr; } +void FlutterDesktopEngineProcessExternalWindowMessage( + FlutterDesktopEngineRef engine, + HWND hwnd, + UINT message, + WPARAM wparam, + LPARAM lparam) {} + FlutterDesktopViewRef FlutterDesktopPluginRegistrarGetView( FlutterDesktopPluginRegistrarRef controller) { // The stub ignores this, so just return an arbitrary non-zero value. diff --git a/shell/platform/windows/flutter_windows.cc b/shell/platform/windows/flutter_windows.cc index 50bc538f1f8e0..742bc7566de5a 100644 --- a/shell/platform/windows/flutter_windows.cc +++ b/shell/platform/windows/flutter_windows.cc @@ -207,6 +207,15 @@ IDXGIAdapter* FlutterDesktopViewGetGraphicsAdapter(FlutterDesktopViewRef view) { return nullptr; } +void FlutterDesktopEngineProcessExternalWindowMessage( + FlutterDesktopEngineRef engine, + HWND hwnd, + UINT message, + WPARAM wparam, + LPARAM lparam) { + EngineFromHandle(engine)->ProcessExternalWindowMessage(hwnd, message, wparam, lparam); +} + FlutterDesktopViewRef FlutterDesktopPluginRegistrarGetView( FlutterDesktopPluginRegistrarRef registrar) { return HandleForView(registrar->engine->view()); diff --git a/shell/platform/windows/public/flutter_windows.h b/shell/platform/windows/public/flutter_windows.h index 767c3468d45d3..85f79d41df11a 100644 --- a/shell/platform/windows/public/flutter_windows.h +++ b/shell/platform/windows/public/flutter_windows.h @@ -212,6 +212,17 @@ FLUTTER_EXPORT HWND FlutterDesktopViewGetHWND(FlutterDesktopViewRef view); FLUTTER_EXPORT IDXGIAdapter* FlutterDesktopViewGetGraphicsAdapter( FlutterDesktopViewRef view); +// Called to pass an external window message to the engine for lifecycle +// state updates. This does not consume the window message. Non-Flutter windows +// must call this method in their WndProc in order to be included in the logic +// for application lifecycle state updates. +FLUTTER_EXPORT void FlutterDesktopEngineProcessExternalWindowMessage( + FlutterDesktopEngineRef engine, + HWND hwnd, + UINT message, + WPARAM wparam, + LPARAM lparam); + // ========== Plugin Registrar (extensions) ========== // These are Windows-specific extensions to flutter_plugin_registrar.h From 1bb2a1d10f80c2bba3cd489bbcf102514388d136 Mon Sep 17 00:00:00 2001 From: schectman Date: Wed, 26 Jul 2023 08:42:43 -0400 Subject: [PATCH 19/33] Test pre-view message --- shell/platform/windows/flutter_window.cc | 5 ++-- .../windows/flutter_window_unittests.cc | 28 +++++++++++++++++++ 2 files changed, 30 insertions(+), 3 deletions(-) diff --git a/shell/platform/windows/flutter_window.cc b/shell/platform/windows/flutter_window.cc index ad5772fd4e740..61e51afa67841 100644 --- a/shell/platform/windows/flutter_window.cc +++ b/shell/platform/windows/flutter_window.cc @@ -346,14 +346,13 @@ void FlutterWindow::OnWindowStateEvent(WindowStateEvent event) { focused_ = false; break; case WindowStateEvent::kFocus: - restored_ = true; - focused_ = false; + focused_ = true; break; case WindowStateEvent::kUnfocus: focused_ = false; break; } - HWND hwnd = GetWindowHandle(); + HWND hwnd = GetPlatformWindow(); if (hwnd && binding_handler_delegate_) { binding_handler_delegate_->OnWindowStateEvent(hwnd, event); } diff --git a/shell/platform/windows/flutter_window_unittests.cc b/shell/platform/windows/flutter_window_unittests.cc index 55e306f5dfa63..47205683c4de2 100644 --- a/shell/platform/windows/flutter_window_unittests.cc +++ b/shell/platform/windows/flutter_window_unittests.cc @@ -330,6 +330,7 @@ TEST(FlutterWindowTest, AlertNode) { TEST(FlutterWindowTest, LifecycleFocusMessages) { MockFlutterWindow win32window; + ON_CALL(win32window, GetPlatformWindow).WillByDefault([](){ return reinterpret_cast(1); }); MockWindowBindingHandlerDelegate delegate; win32window.SetView(&delegate); @@ -352,5 +353,32 @@ TEST(FlutterWindowTest, LifecycleFocusMessages) { EXPECT_EQ(last_event, WindowStateEvent::kUnfocus); } +TEST(FlutterWindowTest, CachedLifecycleMessage) { + MockFlutterWindow win32window; + ON_CALL(win32window, GetPlatformWindow).WillByDefault([](){ return reinterpret_cast(1); }); + + // Restore + win32window.InjectWindowMessage(WM_SIZE, 0, MAKEWORD(1, 1)); + + // Focus + win32window.InjectWindowMessage(WM_SETFOCUS, 0, 0); + + MockWindowBindingHandlerDelegate delegate; + bool focused = false; + bool restored = false; + ON_CALL(delegate, OnWindowStateEvent).WillByDefault([&](HWND hwnd, WindowStateEvent event) { + if (event == WindowStateEvent::kFocus) { + focused = true; + } + else if (event == WindowStateEvent::kShow) { + restored = true; + } + }); + + win32window.SetView(&delegate); + EXPECT_TRUE(focused); + EXPECT_TRUE(restored); +} + } // namespace testing } // namespace flutter From 6d8e612e9cec3d46b9fe36746b2a44a749bf8dc6 Mon Sep 17 00:00:00 2001 From: schectman Date: Wed, 26 Jul 2023 08:56:41 -0400 Subject: [PATCH 20/33] Test external window proc --- .../flutter_engine_unittests.cc | 20 +++++++++++++++++++ .../testing/stub_flutter_windows_api.cc | 6 +++++- .../testing/stub_flutter_windows_api.h | 3 +++ 3 files changed, 28 insertions(+), 1 deletion(-) diff --git a/shell/platform/windows/client_wrapper/flutter_engine_unittests.cc b/shell/platform/windows/client_wrapper/flutter_engine_unittests.cc index a1274998eadce..a7c95b15c47fd 100644 --- a/shell/platform/windows/client_wrapper/flutter_engine_unittests.cc +++ b/shell/platform/windows/client_wrapper/flutter_engine_unittests.cc @@ -56,6 +56,11 @@ class TestFlutterWindowsApi : public testing::StubFlutterWindowsApi { // |flutter::testing::StubFlutterWindowsApi| void EngineReloadSystemFonts() override { reload_fonts_called_ = true; } + // |flutter::testing::StubFlutterWindowsApi| + void EngineProcessExternalWindowMessage(FlutterDesktopEngineRef engine, HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam) { + last_message_ = message; + } + bool create_called() { return create_called_; } bool run_called() { return run_called_; } @@ -74,6 +79,8 @@ class TestFlutterWindowsApi : public testing::StubFlutterWindowsApi { next_frame_callback_ = nullptr; } + UINT last_message() { return last_message_; } + private: bool create_called_ = false; bool run_called_ = false; @@ -82,6 +89,7 @@ class TestFlutterWindowsApi : public testing::StubFlutterWindowsApi { std::vector dart_entrypoint_arguments_; VoidCallback next_frame_callback_ = nullptr; void* next_frame_user_data_ = nullptr; + UINT last_message_ = 0; }; } // namespace @@ -201,4 +209,16 @@ TEST(FlutterEngineTest, SetNextFrameCallback) { EXPECT_TRUE(success); } +TEST(FlutterEngineTest, ProcessExternalWindowMessage) { + testing::ScopedStubFlutterWindowsApi scoped_api_stub( + std::make_unique()); + auto test_api = static_cast(scoped_api_stub.stub()); + + FlutterEngine engine(DartProject(L"fake/project/path")); + + engine.ProcessExternalWindowMessage(reinterpret_cast(1), 1234, 0, 0); + + EXPECT_EQ(test_api->last_message(), 1234); +} + } // namespace flutter diff --git a/shell/platform/windows/client_wrapper/testing/stub_flutter_windows_api.cc b/shell/platform/windows/client_wrapper/testing/stub_flutter_windows_api.cc index 0d27153a0c32b..99294344754c6 100644 --- a/shell/platform/windows/client_wrapper/testing/stub_flutter_windows_api.cc +++ b/shell/platform/windows/client_wrapper/testing/stub_flutter_windows_api.cc @@ -167,7 +167,11 @@ void FlutterDesktopEngineProcessExternalWindowMessage( HWND hwnd, UINT message, WPARAM wparam, - LPARAM lparam) {} + LPARAM lparam) { + if (s_stub_implementation) { + s_stub_implementation->EngineProcessExternalWindowMessage(engine, hwnd, message, wparam, lparam); + } +} FlutterDesktopViewRef FlutterDesktopPluginRegistrarGetView( FlutterDesktopPluginRegistrarRef controller) { diff --git a/shell/platform/windows/client_wrapper/testing/stub_flutter_windows_api.h b/shell/platform/windows/client_wrapper/testing/stub_flutter_windows_api.h index a1864d4f07720..9c72a3b8bdd19 100644 --- a/shell/platform/windows/client_wrapper/testing/stub_flutter_windows_api.h +++ b/shell/platform/windows/client_wrapper/testing/stub_flutter_windows_api.h @@ -89,6 +89,9 @@ class StubFlutterWindowsApi { // FlutterDesktopPluginRegistrarUnregisterTopLevelWindowProcDelegate. virtual void PluginRegistrarUnregisterTopLevelWindowProcDelegate( FlutterDesktopWindowProcCallback delegate) {} + + // Claled for FlutterDesktopEngineProcessExternalWindowMessage. + virtual void EngineProcessExternalWindowMessage(FlutterDesktopEngineRef engine, HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam) {} }; // A test helper that owns a stub implementation, making it the test stub for From b9b3ae085c09e72554454f004414eecc4175c122 Mon Sep 17 00:00:00 2001 From: schectman Date: Wed, 26 Jul 2023 14:08:35 -0400 Subject: [PATCH 21/33] Formatting --- .../windows/client_wrapper/flutter_engine.cc | 7 ++---- .../flutter_engine_unittests.cc | 6 ++++- .../include/flutter/flutter_engine.h | 15 ++++++------ .../testing/stub_flutter_windows_api.cc | 13 +++++----- .../testing/stub_flutter_windows_api.h | 7 +++++- .../windows/flutter_window_unittests.cc | 24 +++++++++++-------- shell/platform/windows/flutter_windows.cc | 3 ++- 7 files changed, 43 insertions(+), 32 deletions(-) diff --git a/shell/platform/windows/client_wrapper/flutter_engine.cc b/shell/platform/windows/client_wrapper/flutter_engine.cc index 7e1b3672aa72a..8255cbfa8b648 100644 --- a/shell/platform/windows/client_wrapper/flutter_engine.cc +++ b/shell/platform/windows/client_wrapper/flutter_engine.cc @@ -104,11 +104,8 @@ void FlutterEngine::ProcessExternalWindowMessage(HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam) { - FlutterDesktopEngineProcessExternalWindowMessage(engine_, - hwnd, - message, - wparam, - lparam); + FlutterDesktopEngineProcessExternalWindowMessage(engine_, hwnd, message, + wparam, lparam); } FlutterDesktopEngineRef FlutterEngine::RelinquishEngine() { diff --git a/shell/platform/windows/client_wrapper/flutter_engine_unittests.cc b/shell/platform/windows/client_wrapper/flutter_engine_unittests.cc index a7c95b15c47fd..62075e3984e7a 100644 --- a/shell/platform/windows/client_wrapper/flutter_engine_unittests.cc +++ b/shell/platform/windows/client_wrapper/flutter_engine_unittests.cc @@ -57,7 +57,11 @@ class TestFlutterWindowsApi : public testing::StubFlutterWindowsApi { void EngineReloadSystemFonts() override { reload_fonts_called_ = true; } // |flutter::testing::StubFlutterWindowsApi| - void EngineProcessExternalWindowMessage(FlutterDesktopEngineRef engine, HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam) { + void EngineProcessExternalWindowMessage(FlutterDesktopEngineRef engine, + HWND hwnd, + UINT message, + WPARAM wparam, + LPARAM lparam) { last_message_ = message; } diff --git a/shell/platform/windows/client_wrapper/include/flutter/flutter_engine.h b/shell/platform/windows/client_wrapper/include/flutter/flutter_engine.h index 7c6e440b89ff2..ec3bc7ba7c103 100644 --- a/shell/platform/windows/client_wrapper/include/flutter/flutter_engine.h +++ b/shell/platform/windows/client_wrapper/include/flutter/flutter_engine.h @@ -85,14 +85,13 @@ class FlutterEngine : public PluginRegistry { void SetNextFrameCallback(std::function callback); // Called to pass an external window message to the engine for lifecycle - // state updates. This does not consume the window message. Non-Flutter windows - // must call this method in their WndProc in order to be included in the logic - // for application lifecycle state updates. - void ProcessExternalWindowMessage( - HWND hwnd, - UINT message, - WPARAM wparam, - LPARAM lparam); + // state updates. This does not consume the window message. Non-Flutter + // windows must call this method in their WndProc in order to be included in + // the logic for application lifecycle state updates. + void ProcessExternalWindowMessage(HWND hwnd, + UINT message, + WPARAM wparam, + LPARAM lparam); private: // For access to RelinquishEngine. diff --git a/shell/platform/windows/client_wrapper/testing/stub_flutter_windows_api.cc b/shell/platform/windows/client_wrapper/testing/stub_flutter_windows_api.cc index 99294344754c6..dd3dd6262baf0 100644 --- a/shell/platform/windows/client_wrapper/testing/stub_flutter_windows_api.cc +++ b/shell/platform/windows/client_wrapper/testing/stub_flutter_windows_api.cc @@ -163,13 +163,14 @@ IDXGIAdapter* FlutterDesktopViewGetGraphicsAdapter(FlutterDesktopViewRef view) { } void FlutterDesktopEngineProcessExternalWindowMessage( - FlutterDesktopEngineRef engine, - HWND hwnd, - UINT message, - WPARAM wparam, - LPARAM lparam) { + FlutterDesktopEngineRef engine, + HWND hwnd, + UINT message, + WPARAM wparam, + LPARAM lparam) { if (s_stub_implementation) { - s_stub_implementation->EngineProcessExternalWindowMessage(engine, hwnd, message, wparam, lparam); + s_stub_implementation->EngineProcessExternalWindowMessage( + engine, hwnd, message, wparam, lparam); } } diff --git a/shell/platform/windows/client_wrapper/testing/stub_flutter_windows_api.h b/shell/platform/windows/client_wrapper/testing/stub_flutter_windows_api.h index 9c72a3b8bdd19..9c73a4a307309 100644 --- a/shell/platform/windows/client_wrapper/testing/stub_flutter_windows_api.h +++ b/shell/platform/windows/client_wrapper/testing/stub_flutter_windows_api.h @@ -91,7 +91,12 @@ class StubFlutterWindowsApi { FlutterDesktopWindowProcCallback delegate) {} // Claled for FlutterDesktopEngineProcessExternalWindowMessage. - virtual void EngineProcessExternalWindowMessage(FlutterDesktopEngineRef engine, HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam) {} + virtual void EngineProcessExternalWindowMessage( + FlutterDesktopEngineRef engine, + HWND hwnd, + UINT message, + WPARAM wparam, + LPARAM lparam) {} }; // A test helper that owns a stub implementation, making it the test stub for diff --git a/shell/platform/windows/flutter_window_unittests.cc b/shell/platform/windows/flutter_window_unittests.cc index 47205683c4de2..8ba12aecd7cda 100644 --- a/shell/platform/windows/flutter_window_unittests.cc +++ b/shell/platform/windows/flutter_window_unittests.cc @@ -330,7 +330,9 @@ TEST(FlutterWindowTest, AlertNode) { TEST(FlutterWindowTest, LifecycleFocusMessages) { MockFlutterWindow win32window; - ON_CALL(win32window, GetPlatformWindow).WillByDefault([](){ return reinterpret_cast(1); }); + ON_CALL(win32window, GetPlatformWindow).WillByDefault([]() { + return reinterpret_cast(1); + }); MockWindowBindingHandlerDelegate delegate; win32window.SetView(&delegate); @@ -355,7 +357,9 @@ TEST(FlutterWindowTest, LifecycleFocusMessages) { TEST(FlutterWindowTest, CachedLifecycleMessage) { MockFlutterWindow win32window; - ON_CALL(win32window, GetPlatformWindow).WillByDefault([](){ return reinterpret_cast(1); }); + ON_CALL(win32window, GetPlatformWindow).WillByDefault([]() { + return reinterpret_cast(1); + }); // Restore win32window.InjectWindowMessage(WM_SIZE, 0, MAKEWORD(1, 1)); @@ -366,14 +370,14 @@ TEST(FlutterWindowTest, CachedLifecycleMessage) { MockWindowBindingHandlerDelegate delegate; bool focused = false; bool restored = false; - ON_CALL(delegate, OnWindowStateEvent).WillByDefault([&](HWND hwnd, WindowStateEvent event) { - if (event == WindowStateEvent::kFocus) { - focused = true; - } - else if (event == WindowStateEvent::kShow) { - restored = true; - } - }); + ON_CALL(delegate, OnWindowStateEvent) + .WillByDefault([&](HWND hwnd, WindowStateEvent event) { + if (event == WindowStateEvent::kFocus) { + focused = true; + } else if (event == WindowStateEvent::kShow) { + restored = true; + } + }); win32window.SetView(&delegate); EXPECT_TRUE(focused); diff --git a/shell/platform/windows/flutter_windows.cc b/shell/platform/windows/flutter_windows.cc index 742bc7566de5a..0ca9d6a6bc323 100644 --- a/shell/platform/windows/flutter_windows.cc +++ b/shell/platform/windows/flutter_windows.cc @@ -213,7 +213,8 @@ void FlutterDesktopEngineProcessExternalWindowMessage( UINT message, WPARAM wparam, LPARAM lparam) { - EngineFromHandle(engine)->ProcessExternalWindowMessage(hwnd, message, wparam, lparam); + EngineFromHandle(engine)->ProcessExternalWindowMessage(hwnd, message, wparam, + lparam); } FlutterDesktopViewRef FlutterDesktopPluginRegistrarGetView( From 8b1fdb2c189e3539bf22fead5a47bb69e3d13849 Mon Sep 17 00:00:00 2001 From: schectman Date: Wed, 26 Jul 2023 17:41:05 -0400 Subject: [PATCH 22/33] Remove TODO --- shell/platform/windows/windows_lifecycle_manager.h | 4 ---- 1 file changed, 4 deletions(-) diff --git a/shell/platform/windows/windows_lifecycle_manager.h b/shell/platform/windows/windows_lifecycle_manager.h index 755b34d0f701c..f9ff784565a5f 100644 --- a/shell/platform/windows/windows_lifecycle_manager.h +++ b/shell/platform/windows/windows_lifecycle_manager.h @@ -47,10 +47,6 @@ class WindowsLifecycleManager { std::optional lparam, UINT exit_code); - // TODO(schectman): Provide an API function for non-Flutter windows' WndProc - // function to call in order to contribute to the application lifecycle. - // https://github.com/flutter/flutter/issues/103637 - // Intercept top level window WM_CLOSE message and listen to events that may // update the application lifecycle. bool WindowProc(HWND hwnd, UINT msg, WPARAM w, LPARAM l, LRESULT* result); From 0e25cfdce481c13adc7c60e175c4cb9dde3c01bd Mon Sep 17 00:00:00 2001 From: schectman Date: Thu, 27 Jul 2023 09:37:47 -0400 Subject: [PATCH 23/33] Test external window message --- .../flutter_windows_engine_unittests.cc | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/shell/platform/windows/flutter_windows_engine_unittests.cc b/shell/platform/windows/flutter_windows_engine_unittests.cc index 5f27d4bf1b676..9eb6e4090ebe7 100644 --- a/shell/platform/windows/flutter_windows_engine_unittests.cc +++ b/shell/platform/windows/flutter_windows_engine_unittests.cc @@ -943,5 +943,26 @@ TEST_F(FlutterWindowsEngineTest, LifecycleStateTransition) { AppLifecycleState::kInactive); } +TEST_F(FlutterWindowsEngineTest, ExternalWindowMessage) { + FlutterWindowsEngineBuilder builder{GetContext()}; + + auto window_binding_handler = + std::make_unique<::testing::NiceMock>(); + MockFlutterWindowsView view(std::move(window_binding_handler)); + view.SetEngine(builder.Build()); + FlutterWindowsEngine* engine = view.GetEngine(); + + EngineModifier modifier(engine); + modifier.embedder_api().RunsAOTCompiledDartCode = []() { return false; }; + // Sets lifecycle state to resumed. + engine->Run(); + + // Ensure HWND(1) is in the set of visible windows before hiding it. + engine->ProcessExternalWindowMessage(reinterpret_cast(1), WM_SHOWWINDOW, TRUE, NULL); + engine->ProcessExternalWindowMessage(reinterpret_cast(1), WM_SHOWWINDOW, FALSE, NULL); + + EXPECT_EQ(engine->GetLifecycleManager()->GetLifecycleState(), AppLifecycleState::kHidden); +} + } // namespace testing } // namespace flutter From df35a6a9454f24fb3b1027bf106e3b13521d799b Mon Sep 17 00:00:00 2001 From: schectman Date: Thu, 27 Jul 2023 09:43:08 -0400 Subject: [PATCH 24/33] Move logic to WindowsLifecycleManager --- .../windows/flutter_windows_engine.cc | 16 +---------- .../windows/windows_lifecycle_manager.cc | 28 +++++++++++++++++++ .../windows/windows_lifecycle_manager.h | 4 +++ 3 files changed, 33 insertions(+), 15 deletions(-) diff --git a/shell/platform/windows/flutter_windows_engine.cc b/shell/platform/windows/flutter_windows_engine.cc index dac2e6d63115c..a64249400ccb8 100644 --- a/shell/platform/windows/flutter_windows_engine.cc +++ b/shell/platform/windows/flutter_windows_engine.cc @@ -805,28 +805,14 @@ void FlutterWindowsEngine::ProcessExternalWindowMessage(HWND hwnd, WPARAM wparam, LPARAM lparam) { if (lifecycle_manager_) { - std::optional event = std::nullopt; - switch (message) { case WM_SHOWWINDOW: - event = wparam ? flutter::WindowStateEvent::kShow - : flutter::WindowStateEvent::kHide; - break; case WM_SIZE: - event = wparam == SIZE_MINIMIZED ? flutter::WindowStateEvent::kHide - : flutter::WindowStateEvent::kShow; - break; case WM_SETFOCUS: - event = flutter::WindowStateEvent::kFocus; - break; case WM_KILLFOCUS: - event = flutter::WindowStateEvent::kUnfocus; + lifecycle_manager_->ExternalWindowMessage(hwnd, message, wparam, lparam); break; } - - if (event.has_value()) { - lifecycle_manager_->OnWindowStateEvent(hwnd, *event); - } } } diff --git a/shell/platform/windows/windows_lifecycle_manager.cc b/shell/platform/windows/windows_lifecycle_manager.cc index 7cbd74ab2d9c1..a7a48a2d53fc9 100644 --- a/shell/platform/windows/windows_lifecycle_manager.cc +++ b/shell/platform/windows/windows_lifecycle_manager.cc @@ -242,4 +242,32 @@ void WindowsLifecycleManager::OnWindowStateEvent(HWND hwnd, } } +void WindowsLifecycleManager::ExternalWindowMessage(HWND hwnd, + UINT message, + WPARAM wparam, + LPARAM lparam) { + std::optional event = std::nullopt; + + switch (message) { + case WM_SHOWWINDOW: + event = wparam ? flutter::WindowStateEvent::kShow + : flutter::WindowStateEvent::kHide; + break; + case WM_SIZE: + event = wparam == SIZE_MINIMIZED ? flutter::WindowStateEvent::kHide + : flutter::WindowStateEvent::kShow; + break; + case WM_SETFOCUS: + event = flutter::WindowStateEvent::kFocus; + break; + case WM_KILLFOCUS: + event = flutter::WindowStateEvent::kUnfocus; + break; + } + + if (event.has_value()) { + OnWindowStateEvent(hwnd, *event); + } +} + } // namespace flutter diff --git a/shell/platform/windows/windows_lifecycle_manager.h b/shell/platform/windows/windows_lifecycle_manager.h index f9ff784565a5f..759089ba6a95d 100644 --- a/shell/platform/windows/windows_lifecycle_manager.h +++ b/shell/platform/windows/windows_lifecycle_manager.h @@ -70,6 +70,10 @@ class WindowsLifecycleManager { AppLifecycleState GetLifecycleState() { return state_; } + // Called by the engine when a non-Flutter window receives an event that may + // alter the lifecycle state. + void ExternalWindowMessage(HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam); + protected: // Check the number of top-level windows associated with this process, and // return true only if there are 1 or fewer. From bad630200785a8e02c9a05b7cb4e6ec04ee94d68 Mon Sep 17 00:00:00 2001 From: schectman Date: Thu, 27 Jul 2023 09:44:20 -0400 Subject: [PATCH 25/33] Comment --- shell/platform/windows/flutter_windows_engine.cc | 3 ++- .../windows/flutter_windows_engine_unittests.cc | 9 ++++++--- shell/platform/windows/windows_lifecycle_manager.cc | 4 ++-- shell/platform/windows/windows_lifecycle_manager.h | 12 ++++++++++-- 4 files changed, 20 insertions(+), 8 deletions(-) diff --git a/shell/platform/windows/flutter_windows_engine.cc b/shell/platform/windows/flutter_windows_engine.cc index a64249400ccb8..ac49065198d60 100644 --- a/shell/platform/windows/flutter_windows_engine.cc +++ b/shell/platform/windows/flutter_windows_engine.cc @@ -810,7 +810,8 @@ void FlutterWindowsEngine::ProcessExternalWindowMessage(HWND hwnd, case WM_SIZE: case WM_SETFOCUS: case WM_KILLFOCUS: - lifecycle_manager_->ExternalWindowMessage(hwnd, message, wparam, lparam); + lifecycle_manager_->ExternalWindowMessage(hwnd, message, wparam, + lparam); break; } } diff --git a/shell/platform/windows/flutter_windows_engine_unittests.cc b/shell/platform/windows/flutter_windows_engine_unittests.cc index 9eb6e4090ebe7..3117d53e7002e 100644 --- a/shell/platform/windows/flutter_windows_engine_unittests.cc +++ b/shell/platform/windows/flutter_windows_engine_unittests.cc @@ -958,10 +958,13 @@ TEST_F(FlutterWindowsEngineTest, ExternalWindowMessage) { engine->Run(); // Ensure HWND(1) is in the set of visible windows before hiding it. - engine->ProcessExternalWindowMessage(reinterpret_cast(1), WM_SHOWWINDOW, TRUE, NULL); - engine->ProcessExternalWindowMessage(reinterpret_cast(1), WM_SHOWWINDOW, FALSE, NULL); + engine->ProcessExternalWindowMessage(reinterpret_cast(1), WM_SHOWWINDOW, + TRUE, NULL); + engine->ProcessExternalWindowMessage(reinterpret_cast(1), WM_SHOWWINDOW, + FALSE, NULL); - EXPECT_EQ(engine->GetLifecycleManager()->GetLifecycleState(), AppLifecycleState::kHidden); + EXPECT_EQ(engine->GetLifecycleManager()->GetLifecycleState(), + AppLifecycleState::kHidden); } } // namespace testing diff --git a/shell/platform/windows/windows_lifecycle_manager.cc b/shell/platform/windows/windows_lifecycle_manager.cc index a7a48a2d53fc9..0ca61bcfffb70 100644 --- a/shell/platform/windows/windows_lifecycle_manager.cc +++ b/shell/platform/windows/windows_lifecycle_manager.cc @@ -251,11 +251,11 @@ void WindowsLifecycleManager::ExternalWindowMessage(HWND hwnd, switch (message) { case WM_SHOWWINDOW: event = wparam ? flutter::WindowStateEvent::kShow - : flutter::WindowStateEvent::kHide; + : flutter::WindowStateEvent::kHide; break; case WM_SIZE: event = wparam == SIZE_MINIMIZED ? flutter::WindowStateEvent::kHide - : flutter::WindowStateEvent::kShow; + : flutter::WindowStateEvent::kShow; break; case WM_SETFOCUS: event = flutter::WindowStateEvent::kFocus; diff --git a/shell/platform/windows/windows_lifecycle_manager.h b/shell/platform/windows/windows_lifecycle_manager.h index 759089ba6a95d..348973837b338 100644 --- a/shell/platform/windows/windows_lifecycle_manager.h +++ b/shell/platform/windows/windows_lifecycle_manager.h @@ -71,8 +71,16 @@ class WindowsLifecycleManager { AppLifecycleState GetLifecycleState() { return state_; } // Called by the engine when a non-Flutter window receives an event that may - // alter the lifecycle state. - void ExternalWindowMessage(HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam); + // alter the lifecycle state. The logic for external windows must differ from + // that used for FlutterWindow instances, because: + // - FlutterWindow does not receive WM_SHOW messages, + // - When FlutterWindow receives WM_SIZE messages, wparam stores no meaningful + // information, whereas it usually indicates the action which changed the + // window size. + void ExternalWindowMessage(HWND hwnd, + UINT message, + WPARAM wparam, + LPARAM lparam); protected: // Check the number of top-level windows associated with this process, and From 1623cd92dd0cab53e7bfbe8ad18c409830799784 Mon Sep 17 00:00:00 2001 From: schectman Date: Thu, 27 Jul 2023 17:14:54 -0400 Subject: [PATCH 26/33] PR feedback --- .../windows/windows_lifecycle_manager.cc | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/shell/platform/windows/windows_lifecycle_manager.cc b/shell/platform/windows/windows_lifecycle_manager.cc index 0ca61bcfffb70..c06bcab333222 100644 --- a/shell/platform/windows/windows_lifecycle_manager.cc +++ b/shell/platform/windows/windows_lifecycle_manager.cc @@ -87,6 +87,8 @@ bool WindowsLifecycleManager::WindowProc(HWND hwnd, case WM_SHOWWINDOW: if (!wpar) { OnWindowStateEvent(hwnd, WindowStateEvent::kHide); + } else { + OnWindowStateEvent(hwnd, WindowStateEvent::kShow); } break; @@ -254,8 +256,15 @@ void WindowsLifecycleManager::ExternalWindowMessage(HWND hwnd, : flutter::WindowStateEvent::kHide; break; case WM_SIZE: - event = wparam == SIZE_MINIMIZED ? flutter::WindowStateEvent::kHide - : flutter::WindowStateEvent::kShow; + switch (wparam) { + case SIZE_MINIMIZED: + event = flutter::WindowStateEvent::kHide; + break; + case SIZE_RESTORED: + case SIZE_MAXIMIZED: + event = flutter::WindowStateEvent::kShow; + break; + } break; case WM_SETFOCUS: event = flutter::WindowStateEvent::kFocus; @@ -263,6 +272,9 @@ void WindowsLifecycleManager::ExternalWindowMessage(HWND hwnd, case WM_KILLFOCUS: event = flutter::WindowStateEvent::kUnfocus; break; + case WM_DESTROY: + event = flutter::WindowStateEvent::kHide; + break; } if (event.has_value()) { From 7c0c0bf20e2246253cc7a4c6f2028c731674529b Mon Sep 17 00:00:00 2001 From: schectman Date: Fri, 28 Jul 2023 12:10:15 -0400 Subject: [PATCH 27/33] Return bool --- shell/platform/windows/client_wrapper/flutter_engine.cc | 6 +++--- .../windows/client_wrapper/flutter_engine_unittests.cc | 3 ++- .../client_wrapper/include/flutter/flutter_engine.h | 5 +++-- .../client_wrapper/testing/stub_flutter_windows_api.cc | 5 +++-- .../client_wrapper/testing/stub_flutter_windows_api.h | 6 ++++-- shell/platform/windows/flutter_windows.cc | 6 +++--- shell/platform/windows/flutter_windows_engine.cc | 8 ++++---- shell/platform/windows/flutter_windows_engine.h | 4 +++- shell/platform/windows/public/flutter_windows.h | 5 +++-- shell/platform/windows/windows_lifecycle_manager.cc | 4 +++- shell/platform/windows/windows_lifecycle_manager.h | 4 +++- 11 files changed, 34 insertions(+), 22 deletions(-) diff --git a/shell/platform/windows/client_wrapper/flutter_engine.cc b/shell/platform/windows/client_wrapper/flutter_engine.cc index 8255cbfa8b648..98456a0600ffe 100644 --- a/shell/platform/windows/client_wrapper/flutter_engine.cc +++ b/shell/platform/windows/client_wrapper/flutter_engine.cc @@ -100,12 +100,12 @@ void FlutterEngine::SetNextFrameCallback(std::function callback) { this); } -void FlutterEngine::ProcessExternalWindowMessage(HWND hwnd, +bool FlutterEngine::ProcessExternalWindowMessage(HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam) { - FlutterDesktopEngineProcessExternalWindowMessage(engine_, hwnd, message, - wparam, lparam); + return FlutterDesktopEngineProcessExternalWindowMessage( + engine_, hwnd, message, wparam, lparam); } FlutterDesktopEngineRef FlutterEngine::RelinquishEngine() { diff --git a/shell/platform/windows/client_wrapper/flutter_engine_unittests.cc b/shell/platform/windows/client_wrapper/flutter_engine_unittests.cc index 62075e3984e7a..eb04c87696a5b 100644 --- a/shell/platform/windows/client_wrapper/flutter_engine_unittests.cc +++ b/shell/platform/windows/client_wrapper/flutter_engine_unittests.cc @@ -57,12 +57,13 @@ class TestFlutterWindowsApi : public testing::StubFlutterWindowsApi { void EngineReloadSystemFonts() override { reload_fonts_called_ = true; } // |flutter::testing::StubFlutterWindowsApi| - void EngineProcessExternalWindowMessage(FlutterDesktopEngineRef engine, + bool EngineProcessExternalWindowMessage(FlutterDesktopEngineRef engine, HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam) { last_message_ = message; + return false; } bool create_called() { return create_called_; } diff --git a/shell/platform/windows/client_wrapper/include/flutter/flutter_engine.h b/shell/platform/windows/client_wrapper/include/flutter/flutter_engine.h index ec3bc7ba7c103..59388f9f25f1b 100644 --- a/shell/platform/windows/client_wrapper/include/flutter/flutter_engine.h +++ b/shell/platform/windows/client_wrapper/include/flutter/flutter_engine.h @@ -87,8 +87,9 @@ class FlutterEngine : public PluginRegistry { // Called to pass an external window message to the engine for lifecycle // state updates. This does not consume the window message. Non-Flutter // windows must call this method in their WndProc in order to be included in - // the logic for application lifecycle state updates. - void ProcessExternalWindowMessage(HWND hwnd, + // the logic for application lifecycle state updates. Returns true when the + // message has been consumed. + bool ProcessExternalWindowMessage(HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam); diff --git a/shell/platform/windows/client_wrapper/testing/stub_flutter_windows_api.cc b/shell/platform/windows/client_wrapper/testing/stub_flutter_windows_api.cc index dd3dd6262baf0..27ff11cc5f714 100644 --- a/shell/platform/windows/client_wrapper/testing/stub_flutter_windows_api.cc +++ b/shell/platform/windows/client_wrapper/testing/stub_flutter_windows_api.cc @@ -162,16 +162,17 @@ IDXGIAdapter* FlutterDesktopViewGetGraphicsAdapter(FlutterDesktopViewRef view) { return nullptr; } -void FlutterDesktopEngineProcessExternalWindowMessage( +bool FlutterDesktopEngineProcessExternalWindowMessage( FlutterDesktopEngineRef engine, HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam) { if (s_stub_implementation) { - s_stub_implementation->EngineProcessExternalWindowMessage( + return s_stub_implementation->EngineProcessExternalWindowMessage( engine, hwnd, message, wparam, lparam); } + return false; } FlutterDesktopViewRef FlutterDesktopPluginRegistrarGetView( diff --git a/shell/platform/windows/client_wrapper/testing/stub_flutter_windows_api.h b/shell/platform/windows/client_wrapper/testing/stub_flutter_windows_api.h index 9c73a4a307309..dcd96b039c27a 100644 --- a/shell/platform/windows/client_wrapper/testing/stub_flutter_windows_api.h +++ b/shell/platform/windows/client_wrapper/testing/stub_flutter_windows_api.h @@ -91,12 +91,14 @@ class StubFlutterWindowsApi { FlutterDesktopWindowProcCallback delegate) {} // Claled for FlutterDesktopEngineProcessExternalWindowMessage. - virtual void EngineProcessExternalWindowMessage( + virtual bool EngineProcessExternalWindowMessage( FlutterDesktopEngineRef engine, HWND hwnd, UINT message, WPARAM wparam, - LPARAM lparam) {} + LPARAM lparam) { + return false; + } }; // A test helper that owns a stub implementation, making it the test stub for diff --git a/shell/platform/windows/flutter_windows.cc b/shell/platform/windows/flutter_windows.cc index 0ca9d6a6bc323..eaf33d339475e 100644 --- a/shell/platform/windows/flutter_windows.cc +++ b/shell/platform/windows/flutter_windows.cc @@ -207,14 +207,14 @@ IDXGIAdapter* FlutterDesktopViewGetGraphicsAdapter(FlutterDesktopViewRef view) { return nullptr; } -void FlutterDesktopEngineProcessExternalWindowMessage( +bool FlutterDesktopEngineProcessExternalWindowMessage( FlutterDesktopEngineRef engine, HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam) { - EngineFromHandle(engine)->ProcessExternalWindowMessage(hwnd, message, wparam, - lparam); + return EngineFromHandle(engine)->ProcessExternalWindowMessage(hwnd, message, + wparam, lparam); } FlutterDesktopViewRef FlutterDesktopPluginRegistrarGetView( diff --git a/shell/platform/windows/flutter_windows_engine.cc b/shell/platform/windows/flutter_windows_engine.cc index ac49065198d60..0c6ed0ed07365 100644 --- a/shell/platform/windows/flutter_windows_engine.cc +++ b/shell/platform/windows/flutter_windows_engine.cc @@ -800,7 +800,7 @@ void FlutterWindowsEngine::OnWindowStateEvent(HWND hwnd, lifecycle_manager_->OnWindowStateEvent(hwnd, event); } -void FlutterWindowsEngine::ProcessExternalWindowMessage(HWND hwnd, +bool FlutterWindowsEngine::ProcessExternalWindowMessage(HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam) { @@ -810,11 +810,11 @@ void FlutterWindowsEngine::ProcessExternalWindowMessage(HWND hwnd, case WM_SIZE: case WM_SETFOCUS: case WM_KILLFOCUS: - lifecycle_manager_->ExternalWindowMessage(hwnd, message, wparam, - lparam); - break; + return lifecycle_manager_->ExternalWindowMessage(hwnd, message, wparam, + lparam); } } + return false; } } // namespace flutter diff --git a/shell/platform/windows/flutter_windows_engine.h b/shell/platform/windows/flutter_windows_engine.h index 3c76bf7dee35b..fcccf7ae2b343 100644 --- a/shell/platform/windows/flutter_windows_engine.h +++ b/shell/platform/windows/flutter_windows_engine.h @@ -271,7 +271,9 @@ class FlutterWindowsEngine { } // Handle a message from a non-Flutter window in the same application. - void ProcessExternalWindowMessage(HWND hwnd, + // Returns true when the message is consumed and should not be processed + // further. + bool ProcessExternalWindowMessage(HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam); diff --git a/shell/platform/windows/public/flutter_windows.h b/shell/platform/windows/public/flutter_windows.h index 85f79d41df11a..ce1abd7b7a8d7 100644 --- a/shell/platform/windows/public/flutter_windows.h +++ b/shell/platform/windows/public/flutter_windows.h @@ -215,8 +215,9 @@ FLUTTER_EXPORT IDXGIAdapter* FlutterDesktopViewGetGraphicsAdapter( // Called to pass an external window message to the engine for lifecycle // state updates. This does not consume the window message. Non-Flutter windows // must call this method in their WndProc in order to be included in the logic -// for application lifecycle state updates. -FLUTTER_EXPORT void FlutterDesktopEngineProcessExternalWindowMessage( +// for application lifecycle state updates. Returns true when the message is +// consumed. +FLUTTER_EXPORT bool FlutterDesktopEngineProcessExternalWindowMessage( FlutterDesktopEngineRef engine, HWND hwnd, UINT message, diff --git a/shell/platform/windows/windows_lifecycle_manager.cc b/shell/platform/windows/windows_lifecycle_manager.cc index c06bcab333222..99e7944de5961 100644 --- a/shell/platform/windows/windows_lifecycle_manager.cc +++ b/shell/platform/windows/windows_lifecycle_manager.cc @@ -244,7 +244,7 @@ void WindowsLifecycleManager::OnWindowStateEvent(HWND hwnd, } } -void WindowsLifecycleManager::ExternalWindowMessage(HWND hwnd, +bool WindowsLifecycleManager::ExternalWindowMessage(HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam) { @@ -280,6 +280,8 @@ void WindowsLifecycleManager::ExternalWindowMessage(HWND hwnd, if (event.has_value()) { OnWindowStateEvent(hwnd, *event); } + + return false; } } // namespace flutter diff --git a/shell/platform/windows/windows_lifecycle_manager.h b/shell/platform/windows/windows_lifecycle_manager.h index 348973837b338..18e16471a9665 100644 --- a/shell/platform/windows/windows_lifecycle_manager.h +++ b/shell/platform/windows/windows_lifecycle_manager.h @@ -77,7 +77,9 @@ class WindowsLifecycleManager { // - When FlutterWindow receives WM_SIZE messages, wparam stores no meaningful // information, whereas it usually indicates the action which changed the // window size. - void ExternalWindowMessage(HWND hwnd, + // When this returns true, the message has been consumed and should not be + // processed further. Currently, it will always return false. + bool ExternalWindowMessage(HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam); From f2929627cf6308d9f2e4a0be2b8ef757c1665859 Mon Sep 17 00:00:00 2001 From: schectman Date: Fri, 28 Jul 2023 13:13:09 -0400 Subject: [PATCH 28/33] Add result and TODO --- shell/platform/windows/client_wrapper/flutter_engine.cc | 5 +++-- .../windows/client_wrapper/flutter_engine_unittests.cc | 6 ++++-- .../client_wrapper/include/flutter/flutter_engine.h | 3 ++- .../client_wrapper/testing/stub_flutter_windows_api.cc | 5 +++-- .../client_wrapper/testing/stub_flutter_windows_api.h | 3 ++- shell/platform/windows/flutter_windows.cc | 7 ++++--- shell/platform/windows/flutter_windows_engine.cc | 5 +++-- shell/platform/windows/flutter_windows_engine.h | 3 ++- shell/platform/windows/flutter_windows_engine_unittests.cc | 4 ++-- shell/platform/windows/public/flutter_windows.h | 3 ++- shell/platform/windows/windows_lifecycle_manager.cc | 5 ++++- shell/platform/windows/windows_lifecycle_manager.h | 3 ++- 12 files changed, 33 insertions(+), 19 deletions(-) diff --git a/shell/platform/windows/client_wrapper/flutter_engine.cc b/shell/platform/windows/client_wrapper/flutter_engine.cc index 98456a0600ffe..8051ca9a28bd3 100644 --- a/shell/platform/windows/client_wrapper/flutter_engine.cc +++ b/shell/platform/windows/client_wrapper/flutter_engine.cc @@ -103,9 +103,10 @@ void FlutterEngine::SetNextFrameCallback(std::function callback) { bool FlutterEngine::ProcessExternalWindowMessage(HWND hwnd, UINT message, WPARAM wparam, - LPARAM lparam) { + LPARAM lparam, + LRESULT* result) { return FlutterDesktopEngineProcessExternalWindowMessage( - engine_, hwnd, message, wparam, lparam); + engine_, hwnd, message, wparam, lparam, result); } FlutterDesktopEngineRef FlutterEngine::RelinquishEngine() { diff --git a/shell/platform/windows/client_wrapper/flutter_engine_unittests.cc b/shell/platform/windows/client_wrapper/flutter_engine_unittests.cc index eb04c87696a5b..7cca35c935a7a 100644 --- a/shell/platform/windows/client_wrapper/flutter_engine_unittests.cc +++ b/shell/platform/windows/client_wrapper/flutter_engine_unittests.cc @@ -61,7 +61,8 @@ class TestFlutterWindowsApi : public testing::StubFlutterWindowsApi { HWND hwnd, UINT message, WPARAM wparam, - LPARAM lparam) { + LPARAM lparam, + LRESULT* result) override { last_message_ = message; return false; } @@ -221,7 +222,8 @@ TEST(FlutterEngineTest, ProcessExternalWindowMessage) { FlutterEngine engine(DartProject(L"fake/project/path")); - engine.ProcessExternalWindowMessage(reinterpret_cast(1), 1234, 0, 0); + engine.ProcessExternalWindowMessage(reinterpret_cast(1), 1234, 0, 0, + nullptr); EXPECT_EQ(test_api->last_message(), 1234); } diff --git a/shell/platform/windows/client_wrapper/include/flutter/flutter_engine.h b/shell/platform/windows/client_wrapper/include/flutter/flutter_engine.h index 59388f9f25f1b..e880c256e05a0 100644 --- a/shell/platform/windows/client_wrapper/include/flutter/flutter_engine.h +++ b/shell/platform/windows/client_wrapper/include/flutter/flutter_engine.h @@ -92,7 +92,8 @@ class FlutterEngine : public PluginRegistry { bool ProcessExternalWindowMessage(HWND hwnd, UINT message, WPARAM wparam, - LPARAM lparam); + LPARAM lparam, + LRESULT* result); private: // For access to RelinquishEngine. diff --git a/shell/platform/windows/client_wrapper/testing/stub_flutter_windows_api.cc b/shell/platform/windows/client_wrapper/testing/stub_flutter_windows_api.cc index 27ff11cc5f714..da0a8c57712d3 100644 --- a/shell/platform/windows/client_wrapper/testing/stub_flutter_windows_api.cc +++ b/shell/platform/windows/client_wrapper/testing/stub_flutter_windows_api.cc @@ -167,10 +167,11 @@ bool FlutterDesktopEngineProcessExternalWindowMessage( HWND hwnd, UINT message, WPARAM wparam, - LPARAM lparam) { + LPARAM lparam, + LRESULT* result) { if (s_stub_implementation) { return s_stub_implementation->EngineProcessExternalWindowMessage( - engine, hwnd, message, wparam, lparam); + engine, hwnd, message, wparam, lparam, result); } return false; } diff --git a/shell/platform/windows/client_wrapper/testing/stub_flutter_windows_api.h b/shell/platform/windows/client_wrapper/testing/stub_flutter_windows_api.h index dcd96b039c27a..73e920d949e32 100644 --- a/shell/platform/windows/client_wrapper/testing/stub_flutter_windows_api.h +++ b/shell/platform/windows/client_wrapper/testing/stub_flutter_windows_api.h @@ -96,7 +96,8 @@ class StubFlutterWindowsApi { HWND hwnd, UINT message, WPARAM wparam, - LPARAM lparam) { + LPARAM lparam, + LRESULT* result) { return false; } }; diff --git a/shell/platform/windows/flutter_windows.cc b/shell/platform/windows/flutter_windows.cc index eaf33d339475e..4b0eff078bd25 100644 --- a/shell/platform/windows/flutter_windows.cc +++ b/shell/platform/windows/flutter_windows.cc @@ -212,9 +212,10 @@ bool FlutterDesktopEngineProcessExternalWindowMessage( HWND hwnd, UINT message, WPARAM wparam, - LPARAM lparam) { - return EngineFromHandle(engine)->ProcessExternalWindowMessage(hwnd, message, - wparam, lparam); + LPARAM lparam, + LRESULT* result) { + return EngineFromHandle(engine)->ProcessExternalWindowMessage( + hwnd, message, wparam, lparam, result); } FlutterDesktopViewRef FlutterDesktopPluginRegistrarGetView( diff --git a/shell/platform/windows/flutter_windows_engine.cc b/shell/platform/windows/flutter_windows_engine.cc index 0c6ed0ed07365..83ae78f050760 100644 --- a/shell/platform/windows/flutter_windows_engine.cc +++ b/shell/platform/windows/flutter_windows_engine.cc @@ -803,7 +803,8 @@ void FlutterWindowsEngine::OnWindowStateEvent(HWND hwnd, bool FlutterWindowsEngine::ProcessExternalWindowMessage(HWND hwnd, UINT message, WPARAM wparam, - LPARAM lparam) { + LPARAM lparam, + LRESULT* result) { if (lifecycle_manager_) { switch (message) { case WM_SHOWWINDOW: @@ -811,7 +812,7 @@ bool FlutterWindowsEngine::ProcessExternalWindowMessage(HWND hwnd, case WM_SETFOCUS: case WM_KILLFOCUS: return lifecycle_manager_->ExternalWindowMessage(hwnd, message, wparam, - lparam); + lparam, result); } } return false; diff --git a/shell/platform/windows/flutter_windows_engine.h b/shell/platform/windows/flutter_windows_engine.h index fcccf7ae2b343..8f3048fc0c4f1 100644 --- a/shell/platform/windows/flutter_windows_engine.h +++ b/shell/platform/windows/flutter_windows_engine.h @@ -276,7 +276,8 @@ class FlutterWindowsEngine { bool ProcessExternalWindowMessage(HWND hwnd, UINT message, WPARAM wparam, - LPARAM lparam); + LPARAM lparam, + LRESULT* result); protected: // Creates the keyboard key handler. diff --git a/shell/platform/windows/flutter_windows_engine_unittests.cc b/shell/platform/windows/flutter_windows_engine_unittests.cc index 3117d53e7002e..c19e64f97f1f4 100644 --- a/shell/platform/windows/flutter_windows_engine_unittests.cc +++ b/shell/platform/windows/flutter_windows_engine_unittests.cc @@ -959,9 +959,9 @@ TEST_F(FlutterWindowsEngineTest, ExternalWindowMessage) { // Ensure HWND(1) is in the set of visible windows before hiding it. engine->ProcessExternalWindowMessage(reinterpret_cast(1), WM_SHOWWINDOW, - TRUE, NULL); + TRUE, NULL, nullptr); engine->ProcessExternalWindowMessage(reinterpret_cast(1), WM_SHOWWINDOW, - FALSE, NULL); + FALSE, NULL, nullptr); EXPECT_EQ(engine->GetLifecycleManager()->GetLifecycleState(), AppLifecycleState::kHidden); diff --git a/shell/platform/windows/public/flutter_windows.h b/shell/platform/windows/public/flutter_windows.h index ce1abd7b7a8d7..beee0f029f4ff 100644 --- a/shell/platform/windows/public/flutter_windows.h +++ b/shell/platform/windows/public/flutter_windows.h @@ -222,7 +222,8 @@ FLUTTER_EXPORT bool FlutterDesktopEngineProcessExternalWindowMessage( HWND hwnd, UINT message, WPARAM wparam, - LPARAM lparam); + LPARAM lparam, + LRESULT* result); // ========== Plugin Registrar (extensions) ========== // These are Windows-specific extensions to flutter_plugin_registrar.h diff --git a/shell/platform/windows/windows_lifecycle_manager.cc b/shell/platform/windows/windows_lifecycle_manager.cc index 99e7944de5961..88e3296701774 100644 --- a/shell/platform/windows/windows_lifecycle_manager.cc +++ b/shell/platform/windows/windows_lifecycle_manager.cc @@ -247,9 +247,12 @@ void WindowsLifecycleManager::OnWindowStateEvent(HWND hwnd, bool WindowsLifecycleManager::ExternalWindowMessage(HWND hwnd, UINT message, WPARAM wparam, - LPARAM lparam) { + LPARAM lparam, + LRESULT* result) { std::optional event = std::nullopt; + // TODO (schectman): Handle WM_CLOSE messages. + // https://github.com/flutter/flutter/issues/131497 switch (message) { case WM_SHOWWINDOW: event = wparam ? flutter::WindowStateEvent::kShow diff --git a/shell/platform/windows/windows_lifecycle_manager.h b/shell/platform/windows/windows_lifecycle_manager.h index 18e16471a9665..f4435ac912fb8 100644 --- a/shell/platform/windows/windows_lifecycle_manager.h +++ b/shell/platform/windows/windows_lifecycle_manager.h @@ -82,7 +82,8 @@ class WindowsLifecycleManager { bool ExternalWindowMessage(HWND hwnd, UINT message, WPARAM wparam, - LPARAM lparam); + LPARAM lparam, + LRESULT* result); protected: // Check the number of top-level windows associated with this process, and From addc73cb62baaafca13d0e9ee3155fbcf59033e6 Mon Sep 17 00:00:00 2001 From: schectman Date: Fri, 28 Jul 2023 14:14:30 -0400 Subject: [PATCH 29/33] Update C++ API to optional --- .../windows/client_wrapper/flutter_engine.cc | 18 +++++++++++------- .../client_wrapper/flutter_engine_unittests.cc | 3 +-- .../include/flutter/flutter_engine.h | 14 +++++++------- .../testing/stub_flutter_windows_api.h | 1 + shell/platform/windows/flutter_windows.cc | 9 +++++++-- .../platform/windows/flutter_windows_engine.cc | 14 +++++++------- .../platform/windows/flutter_windows_engine.h | 11 +++++------ .../flutter_windows_engine_unittests.cc | 4 ++-- .../windows/windows_lifecycle_manager.cc | 12 ++++++------ .../windows/windows_lifecycle_manager.h | 13 ++++++------- 10 files changed, 53 insertions(+), 46 deletions(-) diff --git a/shell/platform/windows/client_wrapper/flutter_engine.cc b/shell/platform/windows/client_wrapper/flutter_engine.cc index 8051ca9a28bd3..53d32f443f88f 100644 --- a/shell/platform/windows/client_wrapper/flutter_engine.cc +++ b/shell/platform/windows/client_wrapper/flutter_engine.cc @@ -100,13 +100,17 @@ void FlutterEngine::SetNextFrameCallback(std::function callback) { this); } -bool FlutterEngine::ProcessExternalWindowMessage(HWND hwnd, - UINT message, - WPARAM wparam, - LPARAM lparam, - LRESULT* result) { - return FlutterDesktopEngineProcessExternalWindowMessage( - engine_, hwnd, message, wparam, lparam, result); +std::optional FlutterEngine::ProcessExternalWindowMessage( + HWND hwnd, + UINT message, + WPARAM wparam, + LPARAM lparam) { + LRESULT result; + if (FlutterDesktopEngineProcessExternalWindowMessage( + engine_, hwnd, message, wparam, lparam, &result)) { + return result; + } + return std::nullopt; } FlutterDesktopEngineRef FlutterEngine::RelinquishEngine() { diff --git a/shell/platform/windows/client_wrapper/flutter_engine_unittests.cc b/shell/platform/windows/client_wrapper/flutter_engine_unittests.cc index 7cca35c935a7a..1a9691f7ed39f 100644 --- a/shell/platform/windows/client_wrapper/flutter_engine_unittests.cc +++ b/shell/platform/windows/client_wrapper/flutter_engine_unittests.cc @@ -222,8 +222,7 @@ TEST(FlutterEngineTest, ProcessExternalWindowMessage) { FlutterEngine engine(DartProject(L"fake/project/path")); - engine.ProcessExternalWindowMessage(reinterpret_cast(1), 1234, 0, 0, - nullptr); + engine.ProcessExternalWindowMessage(reinterpret_cast(1), 1234, 0, 0); EXPECT_EQ(test_api->last_message(), 1234); } diff --git a/shell/platform/windows/client_wrapper/include/flutter/flutter_engine.h b/shell/platform/windows/client_wrapper/include/flutter/flutter_engine.h index e880c256e05a0..b71c5cd479611 100644 --- a/shell/platform/windows/client_wrapper/include/flutter/flutter_engine.h +++ b/shell/platform/windows/client_wrapper/include/flutter/flutter_engine.h @@ -9,6 +9,7 @@ #include #include +#include #include #include "binary_messenger.h" @@ -87,13 +88,12 @@ class FlutterEngine : public PluginRegistry { // Called to pass an external window message to the engine for lifecycle // state updates. This does not consume the window message. Non-Flutter // windows must call this method in their WndProc in order to be included in - // the logic for application lifecycle state updates. Returns true when the - // message has been consumed. - bool ProcessExternalWindowMessage(HWND hwnd, - UINT message, - WPARAM wparam, - LPARAM lparam, - LRESULT* result); + // the logic for application lifecycle state updates. Returns a result when + // the message has been consumed. + std::optional ProcessExternalWindowMessage(HWND hwnd, + UINT message, + WPARAM wparam, + LPARAM lparam); private: // For access to RelinquishEngine. diff --git a/shell/platform/windows/client_wrapper/testing/stub_flutter_windows_api.h b/shell/platform/windows/client_wrapper/testing/stub_flutter_windows_api.h index 73e920d949e32..f4fca22602af4 100644 --- a/shell/platform/windows/client_wrapper/testing/stub_flutter_windows_api.h +++ b/shell/platform/windows/client_wrapper/testing/stub_flutter_windows_api.h @@ -6,6 +6,7 @@ #define FLUTTER_SHELL_PLATFORM_WINDOWS_WRAPPER_TESTING_STUB_FLUTTER_WINDOWS_API_H_ #include +#include #include "flutter/shell/platform/windows/public/flutter_windows.h" diff --git a/shell/platform/windows/flutter_windows.cc b/shell/platform/windows/flutter_windows.cc index 4b0eff078bd25..fd3071d898505 100644 --- a/shell/platform/windows/flutter_windows.cc +++ b/shell/platform/windows/flutter_windows.cc @@ -214,8 +214,13 @@ bool FlutterDesktopEngineProcessExternalWindowMessage( WPARAM wparam, LPARAM lparam, LRESULT* result) { - return EngineFromHandle(engine)->ProcessExternalWindowMessage( - hwnd, message, wparam, lparam, result); + std::optional lresult = + EngineFromHandle(engine)->ProcessExternalWindowMessage(hwnd, message, + wparam, lparam); + if (result && lresult.has_value()) { + *result = lresult.value(); + } + return lresult.has_value(); } FlutterDesktopViewRef FlutterDesktopPluginRegistrarGetView( diff --git a/shell/platform/windows/flutter_windows_engine.cc b/shell/platform/windows/flutter_windows_engine.cc index 83ae78f050760..4eda7612275f9 100644 --- a/shell/platform/windows/flutter_windows_engine.cc +++ b/shell/platform/windows/flutter_windows_engine.cc @@ -800,11 +800,11 @@ void FlutterWindowsEngine::OnWindowStateEvent(HWND hwnd, lifecycle_manager_->OnWindowStateEvent(hwnd, event); } -bool FlutterWindowsEngine::ProcessExternalWindowMessage(HWND hwnd, - UINT message, - WPARAM wparam, - LPARAM lparam, - LRESULT* result) { +std::optional FlutterWindowsEngine::ProcessExternalWindowMessage( + HWND hwnd, + UINT message, + WPARAM wparam, + LPARAM lparam) { if (lifecycle_manager_) { switch (message) { case WM_SHOWWINDOW: @@ -812,10 +812,10 @@ bool FlutterWindowsEngine::ProcessExternalWindowMessage(HWND hwnd, case WM_SETFOCUS: case WM_KILLFOCUS: return lifecycle_manager_->ExternalWindowMessage(hwnd, message, wparam, - lparam, result); + lparam); } } - return false; + return std::nullopt; } } // namespace flutter diff --git a/shell/platform/windows/flutter_windows_engine.h b/shell/platform/windows/flutter_windows_engine.h index 8f3048fc0c4f1..ec6400101e899 100644 --- a/shell/platform/windows/flutter_windows_engine.h +++ b/shell/platform/windows/flutter_windows_engine.h @@ -271,13 +271,12 @@ class FlutterWindowsEngine { } // Handle a message from a non-Flutter window in the same application. - // Returns true when the message is consumed and should not be processed + // Returns a result when the message is consumed and should not be processed // further. - bool ProcessExternalWindowMessage(HWND hwnd, - UINT message, - WPARAM wparam, - LPARAM lparam, - LRESULT* result); + std::optional ProcessExternalWindowMessage(HWND hwnd, + UINT message, + WPARAM wparam, + LPARAM lparam); protected: // Creates the keyboard key handler. diff --git a/shell/platform/windows/flutter_windows_engine_unittests.cc b/shell/platform/windows/flutter_windows_engine_unittests.cc index c19e64f97f1f4..3117d53e7002e 100644 --- a/shell/platform/windows/flutter_windows_engine_unittests.cc +++ b/shell/platform/windows/flutter_windows_engine_unittests.cc @@ -959,9 +959,9 @@ TEST_F(FlutterWindowsEngineTest, ExternalWindowMessage) { // Ensure HWND(1) is in the set of visible windows before hiding it. engine->ProcessExternalWindowMessage(reinterpret_cast(1), WM_SHOWWINDOW, - TRUE, NULL, nullptr); + TRUE, NULL); engine->ProcessExternalWindowMessage(reinterpret_cast(1), WM_SHOWWINDOW, - FALSE, NULL, nullptr); + FALSE, NULL); EXPECT_EQ(engine->GetLifecycleManager()->GetLifecycleState(), AppLifecycleState::kHidden); diff --git a/shell/platform/windows/windows_lifecycle_manager.cc b/shell/platform/windows/windows_lifecycle_manager.cc index 88e3296701774..dbd5e71658a2e 100644 --- a/shell/platform/windows/windows_lifecycle_manager.cc +++ b/shell/platform/windows/windows_lifecycle_manager.cc @@ -244,11 +244,11 @@ void WindowsLifecycleManager::OnWindowStateEvent(HWND hwnd, } } -bool WindowsLifecycleManager::ExternalWindowMessage(HWND hwnd, - UINT message, - WPARAM wparam, - LPARAM lparam, - LRESULT* result) { +std::optional WindowsLifecycleManager::ExternalWindowMessage( + HWND hwnd, + UINT message, + WPARAM wparam, + LPARAM lparam) { std::optional event = std::nullopt; // TODO (schectman): Handle WM_CLOSE messages. @@ -284,7 +284,7 @@ bool WindowsLifecycleManager::ExternalWindowMessage(HWND hwnd, OnWindowStateEvent(hwnd, *event); } - return false; + return std::nullopt; } } // namespace flutter diff --git a/shell/platform/windows/windows_lifecycle_manager.h b/shell/platform/windows/windows_lifecycle_manager.h index f4435ac912fb8..66aefbc589423 100644 --- a/shell/platform/windows/windows_lifecycle_manager.h +++ b/shell/platform/windows/windows_lifecycle_manager.h @@ -77,13 +77,12 @@ class WindowsLifecycleManager { // - When FlutterWindow receives WM_SIZE messages, wparam stores no meaningful // information, whereas it usually indicates the action which changed the // window size. - // When this returns true, the message has been consumed and should not be - // processed further. Currently, it will always return false. - bool ExternalWindowMessage(HWND hwnd, - UINT message, - WPARAM wparam, - LPARAM lparam, - LRESULT* result); + // When this returns a result, the message has been consumed and should not be + // processed further. Currently, it will always return nullopt. + std::optional ExternalWindowMessage(HWND hwnd, + UINT message, + WPARAM wparam, + LPARAM lparam); protected: // Check the number of top-level windows associated with this process, and From 23d93b316632f11b00f95726dc1819a761a4abe4 Mon Sep 17 00:00:00 2001 From: schectman Date: Fri, 28 Jul 2023 15:15:48 -0400 Subject: [PATCH 30/33] Test HWNDs --- .../testing/stub_flutter_windows_api.h | 1 - .../flutter_windows_engine_unittests.cc | 31 +++++++++++++++++++ 2 files changed, 31 insertions(+), 1 deletion(-) diff --git a/shell/platform/windows/client_wrapper/testing/stub_flutter_windows_api.h b/shell/platform/windows/client_wrapper/testing/stub_flutter_windows_api.h index f4fca22602af4..73e920d949e32 100644 --- a/shell/platform/windows/client_wrapper/testing/stub_flutter_windows_api.h +++ b/shell/platform/windows/client_wrapper/testing/stub_flutter_windows_api.h @@ -6,7 +6,6 @@ #define FLUTTER_SHELL_PLATFORM_WINDOWS_WRAPPER_TESTING_STUB_FLUTTER_WINDOWS_API_H_ #include -#include #include "flutter/shell/platform/windows/public/flutter_windows.h" diff --git a/shell/platform/windows/flutter_windows_engine_unittests.cc b/shell/platform/windows/flutter_windows_engine_unittests.cc index 3117d53e7002e..c2b4a13cc8933 100644 --- a/shell/platform/windows/flutter_windows_engine_unittests.cc +++ b/shell/platform/windows/flutter_windows_engine_unittests.cc @@ -608,6 +608,7 @@ class MockFlutterWindowsView : public FlutterWindowsView { MOCK_METHOD2(NotifyWinEventWrapper, void(ui::AXPlatformNodeWin*, ax::mojom::Event)); + MOCK_METHOD0(GetPlatformWindow, HWND()); private: FML_DISALLOW_COPY_AND_ASSIGN(MockFlutterWindowsView); @@ -967,5 +968,35 @@ TEST_F(FlutterWindowsEngineTest, ExternalWindowMessage) { AppLifecycleState::kHidden); } +TEST_F(FlutterWindowsEngineTest, InnerWindowHidden) { + FlutterWindowsEngineBuilder builder{GetContext()}; + HWND outer = reinterpret_cast(1); + HWND inner = reinterpret_cast(2); + + auto window_binding_handler = + std::make_unique<::testing::NiceMock>(); + MockFlutterWindowsView view(std::move(window_binding_handler)); + ON_CALL(view, GetPlatformWindow).WillByDefault([=](){ return inner; }); + view.SetEngine(builder.Build()); + FlutterWindowsEngine* engine = view.GetEngine(); + + EngineModifier modifier(engine); + modifier.embedder_api().RunsAOTCompiledDartCode = []() { return false; }; + // Sets lifecycle state to resumed. + engine->Run(); + + // Show both top-level and Flutter window. + engine->window_proc_delegate_manager()->OnTopLevelWindowProc(outer, WM_SHOWWINDOW, TRUE, NULL); + view.OnWindowStateEvent(inner, WindowStateEvent::kShow); + view.OnWindowStateEvent(inner, WindowStateEvent::kFocus); + + EXPECT_EQ(engine->GetLifecycleManager()->GetLifecycleState(), AppLifecycleState::kResumed); + + // Hide Flutter window, but not top level window. + view.OnWindowStateEvent(inner, WindowStateEvent::kHide); + + EXPECT_EQ(engine->GetLifecycleManager()->GetLifecycleState(), AppLifecycleState::kInactive); +} + } // namespace testing } // namespace flutter From 99f25cc763cb264c58c3daa14c5f8fba91e21e98 Mon Sep 17 00:00:00 2001 From: schectman Date: Fri, 28 Jul 2023 15:19:05 -0400 Subject: [PATCH 31/33] Rename lifecycle_manager getter --- shell/platform/windows/flutter_windows_engine.h | 2 +- .../windows/flutter_windows_engine_unittests.cc | 12 ++++++------ shell/platform/windows/windows_lifecycle_manager.h | 9 +++++---- 3 files changed, 12 insertions(+), 11 deletions(-) diff --git a/shell/platform/windows/flutter_windows_engine.h b/shell/platform/windows/flutter_windows_engine.h index ec6400101e899..08016b6310031 100644 --- a/shell/platform/windows/flutter_windows_engine.h +++ b/shell/platform/windows/flutter_windows_engine.h @@ -266,7 +266,7 @@ class FlutterWindowsEngine { // lifecycle state. void OnWindowStateEvent(HWND hwnd, WindowStateEvent event); - WindowsLifecycleManager* GetLifecycleManager() { + WindowsLifecycleManager* lifecycle_manager() { return lifecycle_manager_.get(); } diff --git a/shell/platform/windows/flutter_windows_engine_unittests.cc b/shell/platform/windows/flutter_windows_engine_unittests.cc index c2b4a13cc8933..b3d7675939f48 100644 --- a/shell/platform/windows/flutter_windows_engine_unittests.cc +++ b/shell/platform/windows/flutter_windows_engine_unittests.cc @@ -930,17 +930,17 @@ TEST_F(FlutterWindowsEngineTest, LifecycleStateTransition) { engine->window_proc_delegate_manager()->OnTopLevelWindowProc( (HWND)1, WM_SIZE, SIZE_RESTORED, 0); - EXPECT_EQ(engine->GetLifecycleManager()->GetLifecycleState(), + EXPECT_EQ(engine->lifecycle_manager()->GetLifecycleState(), AppLifecycleState::kResumed); engine->window_proc_delegate_manager()->OnTopLevelWindowProc( (HWND)1, WM_SIZE, SIZE_MINIMIZED, 0); - EXPECT_EQ(engine->GetLifecycleManager()->GetLifecycleState(), + EXPECT_EQ(engine->lifecycle_manager()->GetLifecycleState(), AppLifecycleState::kHidden); engine->window_proc_delegate_manager()->OnTopLevelWindowProc( (HWND)1, WM_SIZE, SIZE_RESTORED, 0); - EXPECT_EQ(engine->GetLifecycleManager()->GetLifecycleState(), + EXPECT_EQ(engine->lifecycle_manager()->GetLifecycleState(), AppLifecycleState::kInactive); } @@ -964,7 +964,7 @@ TEST_F(FlutterWindowsEngineTest, ExternalWindowMessage) { engine->ProcessExternalWindowMessage(reinterpret_cast(1), WM_SHOWWINDOW, FALSE, NULL); - EXPECT_EQ(engine->GetLifecycleManager()->GetLifecycleState(), + EXPECT_EQ(engine->lifecycle_manager()->GetLifecycleState(), AppLifecycleState::kHidden); } @@ -990,12 +990,12 @@ TEST_F(FlutterWindowsEngineTest, InnerWindowHidden) { view.OnWindowStateEvent(inner, WindowStateEvent::kShow); view.OnWindowStateEvent(inner, WindowStateEvent::kFocus); - EXPECT_EQ(engine->GetLifecycleManager()->GetLifecycleState(), AppLifecycleState::kResumed); + EXPECT_EQ(engine->lifecycle_manager()->GetLifecycleState(), AppLifecycleState::kResumed); // Hide Flutter window, but not top level window. view.OnWindowStateEvent(inner, WindowStateEvent::kHide); - EXPECT_EQ(engine->GetLifecycleManager()->GetLifecycleState(), AppLifecycleState::kInactive); + EXPECT_EQ(engine->lifecycle_manager()->GetLifecycleState(), AppLifecycleState::kInactive); } } // namespace testing diff --git a/shell/platform/windows/windows_lifecycle_manager.h b/shell/platform/windows/windows_lifecycle_manager.h index 66aefbc589423..2a2ca7a3160c1 100644 --- a/shell/platform/windows/windows_lifecycle_manager.h +++ b/shell/platform/windows/windows_lifecycle_manager.h @@ -28,11 +28,12 @@ enum class WindowStateEvent { kUnfocus, }; -/// A manager for lifecycle events of the top-level window. +/// A manager for lifecycle events of the top-level windows. /// -/// Currently handles the following events: -/// 1. WM_CLOSE -/// 2. WM_DWMCOMPOSITIONCHANGED +/// WndProc is called for window messages of the top-level Flutter window. +/// ExternalWindowMessage is called for non-flutter top-level window messages. +/// OnWindowStateEvent is called when the visibility or focus state of a window +/// is changed, including the FlutterView window. class WindowsLifecycleManager { public: WindowsLifecycleManager(FlutterWindowsEngine* engine); From 3b3473f4fe46273853fe3bd8ab15731169380b1c Mon Sep 17 00:00:00 2001 From: schectman Date: Fri, 28 Jul 2023 15:25:38 -0400 Subject: [PATCH 32/33] Organize --- shell/platform/windows/flutter_windows_engine.cc | 10 ++-------- shell/platform/windows/flutter_windows_engine.h | 8 ++++---- .../windows/flutter_windows_engine_unittests.cc | 1 + 3 files changed, 7 insertions(+), 12 deletions(-) diff --git a/shell/platform/windows/flutter_windows_engine.cc b/shell/platform/windows/flutter_windows_engine.cc index 4eda7612275f9..5ddd7a0409c45 100644 --- a/shell/platform/windows/flutter_windows_engine.cc +++ b/shell/platform/windows/flutter_windows_engine.cc @@ -806,14 +806,8 @@ std::optional FlutterWindowsEngine::ProcessExternalWindowMessage( WPARAM wparam, LPARAM lparam) { if (lifecycle_manager_) { - switch (message) { - case WM_SHOWWINDOW: - case WM_SIZE: - case WM_SETFOCUS: - case WM_KILLFOCUS: - return lifecycle_manager_->ExternalWindowMessage(hwnd, message, wparam, - lparam); - } + return lifecycle_manager_->ExternalWindowMessage(hwnd, message, wparam, + lparam); } return std::nullopt; } diff --git a/shell/platform/windows/flutter_windows_engine.h b/shell/platform/windows/flutter_windows_engine.h index 08016b6310031..36147c00c821c 100644 --- a/shell/platform/windows/flutter_windows_engine.h +++ b/shell/platform/windows/flutter_windows_engine.h @@ -266,10 +266,6 @@ class FlutterWindowsEngine { // lifecycle state. void OnWindowStateEvent(HWND hwnd, WindowStateEvent event); - WindowsLifecycleManager* lifecycle_manager() { - return lifecycle_manager_.get(); - } - // Handle a message from a non-Flutter window in the same application. // Returns a result when the message is consumed and should not be processed // further. @@ -278,6 +274,10 @@ class FlutterWindowsEngine { WPARAM wparam, LPARAM lparam); + WindowsLifecycleManager* lifecycle_manager() { + return lifecycle_manager_.get(); + } + protected: // Creates the keyboard key handler. // diff --git a/shell/platform/windows/flutter_windows_engine_unittests.cc b/shell/platform/windows/flutter_windows_engine_unittests.cc index b3d7675939f48..a6d17ca4bd465 100644 --- a/shell/platform/windows/flutter_windows_engine_unittests.cc +++ b/shell/platform/windows/flutter_windows_engine_unittests.cc @@ -995,6 +995,7 @@ TEST_F(FlutterWindowsEngineTest, InnerWindowHidden) { // Hide Flutter window, but not top level window. view.OnWindowStateEvent(inner, WindowStateEvent::kHide); + // The top-level window is still visible, so we ought not enter hidden state. EXPECT_EQ(engine->lifecycle_manager()->GetLifecycleState(), AppLifecycleState::kInactive); } From cc76ca4eef94063cab9ca15823c837798bb6a628 Mon Sep 17 00:00:00 2001 From: schectman Date: Fri, 28 Jul 2023 15:28:45 -0400 Subject: [PATCH 33/33] Nit --- .../client_wrapper/flutter_engine_unittests.cc | 8 ++++---- shell/platform/windows/flutter_windows_engine.cc | 2 +- .../windows/flutter_windows_engine_unittests.cc | 11 +++++++---- 3 files changed, 12 insertions(+), 9 deletions(-) diff --git a/shell/platform/windows/client_wrapper/flutter_engine_unittests.cc b/shell/platform/windows/client_wrapper/flutter_engine_unittests.cc index 1a9691f7ed39f..78ad17b1efdf1 100644 --- a/shell/platform/windows/client_wrapper/flutter_engine_unittests.cc +++ b/shell/platform/windows/client_wrapper/flutter_engine_unittests.cc @@ -63,7 +63,7 @@ class TestFlutterWindowsApi : public testing::StubFlutterWindowsApi { WPARAM wparam, LPARAM lparam, LRESULT* result) override { - last_message_ = message; + last_external_message_ = message; return false; } @@ -85,7 +85,7 @@ class TestFlutterWindowsApi : public testing::StubFlutterWindowsApi { next_frame_callback_ = nullptr; } - UINT last_message() { return last_message_; } + UINT last_external_message() { return last_external_message_; } private: bool create_called_ = false; @@ -95,7 +95,7 @@ class TestFlutterWindowsApi : public testing::StubFlutterWindowsApi { std::vector dart_entrypoint_arguments_; VoidCallback next_frame_callback_ = nullptr; void* next_frame_user_data_ = nullptr; - UINT last_message_ = 0; + UINT last_external_message_ = 0; }; } // namespace @@ -224,7 +224,7 @@ TEST(FlutterEngineTest, ProcessExternalWindowMessage) { engine.ProcessExternalWindowMessage(reinterpret_cast(1), 1234, 0, 0); - EXPECT_EQ(test_api->last_message(), 1234); + EXPECT_EQ(test_api->last_external_message(), 1234); } } // namespace flutter diff --git a/shell/platform/windows/flutter_windows_engine.cc b/shell/platform/windows/flutter_windows_engine.cc index 5ddd7a0409c45..8ab6b7d5fae74 100644 --- a/shell/platform/windows/flutter_windows_engine.cc +++ b/shell/platform/windows/flutter_windows_engine.cc @@ -807,7 +807,7 @@ std::optional FlutterWindowsEngine::ProcessExternalWindowMessage( LPARAM lparam) { if (lifecycle_manager_) { return lifecycle_manager_->ExternalWindowMessage(hwnd, message, wparam, - lparam); + lparam); } return std::nullopt; } diff --git a/shell/platform/windows/flutter_windows_engine_unittests.cc b/shell/platform/windows/flutter_windows_engine_unittests.cc index a6d17ca4bd465..8dcb50846f23c 100644 --- a/shell/platform/windows/flutter_windows_engine_unittests.cc +++ b/shell/platform/windows/flutter_windows_engine_unittests.cc @@ -976,7 +976,7 @@ TEST_F(FlutterWindowsEngineTest, InnerWindowHidden) { auto window_binding_handler = std::make_unique<::testing::NiceMock>(); MockFlutterWindowsView view(std::move(window_binding_handler)); - ON_CALL(view, GetPlatformWindow).WillByDefault([=](){ return inner; }); + ON_CALL(view, GetPlatformWindow).WillByDefault([=]() { return inner; }); view.SetEngine(builder.Build()); FlutterWindowsEngine* engine = view.GetEngine(); @@ -986,17 +986,20 @@ TEST_F(FlutterWindowsEngineTest, InnerWindowHidden) { engine->Run(); // Show both top-level and Flutter window. - engine->window_proc_delegate_manager()->OnTopLevelWindowProc(outer, WM_SHOWWINDOW, TRUE, NULL); + engine->window_proc_delegate_manager()->OnTopLevelWindowProc( + outer, WM_SHOWWINDOW, TRUE, NULL); view.OnWindowStateEvent(inner, WindowStateEvent::kShow); view.OnWindowStateEvent(inner, WindowStateEvent::kFocus); - EXPECT_EQ(engine->lifecycle_manager()->GetLifecycleState(), AppLifecycleState::kResumed); + EXPECT_EQ(engine->lifecycle_manager()->GetLifecycleState(), + AppLifecycleState::kResumed); // Hide Flutter window, but not top level window. view.OnWindowStateEvent(inner, WindowStateEvent::kHide); // The top-level window is still visible, so we ought not enter hidden state. - EXPECT_EQ(engine->lifecycle_manager()->GetLifecycleState(), AppLifecycleState::kInactive); + EXPECT_EQ(engine->lifecycle_manager()->GetLifecycleState(), + AppLifecycleState::kInactive); } } // namespace testing