From dd41a63f0d39778dacf60edefdb5d73544486cf9 Mon Sep 17 00:00:00 2001 From: falkTX Date: Wed, 8 May 2024 00:22:06 +0200 Subject: [PATCH] js-to-cpp and vice-versa on gtk3, IPC still needed Signed-off-by: falkTX --- Makefile.plugins.mk | 3 +- dgl/Web.hpp | 4 + dgl/src/Web.cpp | 19 ++- distrho/extra/WebViewImpl.cpp | 141 ++++++++++++++++++++-- distrho/extra/WebViewImpl.hpp | 5 + distrho/src/DistrhoUI.cpp | 11 +- examples/WebMeters/ExampleUIWebMeters.cpp | 2 + examples/WebMeters/Makefile | 5 + examples/WebMeters/index.html | 25 ++++ 9 files changed, 203 insertions(+), 12 deletions(-) create mode 100644 examples/WebMeters/index.html diff --git a/Makefile.plugins.mk b/Makefile.plugins.mk index f563d4d7..151d2f64 100644 --- a/Makefile.plugins.mk +++ b/Makefile.plugins.mk @@ -247,6 +247,7 @@ DGL_LIBS += -lole32 -luuid endif DGL_LIB = $(DGL_BUILD_DIR)/libdgl-web.a HAVE_DGL = true +USE_WEBVIEW = true endif ifeq ($(UI_TYPE),external) @@ -263,7 +264,7 @@ HAVE_DGL = false endif endif -ifeq ($(HAVE_DGL)$(LINUX)$(USING_WEBVIEW),truetruetrue) +ifeq ($(HAVE_DGL)$(LINUX)$(USE_WEBVIEW),truetruetrue) DGL_LIB_SHARED = $(shell $(CC) -print-file-name=Scrt1.o) endif diff --git a/dgl/Web.hpp b/dgl/Web.hpp index 0a30f858..b366bf70 100644 --- a/dgl/Web.hpp +++ b/dgl/Web.hpp @@ -48,6 +48,10 @@ public: */ ~WebViewWidget() override; + // webview methods + void evaluateJS(const char* js); + void reload(); + protected: void onResize(const ResizeEvent& ev) override; diff --git a/dgl/src/Web.cpp b/dgl/src/Web.cpp index df8662ef..d7a0dc38 100644 --- a/dgl/src/Web.cpp +++ b/dgl/src/Web.cpp @@ -18,6 +18,7 @@ #include "../Color.hpp" #include "../distrho/extra/WebView.hpp" +#include "../distrho/extra/Sleep.hpp" #include "SubWidgetPrivateData.hpp" #include "TopLevelWidgetPrivateData.hpp" @@ -32,7 +33,11 @@ WebViewWidget::WebViewWidget(Window& windowToMapTo) webview(webViewCreate(windowToMapTo.getNativeWindowHandle(), windowToMapTo.getWidth(), windowToMapTo.getHeight(), - windowToMapTo.getScaleFactor())) {} + windowToMapTo.getScaleFactor())) +{ + // FIXME wait till process is here? + d_msleep(200); +} WebViewWidget::~WebViewWidget() { @@ -40,6 +45,18 @@ WebViewWidget::~WebViewWidget() webViewDestroy(webview); } +void WebViewWidget::evaluateJS(const char* const js) +{ + if (webview != nullptr) + webViewEvaluateJS(webview, js); +} + +void WebViewWidget::reload() +{ + if (webview != nullptr) + webViewReload(webview); +} + void WebViewWidget::onResize(const ResizeEvent& ev) { TopLevelWidget::onResize(ev); diff --git a/distrho/extra/WebViewImpl.cpp b/distrho/extra/WebViewImpl.cpp index fc472c0b..f2a42c5b 100644 --- a/distrho/extra/WebViewImpl.cpp +++ b/distrho/extra/WebViewImpl.cpp @@ -21,6 +21,10 @@ # error bad usage #endif +// #include +// #include +// #include + #define WEB_VIEW_USING_CHOC 0 #ifndef WEB_VIEW_USING_CHOC @@ -453,6 +457,19 @@ void webViewDestroy(const WebViewHandle handle) (void)handle; } +void webViewEvaluateJS(const WebViewHandle handle, const char* const js) +{ + #if WEB_VIEW_USING_CHOC + #elif WEB_VIEW_USING_MACOS_WEBKIT + #elif WEB_VIEW_USING_X11_IPC + handle->p.signal(SIGUSR2); + #endif + + // maybe unused + (void)handle; + (void)js; +} + void webViewReload(const WebViewHandle handle) { #if WEB_VIEW_USING_CHOC @@ -513,7 +530,9 @@ void webViewResize(const WebViewHandle handle, const uint width, const uint heig // ----------------------------------------------------------------------------------------------------------- +static std::function evaluateFn; static std::function reloadFn; +static std::function terminateFn; // ----------------------------------------------------------------------------------------------------------- @@ -521,9 +540,14 @@ struct GtkContainer; struct GtkPlug; struct GtkWidget; struct GtkWindow; +struct JSCValue; +struct WebKitJavascriptResult; struct WebKitSettings; +struct WebKitUserContentManager; struct WebKitWebView; +typedef int gboolean; +#define G_CALLBACK(p) reinterpret_cast(p) #define GTK_CONTAINER(p) reinterpret_cast(p) #define GTK_PLUG(p) reinterpret_cast(p) #define GTK_WINDOW(p) reinterpret_cast(p) @@ -554,6 +578,30 @@ struct WebKitWebView; // ----------------------------------------------------------------------------------------------------------- // gtk3 variant +static int gtk_js_cb(WebKitUserContentManager*, WebKitJavascriptResult* const result, void* const arg) +{ + void* const lib = static_cast(arg); + DISTRHO_SAFE_ASSERT_RETURN(lib != nullptr, false); + + using g_free_t = void (*)(void*); + using jsc_value_to_string_t = char* (*)(JSCValue*); + using webkit_javascript_result_get_js_value_t = JSCValue* (*)(WebKitJavascriptResult*); + + CSYM(g_free_t, g_free) + CSYM(jsc_value_to_string_t, jsc_value_to_string) + CSYM(webkit_javascript_result_get_js_value_t, webkit_javascript_result_get_js_value) + + JSCValue* const value = webkit_javascript_result_get_js_value(result); + DISTRHO_SAFE_ASSERT_RETURN(value != nullptr, false); + + char* const string = jsc_value_to_string(value); + DISTRHO_SAFE_ASSERT_RETURN(string != nullptr, false); + + d_stdout("js call received with data '%s'", string); + g_free(string); + return 0; +} + static bool gtk3(Display* const display, const Window winId, const uint x, @@ -568,35 +616,56 @@ static bool gtk3(Display* const display, (lib = dlopen("libwebkit2gtk-4.0.so", RTLD_NOW|RTLD_GLOBAL)) == nullptr) return false; + using g_signal_connect_data_t = ulong (*)(void*, const char*, void*, void*, void*, int); using gdk_set_allowed_backends_t = void (*)(const char*); using gtk_container_add_t = void (*)(GtkContainer*, GtkWidget*); - using gtk_init_check_t = bool (*)(int*, char***); + using gtk_init_check_t = gboolean (*)(int*, char***); using gtk_main_t = void (*)(); + using gtk_main_quit_t = void (*)(); using gtk_plug_get_id_t = Window (*)(GtkPlug*); using gtk_plug_new_t = GtkWidget* (*)(Window); using gtk_widget_show_all_t = void (*)(GtkWidget*); using gtk_window_move_t = void (*)(GtkWindow*, int, int); using gtk_window_set_default_size_t = void (*)(GtkWindow*, int, int); using webkit_settings_new_t = WebKitSettings* (*)(); + using webkit_settings_set_enable_developer_extras_t = void (*)(WebKitSettings*, gboolean); + using webkit_settings_set_enable_write_console_messages_to_stdout_t = void (*)(WebKitSettings*, gboolean); using webkit_settings_set_hardware_acceleration_policy_t = void (*)(WebKitSettings*, int); - using webkit_settings_set_javascript_can_access_clipboard_t = void (*)(WebKitSettings*, bool); + using webkit_settings_set_javascript_can_access_clipboard_t = void (*)(WebKitSettings*, gboolean); + using webkit_user_content_manager_register_script_message_handler_t = gboolean (*)(WebKitUserContentManager*, const char*); + using webkit_web_view_evaluate_javascript_t = void* (*)(WebKitWebView*, const char*, ssize_t, const char*, const char*, void*, void*, void*); + using webkit_web_view_get_user_content_manager_t = WebKitUserContentManager* (*)(WebKitWebView*); using webkit_web_view_load_uri_t = void (*)(WebKitWebView*, const char*); using webkit_web_view_new_with_settings_t = GtkWidget* (*)(WebKitSettings*); + using webkit_web_view_run_javascript_t = void* (*)(WebKitWebView*, const char*, void*, void*, void*); + using webkit_web_view_set_background_color_t = void (*)(WebKitWebView*, const double*); + CSYM(g_signal_connect_data_t, g_signal_connect_data) CSYM(gdk_set_allowed_backends_t, gdk_set_allowed_backends) CSYM(gtk_container_add_t, gtk_container_add) CSYM(gtk_init_check_t, gtk_init_check) CSYM(gtk_main_t, gtk_main) + CSYM(gtk_main_quit_t, gtk_main_quit) CSYM(gtk_plug_get_id_t, gtk_plug_get_id) CSYM(gtk_plug_new_t, gtk_plug_new) CSYM(gtk_widget_show_all_t, gtk_widget_show_all) CSYM(gtk_window_move_t, gtk_window_move) CSYM(gtk_window_set_default_size_t, gtk_window_set_default_size) CSYM(webkit_settings_new_t, webkit_settings_new) + CSYM(webkit_settings_set_enable_developer_extras_t, webkit_settings_set_enable_developer_extras) + CSYM(webkit_settings_set_enable_write_console_messages_to_stdout_t, webkit_settings_set_enable_write_console_messages_to_stdout) CSYM(webkit_settings_set_hardware_acceleration_policy_t, webkit_settings_set_hardware_acceleration_policy) CSYM(webkit_settings_set_javascript_can_access_clipboard_t, webkit_settings_set_javascript_can_access_clipboard) + CSYM(webkit_user_content_manager_register_script_message_handler_t, webkit_user_content_manager_register_script_message_handler) + CSYM(webkit_web_view_get_user_content_manager_t, webkit_web_view_get_user_content_manager) CSYM(webkit_web_view_load_uri_t, webkit_web_view_load_uri) CSYM(webkit_web_view_new_with_settings_t, webkit_web_view_new_with_settings) + CSYM(webkit_web_view_set_background_color_t, webkit_web_view_set_background_color) + + // special case for legacy API handling + webkit_web_view_evaluate_javascript_t webkit_web_view_evaluate_javascript = reinterpret_cast(dlsym(nullptr, "webkit_web_view_evaluate_javascript")); + webkit_web_view_run_javascript_t webkit_web_view_run_javascript = reinterpret_cast(dlsym(nullptr, "webkit_web_view_run_javascript")); + DISTRHO_SAFE_ASSERT_RETURN(webkit_web_view_evaluate_javascript != nullptr || webkit_web_view_run_javascript != nullptr, false); const int gdkScale = std::fmod(scaleFactor, 1.0) >= 0.75 ? static_cast(scaleFactor + 0.5) @@ -639,10 +708,25 @@ static bool gtk3(Display* const display, webkit_settings_set_javascript_can_access_clipboard(settings, true); webkit_settings_set_hardware_acceleration_policy(settings, 2 /* WEBKIT_HARDWARE_ACCELERATION_POLICY_NEVER */); + // if (debug) + { + webkit_settings_set_enable_developer_extras(settings, true); + webkit_settings_set_enable_write_console_messages_to_stdout(settings, true); + } + GtkWidget* const webview = webkit_web_view_new_with_settings(settings); DISTRHO_SAFE_ASSERT_RETURN(webview != nullptr, false); - webkit_web_view_load_uri(WEBKIT_WEB_VIEW (webview), url); + const double color[] = {49.0/255, 54.0/255, 59.0/255, 1}; + webkit_web_view_set_background_color(WEBKIT_WEB_VIEW(webview), color); + + if (WebKitUserContentManager* const manager = webkit_web_view_get_user_content_manager(WEBKIT_WEB_VIEW(webview))) + { + g_signal_connect_data(manager, "script-message-received::external", G_CALLBACK(gtk_js_cb), lib, nullptr, 0); + webkit_user_content_manager_register_script_message_handler(manager, "external"); + } + + webkit_web_view_load_uri(WEBKIT_WEB_VIEW(webview), url); gtk_container_add(GTK_CONTAINER(window), webview); @@ -652,11 +736,30 @@ static bool gtk3(Display* const display, XMapWindow(display, wid); XFlush(display); + evaluateFn = [=](const char* const js){ + if (webkit_web_view_evaluate_javascript != nullptr) + webkit_web_view_evaluate_javascript(WEBKIT_WEB_VIEW(webview), js, -1, + nullptr, nullptr, nullptr, nullptr, nullptr); + else + webkit_web_view_run_javascript(WEBKIT_WEB_VIEW(webview), js, nullptr, nullptr, nullptr); + }; + reloadFn = [=](){ - webkit_web_view_load_uri(WEBKIT_WEB_VIEW (webview), url); + webkit_web_view_load_uri(WEBKIT_WEB_VIEW(webview), url); + }; + + terminateFn = [=](){ + d_stdout("terminateFn"); + static bool quit = true; + if (quit) + { + quit = false; + gtk_main_quit(); + } }; gtk_main(); + d_stdout("quit"); dlclose(lib); return true; @@ -757,6 +860,10 @@ static bool qt5webengine(const Window winId, const double scaleFactor, const cha QWebEngineView_setUrl(webview, *qurl); }; + terminateFn = [=](){ + // TODO + }; + QApplication_exec(); dlclose(lib); @@ -857,6 +964,10 @@ static bool qt6webengine(const Window winId, const double scaleFactor, const cha QWebEngineView_setUrl(webview, *qurl); }; + terminateFn = [=](){ + // TODO + }; + QApplication_exec(); dlclose(lib); @@ -869,9 +980,19 @@ static bool qt6webengine(const Window winId, const double scaleFactor, const cha static void signalHandler(const int sig) { - DISTRHO_SAFE_ASSERT_RETURN(sig == SIGUSR1,); - - reloadFn(); + switch (sig) + { + case SIGINT: + case SIGTERM: + terminateFn(); + break; + case SIGUSR1: + reloadFn(); + break; + case SIGUSR2: + evaluateFn("typeof(parameterChanged) === 'function' && parameterChanged(0, 0);"); + break; + } } int dpf_webview_start(int /* argc */, char** /* argv[] */) @@ -893,14 +1014,16 @@ int dpf_webview_start(int /* argc */, char** /* argv[] */) Display* const display = XOpenDisplay(nullptr); DISTRHO_SAFE_ASSERT_RETURN(display != nullptr, 1); -// const char* url = "file:///home/falktx/"; - const char* url = "https://mastodon.falktx.com/"; + const char* url = "file:///home/falktx/Source/DISTRHO/DPF/examples/WebMeters/index.html"; struct sigaction sig = {}; sig.sa_handler = signalHandler; sig.sa_flags = SA_RESTART; sigemptyset(&sig.sa_mask); + sigaction(SIGINT, &sig, nullptr); + sigaction(SIGTERM, &sig, nullptr); sigaction(SIGUSR1, &sig, nullptr); + sigaction(SIGUSR2, &sig, nullptr); // qt5webengine(winId, scaleFactor, url) || // qt6webengine(winId, scaleFactor, url) || diff --git a/distrho/extra/WebViewImpl.hpp b/distrho/extra/WebViewImpl.hpp index c16a91c6..df9d8efa 100644 --- a/distrho/extra/WebViewImpl.hpp +++ b/distrho/extra/WebViewImpl.hpp @@ -73,6 +73,11 @@ WebViewHandle webViewCreate(uintptr_t windowId, */ void webViewDestroy(WebViewHandle webview); +/** + Evaluate/run JavaScript on the web view. +*/ +void webViewEvaluateJS(WebViewHandle webview, const char* js); + /** Reload the web view current page. */ diff --git a/distrho/src/DistrhoUI.cpp b/distrho/src/DistrhoUI.cpp index 988660de..c768ac51 100644 --- a/distrho/src/DistrhoUI.cpp +++ b/distrho/src/DistrhoUI.cpp @@ -382,6 +382,7 @@ uintptr_t UI::getNextWindowId() noexcept void UI::parameterChanged(const uint32_t index, const float value) { #if DISTRHO_UI_WEB_VIEW + evaluateJS("typeof(parameterChanged) === 'function' && parameterChanged(0, 0);"); #else // unused (void)index; @@ -393,6 +394,7 @@ void UI::parameterChanged(const uint32_t index, const float value) void UI::programLoaded(const uint32_t index) { #if DISTRHO_UI_WEB_VIEW + evaluateJS("typeof(programLoaded) === 'function' && programLoaded(0);"); #else // unused (void)index; @@ -404,6 +406,7 @@ void UI::programLoaded(const uint32_t index) void UI::stateChanged(const char* const key, const char* const value) { #if DISTRHO_UI_WEB_VIEW + evaluateJS("typeof(stateChanged) === 'function' && stateChanged('', '');"); #else // unused (void)key; @@ -415,8 +418,14 @@ void UI::stateChanged(const char* const key, const char* const value) /* ------------------------------------------------------------------------------------------------------------ * DSP/Plugin Callbacks (optional) */ -void UI::sampleRateChanged(double) +void UI::sampleRateChanged(const double sampleRate) { +#if DISTRHO_UI_WEB_VIEW + evaluateJS("typeof(sampleRateChanged) === 'function' && sampleRateChanged(0);"); +#else + // unused + (void)sampleRate; +#endif } /* ------------------------------------------------------------------------------------------------------------ diff --git a/examples/WebMeters/ExampleUIWebMeters.cpp b/examples/WebMeters/ExampleUIWebMeters.cpp index 1cce4bdf..1278b98f 100644 --- a/examples/WebMeters/ExampleUIWebMeters.cpp +++ b/examples/WebMeters/ExampleUIWebMeters.cpp @@ -39,6 +39,8 @@ protected: */ void parameterChanged(uint32_t index, float value) override { + d_stdout("param changed %u %f", index, value); + evaluateJS("if (typeof(parameterChanged) === 'function') { parameterChanged(0, 0); }"); } /** diff --git a/examples/WebMeters/Makefile b/examples/WebMeters/Makefile index a8c2b631..269476e1 100644 --- a/examples/WebMeters/Makefile +++ b/examples/WebMeters/Makefile @@ -25,6 +25,11 @@ FILES_UI = \ UI_TYPE = web include ../../Makefile.plugins.mk +# BUILD_CXX_FLAGS += $(shell pkg-config --cflags gtk+-3.0 webkit2gtk-4.0) +BUILD_CXX_FLAGS += -Wno-unused-parameter -Wno-unused-result +BUILD_CXX_FLAGS += -Wno-deprecated-declarations +# LINK_FLAGS += $(shell pkg-config --libs gtk+-3.0 webkit2gtk-4.0) + # -------------------------------------------------------------- # Enable all possible plugin types diff --git a/examples/WebMeters/index.html b/examples/WebMeters/index.html new file mode 100644 index 00000000..e312877a --- /dev/null +++ b/examples/WebMeters/index.html @@ -0,0 +1,25 @@ + + + + + + + + + +
Hello World!
+
+

This is my text, nice neato.

+
+ + +