| Flutter Windows Embedder |

cursor_handler.cc
Go to the documentation of this file.
1 // Copyright 2013 The Flutter Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "flutter/shell/platform/windows/cursor_handler.h"
6
7 #include <windows.h>
8
9 #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"
12
13 static constexpr char kChannelName[] = "flutter/mousecursor";
14
15 static constexpr char kActivateSystemCursorMethod[] = "activateSystemCursor";
16 static constexpr char kKindKey[] = "kind";
17
18 // This method allows creating a custom cursor with rawBGRA buffer, returns a
19 // string to identify the cursor.
20 static constexpr char kCreateCustomCursorMethod[] =
21 "createCustomCursor/windows";
22 // A string, the custom cursor's name.
23 static constexpr char kCustomCursorNameKey[] = "name";
24 // A list of bytes, the custom cursor's rawBGRA buffer.
25 static constexpr char kCustomCursorBufferKey[] = "buffer";
26 // A double, the x coordinate of the custom cursor's hotspot, starting from
27 // left.
28 static constexpr char kCustomCursorHotXKey[] = "hotX";
29 // A double, the y coordinate of the custom cursor's hotspot, starting from top.
30 static constexpr char kCustomCursorHotYKey[] = "hotY";
31 // An int value for the width of the custom cursor.
32 static constexpr char kCustomCursorWidthKey[] = "width";
33 // An int value for the height of the custom cursor.
34 static constexpr char kCustomCursorHeightKey[] = "height";
35
36 // This method also has an argument `kCustomCursorNameKey` for the name
37 // of the cursor to activate.
38 static constexpr char kSetCustomCursorMethod[] = "setCustomCursor/windows";
39
40 // This method also has an argument `kCustomCursorNameKey` for the name
41 // of the cursor to delete.
42 static constexpr char kDeleteCustomCursorMethod[] =
43 "deleteCustomCursor/windows";
44
45 // Error codes used for responses.
46 static constexpr char kCursorError[] = "Cursor error";
47
48 namespace flutter {
49
50 CursorHandler::CursorHandler(BinaryMessenger* messenger,
51 FlutterWindowsEngine* engine)
52 : channel_(std::make_unique<MethodChannel<EncodableValue>>(
53 messenger,
54 kChannelName,
55 &StandardMethodCodec::GetInstance())),
56 engine_(engine) {
57 channel_->SetMethodCallHandler(
58 [this](const MethodCall<EncodableValue>& call,
59 std::unique_ptr<MethodResult<EncodableValue>> result) {
60 HandleMethodCall(call, std::move(result));
61 });
62 }
63
64 void CursorHandler::HandleMethodCall(
65 const MethodCall<EncodableValue>& method_call,
66 std::unique_ptr<MethodResult<EncodableValue>> result) {
67 const std::string& method = method_call.method_name();
68 if (method.compare(kActivateSystemCursorMethod) == 0) {
69 const auto& arguments = std::get<EncodableMap>(*method_call.arguments());
70 auto kind_iter = arguments.find(EncodableValue(std::string(kKindKey)));
71 if (kind_iter == arguments.end()) {
72 result->Error("Argument error",
73 "Missing argument while trying to activate system cursor");
74 return;
75 }
76 const auto& kind = std::get<std::string>(kind_iter->second);
77
78 // TODO(loicsharma): Remove implicit view assumption.
79 // https://github.com/flutter/flutter/issues/142845
80 FlutterWindowsView* view = engine_->view(kImplicitViewId);
81 if (view == nullptr) {
82 result->Error(kCursorError,
83 "Cursor is not available in Windows headless mode");
84 return;
85 }
86 view->UpdateFlutterCursor(kind);
87 result->Success();
88 } else if (method.compare(kCreateCustomCursorMethod) == 0) {
89 const auto& arguments = std::get<EncodableMap>(*method_call.arguments());
90 auto name_iter =
91 arguments.find(EncodableValue(std::string(kCustomCursorNameKey)));
92 if (name_iter == arguments.end()) {
93 result->Error(
94 "Argument error",
95 "Missing argument name while trying to customize system cursor");
96 return;
97 }
98 auto name = std::get<std::string>(name_iter->second);
99 auto buffer_iter =
100 arguments.find(EncodableValue(std::string(kCustomCursorBufferKey)));
101 if (buffer_iter == arguments.end()) {
102 result->Error(
103 "Argument error",
104 "Missing argument buffer while trying to customize system cursor");
105 return;
106 }
107 auto buffer = std::get<std::vector<uint8_t>>(buffer_iter->second);
108 auto width_iter =
109 arguments.find(EncodableValue(std::string(kCustomCursorWidthKey)));
110 if (width_iter == arguments.end()) {
111 result->Error(
112 "Argument error",
113 "Missing argument width while trying to customize system cursor");
114 return;
115 }
116 auto width = std::get<int>(width_iter->second);
117 auto height_iter =
118 arguments.find(EncodableValue(std::string(kCustomCursorHeightKey)));
119 if (height_iter == arguments.end()) {
120 result->Error(
121 "Argument error",
122 "Missing argument height while trying to customize system cursor");
123 return;
124 }
125 auto height = std::get<int>(height_iter->second);
126 auto hot_x_iter =
127 arguments.find(EncodableValue(std::string(kCustomCursorHotXKey)));
128 if (hot_x_iter == arguments.end()) {
129 result->Error(
130 "Argument error",
131 "Missing argument hotX while trying to customize system cursor");
132 return;
133 }
134 auto hot_x = std::get<double>(hot_x_iter->second);
135 auto hot_y_iter =
136 arguments.find(EncodableValue(std::string(kCustomCursorHotYKey)));
137 if (hot_y_iter == arguments.end()) {
138 result->Error(
139 "Argument error",
140 "Missing argument hotY while trying to customize system cursor");
141 return;
142 }
143 auto hot_y = std::get<double>(hot_y_iter->second);
144 HCURSOR cursor = GetCursorFromBuffer(buffer, hot_x, hot_y, width, height);
145 if (cursor == nullptr) {
146 result->Error("Argument error",
147 "Argument must contains a valid rawBGRA bitmap");
148 return;
149 }
150 // Push the cursor into the cache map.
151 custom_cursors_.emplace(name, std::move(cursor));
152 result->Success(flutter::EncodableValue(std::move(name)));
153 } else if (method.compare(kSetCustomCursorMethod) == 0) {
154 const auto& arguments = std::get<EncodableMap>(*method_call.arguments());
155 auto name_iter =
156 arguments.find(EncodableValue(std::string(kCustomCursorNameKey)));
157 if (name_iter == arguments.end()) {
158 result->Error("Argument error",
159 "Missing argument key while trying to set a custom cursor");
160 return;
161 }
162 auto name = std::get<std::string>(name_iter->second);
163 if (custom_cursors_.find(name) == custom_cursors_.end()) {
164 result->Error(
165 "Argument error",
166 "The custom cursor identified by the argument key cannot be found");
167 return;
168 }
169 HCURSOR cursor = custom_cursors_[name];
170
171 // TODO(loicsharma): Remove implicit view assumption.
172 // https://github.com/flutter/flutter/issues/142845
173 FlutterWindowsView* view = engine_->view(kImplicitViewId);
174 if (view == nullptr) {
175 result->Error(kCursorError,
176 "Cursor is not available in Windows headless mode");
177 return;
178 }
179 view->SetFlutterCursor(cursor);
180 result->Success();
181 } else if (method.compare(kDeleteCustomCursorMethod) == 0) {
182 const auto& arguments = std::get<EncodableMap>(*method_call.arguments());
183 auto name_iter =
184 arguments.find(EncodableValue(std::string(kCustomCursorNameKey)));
185 if (name_iter == arguments.end()) {
186 result->Error(
187 "Argument error",
188 "Missing argument key while trying to delete a custom cursor");
189 return;
190 }
191 auto name = std::get<std::string>(name_iter->second);
192 auto it = custom_cursors_.find(name);
193 // If the specified cursor name is not found, the deletion is a noop and
194 // returns success.
195 if (it != custom_cursors_.end()) {
196 DeleteObject(it->second);
197 custom_cursors_.erase(it);
198 }
199 result->Success();
200 } else {
201 result->NotImplemented();
202 }
203 }
204
205 HCURSOR GetCursorFromBuffer(const std::vector<uint8_t>& buffer,
206 double hot_x,
207 double hot_y,
208 int width,
209 int height) {
210 HCURSOR cursor = nullptr;
211 HDC display_dc = GetDC(NULL);
212 // Flutter should returns rawBGRA, which has 8bits * 4channels.
213 BITMAPINFO bmi;
214 memset(&bmi, 0, sizeof(bmi));
215 bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
216 bmi.bmiHeader.biWidth = width;
217 bmi.bmiHeader.biHeight = -height;
218 bmi.bmiHeader.biPlanes = 1;
219 bmi.bmiHeader.biBitCount = 32;
220 bmi.bmiHeader.biCompression = BI_RGB;
221 bmi.bmiHeader.biSizeImage = width * height * 4;
222 // Create the pixmap DIB section
223 uint8_t* pixels = 0;
224 HBITMAP bitmap =
225 CreateDIBSection(display_dc, &bmi, DIB_RGB_COLORS, (void**)&pixels, 0, 0);
226 ReleaseDC(0, display_dc);
227 if (!bitmap || !pixels) {
228 return nullptr;
229 }
230 int bytes_per_line = width * 4;
231 for (int y = 0; y < height; ++y) {
232 memcpy(pixels + y * bytes_per_line, &buffer[bytes_per_line * y],
233 bytes_per_line);
234 }
235 HBITMAP mask;
236 GetMaskBitmaps(bitmap, mask);
237 ICONINFO icon_info;
238 icon_info.fIcon = 0;
239 icon_info.xHotspot = hot_x;
240 icon_info.yHotspot = hot_y;
241 icon_info.hbmMask = mask;
242 icon_info.hbmColor = bitmap;
243 cursor = CreateIconIndirect(&icon_info);
244 DeleteObject(mask);
245 DeleteObject(bitmap);
246 return cursor;
247 }
248
249 void GetMaskBitmaps(HBITMAP bitmap, HBITMAP& mask_bitmap) {
250 HDC h_dc = ::GetDC(NULL);
251 HDC h_main_dc = ::CreateCompatibleDC(h_dc);
252 HDC h_and_mask_dc = ::CreateCompatibleDC(h_dc);
253
254 // Get the dimensions of the source bitmap
255 BITMAP bm;
256 ::GetObject(bitmap, sizeof(BITMAP), &bm);
257 mask_bitmap = ::CreateCompatibleBitmap(h_dc, bm.bmWidth, bm.bmHeight);
258
259 // Select the bitmaps to DC
260 HBITMAP h_old_main_bitmap = (HBITMAP)::SelectObject(h_main_dc, bitmap);
261 HBITMAP h_old_and_mask_bitmap =
262 (HBITMAP)::SelectObject(h_and_mask_dc, mask_bitmap);
263
264 // Scan each pixel of the souce bitmap and create the masks
265 COLORREF main_bit_pixel;
266 for (int x = 0; x < bm.bmWidth; ++x) {
267 for (int y = 0; y < bm.bmHeight; ++y) {
268 main_bit_pixel = ::GetPixel(h_main_dc, x, y);
269 if (main_bit_pixel == RGB(0, 0, 0)) {
270 ::SetPixel(h_and_mask_dc, x, y, RGB(255, 255, 255));
271 } else {
272 ::SetPixel(h_and_mask_dc, x, y, RGB(0, 0, 0));
273 }
274 }
275 }
276 ::SelectObject(h_main_dc, h_old_main_bitmap);
277 ::SelectObject(h_and_mask_dc, h_old_and_mask_bitmap);
278
279 ::DeleteDC(h_and_mask_dc);
280 ::DeleteDC(h_main_dc);
281
282 ::ReleaseDC(NULL, h_dc);
283 }
284
285 } // namespace flutter
flutter::kImplicitViewId
constexpr FlutterViewId kImplicitViewId
Definition: flutter_windows_engine.h:55
flutter::FlutterWindowsEngine::view
FlutterWindowsView * view(FlutterViewId view_id) const
Definition: flutter_windows_engine.cc:657
kCustomCursorHeightKey
static constexpr char kCustomCursorHeightKey[]
Definition: cursor_handler.cc:34
kCursorError
static constexpr char kCursorError[]
Definition: cursor_handler.cc:46
flutter::MethodChannel
Definition: method_channel.h:34
flutter::GetCursorFromBuffer
HCURSOR GetCursorFromBuffer(const std::vector< uint8_t > &buffer, double hot_x, double hot_y, int width, int height)
Definition: cursor_handler.cc:205
flutter::FlutterWindowsEngine
Definition: flutter_windows_engine.h:90
kDeleteCustomCursorMethod
static constexpr char kDeleteCustomCursorMethod[]
Definition: cursor_handler.cc:42
kCustomCursorHotXKey
static constexpr char kCustomCursorHotXKey[]
Definition: cursor_handler.cc:28
flutter::StandardMethodCodec
Definition: standard_method_codec.h:18
standard_method_codec.h
kSetCustomCursorMethod
static constexpr char kSetCustomCursorMethod[]
Definition: cursor_handler.cc:38
kCustomCursorNameKey
static constexpr char kCustomCursorNameKey[]
Definition: cursor_handler.cc:23
kActivateSystemCursorMethod
static constexpr char kActivateSystemCursorMethod[]
Definition: cursor_handler.cc:15
flutter::BinaryMessenger
Definition: binary_messenger.h:28
flutter_windows_view.h
kKindKey
static constexpr char kKindKey[]
Definition: cursor_handler.cc:16
flutter::MethodCall
Definition: method_call.h:18
flutter::GetMaskBitmaps
void GetMaskBitmaps(HBITMAP bitmap, HBITMAP &mask_bitmap)
Definition: cursor_handler.cc:249
flutter
Definition: accessibility_bridge_windows.cc:11
flutter::FlutterWindowsView::UpdateFlutterCursor
void UpdateFlutterCursor(const std::string &cursor_name)
Definition: flutter_windows_view.cc:179
flutter::FlutterWindowsView::SetFlutterCursor
void SetFlutterCursor(HCURSOR cursor)
Definition: flutter_windows_view.cc:183
kCustomCursorWidthKey
static constexpr char kCustomCursorWidthKey[]
Definition: cursor_handler.cc:32
kCreateCustomCursorMethod
static constexpr char kCreateCustomCursorMethod[]
Definition: cursor_handler.cc:20
kChannelName
static constexpr char kChannelName[]
Definition: cursor_handler.cc:13
flutter::EncodableValue
Definition: encodable_value.h:165
flutter_windows_engine.h
flutter::MethodCall::method_name
const std::string & method_name() const
Definition: method_call.h:31
flutter::MethodResult< EncodableValue >
kCustomCursorBufferKey
static constexpr char kCustomCursorBufferKey[]
Definition: cursor_handler.cc:25
flutter::CursorHandler::CursorHandler
CursorHandler(flutter::BinaryMessenger *messenger, flutter::FlutterWindowsEngine *engine)
Definition: cursor_handler.cc:50
cursor_handler.h
flutter::MethodCall::arguments
const T * arguments() const
Definition: method_call.h:34
kCustomCursorHotYKey
static constexpr char kCustomCursorHotYKey[]
Definition: cursor_handler.cc:30