Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.

Commit 5ffc44f

Browse files
Revert "Revert "Listen to window notifications to update application lifecycle" (#44275)"
This reverts commit 1f1d5cc.
1 parent 42c8bb8 commit 5ffc44f

25 files changed

+620
-11
lines changed

ci/licenses_golden/excluded_files

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -352,6 +352,7 @@
352352
../../../flutter/shell/platform/windows/text_input_plugin_unittest.cc
353353
../../../flutter/shell/platform/windows/window_proc_delegate_manager_unittests.cc
354354
../../../flutter/shell/platform/windows/window_unittests.cc
355+
../../../flutter/shell/platform/windows/windows_lifecycle_manager_unittests.cc
355356
../../../flutter/shell/profiling/sampling_profiler_unittest.cc
356357
../../../flutter/shell/testing
357358
../../../flutter/shell/vmservice/.dart_tool

shell/platform/windows/BUILD.gn

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -221,6 +221,7 @@ executable("flutter_windows_unittests") {
221221
"text_input_plugin_unittest.cc",
222222
"window_proc_delegate_manager_unittests.cc",
223223
"window_unittests.cc",
224+
"windows_lifecycle_manager_unittests.cc",
224225
]
225226

226227
configs +=

shell/platform/windows/client_wrapper/flutter_engine.cc

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,19 @@ void FlutterEngine::SetNextFrameCallback(std::function<void()> callback) {
100100
this);
101101
}
102102

103+
std::optional<LRESULT> FlutterEngine::ProcessExternalWindowMessage(
104+
HWND hwnd,
105+
UINT message,
106+
WPARAM wparam,
107+
LPARAM lparam) {
108+
LRESULT result;
109+
if (FlutterDesktopEngineProcessExternalWindowMessage(
110+
engine_, hwnd, message, wparam, lparam, &result)) {
111+
return result;
112+
}
113+
return std::nullopt;
114+
}
115+
103116
FlutterDesktopEngineRef FlutterEngine::RelinquishEngine() {
104117
owns_engine_ = false;
105118
return engine_;

shell/platform/windows/client_wrapper/flutter_engine_unittests.cc

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,17 @@ class TestFlutterWindowsApi : public testing::StubFlutterWindowsApi {
5656
// |flutter::testing::StubFlutterWindowsApi|
5757
void EngineReloadSystemFonts() override { reload_fonts_called_ = true; }
5858

59+
// |flutter::testing::StubFlutterWindowsApi|
60+
bool EngineProcessExternalWindowMessage(FlutterDesktopEngineRef engine,
61+
HWND hwnd,
62+
UINT message,
63+
WPARAM wparam,
64+
LPARAM lparam,
65+
LRESULT* result) override {
66+
last_external_message_ = message;
67+
return false;
68+
}
69+
5970
bool create_called() { return create_called_; }
6071

6172
bool run_called() { return run_called_; }
@@ -74,6 +85,8 @@ class TestFlutterWindowsApi : public testing::StubFlutterWindowsApi {
7485
next_frame_callback_ = nullptr;
7586
}
7687

88+
UINT last_external_message() { return last_external_message_; }
89+
7790
private:
7891
bool create_called_ = false;
7992
bool run_called_ = false;
@@ -82,6 +95,7 @@ class TestFlutterWindowsApi : public testing::StubFlutterWindowsApi {
8295
std::vector<std::string> dart_entrypoint_arguments_;
8396
VoidCallback next_frame_callback_ = nullptr;
8497
void* next_frame_user_data_ = nullptr;
98+
UINT last_external_message_ = 0;
8599
};
86100

87101
} // namespace
@@ -201,4 +215,16 @@ TEST(FlutterEngineTest, SetNextFrameCallback) {
201215
EXPECT_TRUE(success);
202216
}
203217

218+
TEST(FlutterEngineTest, ProcessExternalWindowMessage) {
219+
testing::ScopedStubFlutterWindowsApi scoped_api_stub(
220+
std::make_unique<TestFlutterWindowsApi>());
221+
auto test_api = static_cast<TestFlutterWindowsApi*>(scoped_api_stub.stub());
222+
223+
FlutterEngine engine(DartProject(L"fake/project/path"));
224+
225+
engine.ProcessExternalWindowMessage(reinterpret_cast<HWND>(1), 1234, 0, 0);
226+
227+
EXPECT_EQ(test_api->last_external_message(), 1234);
228+
}
229+
204230
} // namespace flutter

shell/platform/windows/client_wrapper/include/flutter/flutter_engine.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99

1010
#include <chrono>
1111
#include <memory>
12+
#include <optional>
1213
#include <string>
1314

