From 563d3b0db8d94bc37a554a00812920582a82b002 Mon Sep 17 00:00:00 2001 From: Qixing Cao <6702229+sevenstars@users.noreply.github.com> Date: Thu, 14 Jul 2022 02:15:28 +0800 Subject: [PATCH] [Windows] handle repaint message in FlutterView window (#34306) This PR will fix the blank FlutterView issue by handling the repaint message in FlutterView window. Currently, WM_PAINT msg is not handled in flutter window and the default WindowProc will do nothing but paint the background. In some user cases, e.g., hide/show/min/max the app window, this can result in blank FlutterView if the content is static and there are no running animation. Addresses https://github.com/flutter/flutter/issues/101339 Addresses https://github.com/flutter/flutter/issues/102030 --- AUTHORS | 1 + .../platform/windows/flutter_window_win32.cc | 6 ++++ shell/platform/windows/flutter_window_win32.h | 3 ++ .../windows/flutter_window_win32_unittests.cc | 17 +++++++++++ .../platform/windows/flutter_windows_view.cc | 6 ++++ shell/platform/windows/flutter_windows_view.h | 3 ++ .../windows/flutter_windows_view_unittests.cc | 29 +++++++++++++++++++ .../mock_window_binding_handler_delegate.h | 1 + .../windows/testing/mock_window_win32.h | 1 + .../windows/window_binding_handler_delegate.h | 22 ++++++++------ shell/platform/windows/window_win32.cc | 3 ++ shell/platform/windows/window_win32.h | 3 ++ .../windows/window_win32_unittests.cc | 6 ++++ 13 files changed, 92 insertions(+), 9 deletions(-) diff --git a/AUTHORS b/AUTHORS index 0cab6e7e7b937..f3e9803d92f04 100644 --- a/AUTHORS +++ b/AUTHORS @@ -22,3 +22,4 @@ Callum Moffat Koutaro Mori TheOneWithTheBraid Twin Sun, LLC +Qixing Cao diff --git a/shell/platform/windows/flutter_window_win32.cc b/shell/platform/windows/flutter_window_win32.cc index a6477cbbd0fe2..66e528cb9c395 100644 --- a/shell/platform/windows/flutter_window_win32.cc +++ b/shell/platform/windows/flutter_window_win32.cc @@ -135,6 +135,12 @@ void FlutterWindowWin32::OnResize(unsigned int width, unsigned int height) { } } +void FlutterWindowWin32::OnPaint() { + if (binding_handler_delegate_ != nullptr) { + binding_handler_delegate_->OnWindowRepaint(); + } +} + void FlutterWindowWin32::OnPointerMove(double x, double y, FlutterPointerDeviceKind device_kind, diff --git a/shell/platform/windows/flutter_window_win32.h b/shell/platform/windows/flutter_window_win32.h index 6713bae826a13..657fb31562e1f 100644 --- a/shell/platform/windows/flutter_window_win32.h +++ b/shell/platform/windows/flutter_window_win32.h @@ -36,6 +36,9 @@ class FlutterWindowWin32 : public WindowWin32, public WindowBindingHandler { // |WindowWin32| void OnResize(unsigned int width, unsigned int height) override; + // |WindowWin32| + void OnPaint() override; + // |WindowWin32| void OnPointerMove(double x, double y, diff --git a/shell/platform/windows/flutter_window_win32_unittests.cc b/shell/platform/windows/flutter_window_win32_unittests.cc index 523379834f035..280209ab59b9e 100644 --- a/shell/platform/windows/flutter_window_win32_unittests.cc +++ b/shell/platform/windows/flutter_window_win32_unittests.cc @@ -109,6 +109,13 @@ class MockFlutterWindowWin32 : public FlutterWindowWin32 { // Wrapper for GetCurrentDPI() which is a protected method. UINT GetDpi() { return GetCurrentDPI(); } + // Simulates a WindowProc message from the OS. + LRESULT InjectWindowMessage(UINT const message, + WPARAM const wparam, + LPARAM const lparam) { + return HandleMessage(message, wparam, lparam); + } + MOCK_METHOD1(OnDpiScale, void(unsigned int)); MOCK_METHOD2(OnResize, void(unsigned int, unsigned int)); MOCK_METHOD4(OnPointerMove, @@ -346,5 +353,15 @@ TEST(FlutterWindowWin32Test, OnScrollCallsGetScrollOffsetMultiplier) { kDefaultPointerDeviceId); } +TEST(FlutterWindowWin32Test, OnWindowRepaint) { + MockFlutterWindowWin32 win32window; + MockWindowBindingHandlerDelegate delegate; + win32window.SetView(&delegate); + + EXPECT_CALL(delegate, OnWindowRepaint()).Times(1); + + win32window.InjectWindowMessage(WM_PAINT, 0, 0); +} + } // namespace testing } // namespace flutter diff --git a/shell/platform/windows/flutter_windows_view.cc b/shell/platform/windows/flutter_windows_view.cc index 5d645eea143d0..bbabce9d0cb1a 100644 --- a/shell/platform/windows/flutter_windows_view.cc +++ b/shell/platform/windows/flutter_windows_view.cc @@ -163,6 +163,10 @@ void FlutterWindowsView::OnWindowSizeChanged(size_t width, size_t height) { } } +void FlutterWindowsView::OnWindowRepaint() { + ForceRedraw(); +} + void FlutterWindowsView::OnPointerMove(double x, double y, FlutterPointerDeviceKind device_kind, @@ -603,6 +607,8 @@ void FlutterWindowsView::CreateRenderSurface() { PhysicalWindowBounds bounds = binding_handler_->GetPhysicalWindowBounds(); engine_->surface_manager()->CreateSurface(GetRenderTarget(), bounds.width, bounds.height); + resize_target_width_ = bounds.width; + resize_target_height_ = bounds.height; } } diff --git a/shell/platform/windows/flutter_windows_view.h b/shell/platform/windows/flutter_windows_view.h index 6366c16f3d1c6..1fed9fbdf7d80 100644 --- a/shell/platform/windows/flutter_windows_view.h +++ b/shell/platform/windows/flutter_windows_view.h @@ -99,6 +99,9 @@ class FlutterWindowsView : public WindowBindingHandlerDelegate, // |WindowBindingHandlerDelegate| void OnWindowSizeChanged(size_t width, size_t height) override; + // |WindowBindingHandlerDelegate| + void OnWindowRepaint() override; + // |WindowBindingHandlerDelegate| void OnPointerMove(double x, double y, diff --git a/shell/platform/windows/flutter_windows_view_unittests.cc b/shell/platform/windows/flutter_windows_view_unittests.cc index a263c215cc729..7510a9e8cdb58 100644 --- a/shell/platform/windows/flutter_windows_view_unittests.cc +++ b/shell/platform/windows/flutter_windows_view_unittests.cc @@ -14,6 +14,7 @@ #include "flutter/shell/platform/common/json_message_codec.h" #include "flutter/shell/platform/embedder/test_utils/proc_table_replacement.h" +#include "flutter/shell/platform/windows/flutter_window_win32.h" #include "flutter/shell/platform/windows/flutter_windows_engine.h" #include "flutter/shell/platform/windows/flutter_windows_texture_registrar.h" #include "flutter/shell/platform/windows/testing/engine_modifier.h" @@ -577,5 +578,33 @@ TEST(FlutterWindowsViewTest, WindowResizeTests) { EXPECT_TRUE(send_window_metrics_event_called); } +TEST(FlutterWindowsViewTest, WindowRepaintTests) { + std::unique_ptr engine = GetTestEngine(); + EngineModifier modifier(engine.get()); + + FlutterWindowsView view( + std::make_unique(100, 100)); + view.SetEngine(std::move(engine)); + view.CreateRenderSurface(); + + bool send_window_metrics_event_called = false; + size_t width = 0; + size_t height = 0; + modifier.embedder_api().SendWindowMetricsEvent = MOCK_ENGINE_PROC( + SendWindowMetricsEvent, + ([&send_window_metrics_event_called, &width, &height]( + auto engine, const FlutterWindowMetricsEvent* event) { + send_window_metrics_event_called = true; + width = event->width; + height = event->height; + return kSuccess; + })); + + view.OnWindowRepaint(); + EXPECT_TRUE(send_window_metrics_event_called); + EXPECT_EQ(width, 100); + EXPECT_EQ(height, 100); +} + } // namespace testing } // namespace flutter 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 e4f962bad0375..30d7137685319 100644 --- a/shell/platform/windows/testing/mock_window_binding_handler_delegate.h +++ b/shell/platform/windows/testing/mock_window_binding_handler_delegate.h @@ -22,6 +22,7 @@ class MockWindowBindingHandlerDelegate : public WindowBindingHandlerDelegate { MockWindowBindingHandlerDelegate const&) = delete; MOCK_METHOD2(OnWindowSizeChanged, void(size_t, size_t)); + MOCK_METHOD0(OnWindowRepaint, void()); MOCK_METHOD4(OnPointerMove, void(double, double, FlutterPointerDeviceKind, int32_t)); MOCK_METHOD5(OnPointerDown, diff --git a/shell/platform/windows/testing/mock_window_win32.h b/shell/platform/windows/testing/mock_window_win32.h index cfd7bac65c3ff..86c7fb2f240d0 100644 --- a/shell/platform/windows/testing/mock_window_win32.h +++ b/shell/platform/windows/testing/mock_window_win32.h @@ -37,6 +37,7 @@ class MockWin32Window : public WindowWin32 { MOCK_METHOD1(OnDpiScale, void(unsigned int)); MOCK_METHOD2(OnResize, void(unsigned int, unsigned int)); + MOCK_METHOD0(OnPaint, void()); MOCK_METHOD4(OnPointerMove, void(double, double, FlutterPointerDeviceKind, int32_t)); MOCK_METHOD5(OnPointerDown, diff --git a/shell/platform/windows/window_binding_handler_delegate.h b/shell/platform/windows/window_binding_handler_delegate.h index 03adebbf91bb9..2d73e9cfb5df8 100644 --- a/shell/platform/windows/window_binding_handler_delegate.h +++ b/shell/platform/windows/window_binding_handler_delegate.h @@ -22,15 +22,19 @@ class WindowBindingHandlerDelegate { // called on the platform thread. virtual void OnWindowSizeChanged(size_t width, size_t height) = 0; + // Notifies delegate that backing window needs to be repainted. + // Typically called by currently configured WindowBindingHandler. + virtual void OnWindowRepaint() = 0; + // Notifies delegate that backing window mouse has moved. - // Typically called by currently configured WindowBindingHandler + // Typically called by currently configured WindowBindingHandler. virtual void OnPointerMove(double x, double y, FlutterPointerDeviceKind device_kind, int32_t device_id) = 0; // Notifies delegate that backing window mouse pointer button has been - // pressed. Typically called by currently configured WindowBindingHandler + // pressed. Typically called by currently configured WindowBindingHandler. virtual void OnPointerDown(double x, double y, FlutterPointerDeviceKind device_kind, @@ -38,7 +42,7 @@ class WindowBindingHandlerDelegate { FlutterPointerMouseButtons button) = 0; // Notifies delegate that backing window mouse pointer button has been - // released. Typically called by currently configured WindowBindingHandler + // released. Typically called by currently configured WindowBindingHandler. virtual void OnPointerUp(double x, double y, FlutterPointerDeviceKind device_kind, @@ -46,18 +50,18 @@ class WindowBindingHandlerDelegate { FlutterPointerMouseButtons button) = 0; // Notifies delegate that backing window mouse pointer has left the window. - // Typically called by currently configured WindowBindingHandler + // Typically called by currently configured WindowBindingHandler. virtual void OnPointerLeave(double x, double y, FlutterPointerDeviceKind device_kind, int32_t device_id) = 0; // Notifies delegate that a pan/zoom gesture has started. - // Typically called by DirectManipulationEventHandler + // Typically called by DirectManipulationEventHandler. virtual void OnPointerPanZoomStart(int32_t device_id) = 0; // Notifies delegate that a pan/zoom gesture has updated. - // Typically called by DirectManipulationEventHandler + // Typically called by DirectManipulationEventHandler. virtual void OnPointerPanZoomUpdate(int32_t device_id, double pan_x, double pan_y, @@ -65,11 +69,11 @@ class WindowBindingHandlerDelegate { double rotation) = 0; // Notifies delegate that a pan/zoom gesture has ended. - // Typically called by DirectManipulationEventHandler + // Typically called by DirectManipulationEventHandler. virtual void OnPointerPanZoomEnd(int32_t device_id) = 0; // Notifies delegate that backing window has received text. - // Typically called by currently configured WindowBindingHandler + // Typically called by currently configured WindowBindingHandler. virtual void OnText(const std::u16string&) = 0; // Notifies delegate that backing window size has received key press. Should @@ -109,7 +113,7 @@ class WindowBindingHandlerDelegate { virtual void OnComposeChange(const std::u16string& text, int cursor_pos) = 0; // Notifies delegate that backing window size has recevied scroll. - // Typically called by currently configured WindowBindingHandler + // Typically called by currently configured WindowBindingHandler. virtual void OnScroll(double x, double y, double delta_x, diff --git a/shell/platform/windows/window_win32.cc b/shell/platform/windows/window_win32.cc index 555d9f5611ac1..eb45e38436823 100644 --- a/shell/platform/windows/window_win32.cc +++ b/shell/platform/windows/window_win32.cc @@ -335,6 +335,9 @@ WindowWin32::HandleMessage(UINT const message, current_height_ = height; HandleResize(width, height); break; + case WM_PAINT: + OnPaint(); + break; case WM_TOUCH: { UINT num_points = LOWORD(wparam); touch_points_.resize(num_points); diff --git a/shell/platform/windows/window_win32.h b/shell/platform/windows/window_win32.h index 4e0a9f83f6882..a517821560bc3 100644 --- a/shell/platform/windows/window_win32.h +++ b/shell/platform/windows/window_win32.h @@ -96,6 +96,9 @@ class WindowWin32 : public KeyboardManagerWin32::WindowDelegate { // Called when a resize occurs. virtual void OnResize(UINT width, UINT height) = 0; + // Called when a paint is requested. + virtual void OnPaint() = 0; + // Called when the pointer moves within the // window bounds. virtual void OnPointerMove(double x, diff --git a/shell/platform/windows/window_win32_unittests.cc b/shell/platform/windows/window_win32_unittests.cc index 9f8f010a33217..81d4ca15e1719 100644 --- a/shell/platform/windows/window_win32_unittests.cc +++ b/shell/platform/windows/window_win32_unittests.cc @@ -273,5 +273,11 @@ TEST(MockWin32Window, KeyDownWithCtrlToggled) { SetKeyboardState(keyboard_state); } +TEST(MockWin32Window, Paint) { + MockWin32Window window; + EXPECT_CALL(window, OnPaint()).Times(1); + window.InjectWindowMessage(WM_PAINT, 0, 0); +} + } // namespace testing } // namespace flutter