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 
11 
12 static constexpr char kChannelName[] = "flutter/mousecursor";
13 
14 static constexpr char kActivateSystemCursorMethod[] = "activateSystemCursor";
15 static constexpr char kKindKey[] = "kind";
16 
17 // This method allows creating a custom cursor with rawBGRA buffer, returns a
18 // string to identify the cursor.
19 static constexpr char kCreateCustomCursorMethod[] =
20  "createCustomCursor/windows";
21 // A string, the custom cursor's name.
22 static constexpr char kCustomCursorNameKey[] = "name";
23 // A list of bytes, the custom cursor's rawBGRA buffer.
24 static constexpr char kCustomCursorBufferKey[] = "buffer";
25 // A double, the x coordinate of the custom cursor's hotspot, starting from
26 // left.
27 static constexpr char kCustomCursorHotXKey[] = "hotX";
28 // A double, the y coordinate of the custom cursor's hotspot, starting from top.
29 static constexpr char kCustomCursorHotYKey[] = "hotY";
30 // An int value for the width of the custom cursor.
31 static constexpr char kCustomCursorWidthKey[] = "width";
32 // An int value for the height of the custom cursor.
33 static constexpr char kCustomCursorHeightKey[] = "height";
34 
35 // This method also has an argument `kCustomCursorNameKey` for the name
36 // of the cursor to activate.
37 static constexpr char kSetCustomCursorMethod[] = "setCustomCursor/windows";
38 
39 // This method also has an argument `kCustomCursorNameKey` for the name
40 // of the cursor to delete.
41 static constexpr char kDeleteCustomCursorMethod[] =
42  "deleteCustomCursor/windows";
43 
44 // Error codes used for responses.
45 static constexpr char kCursorError[] = "Cursor error";
46 
47 namespace flutter {
48 
50  FlutterWindowsEngine* engine)
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 
63 void CursorHandler::HandleMethodCall(
64  const MethodCall<EncodableValue>& method_call,
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 
186 HCURSOR 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 
230 void 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
CursorHandler(flutter::BinaryMessenger *messenger, flutter::FlutterWindowsEngine *engine)
void UpdateFlutterCursor(const std::string &cursor_name) const
void SetFlutterCursor(HCURSOR cursor) const
const std::string & method_name() const
Definition: method_call.h:31
const T * arguments() const
Definition: method_call.h:34
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[]
void GetMaskBitmaps(HBITMAP bitmap, HBITMAP &mask_bitmap)
HCURSOR GetCursorFromBuffer(const std::vector< uint8_t > &buffer, double hot_x, double hot_y, int width, int height)