#include "webview_cef_plugin.h" // This must be included before many other Windows headers. #include // For getPlatformVersion; remove unless needed for your plugin implementation. #include #include #include #include #include #include #include #include #include "webview_app.h" namespace webview_cef { bool init = false; int64_t texture_id; flutter::TextureRegistrar* texture_registrar; std::shared_ptr pixel_buffer; std::unique_ptr backing_pixel_buffer; std::mutex buffer_mutex_; std::unique_ptr m_texture = std::make_unique(flutter::PixelBufferTexture([](size_t width, size_t height) -> const FlutterDesktopPixelBuffer* { buffer_mutex_.lock(); auto buffer = pixel_buffer.get(); // Only lock the mutex if the buffer is not null // (to ensure the release callback gets called) if (!buffer) { buffer_mutex_.unlock(); } return buffer; })); CefRefPtr handler(new WebviewHandler()); CefRefPtr app(new WebviewApp(handler)); CefMainArgs mainArgs; std::unique_ptr< flutter::MethodChannel, std::default_delete>> channel = nullptr; void SwapBufferFromBgraToRgba(void* _dest, const void* _src, int width, int height) { int32_t* dest = (int32_t*)_dest; int32_t* src = (int32_t*)_src; int32_t rgba; int32_t bgra; int length = width * height; for (int i = 0; i < length; i++) { bgra = src[i]; // BGRA in hex = 0xAARRGGBB. rgba = (bgra & 0x00ff0000) >> 16 // Red >> Blue. | (bgra & 0xff00ff00) // Green Alpha. | (bgra & 0x000000ff) << 16; // Blue >> Red. dest[i] = rgba; } } void startCEF() { CefWindowInfo window_info; CefBrowserSettings settings; window_info.SetAsWindowless(nullptr); handler.get()->onPaintCallback = [](const void* buffer, int32_t width, int32_t height) { const std::lock_guard lock(buffer_mutex_); if (!pixel_buffer.get() || pixel_buffer.get()->width != width || pixel_buffer.get()->height != height) { if (!pixel_buffer.get()) { pixel_buffer = std::make_unique(); pixel_buffer->release_context = &buffer_mutex_; // Gets invoked after the FlutterDesktopPixelBuffer's // backing buffer has been uploaded. pixel_buffer->release_callback = [](void* opaque) { auto mutex = reinterpret_cast(opaque); // Gets locked just before |CopyPixelBuffer| returns. mutex->unlock(); }; } pixel_buffer->width = width; pixel_buffer->height = height; const auto size = width * height * 4; backing_pixel_buffer.reset(new uint8_t[size]); pixel_buffer->buffer = backing_pixel_buffer.get(); } SwapBufferFromBgraToRgba((void*)pixel_buffer->buffer, buffer, width, height); texture_registrar->MarkTextureFrameAvailable(texture_id); }; handler.get()->onUrlChangedCb = [](std::string url) { channel->InvokeMethod("urlChanged", std::make_unique(url)); }; handler.get()->onTitleChangedCb = [](std::string title) { channel->InvokeMethod("titleChanged", std::make_unique(title)); }; CefSettings cefs; cefs.windowless_rendering_enabled = true; CefInitialize(mainArgs, cefs, app.get(), nullptr); CefRunMessageLoop(); CefShutdown(); } template std::optional GetOptionalValue(const flutter::EncodableMap& map, const std::string& key) { const auto it = map.find(flutter::EncodableValue(key)); if (it != map.end()) { const auto val = std::get_if(&it->second); if (val) { return *val; } } return std::nullopt; } static const std::optional> GetPointFromArgs( const flutter::EncodableValue* args) { const flutter::EncodableList* list = std::get_if(args); if (!list || list->size() != 2) { return std::nullopt; } const auto x = std::get_if(&(*list)[0]); const auto y = std::get_if(&(*list)[1]); if (!x || !y) { return std::nullopt; } return std::make_pair(*x, *y); } // static void WebviewCefPlugin::RegisterWithRegistrar( flutter::PluginRegistrarWindows* registrar) { texture_registrar = registrar->texture_registrar(); channel = std::make_unique>( registrar->messenger(), "webview_cef", &flutter::StandardMethodCodec::GetInstance()); auto plugin = std::make_unique(); channel->SetMethodCallHandler( [plugin_pointer = plugin.get()](const auto& call, auto result) { plugin_pointer->HandleMethodCall(call, std::move(result)); }); registrar->AddPlugin(std::move(plugin)); } void WebviewCefPlugin::sendKeyEvent(CefKeyEvent ev) { handler.get()->sendKeyEvent(ev); } WebviewCefPlugin::WebviewCefPlugin() {} WebviewCefPlugin::~WebviewCefPlugin() {} void WebviewCefPlugin::HandleMethodCall( const flutter::MethodCall& method_call, std::unique_ptr> result) { if (method_call.method_name().compare("init") == 0) { if (!init) { texture_id = texture_registrar->RegisterTexture(m_texture.get()); new std::thread(startCEF); init = true; } result->Success(flutter::EncodableValue(texture_id)); } else if (method_call.method_name().compare("loadUrl") == 0) { if (const auto url = std::get_if(method_call.arguments())) { handler.get()->loadUrl(*url); return result->Success(); } } else if (method_call.method_name().compare("setSize") == 0) { const flutter::EncodableList* list = std::get_if(method_call.arguments()); const auto dpi = *std::get_if(&(*list)[0]); const auto width = *std::get_if(&(*list)[1]); const auto height = *std::get_if(&(*list)[2]); handler.get()->changeSize((float)dpi, (int)std::round(width), (int)std::round(height)); result->Success(); } else if (method_call.method_name().compare("cursorClickDown") == 0) { const auto point = GetPointFromArgs(method_call.arguments()); handler.get()->cursorClick(point->first, point->second, false); result->Success(); } else if (method_call.method_name().compare("cursorClickUp") == 0) { const auto point = GetPointFromArgs(method_call.arguments()); handler.get()->cursorClick(point->first, point->second, true); result->Success(); } else if (method_call.method_name().compare("cursorMove") == 0) { const auto point = GetPointFromArgs(method_call.arguments()); handler.get()->cursorMove(point->first, point->second, false); result->Success(); } else if (method_call.method_name().compare("cursorDragging") == 0) { const auto point = GetPointFromArgs(method_call.arguments()); handler.get()->cursorMove(point->first, point->second, true); result->Success(); } else if (method_call.method_name().compare("setScrollDelta") == 0) { const flutter::EncodableList* list = std::get_if(method_call.arguments()); const auto x = *std::get_if(&(*list)[0]); const auto y = *std::get_if(&(*list)[1]); const auto deltaX = *std::get_if(&(*list)[2]); const auto deltaY = *std::get_if(&(*list)[3]); handler.get()->sendScrollEvent(x, y, deltaX, deltaY); result->Success(); } else if (method_call.method_name().compare("goForward") == 0) { handler.get()->goForward(); result->Success(); } else if (method_call.method_name().compare("goBack") == 0) { handler.get()->goBack(); result->Success(); } else if (method_call.method_name().compare("reload") == 0) { handler.get()->reload(); result->Success(); } else if (method_call.method_name().compare("openDevTools") == 0) { handler.get()->openDevTools(); result->Success(); } else { result->NotImplemented(); } } } // namespace webview_cef