Browse Source

Setup IPC through shared mem for linux web ui

Signed-off-by: falkTX <falktx@falktx.com>
web-ui
falkTX 1 year ago
parent
commit
fe99f0d3d7
Signed by: falkTX <falktx@falktx.com> GPG Key ID: CDBAA37ABC74FBA0
7 changed files with 447 additions and 78 deletions
  1. +3
    -1
      dgl/Web.hpp
  2. +10
    -0
      dgl/src/Web.cpp
  3. +412
    -55
      distrho/extra/WebViewImpl.cpp
  4. +6
    -0
      distrho/extra/WebViewImpl.hpp
  5. +1
    -1
      distrho/src/DistrhoPluginJACK.cpp
  6. +14
    -20
      distrho/src/DistrhoUI.cpp
  7. +1
    -1
      examples/WebMeters/ExampleUIWebMeters.cpp

+ 3
- 1
dgl/Web.hpp View File

@@ -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


+ 10
- 0
dgl/src/Web.cpp View File

@@ -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)


+ 412
- 55
distrho/extra/WebViewImpl.cpp View File

@@ -63,13 +63,24 @@
// #include <QtCore/QSize>
// #undef signals
# include "ChildProcess.hpp"
# include "RingBuffer.hpp"
# include "String.hpp"
# include <clocale>
# include <cstdio>
# include <dlfcn.h>
# include <functional>
# include <linux/limits.h>
# include <dlfcn.h>
# include <fcntl.h>
# include <pthread.h>
# include <unistd.h>
# include <sys/mman.h>
# include <X11/Xlib.h>
# ifdef __linux__
# include <syscall.h>
# include <linux/futex.h>
# include <linux/limits.h>
# else
# include <semaphore.h>
# 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<WebViewSharedBuffer> 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<WebViewRingBuffer*>(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<char*>(buffer));
if (handle->callback != nullptr)
handle->callback(handle->callbackPtr, static_cast<char*>(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<void(const char* js)> evaluateFn;
static std::function<void()> reloadFn;
static std::function<void()> terminateFn;
static std::function<void(WebViewRingBuffer* rb)> 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<void*>(arg);
DISTRHO_SAFE_ASSERT_RETURN(lib != nullptr, false);
WebViewRingBuffer* const shmptr = static_cast<WebViewRingBuffer*>(ptr);

RingBufferControl<WebViewSharedBuffer> 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<char*>(buffer));
evaluateFn(static_cast<char*>(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<WebViewRingBuffer*>(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<WebViewSharedBuffer> 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<WebViewRingBuffer*>(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<WebViewRingBuffer*>(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<WebViewSharedBuffer> 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;
}



+ 6
- 0
distrho/extra/WebViewImpl.hpp View File

@@ -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.
*/


+ 1
- 1
distrho/src/DistrhoPluginJACK.cpp View File

@@ -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)


+ 14
- 20
distrho/src/DistrhoUI.cpp View File

@@ -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;
}


+ 1
- 1
examples/WebMeters/ExampleUIWebMeters.cpp View File

@@ -24,7 +24,7 @@ class ExampleUIMeters : public UI
{
public:
ExampleUIMeters()
: UI(100, 500, true)
: UI(100, 500, false)
{
}



Loading…
Cancel
Save