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