From fe99f0d3d757a2fa92c846ecf0ae90ff163b4329 Mon Sep 17 00:00:00 2001 From: falkTX Date: Thu, 9 May 2024 00:27:53 +0200 Subject: [PATCH] Setup IPC through shared mem for linux web ui Signed-off-by: falkTX --- dgl/Web.hpp | 4 +- dgl/src/Web.cpp | 10 + distrho/extra/WebViewImpl.cpp | 467 +++++++++++++++++++--- distrho/extra/WebViewImpl.hpp | 6 + distrho/src/DistrhoPluginJACK.cpp | 2 +- distrho/src/DistrhoUI.cpp | 34 +- examples/WebMeters/ExampleUIWebMeters.cpp | 2 +- 7 files changed, 447 insertions(+), 78 deletions(-) diff --git a/dgl/Web.hpp b/dgl/Web.hpp index aaeab9ee..67cb3782 100644 --- a/dgl/Web.hpp +++ b/dgl/Web.hpp @@ -36,7 +36,8 @@ START_NAMESPACE_DGL // -------------------------------------------------------------------------------------------------------------------- // WebViewWidget -class WebViewWidget : public TopLevelWidget +class WebViewWidget : public TopLevelWidget, + private IdleCallback { public: /** @@ -59,6 +60,7 @@ protected: private: const DISTRHO_NAMESPACE::WebViewHandle webview; + void idleCallback() override; void onDisplay() override {} // TODO inside private data diff --git a/dgl/src/Web.cpp b/dgl/src/Web.cpp index 4e8257fe..69709b97 100644 --- a/dgl/src/Web.cpp +++ b/dgl/src/Web.cpp @@ -36,12 +36,17 @@ WebViewWidget::WebViewWidget(Window& windowToMapTo) windowToMapTo.getScaleFactor(), WebViewOptions(_on_msg, this))) { + if (webview != nullptr) + addIdleCallback(this, 1000 / 60); } WebViewWidget::~WebViewWidget() { if (webview != nullptr) + { + removeIdleCallback(this); webViewDestroy(webview); + } } void WebViewWidget::evaluateJS(const char* const js) @@ -73,6 +78,11 @@ void WebViewWidget::onResize(const ResizeEvent& ev) webViewResize(webview, ev.size.getWidth(), ev.size.getHeight(), getScaleFactor()); } +void WebViewWidget::idleCallback() +{ + webViewIdle(webview); +} + // ----------------------------------------------------------------------- static void notImplemented(const char* const name) diff --git a/distrho/extra/WebViewImpl.cpp b/distrho/extra/WebViewImpl.cpp index b8904624..fcca8fb4 100644 --- a/distrho/extra/WebViewImpl.cpp +++ b/distrho/extra/WebViewImpl.cpp @@ -63,13 +63,24 @@ // #include // #undef signals # include "ChildProcess.hpp" +# include "RingBuffer.hpp" # include "String.hpp" # include # include -# include # include -# include +# include +# include +# include +# include +# include # include +# ifdef __linux__ +# include +# include +# include +# else +# include +# endif #endif // ----------------------------------------------------------------------------------------------------------- @@ -104,10 +115,10 @@ initiatedByFrame:(WKFrameInfo*)frame completionHandler:(void (^)(void))completionHandler { - NSAlert* const alert = [[NSAlert alloc] init]; - [alert addButtonWithTitle:@"OK"]; + NSAlert* const alert = [[NSAlert alloc] init]; + [alert addButtonWithTitle:@"OK"]; [alert setInformativeText:message]; - [alert setMessageText:@"Alert"]; + [alert setMessageText:@"Alert"]; dispatch_async(dispatch_get_main_queue(), ^ { @@ -125,11 +136,11 @@ initiatedByFrame:(WKFrameInfo*)frame completionHandler:(void (^)(BOOL))completionHandler { - NSAlert* const alert = [[NSAlert alloc] init]; - [alert addButtonWithTitle:@"OK"]; - [alert addButtonWithTitle:@"Cancel"]; + NSAlert* const alert = [[NSAlert alloc] init]; + [alert addButtonWithTitle:@"OK"]; + [alert addButtonWithTitle:@"Cancel"]; [alert setInformativeText:message]; - [alert setMessageText:@"Confirm"]; + [alert setMessageText:@"Confirm"]; dispatch_async(dispatch_get_main_queue(), ^ { @@ -151,7 +162,7 @@ NSTextField* const input = [[NSTextField alloc] initWithFrame:NSMakeRect(0, 0, 250, 30)]; [input setStringValue:defaultText]; - NSAlert* const alert = [[NSAlert alloc] init]; + NSAlert* const alert = [[NSAlert alloc] init]; [alert setAccessoryView:input]; [alert addButtonWithTitle:@"OK"]; [alert addButtonWithTitle:@"Cancel"]; @@ -208,6 +219,8 @@ @end +#elif WEB_VIEW_USING_X11_IPC + #endif // WEB_VIEW_USING_MACOS_WEBKIT // ----------------------------------------------------------------------------------------------------------- @@ -221,8 +234,82 @@ START_NAMESPACE_DISTRHO // ----------------------------------------------------------------------------------------------------------- +#if WEB_VIEW_USING_X11_IPC +#ifdef __linux__ +typedef int32_t ipc_sem_t; +#else +typedef sem_t ipc_sem_t; +#endif + +enum WebViewMessageType { + kWebViewMessageNull, + kWebViewMessageInitData, + kWebViewMessageEvaluateJS, + kWebViewMessageCallback, + kWebViewMessageReload +}; + +struct WebViewSharedBuffer { + static constexpr const uint32_t size = 0x100000; + ipc_sem_t sem; + uint32_t head, tail, wrtn; + bool invalidateCommit; + uint8_t buf[size]; +}; + +struct WebViewRingBuffer { + WebViewSharedBuffer server; + WebViewSharedBuffer client; + bool valid; +}; + +static void webview_wake(ipc_sem_t* const sem) +{ + #ifdef __linux__ + if (__sync_bool_compare_and_swap(sem, 0, 1)) + syscall(SYS_futex, sem, FUTEX_WAKE, 1, nullptr, nullptr, 0); + #else + sem_post(sem); + #endif +} + +static bool webview_timedwait(ipc_sem_t* const sem) +{ + #ifdef __linux__ + const struct timespec timeout = { 1, 0 }; + for (;;) + { + if (__sync_bool_compare_and_swap(sem, 1, 0)) + return true; + if (syscall(SYS_futex, sem, FUTEX_WAIT, 0, &timeout, nullptr, 0) != 0) + if (errno != EAGAIN && errno != EINTR) + return false; + } + #else + struct timespec timeout; + if (clock_gettime(CLOCK_REALTIME, &timeout) != 0) + return false; + + timeout.tv_sec += 1; + + for (int r;;) + { + r = sem_timedwait(sem, &timeout); + + if (r < 0) + r = errno; + + if (r == EINTR) + continue; + + return r == 0; + } + #endif +} + +#endif + struct WebViewData { - const WebViewMessageCallback callback; #if WEB_VIEW_USING_CHOC choc::ui::WebView* const webview; #elif WEB_VIEW_USING_MACOS_WEBKIT @@ -231,10 +318,16 @@ struct WebViewData { NSURLRequest* const urlreq; WEB_VIEW_DELEGATE_CLASS_NAME* const delegate; #elif WEB_VIEW_USING_X11_IPC + int shmfd = 0; + char shmname[128] = {}; + WebViewRingBuffer* shmptr = nullptr; + WebViewMessageCallback callback = nullptr; + void* callbackPtr = nullptr; ChildProcess p; - ::Display* display; - ::Window childWindow; - ::Window ourWindow; + RingBufferControl rbctrl, rbctrl2; + ::Display* display = nullptr; + ::Window childWindow = 0; + ::Window ourWindow = 0; #endif }; @@ -458,6 +551,7 @@ WebViewHandle webViewCreate(const uintptr_t windowId, return new WebViewData{options.callback, view, webview, urlreq, delegate}; #elif WEB_VIEW_USING_X11_IPC + // get startup paths char ldlinux[PATH_MAX] = {}; getFilenameFromFunctionPtr(ldlinux, dlsym(nullptr, "_rtld_global")); @@ -467,6 +561,50 @@ WebViewHandle webViewCreate(const uintptr_t windowId, d_stdout("ld-linux is '%s'", ldlinux); d_stdout("filename is '%s'", filename); + // setup shared memory + int shmfd; + char shmname[128]; + void* shmptr; + + for (int i = 0; i < 9999; ++i) + { + snprintf(shmname, sizeof(shmname) - 1, "/dpf-webview-%d", i + 1); + shmfd = shm_open(shmname, O_CREAT|O_EXCL|O_RDWR, 0666); + + if (shmfd < 0) + continue; + + if (ftruncate(shmfd, sizeof(WebViewRingBuffer)) != 0) + { + close(shmfd); + shm_unlink(shmname); + continue; + } + + break; + } + + if (shmfd < 0) + { + d_stderr("shm_open failed: %s", strerror(errno)); + return nullptr; + } + + shmptr = mmap(nullptr, sizeof(WebViewRingBuffer), PROT_READ|PROT_WRITE, MAP_SHARED, shmfd, 0); + + if (shmptr == nullptr || shmptr == MAP_FAILED) + { + d_stderr("mmap failed: %s", strerror(errno)); + close(shmfd); + shm_unlink(shmname); + return nullptr; + } + + #ifndef __linux__ + sem_init(&handle->shmptr->client.sem, 1, 0); + sem_init(&handle->shmptr->server.sem, 1, 0); + #endif + ::Display* const display = XOpenDisplay(nullptr); DISTRHO_SAFE_ASSERT_RETURN(display != nullptr, nullptr); @@ -495,16 +633,49 @@ WebViewHandle webViewCreate(const uintptr_t windowId, envp[e++] = nullptr; } - WebViewData* const handle = new WebViewData{options.callback, {}, display, 0, windowId}; + WebViewData* const handle = new WebViewData; + handle->callback = options.callback; + handle->callbackPtr = options.callbackPtr; + handle->shmfd = shmfd; + handle->shmptr = static_cast(shmptr); + handle->display = display; + handle->ourWindow = windowId; + std::memcpy(handle->shmname, shmname, sizeof(shmname)); + + handle->shmptr->valid = true; - const char* const args[] = { ldlinux, filename, "dpf-ld-linux-webview", nullptr }; + handle->rbctrl.setRingBuffer(&handle->shmptr->client, false); + handle->rbctrl.flush(); + + handle->rbctrl2.setRingBuffer(&handle->shmptr->server, false); + handle->rbctrl2.flush(); + + const char* const args[] = { ldlinux, filename, "dpf-ld-linux-webview", shmname, nullptr }; handle->p.start(args, envp); for (uint i = 0; envp[i] != nullptr; ++i) std::free(envp[i]); delete[] envp; - return handle; + handle->rbctrl.writeUInt(kWebViewMessageInitData) && + handle->rbctrl.writeULong(windowId) && + handle->rbctrl.writeUInt(initialWidth) && + handle->rbctrl.writeUInt(initialHeight) && + handle->rbctrl.writeDouble(scaleFactor) && + handle->rbctrl.writeInt(options.offset.x) && + handle->rbctrl.writeInt(options.offset.y); + handle->rbctrl.commitWrite(); + webview_wake(&handle->shmptr->client.sem); + + for (int i = 0; i < 5 && handle->p.isRunning(); ++i) + { + if (webview_timedwait(&handle->shmptr->server.sem)) + return handle; + } + + d_stderr("webview client side failed to start"); + webViewDestroy(handle); + return nullptr; #endif // maybe unused @@ -526,11 +697,61 @@ void webViewDestroy(const WebViewHandle handle) [handle->urlreq release]; [handle->delegate release]; #elif WEB_VIEW_USING_X11_IPC + munmap(handle->shmptr, sizeof(WebViewRingBuffer)); + close(handle->shmfd); + shm_unlink(handle->shmname); XCloseDisplay(handle->display); #endif delete handle; } +void webViewIdle(const WebViewHandle handle) +{ + #if WEB_VIEW_USING_X11_IPC + uint32_t size = 0; + void* buffer = nullptr; + + while (handle->rbctrl2.isDataAvailableForReading()) + { + switch (handle->rbctrl2.readUInt()) + { + case kWebViewMessageCallback: + if (const uint32_t len = handle->rbctrl2.readUInt()) + { + if (len > size) + { + size = len; + buffer = std::realloc(buffer, len); + + if (buffer == nullptr) + { + d_stderr("server out of memory, abort!"); + handle->rbctrl2.flush(); + return; + } + } + + if (handle->rbctrl2.readCustomData(buffer, len)) + { + d_debug("server kWebViewMessageCallback -> '%s'", static_cast(buffer)); + if (handle->callback != nullptr) + handle->callback(handle->callbackPtr, static_cast(buffer)); + continue; + } + } + break; + } + + d_stderr("server ringbuffer data race, abort!"); + handle->rbctrl2.flush(); + return; + } + #else + // unused + (void)handle; + #endif +} + void webViewEvaluateJS(const WebViewHandle handle, const char* const js) { #if WEB_VIEW_USING_CHOC @@ -541,7 +762,13 @@ void webViewEvaluateJS(const WebViewHandle handle, const char* const js) [handle->webview evaluateJavaScript:nsjs completionHandler:nullptr]; [nsjs release]; #elif WEB_VIEW_USING_X11_IPC - handle->p.signal(SIGUSR2); + d_debug("evaluateJS '%s'", js); + const size_t len = std::strlen(js) + 1; + handle->rbctrl.writeUInt(kWebViewMessageEvaluateJS) && + handle->rbctrl.writeUInt(len) && + handle->rbctrl.writeCustomData(js, len); + if (handle->rbctrl.commitWrite()) + webview_wake(&handle->shmptr->client.sem); #endif // maybe unused @@ -555,7 +782,10 @@ void webViewReload(const WebViewHandle handle) #elif WEB_VIEW_USING_MACOS_WEBKIT [handle->webview loadRequest:handle->urlreq]; #elif WEB_VIEW_USING_X11_IPC - handle->p.signal(SIGUSR1); + d_stdout("reload"); + handle->rbctrl.writeUInt(kWebViewMessageReload); + if (handle->rbctrl.commitWrite()) + webview_wake(&handle->shmptr->client.sem); #endif // maybe unused @@ -612,6 +842,7 @@ void webViewResize(const WebViewHandle handle, const uint width, const uint heig static std::function evaluateFn; static std::function reloadFn; static std::function terminateFn; +static std::function wakeFn; // ----------------------------------------------------------------------------------------------------------- @@ -657,10 +888,59 @@ typedef int gboolean; // ----------------------------------------------------------------------------------------------------------- // gtk3 variant -static int gtk_js_cb(WebKitUserContentManager*, WebKitJavascriptResult* const result, void* const arg) +static void gtk3_idle(void* const ptr) { - void* const lib = static_cast(arg); - DISTRHO_SAFE_ASSERT_RETURN(lib != nullptr, false); + WebViewRingBuffer* const shmptr = static_cast(ptr); + + RingBufferControl rbctrl; + rbctrl.setRingBuffer(&shmptr->client, false); + + uint32_t size = 0; + void* buffer = nullptr; + + while (rbctrl.isDataAvailableForReading()) + { + switch (rbctrl.readUInt()) + { + case kWebViewMessageEvaluateJS: + if (const uint32_t len = rbctrl.readUInt()) + { + if (len > size) + { + size = len; + buffer = realloc(buffer, len); + + if (buffer == nullptr) + { + d_stderr("lv2ui client out of memory, abort!"); + abort(); + } + } + + if (rbctrl.readCustomData(buffer, len)) + { + d_debug("client kWebViewMessageEvaluateJS -> '%s'", static_cast(buffer)); + evaluateFn(static_cast(buffer)); + continue; + } + } + break; + case kWebViewMessageReload: + d_debug("client kWebViewMessageReload"); + reloadFn(); + continue; + } + + d_stderr("client ringbuffer data race, abort!"); + abort(); + } + + free(buffer); +} + +static int gtk3_js_cb(WebKitUserContentManager*, WebKitJavascriptResult* const result, void* const arg) +{ + WebViewRingBuffer* const shmptr = static_cast(arg); using g_free_t = void (*)(void*); using jsc_value_to_string_t = char* (*)(JSCValue*); @@ -676,25 +956,36 @@ static int gtk_js_cb(WebKitUserContentManager*, WebKitJavascriptResult* const re char* const string = jsc_value_to_string(value); DISTRHO_SAFE_ASSERT_RETURN(string != nullptr, false); - d_stdout("js call received with data '%s'", string); + d_debug("js call received with data '%s'", string); + + const size_t len = std::strlen(string); + RingBufferControl rbctrl2; + rbctrl2.setRingBuffer(&shmptr->server, false); + rbctrl2.writeUInt(kWebViewMessageCallback) && + rbctrl2.writeUInt(len) && + rbctrl2.writeCustomData(string, len); + rbctrl2.commitWrite(); + g_free(string); return 0; } static bool gtk3(Display* const display, const Window winId, - const uint x, - const uint y, + const int x, + const int y, const uint width, const uint height, double scaleFactor, - const char* const url) + const char* const url, + WebViewRingBuffer* const shmptr) { void* lib; if ((lib = dlopen("libwebkit2gtk-4.0.so.37", RTLD_NOW|RTLD_GLOBAL)) == nullptr || (lib = dlopen("libwebkit2gtk-4.0.so", RTLD_NOW|RTLD_GLOBAL)) == nullptr) return false; + using g_main_context_invoke_t = void (*)(void*, void*, void*); 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*); @@ -719,6 +1010,7 @@ static bool gtk3(Display* const display, 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_main_context_invoke_t, g_main_context_invoke) 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) @@ -802,7 +1094,7 @@ static bool gtk3(Display* const display, 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); + g_signal_connect_data(manager, "script-message-received::external", G_CALLBACK(gtk3_js_cb), shmptr, nullptr, 0); webkit_user_content_manager_register_script_message_handler(manager, "external"); } @@ -838,6 +1130,13 @@ static bool gtk3(Display* const display, } }; + wakeFn = [=](WebViewRingBuffer* const rb){ + g_main_context_invoke(NULL, G_CALLBACK(gtk3_idle), rb); + }; + + // notify server we started ok + webview_wake(&shmptr->server.sem); + gtk_main(); d_stdout("quit"); @@ -1062,55 +1361,113 @@ static void signalHandler(const int sig) { 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[] */) +static void* threadHandler(void* const ptr) { - uselocale(newlocale(LC_NUMERIC_MASK, "C", nullptr)); + WebViewRingBuffer* const shmptr = static_cast(ptr); - const char* const envScaleFactor = std::getenv("DPF_WEB_VIEW_SCALE_FACTOR"); - DISTRHO_SAFE_ASSERT_RETURN(envScaleFactor != nullptr, 1); + // TODO wait until page is loaded, or something better + d_sleep(1); - const char* const envWinId = std::getenv("DPF_WEB_VIEW_WIN_ID"); - DISTRHO_SAFE_ASSERT_RETURN(envWinId != nullptr, 1); + while (shmptr->valid) + { + if (webview_timedwait(&shmptr->client.sem)) + wakeFn(shmptr); + } - const Window winId = std::strtoul(envWinId, nullptr, 10); - DISTRHO_SAFE_ASSERT_RETURN(winId != 0, 1); + return nullptr; +} - const double scaleFactor = std::atof(envScaleFactor); - DISTRHO_SAFE_ASSERT_RETURN(scaleFactor > 0.0, 1); +int dpf_webview_start(const int argc, char* argv[]) +{ + d_stdout("started %d %s", argc, argv[1]); + + if (argc != 3) + { + d_stderr("WebView entry point, nothing to see here! ;)"); + return 1; + } + + uselocale(newlocale(LC_NUMERIC_MASK, "C", nullptr)); Display* const display = XOpenDisplay(nullptr); DISTRHO_SAFE_ASSERT_RETURN(display != nullptr, 1); + const char* const shmname = argv[2]; + const int shmfd = shm_open(shmname, O_RDWR, 0); + if (shmfd < 0) + { + d_stderr("shm_open failed: %s", std::strerror(errno)); + return 1; + } + + WebViewRingBuffer* const shmptr = static_cast(mmap(nullptr, + sizeof(WebViewRingBuffer), + PROT_READ|PROT_WRITE, + MAP_SHARED, + shmfd, 0)); + if (shmptr == nullptr || shmptr == nullptr) + { + d_stderr("mmap failed: %s", std::strerror(errno)); + close(shmfd); + return 1; + } + + RingBufferControl rbctrl; + rbctrl.setRingBuffer(&shmptr->client, false); + 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); + // fetch initial data + bool hasInitialData = false; + Window winId = 0; + uint width = 0, height = 0; + double scaleFactor = 0; + int x = 0, y = 0; -// qt5webengine(winId, scaleFactor, url) || -// qt6webengine(winId, scaleFactor, url) || - gtk3(display, winId, 0, 0, 600, 400, scaleFactor, url); + while (shmptr->valid && webview_timedwait(&shmptr->client.sem)) + { + if (rbctrl.isDataAvailableForReading()) + { + DISTRHO_SAFE_ASSERT_RETURN(rbctrl.readUInt() == kWebViewMessageInitData, 1); + + hasInitialData = true; + winId = rbctrl.readULong(); + width = rbctrl.readUInt(); + height = rbctrl.readUInt(); + scaleFactor = rbctrl.readDouble(); + x = rbctrl.readInt(); + y = rbctrl.readInt(); + } + } + + pthread_t thread; + if (hasInitialData && pthread_create(&thread, nullptr, threadHandler, shmptr) == 0) + { + struct sigaction sig = {}; + sig.sa_handler = signalHandler; + sig.sa_flags = SA_RESTART; + sigemptyset(&sig.sa_mask); + sigaction(SIGTERM, &sig, nullptr); + + // qt5webengine(winId, scaleFactor, url) || + // qt6webengine(winId, scaleFactor, url) || + gtk3(display, winId, x, y, width, height, scaleFactor, url, shmptr); + } + + shmptr->valid = false; + pthread_join(thread, nullptr); XCloseDisplay(display); + munmap(shmptr, sizeof(WebViewRingBuffer)); + close(shmfd); + return 0; } diff --git a/distrho/extra/WebViewImpl.hpp b/distrho/extra/WebViewImpl.hpp index 00c5e94e..38c7e802 100644 --- a/distrho/extra/WebViewImpl.hpp +++ b/distrho/extra/WebViewImpl.hpp @@ -90,6 +90,12 @@ WebViewHandle webViewCreate(uintptr_t windowId, */ void webViewDestroy(WebViewHandle webview); +/** + Idle the web view, to be called on regular intervals. + Can cause callbacks to trigger. +*/ +void webViewIdle(WebViewHandle webview); + /** Evaluate/run JavaScript on the web view. */ diff --git a/distrho/src/DistrhoPluginJACK.cpp b/distrho/src/DistrhoPluginJACK.cpp index d6ff6620..6fda2a62 100644 --- a/distrho/src/DistrhoPluginJACK.cpp +++ b/distrho/src/DistrhoPluginJACK.cpp @@ -1007,7 +1007,7 @@ int main(int argc, char* argv[]) #ifdef DPF_USING_LD_LINUX_WEBVIEW if (argc >= 2 && std::strcmp(argv[1], "dpf-ld-linux-webview") == 0) - return dpf_webview_start(argc - 1, argv + 1); + return dpf_webview_start(argc, argv); #endif if (argc == 2 && std::strcmp(argv[1], "selftest") == 0) diff --git a/distrho/src/DistrhoUI.cpp b/distrho/src/DistrhoUI.cpp index eb851f8e..125cd654 100644 --- a/distrho/src/DistrhoUI.cpp +++ b/distrho/src/DistrhoUI.cpp @@ -266,15 +266,13 @@ UI::UI(const uint width, const uint height, const bool automaticallyScaleAndSetA #endif #if DISTRHO_UI_WEB_VIEW - evaluateJS("\ -function editParameter(index, started){ window.webkit.messageHandlers.external.postMessage('editparam ' + index + ' ' + (started ? 1 : 0)) }\ -function setParameterValue(index, value){ window.webkit.messageHandlers.external.postMessage('setparam ' + index + ' ' + value) }\ -"); + evaluateJS( +"editParameter=function(index,started){window.webkit.messageHandlers.external.postMessage('editparam '+index+' '+(started ? 1 : 0))};" +"setParameterValue=function(index,value){window.webkit.messageHandlers.external.postMessage('setparam '+index+' '+value)};" #if DISTRHO_PLUGIN_WANT_STATE - evaluateJS("\ -function setState(key, value){ window.webkit.messageHandlers.external.postMessage('setstate ' + key + ' ' + value) }\ -"); +"setState=function(key,value){window.webkit.messageHandlers.external.postMessage('setstate '+key+' '+value)};" #endif + ); #endif } @@ -558,13 +556,11 @@ void UI::onMessage(char* const message) { if (std::strncmp(message, "setparam ", 9) == 0) { - char* const strindex = message + 9; - char* const sep = std::strchr(strindex, ' '); - DISTRHO_SAFE_ASSERT_RETURN(sep != nullptr,); - *sep = 0; - char* const strvalue = sep + 1; + const char* const strindex = message + 9; + char* strvalue = nullptr; + const ulong index = std::strtoul(strindex, &strvalue, 10); + DISTRHO_SAFE_ASSERT_RETURN(strvalue != nullptr && strindex != strvalue,); - const uint32_t index = std::atoi(strindex); float value; { const ScopedSafeLocale ssl; @@ -576,14 +572,12 @@ void UI::onMessage(char* const message) if (std::strncmp(message, "editparam ", 10) == 0) { - char* const strindex = message + 10; - char* const sep = std::strchr(strindex, ' '); - DISTRHO_SAFE_ASSERT_RETURN(sep != nullptr,); - *sep = 0; - char* const strstarted = sep + 1; + const char* const strindex = message + 10; + char* strvalue = nullptr; + const ulong index = std::strtoul(strindex, &strvalue, 10); + DISTRHO_SAFE_ASSERT_RETURN(strvalue != nullptr && strindex != strvalue,); - const uint32_t index = std::atoi(strindex); - const bool started = std::atoi(strstarted) != 0; + const bool started = strvalue[0] != '0'; editParameter(index, started); return; } diff --git a/examples/WebMeters/ExampleUIWebMeters.cpp b/examples/WebMeters/ExampleUIWebMeters.cpp index 1cf0a70b..68daf206 100644 --- a/examples/WebMeters/ExampleUIWebMeters.cpp +++ b/examples/WebMeters/ExampleUIWebMeters.cpp @@ -24,7 +24,7 @@ class ExampleUIMeters : public UI { public: ExampleUIMeters() - : UI(100, 500, true) + : UI(100, 500, false) { }