1415
#include "binary_messenger.h"
@@ -84,6 +85,16 @@ class FlutterEngine : public PluginRegistry {
8485
// once on the platform thread.
8586
void SetNextFrameCallback(std::function<void()> callback);
8687

88+
// Called to pass an external window message to the engine for lifecycle
89+
// state updates. This does not consume the window message. Non-Flutter
90+
// windows must call this method in their WndProc in order to be included in
91+
// the logic for application lifecycle state updates. Returns a result when
92+
// the message has been consumed.
93+
std::optional<LRESULT> ProcessExternalWindowMessage(HWND hwnd,
94+
UINT message,
95+
WPARAM wparam,
96+
LPARAM lparam);
97+
8798
private:
8899
// For access to RelinquishEngine.
89100
friend class FlutterViewController;

shell/platform/windows/client_wrapper/testing/stub_flutter_windows_api.cc

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,20 @@ IDXGIAdapter* FlutterDesktopViewGetGraphicsAdapter(FlutterDesktopViewRef view) {
162162
return nullptr;
163163
}
164164

165+
bool FlutterDesktopEngineProcessExternalWindowMessage(
166+
FlutterDesktopEngineRef engine,
167+
HWND hwnd,
168+
UINT message,
169+
WPARAM wparam,
170+
LPARAM lparam,
171+
LRESULT* result) {
172+
if (s_stub_implementation) {
173+
return s_stub_implementation->EngineProcessExternalWindowMessage(
174+
engine, hwnd, message, wparam, lparam, result);
175+
}
176+
return false;
177+
}
178+
165179
FlutterDesktopViewRef FlutterDesktopPluginRegistrarGetView(
166180
FlutterDesktopPluginRegistrarRef controller) {
167181
// The stub ignores this, so just return an arbitrary non-zero value.

shell/platform/windows/client_wrapper/testing/stub_flutter_windows_api.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,17 @@ class StubFlutterWindowsApi {
8989
// FlutterDesktopPluginRegistrarUnregisterTopLevelWindowProcDelegate.
9090
virtual void PluginRegistrarUnregisterTopLevelWindowProcDelegate(
9191
FlutterDesktopWindowProcCallback delegate) {}
92+
93+
// Claled for FlutterDesktopEngineProcessExternalWindowMessage.
94+
virtual bool EngineProcessExternalWindowMessage(
95+
FlutterDesktopEngineRef engine,
96+
HWND hwnd,
97+
UINT message,
98+
WPARAM wparam,
99+
LPARAM lparam,
100+
LRESULT* result) {
101+
return false;
102+
}
92103
};
93104

94105
// A test helper that owns a stub implementation, making it the test stub for

shell/platform/windows/flutter_window.cc

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,11 +74,19 @@ FlutterWindow::FlutterWindow(int width, int height)
7474
current_cursor_ = ::LoadCursor(nullptr, IDC_ARROW);
7575
}
7676

77-
FlutterWindow::~FlutterWindow() {}
77+
FlutterWindow::~FlutterWindow() {
78+
OnWindowStateEvent(WindowStateEvent::kHide);
79+
}
7880

7981
void FlutterWindow::SetView(WindowBindingHandlerDelegate* window) {
8082
binding_handler_delegate_ = window;
8183
direct_manipulation_owner_->SetBindingHandlerDelegate(window);
84+
if (restored_) {
85+
OnWindowStateEvent(WindowStateEvent::kShow);
86+
}
87+
if (focused_) {
88+
OnWindowStateEvent(WindowStateEvent::kFocus);
89+
}
8290
}
8391

8492
WindowsRenderTarget FlutterWindow::GetRenderTarget() {
@@ -328,4 +336,26 @@ bool FlutterWindow::NeedsVSync() {
328336
return true;
329337
}
330338

339+
void FlutterWindow::OnWindowStateEvent(WindowStateEvent event) {
340+
switch (event) {
341+
case WindowStateEvent::kShow:
342+
restored_ = true;
343+
break;
344+
case WindowStateEvent::kHide:
345+
restored_ = false;
346+
focused_ = false;
347+
break;
348+
case WindowStateEvent::kFocus:
349+
focused_ = true;
350+
break;
351+
case WindowStateEvent::kUnfocus:
352+
focused_ = false;
353+
break;
354+
}
355+
HWND hwnd = GetPlatformWindow();
356+
if (hwnd && binding_handler_delegate_) {
357+
binding_handler_delegate_->OnWindowStateEvent(hwnd, event);
358+
}
359+
}
360+
331361
} // namespace flutter

shell/platform/windows/flutter_window.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,9 @@ class FlutterWindow : public Window, public WindowBindingHandler {
162162
// |Window|
163163
ui::AXFragmentRootDelegateWin* GetAxFragmentRootDelegate() override;
164164

165+
// |Window|
166+
void OnWindowStateEvent(WindowStateEvent event) override;
167+
165168
private:
166169
// A pointer to a FlutterWindowsView that can be used to update engine
167170
// windowing and input state.
@@ -173,6 +176,12 @@ class FlutterWindow : public Window, public WindowBindingHandler {
173176
// The cursor rect set by Flutter.
174177
RECT cursor_rect_;
175178

179+
// The window receives resize and focus messages before its view is set, so
180+
// these values cache the state of the window in the meantime so that the
181+
// proper application lifecycle state can be updated once the view is set.
182+
bool restored_ = false;
183+
bool focused_ = false;
184+
176185
FML_DISALLOW_COPY_AND_ASSIGN(FlutterWindow);
177186
};
178187

shell/platform/windows/flutter_window_unittests.cc

Lines changed: 61 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ class MockFlutterWindow : public FlutterWindow {
2727
ON_CALL(*this, GetDpiScale())
2828
.WillByDefault(Return(this->FlutterWindow::GetDpiScale()));
2929
}
30-
virtual ~MockFlutterWindow() {}
30+
virtual ~MockFlutterWindow() { SetView(nullptr); }
3131

3232
// Wrapper for GetCurrentDPI() which is a protected method.
3333
UINT GetDpi() { return GetCurrentDPI(); }
@@ -229,6 +229,10 @@ TEST(FlutterWindowTest, OnPointerStarSendsDeviceType) {
229229
kDefaultPointerDeviceId, WM_LBUTTONDOWN);
230230
win32window.OnPointerLeave(10.0, 10.0, kFlutterPointerDeviceKindStylus,
231231
kDefaultPointerDeviceId);
232+
233+
// Destruction of win32window sends a HIDE update. In situ, the window is
234+
// owned by the delegate, and so is destructed first. Not so here.
235+
win32window.SetView(nullptr);
232236
}
233237

234238
// Tests that calls to OnScroll in turn calls GetScrollOffsetMultiplier
@@ -324,5 +328,61 @@ TEST(FlutterWindowTest, AlertNode) {
324328
EXPECT_EQ(role.lVal, ROLE_SYSTEM_ALERT);
325329
}
326330

331+
TEST(FlutterWindowTest, LifecycleFocusMessages) {
332+
MockFlutterWindow win32window;
333+
ON_CALL(win32window, GetPlatformWindow).WillByDefault([]() {
334+
return reinterpret_cast<HWND>(1);
335+
});
336+
MockWindowBindingHandlerDelegate delegate;
337+
win32window.SetView(&delegate);
338+
339+
WindowStateEvent last_event;
340+
ON_CALL(delegate, OnWindowStateEvent)
341+
.WillByDefault([&last_event](HWND hwnd, WindowStateEvent event) {
342+
last_event = event;
343+
});
344+
345+
win32window.InjectWindowMessage(WM_SIZE, 0, 0);
346+
EXPECT_EQ(last_event, WindowStateEvent::kHide);
347+
348+
win32window.InjectWindowMessage(WM_SIZE, 0, MAKEWORD(1, 1));
349+
EXPECT_EQ(last_event, WindowStateEvent::kShow);
350+
351+
win32window.InjectWindowMessage(WM_SETFOCUS, 0, 0);
352+
EXPECT_EQ(last_event, WindowStateEvent::kFocus);
353+
354+
win32window.InjectWindowMessage(WM_KILLFOCUS, 0, 0);
355+
EXPECT_EQ(last_event, WindowStateEvent::kUnfocus);
356+
}
357+
358+
TEST(FlutterWindowTest, CachedLifecycleMessage) {
359+
MockFlutterWindow win32window;
360+
ON_CALL(win32window, GetPlatformWindow).WillByDefault([]() {
361+
return reinterpret_cast<HWND>(1);
362+
});
363+
364+
// Restore
365+
win32window.InjectWindowMessage(WM_SIZE, 0, MAKEWORD(1, 1));
366+
367+
// Focus
368+
win32window.InjectWindowMessage(WM_SETFOCUS, 0, 0);
369+
370+
MockWindowBindingHandlerDelegate delegate;
371+
bool focused = false;
372+
bool restored = false;
373+
ON_CALL(delegate, OnWindowStateEvent)
374+
.WillByDefault([&](HWND hwnd, WindowStateEvent event) {
375+
if (event == WindowStateEvent::kFocus) {
376+
focused = true;
377+
} else if (event == WindowStateEvent::kShow) {
378+
restored = true;
379+
}
380+
});
381+
382+
win32window.SetView(&delegate);
383+
EXPECT_TRUE(focused);
384+
EXPECT_TRUE(restored);
385+
}
386+
327387
} // namespace testing
328388
} // namespace flutter

0 commit comments

Comments
 (0)