| @@ -238,16 +238,6 @@ endif | |||
| ifeq ($(UI_TYPE),web) | |||
| DGL_FLAGS += -DDGL_WEB -DHAVE_DGL | |||
| ifeq ($(MACOS),true) | |||
| # BUILD_CXX_FLAGS += -std=gnu++17 | |||
| DGL_LIBS += -framework WebKit | |||
| else ifeq ($(WINDOWS),true) | |||
| # DGL_FLAGS += -std=gnu++17 | |||
| DGL_LIBS += -lole32 -luuid | |||
| else | |||
| DGL_FLAGS += -pthread | |||
| DGL_LIBS += -pthread -lrt | |||
| endif | |||
| DGL_LIB = $(DGL_BUILD_DIR)/libdgl-web.a | |||
| HAVE_DGL = true | |||
| USE_WEBVIEW = true | |||
| @@ -271,6 +261,19 @@ ifeq ($(HAVE_DGL)$(LINUX)$(USE_WEBVIEW),truetruetrue) | |||
| DGL_LIB_SHARED = $(shell $(CC) -print-file-name=Scrt1.o) | |||
| endif | |||
| ifeq ($(USE_WEBVIEW),true) | |||
| ifeq ($(MACOS),true) | |||
| # BUILD_CXX_FLAGS += -std=gnu++17 | |||
| DGL_LIBS += -framework WebKit | |||
| else ifeq ($(WINDOWS),true) | |||
| # DGL_FLAGS += -std=gnu++17 | |||
| DGL_LIBS += -lole32 -luuid | |||
| else | |||
| DGL_FLAGS += -pthread | |||
| DGL_LIBS += -pthread -lrt | |||
| endif | |||
| endif | |||
| DGL_LIBS += $(DGL_SYSTEM_LIBS) -lm | |||
| # TODO split dsp and ui object build flags | |||
| @@ -43,20 +43,20 @@ public: | |||
| /** | |||
| Constructor for a WebViewWidget. | |||
| */ | |||
| explicit WebViewWidget(Window& windowToMapTo, bool initLater = false); | |||
| explicit WebViewWidget(Window& windowToMapTo); | |||
| /** | |||
| Destructor. | |||
| */ | |||
| ~WebViewWidget() override; | |||
| void init(const char* url, const char* initialJS); | |||
| // webview methods | |||
| void evaluateJS(const char* js); | |||
| void reload(); | |||
| protected: | |||
| void init(const char* initialJS); | |||
| virtual void onMessage(char* message); | |||
| void onResize(const ResizeEvent& ev) override; | |||
| @@ -28,18 +28,10 @@ START_NAMESPACE_DGL | |||
| // -------------------------------------------------------------------------------------------------------------------- | |||
| WebViewWidget::WebViewWidget(Window& windowToMapTo, bool initLater) | |||
| WebViewWidget::WebViewWidget(Window& windowToMapTo) | |||
| : TopLevelWidget(windowToMapTo), | |||
| webview(initLater ? nullptr : webViewCreate(windowToMapTo.getNativeWindowHandle(), | |||
| windowToMapTo.getWidth(), | |||
| windowToMapTo.getHeight(), | |||
| windowToMapTo.getScaleFactor(), | |||
| WebViewOptions(_on_msg, this))) | |||
| webview(nullptr) | |||
| { | |||
| #if !(defined(DISTRHO_OS_MAC) || defined(DISTRHO_OS_WINDOWS)) | |||
| if (webview != nullptr) | |||
| addIdleCallback(this, 1000 / 60); | |||
| #endif | |||
| } | |||
| WebViewWidget::~WebViewWidget() | |||
| @@ -53,13 +45,13 @@ WebViewWidget::~WebViewWidget() | |||
| } | |||
| } | |||
| void WebViewWidget::init(const char* const initialJS) | |||
| void WebViewWidget::init(const char* const url, const char* const initialJS) | |||
| { | |||
| DISTRHO_SAFE_ASSERT_RETURN(webview == nullptr,); | |||
| WebViewOptions options(_on_msg, this); | |||
| options.initialJS = initialJS; | |||
| webview = webViewCreate(getWindow().getNativeWindowHandle(), getWidth(), getHeight(), getScaleFactor(), options); | |||
| webview = webViewCreate(url, getWindow().getNativeWindowHandle(), getWidth(), getHeight(), getScaleFactor(), options); | |||
| // FIXME implement initialJS | |||
| if (webview != nullptr) | |||
| @@ -44,9 +44,9 @@ END_NAMESPACE_DISTRHO | |||
| # include "extra/WebViewImpl.cpp" | |||
| #endif | |||
| #if DISTRHO_PLUGIN_HAS_EXTERNAL_UI | |||
| # include <algorithm> | |||
| # include <cmath> | |||
| #include <algorithm> | |||
| #include <cmath> | |||
| START_NAMESPACE_DISTRHO | |||
| double getDesktopScaleFactor(const uintptr_t parentWindowHandle) | |||
| { | |||
| @@ -61,4 +61,3 @@ double getDesktopScaleFactor(const uintptr_t parentWindowHandle) | |||
| return [NSScreen mainScreen].backingScaleFactor; | |||
| } | |||
| END_NAMESPACE_DISTRHO | |||
| #endif | |||
| @@ -406,7 +406,8 @@ static void getFilenameFromFunctionPtr(char filename[PATH_MAX], const void* cons | |||
| // ----------------------------------------------------------------------------------------------------------- | |||
| WebViewHandle webViewCreate(const uintptr_t windowId, | |||
| WebViewHandle webViewCreate(const char* const url, | |||
| const uintptr_t windowId, | |||
| const uint initialWidth, | |||
| const uint initialHeight, | |||
| const double scaleFactor, | |||
| @@ -464,11 +465,6 @@ WebViewHandle webViewCreate(const uintptr_t windowId, | |||
| #elif WEB_VIEW_USING_MACOS_WEBKIT | |||
| NSView* const view = reinterpret_cast<NSView*>(windowId); | |||
| const CGRect rect = CGRectMake(options.offset.x, | |||
| options.offset.y, | |||
| (initialWidth - options.offset.x), | |||
| (initialHeight - options.offset.y)); | |||
| WKPreferences* const prefs = [[WKPreferences alloc] init]; | |||
| [prefs setValue:@YES forKey:@"javaScriptCanAccessClipboard"]; | |||
| [prefs setValue:@YES forKey:@"DOMPasteAllowed"]; | |||
| @@ -483,6 +479,11 @@ WebViewHandle webViewCreate(const uintptr_t windowId, | |||
| config.limitsNavigationsToAppBoundDomains = false; | |||
| config.preferences = prefs; | |||
| const CGRect rect = CGRectMake(options.offset.x / scaleFactor, | |||
| options.offset.y / scaleFactor, | |||
| initialWidth, | |||
| initialHeight); | |||
| WKWebView* const webview = [[WKWebView alloc] initWithFrame:rect | |||
| configuration:config]; | |||
| [webview setHidden:YES]; | |||
| @@ -495,31 +496,61 @@ WebViewHandle webViewCreate(const uintptr_t windowId, | |||
| delegate->callbackPtr = options.callbackPtr; | |||
| delegate->loaded = false; | |||
| webview.navigationDelegate = delegate; | |||
| webview.UIDelegate = delegate; | |||
| if (WKUserContentController* const controller = [config userContentController]) | |||
| { | |||
| [controller retain]; | |||
| [controller addScriptMessageHandler:delegate name:@"external"]; | |||
| if (options.initialJS != nullptr) | |||
| { | |||
| NSString* const nsInitialJS = [[NSString alloc] initWithBytes:options.initialJS | |||
| length:std::strlen(options.initialJS) | |||
| encoding:NSUTF8StringEncoding]; | |||
| WKUserScript* const script = [[WKUserScript alloc] initWithSource:nsInitialJS | |||
| injectionTime:WKUserScriptInjectionTimeAtDocumentStart | |||
| forMainFrameOnly:true]; | |||
| [controller addUserScript:script]; | |||
| [script release]; | |||
| [nsInitialJS release]; | |||
| } | |||
| } | |||
| const char* const url = "file:///Users/falktx/Source/DISTRHO/DPF/examples/WebMeters/index.html"; | |||
| [webview setNavigationDelegate:delegate]; | |||
| [webview setUIDelegate:delegate]; | |||
| NSString* const nsurl = [[NSString alloc] initWithBytes:url | |||
| length:std::strlen(url) | |||
| encoding:NSUTF8StringEncoding]; | |||
| NSURLRequest* const urlreq = [[NSURLRequest alloc] initWithURL: [NSURL URLWithString: nsurl]]; | |||
| // [webview loadRequest:urlreq]; | |||
| [webview loadFileRequest:urlreq | |||
| allowingReadAccessToURL:[NSURL URLWithString:@"file:///Users/falktx/Source/DISTRHO/DPF/examples/WebMeters/"]]; | |||
| d_stdout("url is '%s'", url); | |||
| if (std::strncmp(url, "file://", 7) == 0) | |||
| { | |||
| const char* const lastsep = std::strrchr(url + 7, '/'); | |||
| NSString* const urlpath = [[NSString alloc] initWithBytes:url | |||
| length:(lastsep - url) | |||
| encoding:NSUTF8StringEncoding]; | |||
| [webview loadFileRequest:urlreq | |||
| allowingReadAccessToURL:[NSURL URLWithString:urlpath]]; | |||
| [urlpath release]; | |||
| } | |||
| else | |||
| { | |||
| [webview loadRequest:urlreq]; | |||
| } | |||
| d_stdout("waiting for load"); | |||
| if (! delegate->loaded) | |||
| { | |||
| NSAutoreleasePool* const pool = [[NSAutoreleasePool alloc] init]; | |||
| NSDate* const date = [NSDate distantFuture]; | |||
| NSDate* const date = [NSDate dateWithTimeIntervalSinceNow:0.05]; | |||
| NSEvent* event; | |||
| while (! delegate->loaded) | |||
| @@ -702,7 +733,7 @@ void webViewDestroy(const WebViewHandle handle) | |||
| [handle->webview setHidden:YES]; | |||
| [handle->webview removeFromSuperview]; | |||
| [handle->urlreq release]; | |||
| [handle->delegate release]; | |||
| // [handle->delegate release]; | |||
| #elif WEB_VIEW_USING_X11_IPC | |||
| munmap(handle->shmptr, sizeof(WebViewRingBuffer)); | |||
| close(handle->shmfd); | |||
| @@ -804,16 +835,15 @@ void webViewResize(const WebViewHandle handle, const uint width, const uint heig | |||
| #if WEB_VIEW_USING_CHOC | |||
| #ifdef DISTRHO_OS_MAC | |||
| NSView* const view = static_cast<NSView*>(handle->webview->getViewHandle()); | |||
| [view setFrameSize:NSMakeSize(width, height)]; | |||
| [view setFrameSize:NSMakeSize(width / scaleFactor, height / scaleFactor)]; | |||
| #else | |||
| const HWND hwnd = static_cast<HWND>(handle->webview->getViewHandle()); | |||
| SetWindowPos(hwnd, nullptr, 0, 0, | |||
| width * scaleFactor, | |||
| height * scaleFactor, | |||
| width, height, | |||
| SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOZORDER); | |||
| #endif | |||
| #elif WEB_VIEW_USING_MACOS_WEBKIT | |||
| [handle->webview setFrameSize:NSMakeSize(width, height)]; | |||
| [handle->webview setFrameSize:NSMakeSize(width / scaleFactor, height / scaleFactor)]; | |||
| #elif WEB_VIEW_USING_X11_IPC | |||
| if (handle->childWindow == 0) | |||
| { | |||
| @@ -35,10 +35,10 @@ struct WebViewOptions { | |||
| Position offset, for cases of mixing regular widgets with web views. | |||
| */ | |||
| struct PositionOffset { | |||
| /** Horizontal offset */ | |||
| /** Horizontal offset, with scale factor pre-applied */ | |||
| int x; | |||
| /** Vertical offset */ | |||
| /** Vertical offset, with scale factor pre-applied */ | |||
| int y; | |||
| /** Constructor for default values */ | |||
| @@ -83,10 +83,11 @@ struct WebViewOptions { | |||
| Provided metrics must not have scale factor pre-applied. | |||
| @p windowId: The native window id to attach this view to (X11 Window, HWND or NSView*) | |||
| @p scaleFactor: Scale factor to use (ignored on macOS) | |||
| @p scaleFactor: Scale factor in use | |||
| @p options: Extra options, optional | |||
| */ | |||
| WebViewHandle webViewCreate(uintptr_t windowId, | |||
| WebViewHandle webViewCreate(const char* url, | |||
| uintptr_t windowId, | |||
| uint initialWidth, | |||
| uint initialHeight, | |||
| double scaleFactor, | |||
| @@ -980,15 +980,11 @@ int main(int argc, char* argv[]) | |||
| String tmpPath(getBinaryFilename()); | |||
| tmpPath.truncate(tmpPath.rfind(DISTRHO_OS_SEP)); | |||
| #if defined(DISTRHO_OS_MAC) | |||
| if (tmpPath.endsWith("/MacOS")) | |||
| if (tmpPath.endsWith("/Contents/MacOS")) | |||
| { | |||
| tmpPath.truncate(tmpPath.rfind('/')); | |||
| if (tmpPath.endsWith("/Contents")) | |||
| { | |||
| tmpPath.truncate(tmpPath.rfind('/')); | |||
| bundlePath = tmpPath; | |||
| d_nextBundlePath = bundlePath.buffer(); | |||
| } | |||
| tmpPath.truncate(tmpPath.length() - 15); | |||
| bundlePath = tmpPath; | |||
| d_nextBundlePath = bundlePath.buffer(); | |||
| } | |||
| #else | |||
| #ifdef DISTRHO_OS_WINDOWS | |||
| @@ -15,8 +15,8 @@ | |||
| */ | |||
| #include "DistrhoDetails.hpp" | |||
| #include "DistrhoPluginUtils.hpp" | |||
| #include "src/DistrhoPluginChecks.h" | |||
| #include "src/DistrhoDefines.h" | |||
| #include <cstddef> | |||
| @@ -88,7 +88,6 @@ uintptr_t g_nextWindowId = 0; | |||
| double g_nextScaleFactor = 1.0; | |||
| #endif | |||
| #if DISTRHO_PLUGIN_HAS_EXTERNAL_UI | |||
| /* ------------------------------------------------------------------------------------------------------------ | |||
| * get global scale factor */ | |||
| @@ -101,23 +100,23 @@ static double getDesktopScaleFactor(const uintptr_t parentWindowHandle) | |||
| if (const char* const scale = getenv("DPF_SCALE_FACTOR")) | |||
| return std::max(1.0, std::atof(scale)); | |||
| #if defined(DISTRHO_OS_WINDOWS) | |||
| #if defined(DISTRHO_OS_WINDOWS) | |||
| if (const HMODULE Shcore = LoadLibraryA("Shcore.dll")) | |||
| { | |||
| typedef HRESULT(WINAPI* PFN_GetProcessDpiAwareness)(HANDLE, DWORD*); | |||
| typedef HRESULT(WINAPI* PFN_GetScaleFactorForMonitor)(HMONITOR, DWORD*); | |||
| # if defined(__GNUC__) && (__GNUC__ >= 9) | |||
| # pragma GCC diagnostic push | |||
| # pragma GCC diagnostic ignored "-Wcast-function-type" | |||
| # endif | |||
| #if defined(__GNUC__) && (__GNUC__ >= 9) | |||
| #pragma GCC diagnostic push | |||
| #pragma GCC diagnostic ignored "-Wcast-function-type" | |||
| #endif | |||
| const PFN_GetProcessDpiAwareness GetProcessDpiAwareness | |||
| = (PFN_GetProcessDpiAwareness)GetProcAddress(Shcore, "GetProcessDpiAwareness"); | |||
| const PFN_GetScaleFactorForMonitor GetScaleFactorForMonitor | |||
| = (PFN_GetScaleFactorForMonitor)GetProcAddress(Shcore, "GetScaleFactorForMonitor"); | |||
| # if defined(__GNUC__) && (__GNUC__ >= 9) | |||
| # pragma GCC diagnostic pop | |||
| # endif | |||
| #if defined(__GNUC__) && (__GNUC__ >= 9) | |||
| #pragma GCC diagnostic pop | |||
| #endif | |||
| DWORD dpiAware = 0; | |||
| DWORD scaleFactor = 100; | |||
| @@ -133,7 +132,7 @@ static double getDesktopScaleFactor(const uintptr_t parentWindowHandle) | |||
| FreeLibrary(Shcore); | |||
| return static_cast<double>(scaleFactor) / 100.0; | |||
| } | |||
| #elif defined(HAVE_X11) | |||
| #elif defined(HAVE_X11) | |||
| ::Display* const display = XOpenDisplay(nullptr); | |||
| DISTRHO_SAFE_ASSERT_RETURN(display != nullptr, 1.0); | |||
| @@ -164,7 +163,7 @@ static double getDesktopScaleFactor(const uintptr_t parentWindowHandle) | |||
| XCloseDisplay(display); | |||
| return dpi / 96; | |||
| #endif | |||
| #endif | |||
| return 1.0; | |||
| @@ -173,8 +172,6 @@ static double getDesktopScaleFactor(const uintptr_t parentWindowHandle) | |||
| } | |||
| #endif // !DISTRHO_OS_MAC | |||
| #endif | |||
| /* ------------------------------------------------------------------------------------------------------------ | |||
| * UI::PrivateData special handling */ | |||
| @@ -188,7 +185,6 @@ PluginWindow& | |||
| UI::PrivateData::createNextWindow(UI* const ui, uint width, uint height, const bool adjustForScaleFactor) | |||
| { | |||
| UI::PrivateData* const pData = s_nextPrivateData; | |||
| #if DISTRHO_PLUGIN_HAS_EXTERNAL_UI | |||
| const double scaleFactor = d_isNotZero(pData->scaleFactor) ? pData->scaleFactor : getDesktopScaleFactor(pData->winId); | |||
| if (adjustForScaleFactor && d_isNotZero(scaleFactor) && d_isNotEqual(scaleFactor, 1.0)) | |||
| @@ -197,6 +193,7 @@ UI::PrivateData::createNextWindow(UI* const ui, uint width, uint height, const b | |||
| height *= scaleFactor; | |||
| } | |||
| #if DISTRHO_PLUGIN_HAS_EXTERNAL_UI | |||
| pData->window = new PluginWindow(ui, pData->app); | |||
| ExternalWindow::PrivateData ewData; | |||
| ewData.parentWindowHandle = pData->winId; | |||
| @@ -207,14 +204,7 @@ UI::PrivateData::createNextWindow(UI* const ui, uint width, uint height, const b | |||
| ewData.isStandalone = DISTRHO_UI_IS_STANDALONE; | |||
| return ewData; | |||
| #else | |||
| const double scaleFactor = pData->scaleFactor; | |||
| if (adjustForScaleFactor && d_isNotZero(scaleFactor) && d_isNotEqual(scaleFactor, 1.0)) | |||
| { | |||
| width *= scaleFactor; | |||
| height *= scaleFactor; | |||
| } | |||
| d_stdout("createNextWindow %u %u %f %d", width, height, scaleFactor, adjustForScaleFactor); | |||
| pData->window = new PluginWindow(ui, pData->app, pData->winId, width, height, scaleFactor); | |||
| // If there are no callbacks, this is most likely a temporary window, so ignore idle callbacks | |||
| @@ -246,11 +236,7 @@ UI::UI(const uint width, const uint height, const bool automaticallyScaleAndSetA | |||
| #else | |||
| false | |||
| #endif | |||
| ) | |||
| #if DISTRHO_UI_WEB_VIEW | |||
| , true | |||
| #endif | |||
| ), | |||
| )), | |||
| uiData(UI::PrivateData::s_nextPrivateData) | |||
| { | |||
| #if !DISTRHO_PLUGIN_HAS_EXTERNAL_UI | |||
| @@ -273,7 +259,37 @@ UI::UI(const uint width, const uint height, const bool automaticallyScaleAndSetA | |||
| #endif | |||
| #if DISTRHO_UI_WEB_VIEW | |||
| init( | |||
| String path(uiData->bundlePath); | |||
| if (path.isNotEmpty()) | |||
| { | |||
| // FIXME get resource path | |||
| #ifdef DISTRHO_OS_MAC | |||
| path += "/Contents/Resources"; | |||
| #endif | |||
| } | |||
| else | |||
| { | |||
| path = getBinaryFilename(); | |||
| path.truncate(path.rfind(DISTRHO_OS_SEP)); | |||
| #ifdef DISTRHO_OS_WINDOWS | |||
| // TODO make valid URL | |||
| #endif | |||
| #ifdef DISTRHO_OS_MAC | |||
| if (path.endsWith("/Contents/MacOS")) | |||
| { | |||
| path.truncate(path.length() - 5); | |||
| path += "Resources"; | |||
| } | |||
| else | |||
| #endif | |||
| { | |||
| path += "/resources"; | |||
| } | |||
| // TODO encode for HTML URL | |||
| } | |||
| init("file://" + path + "/index.html", | |||
| "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 | |||
| @@ -1636,6 +1636,7 @@ v3_plugin_view** dpf_plugin_view_create(v3_host_application** const host, | |||
| void* const instancePointer, | |||
| const double sampleRate) | |||
| { | |||
| g_nextBundlePath = d_nextBundlePath; | |||
| dpf_plugin_view** const viewptr = new dpf_plugin_view*; | |||
| *viewptr = new dpf_plugin_view(host, instancePointer, sampleRate); | |||
| return static_cast<v3_plugin_view**>(static_cast<void*>(viewptr)); | |||
| @@ -39,6 +39,5 @@ | |||
| #define DISTRHO_UI_DEFAULT_WIDTH 100 | |||
| #define DISTRHO_UI_DEFAULT_HEIGHT 500 | |||
| #define kVerticalOffset 0 | |||
| #endif // DISTRHO_PLUGIN_INFO_H_INCLUDED | |||
| @@ -51,6 +51,18 @@ TARGETS += au | |||
| endif # HAVE_OPENGL | |||
| all: $(TARGETS) | |||
| ifeq ($(MACOS_APP_BUNDLE),true) | |||
| jackfiles += $(TARGET_DIR)/$(NAME).app/Contents/Resources/index.html | |||
| else | |||
| jackfiles += $(TARGET_DIR)/resources/index.html | |||
| endif | |||
| vst3files += $(TARGET_DIR)/$(NAME).vst3/Contents/Resources/index.html | |||
| all: $(TARGETS) $(jackfiles) $(vst3files) | |||
| %/index.html: index.html | |||
| -$(SILENT)$(shell mkdir -p "$(shell dirname $(abspath $@))") | |||
| install -m 644 $< $(abspath $@) | |||
| # -------------------------------------------------------------- | |||