forked from LeenkxTeam/LNXRNT
Webview
This commit is contained in:
362
Sources/webview_linux.cpp
Normal file
362
Sources/webview_linux.cpp
Normal file
@ -0,0 +1,362 @@
|
||||
#ifdef WITH_WEBVIEW
|
||||
#ifdef KINC_LINUX
|
||||
|
||||
#include "webview.h"
|
||||
#include <kinc/log.h>
|
||||
#include <kinc/window.h>
|
||||
|
||||
#include <gtk/gtk.h>
|
||||
#include <webkit2/webkit2.h>
|
||||
|
||||
struct LinuxWebViewState {
|
||||
WebKitWebView* webView;
|
||||
GtkWidget* detachedWindow;
|
||||
bool isDetached;
|
||||
gulong closeHandlerId;
|
||||
gulong messageHandlerId;
|
||||
gulong loadHandlerId;
|
||||
int webviewId;
|
||||
};
|
||||
|
||||
static bool g_gtkInitialized = false;
|
||||
|
||||
static void ensureGtkInit() {
|
||||
if (g_gtkInitialized) return;
|
||||
if (!gtk_init_check(nullptr, nullptr)) {
|
||||
kinc_log(KINC_LOG_LEVEL_ERROR, "Failed to initialize webview for GTK");
|
||||
return;
|
||||
}
|
||||
g_gtkInitialized = true;
|
||||
}
|
||||
|
||||
static void onMessageReceived(WebKitUserContentManager* manager,
|
||||
WebKitJavascriptResult* result, gpointer userData) {
|
||||
auto* state = (LinuxWebViewState*)userData;
|
||||
JSCValue* value = webkit_javascript_result_get_js_value(result);
|
||||
if (jsc_value_is_string(value)) {
|
||||
char* str = jsc_value_to_string(value);
|
||||
WebViewManager::instance().dispatchMessage(state->webviewId, std::string(str));
|
||||
g_free(str);
|
||||
} else {
|
||||
char* str = jsc_value_to_string(value);
|
||||
WebViewManager::instance().dispatchMessage(state->webviewId, std::string(str));
|
||||
g_free(str);
|
||||
}
|
||||
}
|
||||
|
||||
static void onLoadChanged(WebKitWebView* webView, WebKitLoadEvent loadEvent,
|
||||
gpointer userData) {
|
||||
auto* state = (LinuxWebViewState*)userData;
|
||||
if (loadEvent == WEBKIT_LOAD_FINISHED) {
|
||||
WebViewManager::instance().dispatchLoad(state->webviewId);
|
||||
}
|
||||
}
|
||||
|
||||
static gboolean onDetachedClose(GtkWidget* widget, GdkEvent* event, gpointer userData) {
|
||||
auto* state = (LinuxWebViewState*)userData;
|
||||
WebViewManager::instance().dispatchClose(state->webviewId);
|
||||
gtk_widget_hide(widget);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
int platformCreate(WebViewInstance* wv) {
|
||||
ensureGtkInit();
|
||||
if (!g_gtkInitialized) {
|
||||
kinc_log(KINC_LOG_LEVEL_ERROR, "GTK WebView not initialized");
|
||||
return -1;
|
||||
}
|
||||
|
||||
auto* state = new LinuxWebViewState();
|
||||
state->webView = nullptr;
|
||||
state->detachedWindow = nullptr;
|
||||
state->isDetached = (wv->parentWindow < 0);
|
||||
state->webviewId = wv->id;
|
||||
wv->platformHandle = state;
|
||||
|
||||
state->webView = WEBKIT_WEB_VIEW(webkit_web_view_new());
|
||||
|
||||
if (wv->transparent) {
|
||||
GdkRGBA rgba = {0.0, 0.0, 0.0, 0.0};
|
||||
webkit_web_view_set_background_color(state->webView, &rgba);
|
||||
}
|
||||
|
||||
WebKitUserContentManager* contentManager =
|
||||
webkit_web_view_get_user_content_manager(state->webView);
|
||||
webkit_user_content_manager_register_script_message_handler(contentManager, "lnxrnt");
|
||||
state->messageHandlerId = g_signal_connect(contentManager, "script-message-received::lnxrnt",
|
||||
G_CALLBACK(onMessageReceived), state);
|
||||
|
||||
state->loadHandlerId = g_signal_connect(state->webView, "load-changed",
|
||||
G_CALLBACK(onLoadChanged), state);
|
||||
|
||||
if (state->isDetached) {
|
||||
state->detachedWindow = gtk_window_new(GTK_WINDOW_TOPLEVEL);
|
||||
gtk_window_set_title(GTK_WINDOW(state->detachedWindow), wv->title.c_str());
|
||||
gtk_window_set_default_size(GTK_WINDOW(state->detachedWindow), wv->width, wv->height);
|
||||
gtk_window_move(GTK_WINDOW(state->detachedWindow), wv->x, wv->y);
|
||||
|
||||
GtkWidget* scrolledWindow = gtk_scrolled_window_new(nullptr, nullptr);
|
||||
gtk_container_add(GTK_CONTAINER(scrolledWindow), GTK_WIDGET(state->webView));
|
||||
gtk_container_add(GTK_CONTAINER(state->detachedWindow), scrolledWindow);
|
||||
|
||||
state->closeHandlerId = g_signal_connect(state->detachedWindow, "delete-event",
|
||||
G_CALLBACK(onDetachedClose), state);
|
||||
} else {
|
||||
state->detachedWindow = gtk_window_new(GTK_WINDOW_POPUP);
|
||||
gtk_window_set_decorated(GTK_WINDOW(state->detachedWindow), FALSE);
|
||||
gtk_window_set_default_size(GTK_WINDOW(state->detachedWindow), wv->width, wv->height);
|
||||
|
||||
gtk_window_move(GTK_WINDOW(state->detachedWindow), wv->x, wv->y);
|
||||
|
||||
GtkWidget* scrolledWindow = gtk_scrolled_window_new(nullptr, nullptr);
|
||||
gtk_container_add(GTK_CONTAINER(scrolledWindow), GTK_WIDGET(state->webView));
|
||||
gtk_container_add(GTK_CONTAINER(state->detachedWindow), scrolledWindow);
|
||||
}
|
||||
|
||||
return wv->id;
|
||||
}
|
||||
|
||||
void platformDestroy(WebViewInstance* wv) {
|
||||
auto* state = (LinuxWebViewState*)wv->platformHandle;
|
||||
if (!state) return;
|
||||
|
||||
if (state->messageHandlerId) {
|
||||
WebKitUserContentManager* cm = webkit_web_view_get_user_content_manager(state->webView);
|
||||
g_signal_handler_disconnect(cm, state->messageHandlerId);
|
||||
}
|
||||
if (state->loadHandlerId) {
|
||||
g_signal_handler_disconnect(state->webView, state->loadHandlerId);
|
||||
}
|
||||
if (state->closeHandlerId && state->detachedWindow) {
|
||||
g_signal_handler_disconnect(state->detachedWindow, state->closeHandlerId);
|
||||
}
|
||||
if (state->detachedWindow) {
|
||||
gtk_widget_destroy(state->detachedWindow);
|
||||
}
|
||||
delete state;
|
||||
wv->platformHandle = nullptr;
|
||||
}
|
||||
|
||||
void platformLoadHTML(WebViewInstance* wv, const std::string& html) {
|
||||
auto* state = (LinuxWebViewState*)wv->platformHandle;
|
||||
if (!state || !state->webView) return;
|
||||
webkit_web_view_load_html(state->webView, html.c_str(), nullptr);
|
||||
}
|
||||
|
||||
void platformLoadURL(WebViewInstance* wv, const std::string& url) {
|
||||
auto* state = (LinuxWebViewState*)wv->platformHandle;
|
||||
if (!state || !state->webView) return;
|
||||
webkit_web_view_load_uri(state->webView, url.c_str());
|
||||
}
|
||||
|
||||
void platformEvalJS(WebViewInstance* wv, const std::string& js) {
|
||||
auto* state = (LinuxWebViewState*)wv->platformHandle;
|
||||
if (!state || !state->webView) return;
|
||||
webkit_web_view_evaluate_javascript(state->webView, js.c_str(), -1, nullptr, nullptr, nullptr, nullptr, nullptr);
|
||||
}
|
||||
|
||||
static void platformEvalJSAsyncCallback(GObject* source, GAsyncResult* res, gpointer user_data) {
|
||||
int webviewId = (int)(intptr_t)user_data;
|
||||
WebKitJavascriptResult* jsResult = webkit_web_view_evaluate_javascript_finish(WEBKIT_WEB_VIEW(source), res, nullptr);
|
||||
std::string resultStr = "null";
|
||||
if (jsResult) {
|
||||
JSCValue* value = webkit_javascript_result_get_js_value(jsResult);
|
||||
if (value && !jsc_value_is_undefined(value) && !jsc_value_is_null(value)) {
|
||||
JSCValue* jsonStr = jsc_value_to_string(value);
|
||||
gchar* str = (gchar*)jsc_value_to_string(jsonStr);
|
||||
if (str) {
|
||||
resultStr = std::string(str);
|
||||
g_free(str);
|
||||
}
|
||||
g_object_unref(jsonStr);
|
||||
}
|
||||
webkit_javascript_result_unref(jsResult);
|
||||
}
|
||||
WebViewManager::instance().dispatchEvalResult(webviewId, resultStr);
|
||||
}
|
||||
|
||||
void platformEvalJSAsync(WebViewInstance* wv, const std::string& js) {
|
||||
auto* state = (LinuxWebViewState*)wv->platformHandle;
|
||||
if (!state || !state->webView) return;
|
||||
webkit_web_view_evaluate_javascript(state->webView, js.c_str(), -1, nullptr, nullptr,
|
||||
(GAsyncReadyCallback)platformEvalJSAsyncCallback, (gpointer)(intptr_t)wv->id);
|
||||
}
|
||||
|
||||
void platformShow(WebViewInstance* wv) {
|
||||
auto* state = (LinuxWebViewState*)wv->platformHandle;
|
||||
if (!state) return;
|
||||
if (state->detachedWindow) {
|
||||
gtk_widget_show_all(state->detachedWindow);
|
||||
}
|
||||
}
|
||||
|
||||
void platformHide(WebViewInstance* wv) {
|
||||
auto* state = (LinuxWebViewState*)wv->platformHandle;
|
||||
if (!state) return;
|
||||
if (state->detachedWindow) {
|
||||
gtk_widget_hide(state->detachedWindow);
|
||||
}
|
||||
}
|
||||
|
||||
void platformResize(WebViewInstance* wv, int width, int height) {
|
||||
auto* state = (LinuxWebViewState*)wv->platformHandle;
|
||||
if (!state || !state->detachedWindow) return;
|
||||
gtk_window_resize(GTK_WINDOW(state->detachedWindow), width, height);
|
||||
}
|
||||
|
||||
void platformMove(WebViewInstance* wv, int x, int y) {
|
||||
auto* state = (LinuxWebViewState*)wv->platformHandle;
|
||||
if (!state || !state->detachedWindow) return;
|
||||
gtk_window_move(GTK_WINDOW(state->detachedWindow), x, y);
|
||||
}
|
||||
|
||||
void platformSetTransparent(WebViewInstance* wv, bool transparent) {
|
||||
auto* state = (LinuxWebViewState*)wv->platformHandle;
|
||||
if (!state || !state->webView) return;
|
||||
if (transparent) {
|
||||
GdkRGBA rgba = {0.0, 0.0, 0.0, 0.0};
|
||||
webkit_web_view_set_background_color(state->webView, &rgba);
|
||||
} else {
|
||||
GdkRGBA rgba = {1.0, 1.0, 1.0, 1.0};
|
||||
webkit_web_view_set_background_color(state->webView, &rgba);
|
||||
}
|
||||
}
|
||||
|
||||
void platformSetClickThrough(WebViewInstance* wv, bool enabled) {
|
||||
auto* state = (LinuxWebViewState*)wv->platformHandle;
|
||||
if (!state || !state->webView) return;
|
||||
const char* js = enabled
|
||||
? "document.body.style.pointerEvents = 'none';"
|
||||
: "document.body.style.pointerEvents = 'auto';";
|
||||
webkit_web_view_evaluate_javascript(state->webView, js, -1, nullptr, nullptr, nullptr, nullptr, nullptr);
|
||||
}
|
||||
|
||||
void platformSetTitle(WebViewInstance* wv, const std::string& title) {
|
||||
auto* state = (LinuxWebViewState*)wv->platformHandle;
|
||||
if (!state || !state->isDetached || !state->detachedWindow) return;
|
||||
gtk_window_set_title(GTK_WINDOW(state->detachedWindow), title.c_str());
|
||||
}
|
||||
|
||||
void platformSend(WebViewInstance* wv, const std::string& message) {
|
||||
auto* state = (LinuxWebViewState*)wv->platformHandle;
|
||||
if (!state || !state->webView) return;
|
||||
std::string escaped;
|
||||
for (size_t i = 0; i < message.size(); i++) {
|
||||
char c = message[i];
|
||||
switch (c) {
|
||||
case '\\': escaped += "\\\\"; break;
|
||||
case '\'': escaped += "\\'"; break;
|
||||
case '\n': escaped += "\\n"; break;
|
||||
case '\r': escaped += "\\r"; break;
|
||||
default: escaped += c; break;
|
||||
}
|
||||
}
|
||||
std::string js = "if(window.__lnxrntOnMessage)window.__lnxrntOnMessage('" + escaped + "');";
|
||||
webkit_web_view_evaluate_javascript(state->webView, js.c_str(), -1, nullptr, nullptr, nullptr, nullptr, nullptr);
|
||||
}
|
||||
|
||||
void platformGoBack(WebViewInstance* wv) {
|
||||
auto* state = (LinuxWebViewState*)wv->platformHandle;
|
||||
if (!state || !state->webView) return;
|
||||
webkit_web_view_go_back(state->webView);
|
||||
}
|
||||
|
||||
void platformGoForward(WebViewInstance* wv) {
|
||||
auto* state = (LinuxWebViewState*)wv->platformHandle;
|
||||
if (!state || !state->webView) return;
|
||||
webkit_web_view_go_forward(state->webView);
|
||||
}
|
||||
|
||||
void platformReload(WebViewInstance* wv) {
|
||||
auto* state = (LinuxWebViewState*)wv->platformHandle;
|
||||
if (!state || !state->webView) return;
|
||||
webkit_web_view_reload(state->webView);
|
||||
}
|
||||
|
||||
bool platformCanGoBack(WebViewInstance* wv) {
|
||||
auto* state = (LinuxWebViewState*)wv->platformHandle;
|
||||
if (!state || !state->webView) return false;
|
||||
return webkit_web_view_can_go_back(state->webView);
|
||||
}
|
||||
|
||||
bool platformCanGoForward(WebViewInstance* wv) {
|
||||
auto* state = (LinuxWebViewState*)wv->platformHandle;
|
||||
if (!state || !state->webView) return false;
|
||||
return webkit_web_view_can_go_forward(state->webView);
|
||||
}
|
||||
|
||||
std::string platformGetURL(WebViewInstance* wv) {
|
||||
auto* state = (LinuxWebViewState*)wv->platformHandle;
|
||||
if (!state || !state->webView) return "";
|
||||
const gchar* uri = webkit_web_view_get_uri(state->webView);
|
||||
return uri ? std::string(uri) : "";
|
||||
}
|
||||
|
||||
std::string platformGetTitle(WebViewInstance* wv) {
|
||||
auto* state = (LinuxWebViewState*)wv->platformHandle;
|
||||
if (!state || !state->webView) return "";
|
||||
const gchar* title = webkit_web_view_get_title(state->webView);
|
||||
return title ? std::string(title) : "";
|
||||
}
|
||||
|
||||
void platformMinimize(WebViewInstance* wv) {
|
||||
auto* state = (LinuxWebViewState*)wv->platformHandle;
|
||||
if (!state || !state->isDetached || !state->detachedWindow) return;
|
||||
gtk_window_iconify(GTK_WINDOW(state->detachedWindow));
|
||||
}
|
||||
|
||||
void platformMaximize(WebViewInstance* wv) {
|
||||
auto* state = (LinuxWebViewState*)wv->platformHandle;
|
||||
if (!state || !state->isDetached || !state->detachedWindow) return;
|
||||
gtk_window_maximize(GTK_WINDOW(state->detachedWindow));
|
||||
}
|
||||
|
||||
void platformRestore(WebViewInstance* wv) {
|
||||
auto* state = (LinuxWebViewState*)wv->platformHandle;
|
||||
if (!state || !state->isDetached || !state->detachedWindow) return;
|
||||
gtk_window_unmaximize(GTK_WINDOW(state->detachedWindow));
|
||||
gtk_window_deiconify(GTK_WINDOW(state->detachedWindow));
|
||||
}
|
||||
|
||||
void platformSetFullscreen(WebViewInstance* wv, bool fullscreen) {
|
||||
auto* state = (LinuxWebViewState*)wv->platformHandle;
|
||||
if (!state || !state->isDetached || !state->detachedWindow) return;
|
||||
if (fullscreen) {
|
||||
gtk_window_fullscreen(GTK_WINDOW(state->detachedWindow));
|
||||
} else {
|
||||
gtk_window_unfullscreen(GTK_WINDOW(state->detachedWindow));
|
||||
}
|
||||
}
|
||||
|
||||
bool platformIsFullscreen(WebViewInstance* wv) {
|
||||
auto* state = (LinuxWebViewState*)wv->platformHandle;
|
||||
if (!state || !state->isDetached || !state->detachedWindow) return false;
|
||||
GdkWindow* gdkWin = gtk_widget_get_window(state->detachedWindow);
|
||||
if (!gdkWin) return false;
|
||||
GdkWindowState state2 = gdk_window_get_state(gdkWin);
|
||||
return (state2 & GDK_WINDOW_STATE_FULLSCREEN) != 0;
|
||||
}
|
||||
|
||||
void platformEnableDevTools(WebViewInstance* wv, bool enabled) {
|
||||
auto* state = (LinuxWebViewState*)wv->platformHandle;
|
||||
if (!state || !state->webView) return;
|
||||
WebKitSettings* settings = webkit_web_view_get_settings(state->webView);
|
||||
webkit_settings_set_enable_developer_extras(settings, enabled);
|
||||
}
|
||||
|
||||
void platformSetContextMenuEnabled(WebViewInstance* wv, bool enabled) {
|
||||
auto* state = (LinuxWebViewState*)wv->platformHandle;
|
||||
if (!state || !state->webView) return;
|
||||
WebKitSettings* settings = webkit_web_view_get_settings(state->webView);
|
||||
webkit_settings_set_enable_context_menu(settings, enabled);
|
||||
}
|
||||
|
||||
void platformTick() {
|
||||
if (!g_gtkInitialized) return;
|
||||
while (gtk_events_pending()) {
|
||||
gtk_main_iteration_do(FALSE);
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
#endif
|
||||
Reference in New Issue
Block a user