Flutter Engine
The Flutter Engine
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
13static constexpr char kChannelName[] = "flutter/mousecursor";
14
15static constexpr char kActivateSystemCursorMethod[] = "activateSystemCursor";
16static constexpr char kKindKey[] = "kind";
17
18// This method allows creating a custom cursor with rawBGRA buffer, returns a
19// string to identify the cursor.
20static constexpr char kCreateCustomCursorMethod[] =
21 "createCustomCursor/windows";
22// A string, the custom cursor's name.
23static constexpr char kCustomCursorNameKey[] = "name";
24// A list of bytes, the custom cursor's rawBGRA buffer.
25static constexpr char kCustomCursorBufferKey[] = "buffer";
26// A double, the x coordinate of the custom cursor's hotspot, starting from
27// left.
28static constexpr char kCustomCursorHotXKey[] = "hotX";
29// A double, the y coordinate of the custom cursor's hotspot, starting from top.
30static constexpr char kCustomCursorHotYKey[] = "hotY";
31// An int value for the width of the custom cursor.
32static constexpr char kCustomCursorWidthKey[] = "width";
33// An int value for the height of the custom cursor.
34static constexpr char kCustomCursorHeightKey[] = "height";
35
36// This method also has an argument `kCustomCursorNameKey` for the name
37// of the cursor to activate.
38static constexpr char kSetCustomCursorMethod[] = "setCustomCursor/windows";
39
40// This method also has an argument `kCustomCursorNameKey` for the name
41// of the cursor to delete.
42static constexpr char kDeleteCustomCursorMethod[] =
43 "deleteCustomCursor/windows";
44
45// Error codes used for responses.
46static constexpr char kCursorError[] = "Cursor error";
47
48namespace flutter {
49
52 : channel_(std::make_unique<MethodChannel<EncodableValue>>(
53 messenger,
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
64void CursorHandler::HandleMethodCall(
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
205HCURSOR 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
249void 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
CursorHandler(flutter::BinaryMessenger *messenger, flutter::FlutterWindowsEngine *engine)
FlutterWindowsView * view(FlutterViewId view_id) const
static constexpr char kDeleteCustomCursorMethod[]
static constexpr char kActivateSystemCursorMethod[]
static constexpr char kCustomCursorHotXKey[]
static constexpr char kCustomCursorWidthKey[]
static constexpr char kKindKey[]
static constexpr char kChannelName[]
static constexpr char kCustomCursorHotYKey[]
static constexpr char kCustomCursorNameKey[]
static constexpr char kCreateCustomCursorMethod[]
static constexpr char kSetCustomCursorMethod[]
static constexpr char kCustomCursorBufferKey[]
static constexpr char kCursorError[]
static constexpr char kCustomCursorHeightKey[]
FlutterEngine engine
Definition: main.cc:68
G_BEGIN_DECLS G_MODULE_EXPORT FlMethodCall * method_call
GAsyncResult * result
double y
double x
Definition: bitmap.py:1
static void GetObject(Thread *thread, JSONStream *js)
Definition: service.cc:5046
def call(args)
Definition: dom.py:159
void GetMaskBitmaps(HBITMAP bitmap, HBITMAP &mask_bitmap)
DEF_SWITCHES_START aot vmservice shared library name
Definition: switches.h:32
constexpr FlutterViewId kImplicitViewId
HCURSOR GetCursorFromBuffer(const std::vector< uint8_t > &buffer, double hot_x, double hot_y, int width, int height)
DEF_SWITCHES_START aot vmservice shared library Name of the *so containing AOT compiled Dart assets for launching the service isolate vm snapshot The VM snapshot data that will be memory mapped as read only SnapshotAssetPath must be present isolate snapshot The isolate snapshot data that will be memory mapped as read only SnapshotAssetPath must be present cache dir Path to the cache directory This is different from the persistent_cache_path in embedder which is used for Skia shader cache icu native lib Path to the library file that exports the ICU data vm service The hostname IP address on which the Dart VM Service should be served If not defaults to or::depending on whether ipv6 is specified vm service A custom Dart VM Service port The default is to pick a randomly available open port disable vm Disable the Dart VM Service The Dart VM Service is never available in release mode disable vm service Disable mDNS Dart VM Service publication Bind to the IPv6 localhost address for the Dart VM Service Ignored if vm service host is set endless trace buffer
Definition: switches.h:126
Definition: ref_ptr.h:256
int32_t height
int32_t width