Files
webview_cef/windows/webview_cef_plugin.cpp
2023-05-06 10:49:25 +08:00

242 lines
8.0 KiB
C++

#include "webview_cef_plugin.h"
// This must be included before many other Windows headers.
#include <windows.h>
// For getPlatformVersion; remove unless needed for your plugin implementation.
#include <VersionHelpers.h>
#include <flutter/method_channel.h>
#include <flutter/plugin_registrar_windows.h>
#include <flutter/standard_method_codec.h>
#include <memory>
#include <thread>
#include<iostream>
#include <mutex>
#include "webview_app.h"
namespace webview_cef {
bool init = false;
int64_t texture_id;
flutter::TextureRegistrar* texture_registrar;
std::shared_ptr<FlutterDesktopPixelBuffer> pixel_buffer;
std::unique_ptr<uint8_t> backing_pixel_buffer;
std::mutex buffer_mutex_;
std::unique_ptr<flutter::TextureVariant> m_texture = std::make_unique<flutter::TextureVariant>(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<WebviewHandler> handler(new WebviewHandler());
CefRefPtr<WebviewApp> app(new WebviewApp(handler));
CefMainArgs mainArgs;
std::unique_ptr<
flutter::MethodChannel<flutter::EncodableValue>,
std::default_delete<flutter::MethodChannel<flutter::EncodableValue>>>
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<std::mutex> 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<FlutterDesktopPixelBuffer>();
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<std::mutex*>(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<flutter::EncodableValue>(url));
};
handler.get()->onTitleChangedCb = [](std::string title) {
channel->InvokeMethod("titleChanged", std::make_unique<flutter::EncodableValue>(title));
};
CefSettings cefs;
cefs.windowless_rendering_enabled = true;
CefInitialize(mainArgs, cefs, app.get(), nullptr);
CefRunMessageLoop();
CefShutdown();
}
template <typename T>
std::optional<T> 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<T>(&it->second);
if (val) {
return *val;
}
}
return std::nullopt;
}
static const std::optional<std::pair<int, int>> GetPointFromArgs(
const flutter::EncodableValue* args) {
const flutter::EncodableList* list =
std::get_if<flutter::EncodableList>(args);
if (!list || list->size() != 2) {
return std::nullopt;
}
const auto x = std::get_if<int>(&(*list)[0]);
const auto y = std::get_if<int>(&(*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<flutter::MethodChannel<flutter::EncodableValue>>(
registrar->messenger(), "webview_cef",
&flutter::StandardMethodCodec::GetInstance());
auto plugin = std::make_unique<WebviewCefPlugin>();
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<flutter::EncodableValue>& method_call,
std::unique_ptr<flutter::MethodResult<flutter::EncodableValue>> 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<std::string>(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<flutter::EncodableList>(method_call.arguments());
const auto dpi = *std::get_if<double>(&(*list)[0]);
const auto width = *std::get_if<double>(&(*list)[1]);
const auto height = *std::get_if<double>(&(*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<flutter::EncodableList>(method_call.arguments());
const auto x = *std::get_if<int>(&(*list)[0]);
const auto y = *std::get_if<int>(&(*list)[1]);
const auto deltaX = *std::get_if<int>(&(*list)[2]);
const auto deltaY = *std::get_if<int>(&(*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