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