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 
6 
7 #include <windows.h>
8 
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 
51  FlutterWindowsEngine* engine)
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 
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  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);
84  result->Success();
85  } else if (method.compare(kCreateCustomCursorMethod) == 0) {
86  const auto& arguments = std::get<EncodableMap>(*method_call.arguments());
87  auto name_iter =
88  arguments.find(EncodableValue(std::string(kCustomCursorNameKey)));
89  if (name_iter == arguments.end()) {
90  result->Error(
91  "Argument error",
92  "Missing argument name while trying to customize system cursor");
93  return;
94  }
95  auto name = std::get<std::string>(name_iter->second);
96  auto buffer_iter =
97  arguments.find(EncodableValue(std::string(kCustomCursorBufferKey)));
98  if (buffer_iter == arguments.end()) {
99  result->Error(
100  "Argument error",
101  "Missing argument buffer while trying to customize system cursor");
102  return;
103  }
104  auto buffer = std::get<std::vector<uint8_t>>(buffer_iter->second);
105  auto width_iter =
106  arguments.find(EncodableValue(std::string(kCustomCursorWidthKey)));
107  if (width_iter == arguments.end()) {
108  result->Error(
109  "Argument error",
110  "Missing argument width while trying to customize system cursor");
111  return;
112  }
113  auto width = std::get<int>(width_iter->second);
114  auto height_iter =
115  arguments.find(EncodableValue(std::string(kCustomCursorHeightKey)));
116  if (height_iter == arguments.end()) {
117  result->Error(
118  "Argument error",
119  "Missing argument height while trying to customize system cursor");
120  return;
121  }
122  auto height = std::get<int>(height_iter->second);
123  auto hot_x_iter =
124  arguments.find(EncodableValue(std::string(kCustomCursorHotXKey)));
125  if (hot_x_iter == arguments.end()) {
126  result->Error(
127  "Argument error",
128  "Missing argument hotX while trying to customize system cursor");
129  return;
130  }
131  auto hot_x = std::get<double>(hot_x_iter->second);
132  auto hot_y_iter =
133  arguments.find(EncodableValue(std::string(kCustomCursorHotYKey)));
134  if (hot_y_iter == arguments.end()) {
135  result->Error(
136  "Argument error",
137  "Missing argument hotY while trying to customize system cursor");
138  return;
139  }
140  auto hot_y = std::get<double>(hot_y_iter->second);
141  HCURSOR cursor = GetCursorFromBuffer(buffer, hot_x, hot_y, width, height);
142  if (cursor == nullptr) {
143  result->Error("Argument error",
144  "Argument must contains a valid rawBGRA bitmap");
145  return;
146  }
147  // Push the cursor into the cache map.
148  custom_cursors_.emplace(name, std::move(cursor));
149  result->Success(flutter::EncodableValue(std::move(name)));
150  } else if (method.compare(kSetCustomCursorMethod) == 0) {
151  const auto& arguments = std::get<EncodableMap>(*method_call.arguments());
152  auto name_iter =
153  arguments.find(EncodableValue(std::string(kCustomCursorNameKey)));
154  if (name_iter == arguments.end()) {
155  result->Error("Argument error",
156  "Missing argument key while trying to set a custom cursor");
157  return;
158  }
159  auto name = std::get<std::string>(name_iter->second);
160  if (custom_cursors_.find(name) == custom_cursors_.end()) {
161  result->Error(
162  "Argument error",
163  "The custom cursor identified by the argument key cannot be found");
164  return;
165  }
166  HCURSOR cursor = custom_cursors_[name];
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);
174  result->Success();
175  } else if (method.compare(kDeleteCustomCursorMethod) == 0) {
176  const auto& arguments = std::get<EncodableMap>(*method_call.arguments());
177  auto name_iter =
178  arguments.find(EncodableValue(std::string(kCustomCursorNameKey)));
179  if (name_iter == arguments.end()) {
180  result->Error(
181  "Argument error",
182  "Missing argument key while trying to delete a custom cursor");
183  return;
184  }
185  auto name = std::get<std::string>(name_iter->second);
186  auto it = custom_cursors_.find(name);
187  // If the specified cursor name is not found, the deletion is a noop and
188  // returns success.
189  if (it != custom_cursors_.end()) {
190  DeleteObject(it->second);
191  custom_cursors_.erase(it);
192  }
193  result->Success();
194  } else {
195  result->NotImplemented();
196  }
197 }
198 
199 HCURSOR GetCursorFromBuffer(const std::vector<uint8_t>& buffer,
200  double hot_x,
201  double hot_y,
202  int width,
203  int height) {
204  HCURSOR cursor = nullptr;
205  HDC display_dc = GetDC(NULL);
206  // Flutter should returns rawBGRA, which has 8bits * 4channels.
207  BITMAPINFO bmi;
208  memset(&bmi, 0, sizeof(bmi));
209  bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
210  bmi.bmiHeader.biWidth = width;
211  bmi.bmiHeader.biHeight = -height;
212  bmi.bmiHeader.biPlanes = 1;
213  bmi.bmiHeader.biBitCount = 32;
214  bmi.bmiHeader.biCompression = BI_RGB;
215  bmi.bmiHeader.biSizeImage = width * height * 4;
216  // Create the pixmap DIB section
217  uint8_t* pixels = 0;
218  HBITMAP bitmap =
219  CreateDIBSection(display_dc, &bmi, DIB_RGB_COLORS, (void**)&pixels, 0, 0);
220  ReleaseDC(0, display_dc);
221  if (!bitmap || !pixels) {
222  return nullptr;
223  }
224  int bytes_per_line = width * 4;
225  for (int y = 0; y < height; ++y) {
226  memcpy(pixels + y * bytes_per_line, &buffer[bytes_per_line * y],
227  bytes_per_line);
228  }
229  HBITMAP mask;
230  GetMaskBitmaps(bitmap, mask);
231  ICONINFO icon_info;
232  icon_info.fIcon = 0;
233  icon_info.xHotspot = hot_x;
234  icon_info.yHotspot = hot_y;
235  icon_info.hbmMask = mask;
236  icon_info.hbmColor = bitmap;
237  cursor = CreateIconIndirect(&icon_info);
238  DeleteObject(mask);
239  DeleteObject(bitmap);
240  return cursor;
241 }
242 
243 void GetMaskBitmaps(HBITMAP bitmap, HBITMAP& mask_bitmap) {
244  HDC h_dc = ::GetDC(NULL);
245  HDC h_main_dc = ::CreateCompatibleDC(h_dc);
246  HDC h_and_mask_dc = ::CreateCompatibleDC(h_dc);
247 
248  // Get the dimensions of the source bitmap
249  BITMAP bm;
250  ::GetObject(bitmap, sizeof(BITMAP), &bm);
251  mask_bitmap = ::CreateCompatibleBitmap(h_dc, bm.bmWidth, bm.bmHeight);
252 
253  // Select the bitmaps to DC
254  HBITMAP h_old_main_bitmap = (HBITMAP)::SelectObject(h_main_dc, bitmap);
255  HBITMAP h_old_and_mask_bitmap =
256  (HBITMAP)::SelectObject(h_and_mask_dc, mask_bitmap);
257 
258  // Scan each pixel of the souce bitmap and create the masks
259  COLORREF main_bit_pixel;
260  for (int x = 0; x < bm.bmWidth; ++x) {
261  for (int y = 0; y < bm.bmHeight; ++y) {
262  main_bit_pixel = ::GetPixel(h_main_dc, x, y);
263  if (main_bit_pixel == RGB(0, 0, 0)) {
264  ::SetPixel(h_and_mask_dc, x, y, RGB(255, 255, 255));
265  } else {
266  ::SetPixel(h_and_mask_dc, x, y, RGB(0, 0, 0));
267  }
268  }
269  }
270  ::SelectObject(h_main_dc, h_old_main_bitmap);
271  ::SelectObject(h_and_mask_dc, h_old_and_mask_bitmap);
272 
273  ::DeleteDC(h_and_mask_dc);
274  ::DeleteDC(h_main_dc);
275 
276  ::ReleaseDC(NULL, h_dc);
277 }
278 
279 } // namespace flutter
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:199
flutter::FlutterWindowsEngine
Definition: flutter_windows_engine.h:78
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:243
flutter
Definition: accessibility_bridge_windows.cc:11
flutter::FlutterWindowsView::UpdateFlutterCursor
void UpdateFlutterCursor(const std::string &cursor_name)
Definition: flutter_windows_view.cc:128
flutter::FlutterWindowsView::SetFlutterCursor
void SetFlutterCursor(HCURSOR cursor)
Definition: flutter_windows_view.cc:132
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 >
flutter::FlutterWindowsEngine::view
FlutterWindowsView * view()
Definition: flutter_windows_engine.h:117
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