diff --git a/shell/platform/windows/cursor_handler.cc b/shell/platform/windows/cursor_handler.cc index 1b9cf488a299c..048f7981f9ff2 100644 --- a/shell/platform/windows/cursor_handler.cc +++ b/shell/platform/windows/cursor_handler.cc @@ -7,6 +7,8 @@ #include #include "flutter/shell/platform/common/client_wrapper/include/flutter/standard_method_codec.h" +#include "flutter/shell/platform/windows/flutter_windows_engine.h" +#include "flutter/shell/platform/windows/flutter_windows_view.h" static constexpr char kChannelName[] = "flutter/mousecursor"; @@ -40,15 +42,18 @@ static constexpr char kSetCustomCursorMethod[] = "setCustomCursor/windows"; static constexpr char kDeleteCustomCursorMethod[] = "deleteCustomCursor/windows"; +// Error codes used for responses. +static constexpr char kCursorError[] = "Cursor error"; + namespace flutter { CursorHandler::CursorHandler(BinaryMessenger* messenger, - WindowBindingHandler* delegate) + FlutterWindowsEngine* engine) : channel_(std::make_unique>( messenger, kChannelName, &StandardMethodCodec::GetInstance())), - delegate_(delegate) { + engine_(engine) { channel_->SetMethodCallHandler( [this](const MethodCall& call, std::unique_ptr> result) { @@ -69,7 +74,13 @@ void CursorHandler::HandleMethodCall( return; } const auto& kind = std::get(kind_iter->second); - delegate_->UpdateFlutterCursor(kind); + FlutterWindowsView* view = engine_->view(); + if (view == nullptr) { + result->Error(kCursorError, + "Cursor is not available in Windows headless mode"); + return; + } + view->UpdateFlutterCursor(kind); result->Success(); } else if (method.compare(kCreateCustomCursorMethod) == 0) { const auto& arguments = std::get(*method_call.arguments()); @@ -153,7 +164,13 @@ void CursorHandler::HandleMethodCall( return; } HCURSOR cursor = custom_cursors_[name]; - delegate_->SetFlutterCursor(cursor); + FlutterWindowsView* view = engine_->view(); + if (view == nullptr) { + result->Error(kCursorError, + "Cursor is not available in Windows headless mode"); + return; + } + view->SetFlutterCursor(cursor); result->Success(); } else if (method.compare(kDeleteCustomCursorMethod) == 0) { const auto& arguments = std::get(*method_call.arguments()); diff --git a/shell/platform/windows/cursor_handler.h b/shell/platform/windows/cursor_handler.h index 759ade7e673fd..df6d7a3df6095 100644 --- a/shell/platform/windows/cursor_handler.h +++ b/shell/platform/windows/cursor_handler.h @@ -15,11 +15,13 @@ namespace flutter { +class FlutterWindowsEngine; + // Handler for the cursor system channel. class CursorHandler { public: explicit CursorHandler(flutter::BinaryMessenger* messenger, - WindowBindingHandler* delegate); + flutter::FlutterWindowsEngine* engine); private: // Called when a method is called on |channel_|; @@ -30,8 +32,8 @@ class CursorHandler { // The MethodChannel used for communication with the Flutter engine. std::unique_ptr> channel_; - // The delegate for cursor updates. - WindowBindingHandler* delegate_; + // The Flutter engine that will be notified for cursor updates. + FlutterWindowsEngine* engine_; // The cache map for custom cursors. std::unordered_map custom_cursors_; diff --git a/shell/platform/windows/cursor_handler_unittests.cc b/shell/platform/windows/cursor_handler_unittests.cc index 91bf13e7b82a6..358e9f368beb4 100644 --- a/shell/platform/windows/cursor_handler_unittests.cc +++ b/shell/platform/windows/cursor_handler_unittests.cc @@ -10,8 +10,10 @@ #include "flutter/shell/platform/common/client_wrapper/include/flutter/standard_message_codec.h" #include "flutter/shell/platform/common/client_wrapper/include/flutter/standard_method_codec.h" #include "flutter/shell/platform/windows/flutter_windows_view.h" +#include "flutter/shell/platform/windows/testing/flutter_windows_engine_builder.h" #include "flutter/shell/platform/windows/testing/mock_window_binding_handler.h" #include "flutter/shell/platform/windows/testing/test_binary_messenger.h" +#include "flutter/shell/platform/windows/testing/windows_test.h" #include "gmock/gmock.h" #include "gtest/gtest.h" @@ -50,12 +52,46 @@ void SimulateCursorMessage(TestBinaryMessenger* messenger, } // namespace -TEST(CursorHandlerTest, ActivateSystemCursor) { +class CursorHandlerTest : public WindowsTest { + protected: + FlutterWindowsEngine* engine() { return engine_.get(); } + FlutterWindowsView* view() { return view_.get(); } + MockWindowBindingHandler* window() { return window_; } + + void use_headless_engine() { + FlutterWindowsEngineBuilder builder{GetContext()}; + + engine_ = builder.Build(); + } + + void use_engine_with_view() { + FlutterWindowsEngineBuilder builder{GetContext()}; + + auto window = std::make_unique(); + + window_ = window.get(); + EXPECT_CALL(*window_, SetView).Times(1); + EXPECT_CALL(*window_, GetRenderTarget).WillOnce(Return(nullptr)); + + engine_ = builder.Build(); + view_ = std::make_unique(std::move(window)); + + engine_->SetView(view_.get()); + } + + private: + std::unique_ptr engine_; + std::unique_ptr view_; + MockWindowBindingHandler* window_; +}; + +TEST_F(CursorHandlerTest, ActivateSystemCursor) { + use_engine_with_view(); + TestBinaryMessenger messenger; - MockWindowBindingHandler window; - CursorHandler cursor_handler(&messenger, &window); + CursorHandler cursor_handler(&messenger, engine()); - EXPECT_CALL(window, UpdateFlutterCursor("click")).Times(1); + EXPECT_CALL(*window(), UpdateFlutterCursor("click")).Times(1); bool success = false; MethodResultFunctions<> result_handler( @@ -75,10 +111,38 @@ TEST(CursorHandlerTest, ActivateSystemCursor) { EXPECT_TRUE(success); } -TEST(CursorHandlerTest, CreateCustomCursor) { +TEST_F(CursorHandlerTest, ActivateSystemCursorRequiresView) { + use_headless_engine(); + TestBinaryMessenger messenger; - MockWindowBindingHandler window; - CursorHandler cursor_handler(&messenger, &window); + CursorHandler cursor_handler(&messenger, engine()); + + bool error = false; + MethodResultFunctions<> result_handler( + nullptr, + [&error](const std::string& error_code, const std::string& error_message, + const EncodableValue* value) { + error = true; + EXPECT_EQ(error_message, + "Cursor is not available in Windows headless mode"); + }, + nullptr); + + SimulateCursorMessage(&messenger, kActivateSystemCursorMethod, + std::make_unique(EncodableMap{ + {EncodableValue("device"), EncodableValue(0)}, + {EncodableValue("kind"), EncodableValue("click")}, + }), + &result_handler); + + EXPECT_TRUE(error); +} + +TEST_F(CursorHandlerTest, CreateCustomCursor) { + use_engine_with_view(); + + TestBinaryMessenger messenger; + CursorHandler cursor_handler(&messenger, engine()); // Create a 4x4 raw BGRA test cursor buffer. std::vector buffer(4 * 4 * 4, 0); @@ -105,10 +169,11 @@ TEST(CursorHandlerTest, CreateCustomCursor) { EXPECT_TRUE(success); } -TEST(CursorHandlerTest, SetCustomCursor) { +TEST_F(CursorHandlerTest, SetCustomCursor) { + use_engine_with_view(); + TestBinaryMessenger messenger; - MockWindowBindingHandler window; - CursorHandler cursor_handler(&messenger, &window); + CursorHandler cursor_handler(&messenger, engine()); // Create a 4x4 raw BGRA test cursor buffer. std::vector buffer(4 * 4 * 4, 0); @@ -122,7 +187,7 @@ TEST(CursorHandlerTest, SetCustomCursor) { }, nullptr, nullptr); - EXPECT_CALL(window, SetFlutterCursor(/*cursor=*/NotNull())).Times(1); + EXPECT_CALL(*window(), SetFlutterCursor(/*cursor=*/NotNull())).Times(1); SimulateCursorMessage(&messenger, kCreateCustomCursorMethod, std::make_unique(EncodableMap{ @@ -144,10 +209,52 @@ TEST(CursorHandlerTest, SetCustomCursor) { EXPECT_TRUE(success); } -TEST(CursorHandlerTest, SetNonexistentCustomCursor) { +TEST_F(CursorHandlerTest, SetCustomCursorRequiresView) { + use_headless_engine(); + TestBinaryMessenger messenger; - MockWindowBindingHandler window; - CursorHandler cursor_handler(&messenger, &window); + CursorHandler cursor_handler(&messenger, engine()); + + // Create a 4x4 raw BGRA test cursor buffer. + std::vector buffer(4 * 4 * 4, 0); + + bool error = false; + MethodResultFunctions<> create_result_handler(nullptr, nullptr, nullptr); + MethodResultFunctions<> set_result_handler( + nullptr, + [&error](const std::string& error_code, const std::string& error_message, + const EncodableValue* value) { + error = true; + EXPECT_EQ(error_message, + "Cursor is not available in Windows headless mode"); + }, + nullptr); + + SimulateCursorMessage(&messenger, kCreateCustomCursorMethod, + std::make_unique(EncodableMap{ + {EncodableValue("name"), EncodableValue("hello")}, + {EncodableValue("buffer"), EncodableValue(buffer)}, + {EncodableValue("width"), EncodableValue(4)}, + {EncodableValue("height"), EncodableValue(4)}, + {EncodableValue("hotX"), EncodableValue(0.0)}, + {EncodableValue("hotY"), EncodableValue(0.0)}, + }), + &create_result_handler); + + SimulateCursorMessage(&messenger, kSetCustomCursorMethod, + std::make_unique(EncodableMap{ + {EncodableValue("name"), EncodableValue("hello")}, + }), + &set_result_handler); + + EXPECT_TRUE(error); +} + +TEST_F(CursorHandlerTest, SetNonexistentCustomCursor) { + use_engine_with_view(); + + TestBinaryMessenger messenger; + CursorHandler cursor_handler(&messenger, engine()); bool error = false; MethodResultFunctions<> result_handler( @@ -161,7 +268,7 @@ TEST(CursorHandlerTest, SetNonexistentCustomCursor) { }, nullptr); - EXPECT_CALL(window, SetFlutterCursor).Times(0); + EXPECT_CALL(*window(), SetFlutterCursor).Times(0); SimulateCursorMessage(&messenger, kSetCustomCursorMethod, std::make_unique(EncodableMap{ @@ -172,10 +279,11 @@ TEST(CursorHandlerTest, SetNonexistentCustomCursor) { EXPECT_TRUE(error); } -TEST(CursorHandlerTest, DeleteCustomCursor) { +TEST_F(CursorHandlerTest, DeleteCustomCursor) { + use_engine_with_view(); + TestBinaryMessenger messenger; - MockWindowBindingHandler window; - CursorHandler cursor_handler(&messenger, &window); + CursorHandler cursor_handler(&messenger, engine()); // Create a 4x4 raw BGRA test cursor buffer. std::vector buffer(4 * 4 * 4, 0); @@ -209,10 +317,11 @@ TEST(CursorHandlerTest, DeleteCustomCursor) { EXPECT_TRUE(success); } -TEST(CursorHandlerTest, DeleteNonexistentCustomCursor) { +TEST_F(CursorHandlerTest, DeleteNonexistentCustomCursor) { + use_engine_with_view(); + TestBinaryMessenger messenger; - MockWindowBindingHandler handler; - CursorHandler cursor_handler(&messenger, &handler); + CursorHandler cursor_handler(&messenger, engine()); bool success = false; MethodResultFunctions<> result_handler( diff --git a/shell/platform/windows/flutter_windows_engine.cc b/shell/platform/windows/flutter_windows_engine.cc index 5118f1e39d5bb..a9e61fda450c3 100644 --- a/shell/platform/windows/flutter_windows_engine.cc +++ b/shell/platform/windows/flutter_windows_engine.cc @@ -207,6 +207,8 @@ FlutterWindowsEngine::FlutterWindowsEngine( // Set up internal channels. // TODO: Replace this with an embedder.h API. See // https://github.com/flutter/flutter/issues/71099 + cursor_handler_ = + std::make_unique(messenger_wrapper_.get(), this); platform_handler_ = std::make_unique(messenger_wrapper_.get(), this); settings_plugin_ = std::make_unique(messenger_wrapper_.get(), diff --git a/shell/platform/windows/flutter_windows_engine.h b/shell/platform/windows/flutter_windows_engine.h index 193cc8f03f520..e461d086a0db8 100644 --- a/shell/platform/windows/flutter_windows_engine.h +++ b/shell/platform/windows/flutter_windows_engine.h @@ -21,6 +21,7 @@ #include "flutter/shell/platform/embedder/embedder.h" #include "flutter/shell/platform/windows/accessibility_bridge_windows.h" #include "flutter/shell/platform/windows/angle_surface_manager.h" +#include "flutter/shell/platform/windows/cursor_handler.h" #include "flutter/shell/platform/windows/flutter_desktop_messenger.h" #include "flutter/shell/platform/windows/flutter_project_bundle.h" #include "flutter/shell/platform/windows/flutter_windows_texture_registrar.h" @@ -308,6 +309,9 @@ class FlutterWindowsEngine { // May be nullptr if ANGLE failed to initialize. std::unique_ptr surface_manager_; + // Handler for cursor events. + std::unique_ptr cursor_handler_; + // Handler for the flutter/platform channel. std::unique_ptr platform_handler_; diff --git a/shell/platform/windows/flutter_windows_view.cc b/shell/platform/windows/flutter_windows_view.cc index a557c2f0ae7e1..1aee3c0ae8d2a 100644 --- a/shell/platform/windows/flutter_windows_view.cc +++ b/shell/platform/windows/flutter_windows_view.cc @@ -63,8 +63,6 @@ void FlutterWindowsView::SetEngine( // Set up the system channel handlers. auto internal_plugin_messenger = internal_plugin_registrar_->messenger(); InitializeKeyboard(); - cursor_handler_ = std::make_unique(internal_plugin_messenger, - binding_handler_.get()); PhysicalWindowBounds bounds = binding_handler_->GetPhysicalWindowBounds(); @@ -114,6 +112,14 @@ uint32_t FlutterWindowsView::GetFrameBufferId(size_t width, size_t height) { return kWindowFrameBufferID; } +void FlutterWindowsView::UpdateFlutterCursor(const std::string& cursor_name) { + binding_handler_->UpdateFlutterCursor(cursor_name); +} + +void FlutterWindowsView::SetFlutterCursor(HCURSOR cursor) { + binding_handler_->SetFlutterCursor(cursor); +} + void FlutterWindowsView::ForceRedraw() { if (resize_status_ == ResizeState::kDone) { // Request new frame. diff --git a/shell/platform/windows/flutter_windows_view.h b/shell/platform/windows/flutter_windows_view.h index c941492c94acd..083fb13e5ece6 100644 --- a/shell/platform/windows/flutter_windows_view.h +++ b/shell/platform/windows/flutter_windows_view.h @@ -16,7 +16,6 @@ #include "flutter/shell/platform/common/geometry.h" #include "flutter/shell/platform/embedder/embedder.h" #include "flutter/shell/platform/windows/angle_surface_manager.h" -#include "flutter/shell/platform/windows/cursor_handler.h" #include "flutter/shell/platform/windows/flutter_windows_engine.h" #include "flutter/shell/platform/windows/keyboard_handler_base.h" #include "flutter/shell/platform/windows/keyboard_key_embedder_handler.h" @@ -96,6 +95,13 @@ class FlutterWindowsView : public WindowBindingHandlerDelegate, // Returns the frame buffer id for the engine to render to. uint32_t GetFrameBufferId(size_t width, size_t height); + // Sets the cursor that should be used when the mouse is over the Flutter + // content. See mouse_cursor.dart for the values and meanings of cursor_name. + void UpdateFlutterCursor(const std::string& cursor_name); + + // Sets the cursor directly from a cursor handle. + void SetFlutterCursor(HCURSOR cursor); + // Invoked by the engine right before the engine is restarted. // // This should reset necessary states to as if the view has just been @@ -381,9 +387,6 @@ class FlutterWindowsView : public WindowBindingHandlerDelegate, // Handlers for text events from Windows. std::unique_ptr text_input_plugin_; - // Handler for cursor events. - std::unique_ptr cursor_handler_; - // Currently configured WindowBindingHandler for view. std::unique_ptr binding_handler_;