Skip to content

Commit 336e188

Browse files
committed
[Windows] Make the engine own the cursor plugin (flutter#38570)
1 parent d85adb6 commit 336e188

7 files changed

+177
-34
lines changed

shell/platform/windows/cursor_handler.cc

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77
#include <windows.h>
88

99
#include "flutter/shell/platform/common/client_wrapper/include/flutter/standard_method_codec.h"
10+
#include "flutter/shell/platform/windows/flutter_windows_engine.h"
11+
#include "flutter/shell/platform/windows/flutter_windows_view.h"
1012

1113
static constexpr char kChannelName[] = "flutter/mousecursor";
1214

@@ -40,15 +42,18 @@ static constexpr char kSetCustomCursorMethod[] = "setCustomCursor/windows";
4042
static constexpr char kDeleteCustomCursorMethod[] =
4143
"deleteCustomCursor/windows";
4244

45+
// Error codes used for responses.
46+
static constexpr char kCursorError[] = "Cursor error";
47+
4348
namespace flutter {
4449

4550
CursorHandler::CursorHandler(BinaryMessenger* messenger,
46-
WindowBindingHandler* delegate)
51+
FlutterWindowsEngine* engine)
4752
: channel_(std::make_unique<MethodChannel<EncodableValue>>(
4853
messenger,
4954
kChannelName,
5055
&StandardMethodCodec::GetInstance())),
51-
delegate_(delegate) {
56+
engine_(engine) {
5257
channel_->SetMethodCallHandler(
5358
[this](const MethodCall<EncodableValue>& call,
5459
std::unique_ptr<MethodResult<EncodableValue>> result) {
@@ -69,7 +74,13 @@ void CursorHandler::HandleMethodCall(
6974
return;
7075
}
7176
const auto& kind = std::get<std::string>(kind_iter->second);
72-
delegate_->UpdateFlutterCursor(kind);
77+
FlutterWindowsView* view = engine_->view();
78+
if (view == nullptr) {
79+
result->Error(kCursorError,
80+
"Cursor is not available in Windows headless mode");
81+
return;
82+
}
83+
view->UpdateFlutterCursor(kind);
7384
result->Success();
7485
} else if (method.compare(kCreateCustomCursorMethod) == 0) {
7586
const auto& arguments = std::get<EncodableMap>(*method_call.arguments());
@@ -153,7 +164,13 @@ void CursorHandler::HandleMethodCall(
153164
return;
154165
}
155166
HCURSOR cursor = custom_cursors_[name];
156-
delegate_->SetFlutterCursor(cursor);
167+
FlutterWindowsView* view = engine_->view();
168+
if (view == nullptr) {
169+
result->Error(kCursorError,
170+
"Cursor is not available in Windows headless mode");
171+
return;
172+
}
173+
view->SetFlutterCursor(cursor);
157174
result->Success();
158175
} else if (method.compare(kDeleteCustomCursorMethod) == 0) {
159176
const auto& arguments = std::get<EncodableMap>(*method_call.arguments());

shell/platform/windows/cursor_handler.h

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,13 @@
1515

1616
namespace flutter {
1717

18+
class FlutterWindowsEngine;
19+
1820
// Handler for the cursor system channel.
1921
class CursorHandler {
2022
public:
2123
explicit CursorHandler(flutter::BinaryMessenger* messenger,
22-
WindowBindingHandler* delegate);
24+
flutter::FlutterWindowsEngine* engine);
2325

2426
private:
2527
// Called when a method is called on |channel_|;
@@ -30,8 +32,8 @@ class CursorHandler {
3032
// The MethodChannel used for communication with the Flutter engine.
3133
std::unique_ptr<flutter::MethodChannel<EncodableValue>> channel_;
3234

33-
// The delegate for cursor updates.
34-
WindowBindingHandler* delegate_;
35+
// The Flutter engine that will be notified for cursor updates.
36+
FlutterWindowsEngine* engine_;
3537

3638
// The cache map for custom cursors.
3739
std::unordered_map<std::string, HCURSOR> custom_cursors_;

shell/platform/windows/cursor_handler_unittests.cc

Lines changed: 130 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,10 @@
1010
#include "flutter/shell/platform/common/client_wrapper/include/flutter/standard_message_codec.h"
1111
#include "flutter/shell/platform/common/client_wrapper/include/flutter/standard_method_codec.h"
1212
#include "flutter/shell/platform/windows/flutter_windows_view.h"
13+
#include "flutter/shell/platform/windows/testing/flutter_windows_engine_builder.h"
1314
#include "flutter/shell/platform/windows/testing/mock_window_binding_handler.h"
1415
#include "flutter/shell/platform/windows/testing/test_binary_messenger.h"
16+
#include "flutter/shell/platform/windows/testing/windows_test.h"
1517
#include "gmock/gmock.h"
1618
#include "gtest/gtest.h"
1719

@@ -50,12 +52,46 @@ void SimulateCursorMessage(TestBinaryMessenger* messenger,
5052

5153
} // namespace
5254

53-
TEST(CursorHandlerTest, ActivateSystemCursor) {
55+
class CursorHandlerTest : public WindowsTest {
56+
protected:
57+
FlutterWindowsEngine* engine() { return engine_.get(); }
58+
FlutterWindowsView* view() { return view_.get(); }
59+
MockWindowBindingHandler* window() { return window_; }
60+
61+
void use_headless_engine() {
62+
FlutterWindowsEngineBuilder builder{GetContext()};
63+
64+
engine_ = builder.Build();
65+
}
66+
67+
void use_engine_with_view() {
68+
FlutterWindowsEngineBuilder builder{GetContext()};
69+
70+
auto window = std::make_unique<MockWindowBindingHandler>();
71+
72+
window_ = window.get();
73+
EXPECT_CALL(*window_, SetView).Times(1);
74+
EXPECT_CALL(*window_, GetRenderTarget).WillOnce(Return(nullptr));
75+
76+
engine_ = builder.Build();
77+
view_ = std::make_unique<FlutterWindowsView>(std::move(window));
78+
79+
engine_->SetView(view_.get());
80+
}
81+
82+
private:
83+
std::unique_ptr<FlutterWindowsEngine> engine_;
84+
std::unique_ptr<FlutterWindowsView> view_;
85+
MockWindowBindingHandler* window_;
86+
};
87+
88+
TEST_F(CursorHandlerTest, ActivateSystemCursor) {
89+
use_engine_with_view();
90+
5491
TestBinaryMessenger messenger;
55-
MockWindowBindingHandler window;
56-
CursorHandler cursor_handler(&messenger, &window);
92+
CursorHandler cursor_handler(&messenger, engine());
5793

58-
EXPECT_CALL(window, UpdateFlutterCursor("click")).Times(1);
94+
EXPECT_CALL(*window(), UpdateFlutterCursor("click")).Times(1);
5995

6096
bool success = false;
6197
MethodResultFunctions<> result_handler(
@@ -75,10 +111,38 @@ TEST(CursorHandlerTest, ActivateSystemCursor) {
75111
EXPECT_TRUE(success);
76112
}
77113

78-
TEST(CursorHandlerTest, CreateCustomCursor) {
114+
TEST_F(CursorHandlerTest, ActivateSystemCursorRequiresView) {
115+
use_headless_engine();
116+
79117
TestBinaryMessenger messenger;
80-
MockWindowBindingHandler window;
81-
CursorHandler cursor_handler(&messenger, &window);
118+
CursorHandler cursor_handler(&messenger, engine());
119+
120+
bool error = false;
121+
MethodResultFunctions<> result_handler(
122+
nullptr,
123+
[&error](const std::string& error_code, const std::string& error_message,
124+
const EncodableValue* value) {
125+
error = true;
126+
EXPECT_EQ(error_message,
127+
"Cursor is not available in Windows headless mode");
128+
},
129+
nullptr);
130+
131+
SimulateCursorMessage(&messenger, kActivateSystemCursorMethod,
132+
std::make_unique<EncodableValue>(EncodableMap{
133+
{EncodableValue("device"), EncodableValue(0)},
134+
{EncodableValue("kind"), EncodableValue("click")},
135+
}),
136+
&result_handler);
137+
138+
EXPECT_TRUE(error);
139+
}
140+
141+
TEST_F(CursorHandlerTest, CreateCustomCursor) {
142+
use_engine_with_view();
143+
144+
TestBinaryMessenger messenger;
145+
CursorHandler cursor_handler(&messenger, engine());
82146

83147
// Create a 4x4 raw BGRA test cursor buffer.
84148
std::vector<uint8_t> buffer(4 * 4 * 4, 0);
@@ -105,10 +169,11 @@ TEST(CursorHandlerTest, CreateCustomCursor) {
105169
EXPECT_TRUE(success);
106170
}
107171

108-
TEST(CursorHandlerTest, SetCustomCursor) {
172+
TEST_F(CursorHandlerTest, SetCustomCursor) {
173+
use_engine_with_view();
174+
109175
TestBinaryMessenger messenger;
110-
MockWindowBindingHandler window;
111-
CursorHandler cursor_handler(&messenger, &window);
176+
CursorHandler cursor_handler(&messenger, engine());
112177

113178
// Create a 4x4 raw BGRA test cursor buffer.
114179
std::vector<uint8_t> buffer(4 * 4 * 4, 0);
@@ -122,7 +187,7 @@ TEST(CursorHandlerTest, SetCustomCursor) {
122187
},
123188
nullptr, nullptr);
124189

125-
EXPECT_CALL(window, SetFlutterCursor(/*cursor=*/NotNull())).Times(1);
190+
EXPECT_CALL(*window(), SetFlutterCursor(/*cursor=*/NotNull())).Times(1);
126191

127192
SimulateCursorMessage(&messenger, kCreateCustomCursorMethod,
128193
std::make_unique<EncodableValue>(EncodableMap{
@@ -144,10 +209,52 @@ TEST(CursorHandlerTest, SetCustomCursor) {
144209
EXPECT_TRUE(success);
145210
}
146211

147-
TEST(CursorHandlerTest, SetNonexistentCustomCursor) {
212+
TEST_F(CursorHandlerTest, SetCustomCursorRequiresView) {
213+
use_headless_engine();
214+
148215
TestBinaryMessenger messenger;
149-
MockWindowBindingHandler window;
150-
CursorHandler cursor_handler(&messenger, &window);
216+
CursorHandler cursor_handler(&messenger, engine());
217+
218+
// Create a 4x4 raw BGRA test cursor buffer.
219+
std::vector<uint8_t> buffer(4 * 4 * 4, 0);
220+
221+
bool error = false;
222+
MethodResultFunctions<> create_result_handler(nullptr, nullptr, nullptr);
223+
MethodResultFunctions<> set_result_handler(
224+
nullptr,
225+
[&error](const std::string& error_code, const std::string& error_message,
226+
const EncodableValue* value) {
227+
error = true;
228+
EXPECT_EQ(error_message,
229+
"Cursor is not available in Windows headless mode");
230+
},
231+
nullptr);
232+
233+
SimulateCursorMessage(&messenger, kCreateCustomCursorMethod,
234+
std::make_unique<EncodableValue>(EncodableMap{
235+
{EncodableValue("name"), EncodableValue("hello")},
236+
{EncodableValue("buffer"), EncodableValue(buffer)},
237+
{EncodableValue("width"), EncodableValue(4)},
238+
{EncodableValue("height"), EncodableValue(4)},
239+
{EncodableValue("hotX"), EncodableValue(0.0)},
240+
{EncodableValue("hotY"), EncodableValue(0.0)},
241+
}),
242+
&create_result_handler);
243+
244+
SimulateCursorMessage(&messenger, kSetCustomCursorMethod,
245+
std::make_unique<EncodableValue>(EncodableMap{
246+
{EncodableValue("name"), EncodableValue("hello")},
247+
}),
248+
&set_result_handler);
249+
250+
EXPECT_TRUE(error);
251+
}
252+
253+
TEST_F(CursorHandlerTest, SetNonexistentCustomCursor) {
254+
use_engine_with_view();
255+
256+
TestBinaryMessenger messenger;
257+
CursorHandler cursor_handler(&messenger, engine());
151258

152259
bool error = false;
153260
MethodResultFunctions<> result_handler(
@@ -161,7 +268,7 @@ TEST(CursorHandlerTest, SetNonexistentCustomCursor) {
161268
},
162269
nullptr);
163270

164-
EXPECT_CALL(window, SetFlutterCursor).Times(0);
271+
EXPECT_CALL(*window(), SetFlutterCursor).Times(0);
165272

166273
SimulateCursorMessage(&messenger, kSetCustomCursorMethod,
167274
std::make_unique<EncodableValue>(EncodableMap{
@@ -172,10 +279,11 @@ TEST(CursorHandlerTest, SetNonexistentCustomCursor) {
172279
EXPECT_TRUE(error);
173280
}
174281

175-
TEST(CursorHandlerTest, DeleteCustomCursor) {
282+
TEST_F(CursorHandlerTest, DeleteCustomCursor) {
283+
use_engine_with_view();
284+
176285
TestBinaryMessenger messenger;
177-
MockWindowBindingHandler window;
178-
CursorHandler cursor_handler(&messenger, &window);
286+
CursorHandler cursor_handler(&messenger, engine());
179287

180288
// Create a 4x4 raw BGRA test cursor buffer.
181289
std::vector<uint8_t> buffer(4 * 4 * 4, 0);
@@ -209,10 +317,11 @@ TEST(CursorHandlerTest, DeleteCustomCursor) {
209317
EXPECT_TRUE(success);
210318
}
211319

212-
TEST(CursorHandlerTest, DeleteNonexistentCustomCursor) {
320+
TEST_F(CursorHandlerTest, DeleteNonexistentCustomCursor) {
321+
use_engine_with_view();
322+
213323
TestBinaryMessenger messenger;
214-
MockWindowBindingHandler handler;
215-
CursorHandler cursor_handler(&messenger, &handler);
324+
CursorHandler cursor_handler(&messenger, engine());
216325

217326
bool success = false;
218327
MethodResultFunctions<> result_handler(

shell/platform/windows/flutter_windows_engine.cc

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -207,6 +207,8 @@ FlutterWindowsEngine::FlutterWindowsEngine(
207207
// Set up internal channels.
208208
// TODO: Replace this with an embedder.h API. See
209209
// https://github.com/flutter/flutter/issues/71099
210+
cursor_handler_ =
211+
std::make_unique<CursorHandler>(messenger_wrapper_.get(), this);
210212
platform_handler_ =
211213
std::make_unique<PlatformHandler>(messenger_wrapper_.get(), this);
212214
settings_plugin_ = std::make_unique<SettingsPlugin>(messenger_wrapper_.get(),

shell/platform/windows/flutter_windows_engine.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
#include "flutter/shell/platform/embedder/embedder.h"
2222
#include "flutter/shell/platform/windows/accessibility_bridge_windows.h"
2323
#include "flutter/shell/platform/windows/angle_surface_manager.h"
24+
#include "flutter/shell/platform/windows/cursor_handler.h"
2425
#include "flutter/shell/platform/windows/flutter_desktop_messenger.h"
2526
#include "flutter/shell/platform/windows/flutter_project_bundle.h"
2627
#include "flutter/shell/platform/windows/flutter_windows_texture_registrar.h"
@@ -308,6 +309,9 @@ class FlutterWindowsEngine {
308309
// May be nullptr if ANGLE failed to initialize.
309310
std::unique_ptr<AngleSurfaceManager> surface_manager_;
310311

312+
// Handler for cursor events.
313+
std::unique_ptr<CursorHandler> cursor_handler_;
314+
311315
// Handler for the flutter/platform channel.
312316
std::unique_ptr<PlatformHandler> platform_handler_;
313317

shell/platform/windows/flutter_windows_view.cc

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -63,8 +63,6 @@ void FlutterWindowsView::SetEngine(
6363
// Set up the system channel handlers.
6464
auto internal_plugin_messenger = internal_plugin_registrar_->messenger();
6565
InitializeKeyboard();
66-
cursor_handler_ = std::make_unique<CursorHandler>(internal_plugin_messenger,
67-
binding_handler_.get());
6866

6967
PhysicalWindowBounds bounds = binding_handler_->GetPhysicalWindowBounds();
7068

@@ -114,6 +112,14 @@ uint32_t FlutterWindowsView::GetFrameBufferId(size_t width, size_t height) {
114112
return kWindowFrameBufferID;
115113
}
116114

115+
void FlutterWindowsView::UpdateFlutterCursor(const std::string& cursor_name) {
116+
binding_handler_->UpdateFlutterCursor(cursor_name);
117+
}
118+
119+
void FlutterWindowsView::SetFlutterCursor(HCURSOR cursor) {
120+
binding_handler_->SetFlutterCursor(cursor);
121+
}
122+
117123
void FlutterWindowsView::ForceRedraw() {
118124
if (resize_status_ == ResizeState::kDone) {
119125
// Request new frame.

shell/platform/windows/flutter_windows_view.h

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@
1616
#include "flutter/shell/platform/common/geometry.h"
1717
#include "flutter/shell/platform/embedder/embedder.h"
1818
#include "flutter/shell/platform/windows/angle_surface_manager.h"
19-
#include "flutter/shell/platform/windows/cursor_handler.h"
2019
#include "flutter/shell/platform/windows/flutter_windows_engine.h"
2120
#include "flutter/shell/platform/windows/keyboard_handler_base.h"
2221
#include "flutter/shell/platform/windows/keyboard_key_embedder_handler.h"
@@ -96,6 +95,13 @@ class FlutterWindowsView : public WindowBindingHandlerDelegate,
9695
// Returns the frame buffer id for the engine to render to.
9796
uint32_t GetFrameBufferId(size_t width, size_t height);
9897

98+
// Sets the cursor that should be used when the mouse is over the Flutter
99+
// content. See mouse_cursor.dart for the values and meanings of cursor_name.
100+
void UpdateFlutterCursor(const std::string& cursor_name);
101+
102+
// Sets the cursor directly from a cursor handle.
103+
void SetFlutterCursor(HCURSOR cursor);
104+
99105
// Invoked by the engine right before the engine is restarted.
100106
//
101107
// This should reset necessary states to as if the view has just been
@@ -381,9 +387,6 @@ class FlutterWindowsView : public WindowBindingHandlerDelegate,
381387
// Handlers for text events from Windows.
382388
std::unique_ptr<TextInputPlugin> text_input_plugin_;
383389

384-
// Handler for cursor events.
385-
std::unique_ptr<CursorHandler> cursor_handler_;
386-
387390
// Currently configured WindowBindingHandler for view.
388391
std::unique_ptr<WindowBindingHandler> binding_handler_;
389392

0 commit comments

Comments
 (0)