#ifdef WITH_WEBVIEW #ifdef KINC_LINUX #include "webview.h" #include #include #include #include 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