From 1e51ad13c1ea658e1ac61f1df8783b21bbce4c5e Mon Sep 17 00:00:00 2001 From: falkTX Date: Sun, 6 Oct 2024 12:08:04 +0200 Subject: [PATCH] Update to latest DPF Signed-off-by: falkTX --- .github/workflows/build.yml | 13 +- dpf/dgl/src/pugl-upstream/.clang-tidy | 3 +- dpf/dgl/src/pugl-upstream/src/.clang-tidy | 4 +- dpf/dgl/src/pugl-upstream/src/mac.m | 10 +- dpf/distrho/DistrhoUtils.hpp | 4 + dpf/distrho/extra/Base64.hpp | 80 +- dpf/distrho/extra/ChildProcess.hpp | 30 +- dpf/distrho/extra/ExternalWindow.hpp | 6 +- dpf/distrho/extra/FileBrowserDialogImpl.cpp | 10 +- dpf/distrho/extra/RingBuffer.hpp | 22 +- dpf/distrho/extra/String.hpp | 202 +++- dpf/distrho/extra/WebViewImpl.cpp | 1038 ++++++++++++++----- dpf/distrho/extra/WebViewImpl.hpp | 1 + dpf/distrho/src/DistrhoDefines.h | 6 + dpf/distrho/src/DistrhoPluginAU.cpp | 2 +- dpf/distrho/src/DistrhoPluginCLAP.cpp | 5 +- dpf/distrho/src/DistrhoPluginVST2.cpp | 5 +- dpf/distrho/src/DistrhoPluginVST3.cpp | 13 +- dpf/distrho/src/DistrhoUI.cpp | 24 +- 19 files changed, 1163 insertions(+), 315 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 9babc6a..706d576 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -2,6 +2,11 @@ name: build on: push: + branches: + - '*' + pull_request: + branches: + - '*' jobs: linux: @@ -10,7 +15,7 @@ jobs: target: [linux-arm64, linux-armhf, linux-i686, linux-riscv64, linux-x86_64] runs-on: ubuntu-20.04 steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 with: submodules: recursive - uses: distrho/dpf-makefile-action@v1 @@ -23,7 +28,7 @@ jobs: target: [macos-intel, macos-universal] runs-on: macos-11 steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 with: submodules: recursive - uses: distrho/dpf-makefile-action@v1 @@ -36,7 +41,7 @@ jobs: target: [win32, win64] runs-on: ubuntu-20.04 steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 with: submodules: recursive - uses: distrho/dpf-makefile-action@v1 @@ -47,7 +52,7 @@ jobs: pluginval: runs-on: ubuntu-20.04 steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 with: submodules: recursive - uses: distrho/dpf-makefile-action@v1 diff --git a/dpf/dgl/src/pugl-upstream/.clang-tidy b/dpf/dgl/src/pugl-upstream/.clang-tidy index 7f81150..9af40ce 100644 --- a/dpf/dgl/src/pugl-upstream/.clang-tidy +++ b/dpf/dgl/src/pugl-upstream/.clang-tidy @@ -1,4 +1,4 @@ -# Copyright 2020-2022 David Robillard +# Copyright 2020-2024 David Robillard # SPDX-License-Identifier: 0BSD OR ISC Checks: > @@ -11,6 +11,7 @@ Checks: > -clang-diagnostic-unused-macros, -llvmlibc-*, -misc-include-cleaner, + -readability-avoid-nested-conditional-operator, -readability-identifier-length, CheckOptions: - key: hicpp-uppercase-literal-suffix.NewSuffixes diff --git a/dpf/dgl/src/pugl-upstream/src/.clang-tidy b/dpf/dgl/src/pugl-upstream/src/.clang-tidy index d57b1a8..6d51f06 100644 --- a/dpf/dgl/src/pugl-upstream/src/.clang-tidy +++ b/dpf/dgl/src/pugl-upstream/src/.clang-tidy @@ -1,8 +1,10 @@ -# Copyright 2020-2022 David Robillard +# Copyright 2020-2024 David Robillard # SPDX-License-Identifier: 0BSD OR ISC Checks: > -bugprone-easily-swappable-parameters, + -bugprone-multi-level-implicit-pointer-conversion, + -clang-analyzer-optin.core.EnumCastOutOfRange, -hicpp-multiway-paths-covered, -hicpp-signed-bitwise, -llvm-header-guard, diff --git a/dpf/dgl/src/pugl-upstream/src/mac.m b/dpf/dgl/src/pugl-upstream/src/mac.m index 727f142..21469e4 100644 --- a/dpf/dgl/src/pugl-upstream/src/mac.m +++ b/dpf/dgl/src/pugl-upstream/src/mac.m @@ -633,11 +633,11 @@ handleCrossing(PuglWrapperView* view, NSEvent* event, const PuglEventType type) const NSPoint wloc = [self eventLocation:event]; const NSPoint rloc = [NSEvent mouseLocation]; - double dx = -[event scrollingDeltaX]; - double dy = [event scrollingDeltaY]; - if (![event hasPreciseScrollingDeltas]) { - dx *= 10.0; - dy *= 10.0; + double dx = -[event scrollingDeltaX] / 2.0; + double dy = [event scrollingDeltaY] / 2.0; + if ([event hasPreciseScrollingDeltas]) { + dx /= 10.0; + dy /= 10.0; } const PuglScrollDirection dir = diff --git a/dpf/distrho/DistrhoUtils.hpp b/dpf/distrho/DistrhoUtils.hpp index bdd367a..6b9b9af 100644 --- a/dpf/distrho/DistrhoUtils.hpp +++ b/dpf/distrho/DistrhoUtils.hpp @@ -19,6 +19,10 @@ #include "src/DistrhoDefines.h" +#ifndef __STDC_LIMIT_MACROS +# define __STDC_LIMIT_MACROS +#endif + #include #include #include diff --git a/dpf/distrho/extra/Base64.hpp b/dpf/distrho/extra/Base64.hpp index d3eed6b..6078716 100644 --- a/dpf/distrho/extra/Base64.hpp +++ b/dpf/distrho/extra/Base64.hpp @@ -1,6 +1,6 @@ /* * DISTRHO Plugin Framework (DPF) - * Copyright (C) 2012-2016 Filipe Coelho + * Copyright (C) 2012-2024 Filipe Coelho * * Permission to use, copy, modify, and/or distribute this software for any purpose with * or without fee is hereby granted, provided that the above copyright notice and this @@ -19,7 +19,6 @@ #include "../DistrhoUtils.hpp" -#include #include // ----------------------------------------------------------------------- @@ -55,7 +54,7 @@ #ifndef DOXYGEN namespace DistrhoBase64Helpers { -static const char* const kBase64Chars = +static constexpr const char* const kBase64Chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" "abcdefghijklmnopqrstuvwxyz" "0123456789+/"; @@ -63,7 +62,7 @@ static const char* const kBase64Chars = static inline uint8_t findBase64CharIndex(const char c) { - static const uint8_t kBase64CharsLen(static_cast(std::strlen(kBase64Chars))); + static const uint8_t kBase64CharsLen = static_cast(std::strlen(kBase64Chars)); for (uint8_t i=0; i + * + * Permission to use, copy, modify, and/or distribute this software for any purpose with + * or without fee is hereby granted, provided that the above copyright notice and this + * permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD + * TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN + * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ #pragma once @@ -24,13 +37,18 @@ START_NAMESPACE_DISTRHO class ChildProcess { #ifdef _WIN32 - PROCESS_INFORMATION pinfo = { INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE, 0, 0 }; + PROCESS_INFORMATION pinfo; #else - pid_t pid = -1; + pid_t pid; #endif public: ChildProcess() + #ifdef _WIN32 + : pinfo(CPP_AGGREGATE_INIT(PROCESS_INFORMATION){ INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE, 0, 0 }) + #else + : pid(-1) + #endif { } @@ -120,7 +138,7 @@ public: return; const PROCESS_INFORMATION opinfo = pinfo; - pinfo = { INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE, 0, 0 }; + pinfo = (PROCESS_INFORMATION){ INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE, 0, 0 }; for (DWORD exitCode;;) { @@ -226,7 +244,7 @@ public: WaitForSingleObject(pinfo.hProcess, 0) != WAIT_TIMEOUT) { const PROCESS_INFORMATION opinfo = pinfo; - pinfo = { INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE, 0, 0 }; + pinfo = (PROCESS_INFORMATION){ INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE, 0, 0 }; CloseHandle(opinfo.hThread); CloseHandle(opinfo.hProcess); return false; diff --git a/dpf/distrho/extra/ExternalWindow.hpp b/dpf/distrho/extra/ExternalWindow.hpp index 1f1c360..65ca1cf 100644 --- a/dpf/distrho/extra/ExternalWindow.hpp +++ b/dpf/distrho/extra/ExternalWindow.hpp @@ -1,6 +1,6 @@ /* * DISTRHO Plugin Framework (DPF) - * Copyright (C) 2012-2021 Filipe Coelho + * Copyright (C) 2012-2024 Filipe Coelho * * Permission to use, copy, modify, and/or distribute this software for any purpose with * or without fee is hereby granted, provided that the above copyright notice and this @@ -78,13 +78,13 @@ public: Constructor. */ explicit ExternalWindow() - : pData() {} + : pData() {} /** Constructor for DPF internal use. */ explicit ExternalWindow(const PrivateData& data) - : pData(data) {} + : pData(data) {} /** Destructor. diff --git a/dpf/distrho/extra/FileBrowserDialogImpl.cpp b/dpf/distrho/extra/FileBrowserDialogImpl.cpp index 06d8c1b..bb431ab 100644 --- a/dpf/distrho/extra/FileBrowserDialogImpl.cpp +++ b/dpf/distrho/extra/FileBrowserDialogImpl.cpp @@ -691,17 +691,17 @@ bool fileBrowserIdle(const FileBrowserHandle handle) while (dbus_connection_dispatch(dbuscon) == DBUS_DISPATCH_DATA_REMAINS) {} dbus_connection_read_write_dispatch(dbuscon, 0); - if (DBusMessage* const message = dbus_connection_pop_message(dbuscon)) + if (DBusMessage* const msg = dbus_connection_pop_message(dbuscon)) { - const char* const interface = dbus_message_get_interface(message); - const char* const member = dbus_message_get_member(message); + const char* const interface = dbus_message_get_interface(msg); + const char* const member = dbus_message_get_member(msg); if (interface != nullptr && std::strcmp(interface, "org.freedesktop.portal.Request") == 0 && member != nullptr && std::strcmp(member, "Response") == 0) { do { DBusMessageIter iter; - dbus_message_iter_init(message, &iter); + dbus_message_iter_init(msg, &iter); // starts with uint32 for return/exit code DISTRHO_SAFE_ASSERT_BREAK(dbus_message_iter_get_arg_type(&iter) == DBUS_TYPE_UINT32); @@ -813,6 +813,8 @@ bool fileBrowserIdle(const FileBrowserHandle handle) if (handle->selectedFile == nullptr) handle->selectedFile = kSelectedFileCancelled; } + + dbus_message_unref(msg); } } #endif diff --git a/dpf/distrho/extra/RingBuffer.hpp b/dpf/distrho/extra/RingBuffer.hpp index be744dd..f23104d 100644 --- a/dpf/distrho/extra/RingBuffer.hpp +++ b/dpf/distrho/extra/RingBuffer.hpp @@ -1,6 +1,6 @@ /* * DISTRHO Plugin Framework (DPF) - * Copyright (C) 2012-2021 Filipe Coelho + * Copyright (C) 2012-2024 Filipe Coelho * * Permission to use, copy, modify, and/or distribute this software for any purpose with * or without fee is hereby granted, provided that the above copyright notice and this @@ -119,14 +119,6 @@ struct HugeStackBuffer { uint8_t buf[size]; }; -#ifdef DISTRHO_PROPER_CPP11_SUPPORT -# define HeapBuffer_INIT {0, 0, 0, 0, false, nullptr} -# define StackBuffer_INIT {0, 0, 0, false, {0}} -#else -# define HeapBuffer_INIT -# define StackBuffer_INIT -#endif - // ----------------------------------------------------------------------- // RingBufferControl templated class @@ -799,12 +791,7 @@ class HeapRingBuffer : public RingBufferControl public: /** Constructor. */ HeapRingBuffer() noexcept - : heapBuffer(HeapBuffer_INIT) - { -#ifndef DISTRHO_PROPER_CPP11_SUPPORT - std::memset(&heapBuffer, 0, sizeof(heapBuffer)); -#endif - } + : heapBuffer(CPP_AGGREGATE_INIT(HeapBuffer){0, 0, 0, 0, false, nullptr}) {} /** Destructor. */ ~HeapRingBuffer() noexcept override @@ -874,11 +861,8 @@ class SmallStackRingBuffer : public RingBufferControl public: /** Constructor. */ SmallStackRingBuffer() noexcept - : stackBuffer(StackBuffer_INIT) + : stackBuffer(CPP_AGGREGATE_INIT(SmallStackBuffer){0, 0, 0, false, {0}}) { -#ifndef DISTRHO_PROPER_CPP11_SUPPORT - std::memset(&stackBuffer, 0, sizeof(stackBuffer)); -#endif setRingBuffer(&stackBuffer, true); } diff --git a/dpf/distrho/extra/String.hpp b/dpf/distrho/extra/String.hpp index 08bd6be..0255bcd 100644 --- a/dpf/distrho/extra/String.hpp +++ b/dpf/distrho/extra/String.hpp @@ -591,7 +591,7 @@ public: */ String& toLower() noexcept { - static const char kCharDiff('a' - 'A'); + static constexpr const char kCharDiff = 'a' - 'A'; for (std::size_t i=0; i < fBufferLen; ++i) { @@ -607,7 +607,7 @@ public: */ String& toUpper() noexcept { - static const char kCharDiff('a' - 'A'); + static constexpr const char kCharDiff = 'a' - 'A'; for (std::size_t i=0; i < fBufferLen; ++i) { @@ -676,7 +676,7 @@ public: static String asBase64(const void* const data, const std::size_t dataSize) { - static const char* const kBase64Chars = + static constexpr const char* const kBase64Chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" "abcdefghijklmnopqrstuvwxyz" "0123456789+/"; @@ -749,6 +749,192 @@ public: return ret; } + /* + * Convert to a URL encoded string. + */ + String& urlEncode() noexcept + { + static constexpr const char* const kHexChars = "0123456789ABCDEF"; + + if (fBufferLen == 0) + return *this; + + char* const newbuf = static_cast(std::malloc(fBufferLen * 3 + 1)); + DISTRHO_SAFE_ASSERT_RETURN(newbuf != nullptr, *this); + + char* newbufptr = newbuf; + + for (std::size_t i=0; i < fBufferLen; ++i) + { + const char c = fBuffer[i]; + + switch (c) + { + case '!': // 33 + case '#': // 35 + case '$': // 36 + case '&': // 38 + case '\'': // 39 + case '(': // 40 + case ')': // 41 + case '*': // 42 + case '+': // 43 + case ',': // 44 + case '-': // 45 + case '.': // 46 + case '/': // 47 + case '0': // 48 + case '1': // 49 + case '2': // 50 + case '3': // 51 + case '4': // 52 + case '5': // 53 + case '6': // 54 + case '7': // 55 + case '8': // 56 + case '9': // 57 + case ':': // 58 + case ';': // 59 + case '=': // 61 + case '?': // 63 + case '@': // 64 + case 'A': // 65 + case 'B': // 66 + case 'C': // 67 + case 'D': // 68 + case 'E': // 69 + case 'F': // 70 + case 'G': // 71 + case 'H': // 72 + case 'I': // 73 + case 'J': // 74 + case 'K': // 75 + case 'L': // 76 + case 'M': // 77 + case 'N': // 78 + case 'O': // 79 + case 'P': // 80 + case 'Q': // 81 + case 'R': // 82 + case 'S': // 83 + case 'T': // 84 + case 'U': // 85 + case 'V': // 86 + case 'W': // 87 + case 'X': // 88 + case 'Y': // 89 + case 'Z': // 90 + case '[': // 91 + case ']': // 93 + case '_': // 95 + case 'a': // 97 + case 'b': // 98 + case 'c': // 99 + case 'd': // 100 + case 'e': // 101 + case 'f': // 102 + case 'g': // 103 + case 'h': // 104 + case 'i': // 105 + case 'j': // 106 + case 'k': // 107 + case 'l': // 108 + case 'm': // 109 + case 'n': // 110 + case 'o': // 111 + case 'p': // 112 + case 'q': // 113 + case 'r': // 114 + case 's': // 115 + case 't': // 116 + case 'u': // 117 + case 'v': // 118 + case 'w': // 119 + case 'x': // 120 + case 'y': // 121 + case 'z': // 122 + case '~': // 126 + *newbufptr++ = c; + break; + default: + *newbufptr++ = '%'; + *newbufptr++ = kHexChars[(c >> 4) & 0xf]; + *newbufptr++ = kHexChars[c & 0xf]; + break; + } + } + + *newbufptr = '\0'; + + std::free(fBuffer); + fBuffer = newbuf; + fBufferLen = std::strlen(newbuf); + fBufferAlloc = true; + + return *this; + } + + /* + * Convert to a URL decoded string. + */ + String& urlDecode() noexcept + { + if (fBufferLen == 0) + return *this; + + char* const newbuf = static_cast(std::malloc(fBufferLen + 1)); + DISTRHO_SAFE_ASSERT_RETURN(newbuf != nullptr, *this); + + char* newbufptr = newbuf; + + for (std::size_t i=0; i < fBufferLen; ++i) + { + const char c = fBuffer[i]; + + if (c == '%') + { + DISTRHO_SAFE_ASSERT_CONTINUE(fBufferLen > i + 2); + + char c1 = fBuffer[i + 1]; + char c2 = fBuffer[i + 2]; + i += 2; + + /**/ if (c1 >= '0' && c1 <= '9') + c1 -= '0'; + else if (c1 >= 'A' && c1 <= 'Z') + c1 -= 'A' - 10; + else if (c1 >= 'a' && c1 <= 'z') + c1 -= 'a' - 10; + else + continue; + + /**/ if (c2 >= '0' && c2 <= '9') + c2 -= '0'; + else if (c2 >= 'A' && c2 <= 'Z') + c2 -= 'A' - 10; + else if (c2 >= 'a' && c2 <= 'z') + c2 -= 'a' - 10; + else + continue; + + *newbufptr++ = c1 << 4 | c2; + } + else + { + *newbufptr++ = c; + } + } + + *newbufptr = '\0'; + + std::free(fBuffer); + fBuffer = newbuf; + fBufferLen = std::strlen(newbuf); + fBufferAlloc = true; + + return *this; + } + // ------------------------------------------------------------------- // public operators @@ -830,7 +1016,7 @@ public: } // we have some data ourselves, reallocate to add the new stuff - char* const newBuf = (char*)realloc(fBuffer, fBufferLen + strBufLen + 1); + char* const newBuf = static_cast(std::realloc(fBuffer, fBufferLen + strBufLen + 1)); DISTRHO_SAFE_ASSERT_RETURN(newBuf != nullptr, *this); std::memcpy(newBuf + fBufferLen, strBuf, strBufLen + 1); @@ -855,7 +1041,7 @@ public: const std::size_t strBufLen = std::strlen(strBuf); const std::size_t newBufSize = fBufferLen + strBufLen; - char* const newBuf = (char*)malloc(newBufSize + 1); + char* const newBuf = static_cast(std::malloc(newBufSize + 1)); DISTRHO_SAFE_ASSERT_RETURN(newBuf != nullptr, String()); std::memcpy(newBuf, fBuffer, fBufferLen); @@ -912,7 +1098,7 @@ private: std::free(fBuffer); fBufferLen = (size > 0) ? size : std::strlen(strBuf); - fBuffer = (char*)std::malloc(fBufferLen+1); + fBuffer = static_cast(std::malloc(fBufferLen + 1)); if (fBuffer == nullptr) { @@ -960,7 +1146,7 @@ String operator+(const String& strBefore, const char* const strBufAfter) noexcep const std::size_t strBeforeLen = strBefore.length(); const std::size_t strBufAfterLen = std::strlen(strBufAfter); const std::size_t newBufSize = strBeforeLen + strBufAfterLen; - char* const newBuf = (char*)malloc(newBufSize + 1); + char* const newBuf = static_cast(malloc(newBufSize + 1)); DISTRHO_SAFE_ASSERT_RETURN(newBuf != nullptr, String()); std::memcpy(newBuf, strBefore.buffer(), strBeforeLen); @@ -980,7 +1166,7 @@ String operator+(const char* const strBufBefore, const String& strAfter) noexcep const std::size_t strBufBeforeLen = std::strlen(strBufBefore); const std::size_t strAfterLen = strAfter.length(); const std::size_t newBufSize = strBufBeforeLen + strAfterLen; - char* const newBuf = (char*)malloc(newBufSize + 1); + char* const newBuf = static_cast(malloc(newBufSize + 1)); DISTRHO_SAFE_ASSERT_RETURN(newBuf != nullptr, String()); std::memcpy(newBuf, strBufBefore, strBufBeforeLen); diff --git a/dpf/distrho/extra/WebViewImpl.cpp b/dpf/distrho/extra/WebViewImpl.cpp index d247c1b..cea6357 100644 --- a/dpf/distrho/extra/WebViewImpl.cpp +++ b/dpf/distrho/extra/WebViewImpl.cpp @@ -21,6 +21,24 @@ # error bad usage #endif +// #undef Bool +// #undef CursorShape +// #undef Expose +// #undef FocusIn +// #undef FocusOut +// #undef FontChange +// #undef KeyPress +// #undef KeyRelease +// #undef None +// #undef Status +// #define QT_NO_VERSION_TAGGING +// #include +// #include +// #include +// #include +// #include +// #undef signals + // #include // #include // #include @@ -50,23 +68,20 @@ # include # include #elif WEB_VIEW_USING_X11_IPC -// #define QT_NO_VERSION_TAGGING -// #include -// #include -// #include -// #undef signals # include "ChildProcess.hpp" # include "RingBuffer.hpp" # include "String.hpp" # include # include -# include # include # include # include # include # include # include +# ifdef DISTRHO_PROPER_CPP11_SUPPORT +# include +# endif # ifdef __linux__ # include # include @@ -338,21 +353,49 @@ struct WebViewData { NSURLRequest* urlreq; WEB_VIEW_DELEGATE_CLASS_NAME* delegate; #elif WEB_VIEW_USING_X11_IPC - int shmfd = 0; - char shmname[128] = {}; - WebViewRingBuffer* shmptr = nullptr; - WebViewMessageCallback callback = nullptr; - void* callbackPtr = nullptr; + int shmfd; + char shmname[128]; + WebViewRingBuffer* shmptr; + WebViewMessageCallback callback; + void* callbackPtr; ChildProcess p; RingBufferControl rbctrl, rbctrl2; - ::Display* display = nullptr; - ::Window childWindow = 0; - ::Window ourWindow = 0; + ::Display* display; + ::Window childWindow; + ::Window ourWindow; #endif - WebViewData() {} + WebViewData(); DISTRHO_DECLARE_NON_COPYABLE(WebViewData); }; +WebViewData::WebViewData() + #if WEB_VIEW_USING_CHOC + : webview(nullptr), + url() + #elif WEB_VIEW_USING_MACOS_WEBKIT + : view(nullptr), + webview(nullptr), + urlreq(nullptr), + delegate(nullptr) + #elif WEB_VIEW_USING_X11_IPC + : shmfd(0), + shmname(), + shmptr(nullptr), + callback(nullptr), + callbackPtr(nullptr), + p(), + rbctrl(), + rbctrl2(), + display(nullptr), + childWindow(0), + ourWindow(0) + #endif +{ + #if WEB_VIEW_USING_X11_IPC + std::memset(shmname, 0, sizeof(shmname)); + #endif +} + // ----------------------------------------------------------------------------------------------------------- WebViewHandle webViewCreate(const char* const url, @@ -631,7 +674,7 @@ WebViewHandle webViewCreate(const char* const url, std::free(envp[i]); delete[] envp; - const size_t urllen = std::strlen(url); + const size_t urllen = std::strlen(url) + 1; const size_t initjslen = options.initialJS != nullptr ? std::strlen(options.initialJS) + 1 : 0; handle->rbctrl.writeUInt(kWebViewMessageInitData) && handle->rbctrl.writeULong(windowId) && @@ -646,6 +689,7 @@ WebViewHandle webViewCreate(const char* const url, initjslen != 0 && handle->rbctrl.writeCustomData(options.initialJS, initjslen); handle->rbctrl.commitWrite(); + webview_wake(&handle->shmptr->client.sem); for (int i = 0; i < 5 && handle->p.isRunning(); ++i) @@ -818,10 +862,14 @@ 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; +static bool running = false; +static struct WebFramework { + virtual ~WebFramework() {} + virtual void evaluate(const char* js) = 0; + virtual void reload() = 0; + virtual void terminate() = 0; + virtual void wake(WebViewRingBuffer* rb) = 0; +}* webFramework = nullptr; // ----------------------------------------------------------------------------------------------------------- @@ -843,20 +891,48 @@ typedef int gboolean; #define GTK_WINDOW(p) reinterpret_cast(p) #define WEBKIT_WEB_VIEW(p) reinterpret_cast(p) -// struct QApplication; -// struct QUrl; -// struct QWebEngineView; -// struct QWindow; +// ----------------------------------------------------------------------------------------------------------- + +class QApplication { uint8_t _[16 * 2]; }; +class QByteArray { public: uint8_t _[24 * 2]; }; +class QChar; +class QChildEvent; +class QColor; +class QEvent { uint8_t _[16 * 2]; }; +class QIODevice; +class QJsonObject; +class QJsonValue { uint8_t _[128 /* TODO */ * 2]; }; +class QMetaMethod; +class QMetaObject { uint8_t _[56 * 2]; }; +class QString { uint8_t _[8 * 4]; }; +class QTimerEvent; +class QUrl { uint8_t _[8 * 4]; }; +class QWebChannel { uint8_t _[128 /* TODO */ * 2]; }; +class QWebEnginePage { uint8_t _[128 /* TODO */ * 2]; }; +class QWebEngineProfile { uint8_t _[128 /* TODO */ * 2]; }; +class QWebEngineScript { uint8_t _[128 /* TODO */ * 2]; }; +class QWebEngineScriptCollection; +class QWebEngineSettings; +class QWebEngineUrlRequestJob; +class QWebEngineUrlScheme { uint8_t _[128 /* TODO */ * 2]; }; +class QWebEngineUrlSchemeHandler; +class QWebEngineView { uint8_t _[56 * 2]; }; +class QWindow; + +struct QPoint { + int _x, _y; + QPoint(int x, int y) : _x(x), _y(y) {} +}; + +struct QSize { + int _w, _h; + QSize(int w, int h) : _w(w), _h(h) {} +}; // ----------------------------------------------------------------------------------------------------------- #define JOIN(A, B) A ## B -#define AUTOSYM(S) \ - using JOIN(gtk3_, S) = decltype(&S); \ - JOIN(gtk3_, S) S = reinterpret_cast(dlsym(nullptr, #S)); \ - DISTRHO_SAFE_ASSERT_RETURN(S != nullptr, false); - #define CSYM(S, NAME) \ S NAME = reinterpret_cast(dlsym(nullptr, #NAME)); \ DISTRHO_SAFE_ASSERT_RETURN(NAME != nullptr, false); @@ -865,10 +941,7 @@ typedef int gboolean; S NAME = reinterpret_cast(dlsym(nullptr, #SN)); \ DISTRHO_SAFE_ASSERT_RETURN(NAME != nullptr, false); -// ----------------------------------------------------------------------------------------------------------- -// gtk3 variant - -static void gtk3_idle(void* const ptr) +static void web_wake_idle(void* const ptr) { WebViewRingBuffer* const shmptr = static_cast(ptr); @@ -892,7 +965,7 @@ static void gtk3_idle(void* const ptr) if (buffer == nullptr) { - d_stderr("lv2ui client out of memory, abort!"); + d_stderr("client kWebViewMessageEvaluateJS out of memory, abort!"); abort(); } } @@ -900,14 +973,14 @@ static void gtk3_idle(void* const ptr) if (rbctrl.readCustomData(buffer, len)) { d_debug("client kWebViewMessageEvaluateJS -> '%s'", static_cast(buffer)); - evaluateFn(static_cast(buffer)); + webFramework->evaluate(static_cast(buffer)); continue; } } break; case kWebViewMessageReload: d_debug("client kWebViewMessageReload"); - reloadFn(); + webFramework->reload(); continue; } @@ -918,13 +991,16 @@ static void gtk3_idle(void* const ptr) free(buffer); } +// ----------------------------------------------------------------------------------------------------------- +// gtk3 variant + 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*); - using webkit_javascript_result_get_js_value_t = JSCValue* (*)(WebKitJavascriptResult*); + typedef void (*g_free_t)(void*); + typedef char* (*jsc_value_to_string_t)(JSCValue*); + typedef JSCValue* (*webkit_javascript_result_get_js_value_t)(WebKitJavascriptResult*); CSYM(g_free_t, g_free) CSYM(jsc_value_to_string_t, jsc_value_to_string) @@ -938,7 +1014,7 @@ static int gtk3_js_cb(WebKitUserContentManager*, WebKitJavascriptResult* const r d_debug("js call received with data '%s'", string); - const size_t len = std::strlen(string); + const size_t len = std::strlen(string) + 1; RingBufferControl rbctrl2; rbctrl2.setRingBuffer(&shmptr->server, false); rbctrl2.writeUInt(kWebViewMessageCallback) && @@ -962,36 +1038,39 @@ static bool gtk3(Display* const display, WebViewRingBuffer* const shmptr) { void* lib; - if ((lib = dlopen("libwebkit2gtk-4.0.so.37", RTLD_NOW|RTLD_GLOBAL)) == nullptr || + if ((lib = dlopen("libwebkit2gtk-4.0.so.37", RTLD_NOW|RTLD_GLOBAL)) == nullptr && (lib = dlopen("libwebkit2gtk-4.0.so", RTLD_NOW|RTLD_GLOBAL)) == nullptr) + { + d_stdout("WebView gtk3 platform not available: %s", dlerror()); 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*); - 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*, gboolean); - using webkit_user_content_manager_add_script_t = void (*)(WebKitUserContentManager*, WebKitUserScript*); - using webkit_user_content_manager_register_script_message_handler_t = gboolean (*)(WebKitUserContentManager*, const char*); - using webkit_user_script_new_t = WebKitUserScript* (*)(const char*, int, int, const char* const*, const char* const*); - 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*); + typedef void (*g_main_context_invoke_t)(void*, void*, void*); + typedef ulong (*g_signal_connect_data_t)(void*, const char*, void*, void*, void*, int); + typedef void (*gdk_set_allowed_backends_t)(const char*); + typedef void (*gtk_container_add_t)(GtkContainer*, GtkWidget*); + typedef gboolean (*gtk_init_check_t)(int*, char***); + typedef void (*gtk_main_t)(); + typedef void (*gtk_main_quit_t)(); + typedef Window (*gtk_plug_get_id_t)(GtkPlug*); + typedef GtkWidget* (*gtk_plug_new_t)(Window); + typedef void (*gtk_widget_show_all_t)(GtkWidget*); + typedef void (*gtk_window_move_t)(GtkWindow*, int, int); + typedef void (*gtk_window_set_default_size_t)(GtkWindow*, int, int); + typedef WebKitSettings* (*webkit_settings_new_t)(); + typedef void (*webkit_settings_set_enable_developer_extras_t)(WebKitSettings*, gboolean); + typedef void (*webkit_settings_set_enable_write_console_messages_to_stdout_t)(WebKitSettings*, gboolean); + typedef void (*webkit_settings_set_hardware_acceleration_policy_t)(WebKitSettings*, int); + typedef void (*webkit_settings_set_javascript_can_access_clipboard_t)(WebKitSettings*, gboolean); + typedef void (*webkit_user_content_manager_add_script_t)(WebKitUserContentManager*, WebKitUserScript*); + typedef gboolean (*webkit_user_content_manager_register_script_message_handler_t)(WebKitUserContentManager*, const char*); + typedef WebKitUserScript* (*webkit_user_script_new_t)(const char*, int, int, const char* const*, const char* const*); + typedef void* (*webkit_web_view_evaluate_javascript_t)(WebKitWebView*, const char*, ssize_t, const char*, const char*, void*, void*, void*); + typedef WebKitUserContentManager* (*webkit_web_view_get_user_content_manager_t)(WebKitWebView*); + typedef void (*webkit_web_view_load_uri_t)(WebKitWebView*, const char*); + typedef GtkWidget* (*webkit_web_view_new_with_settings_t)(WebKitSettings*); + typedef void* (*webkit_web_view_run_javascript_t)(WebKitWebView*, const char*, void*, void*, void*); + typedef void (*webkit_web_view_set_background_color_t)(WebKitWebView*, const double*); CSYM(g_main_context_invoke_t, g_main_context_invoke) CSYM(g_signal_connect_data_t, g_signal_connect_data) @@ -1047,8 +1126,11 @@ static bool gtk3(Display* const display, gdk_set_allowed_backends("x11"); - if (! gtk_init_check (nullptr, nullptr)) + if (! gtk_init_check(nullptr, nullptr)) + { + d_stderr("WebView gtk_init_check failed"); return false; + } GtkWidget* const window = gtk_plug_new(winId); DISTRHO_SAFE_ASSERT_RETURN(window != nullptr, false); @@ -1103,180 +1185,428 @@ 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); - }; + struct Gtk3WebFramework : WebFramework { + const char* const _url; + WebViewRingBuffer* const _shmptr; + GtkWidget* const _webview; + const webkit_web_view_evaluate_javascript_t _webkit_web_view_evaluate_javascript; + const webkit_web_view_run_javascript_t _webkit_web_view_run_javascript; + const webkit_web_view_load_uri_t _webkit_web_view_load_uri; + const gtk_main_quit_t _gtk_main_quit; + const g_main_context_invoke_t _g_main_context_invoke; + + Gtk3WebFramework(const char* const url, + WebViewRingBuffer* const shmptr, + GtkWidget* const webview, + const webkit_web_view_evaluate_javascript_t webkit_web_view_evaluate_javascript, + const webkit_web_view_run_javascript_t webkit_web_view_run_javascript, + const webkit_web_view_load_uri_t webkit_web_view_load_uri, + const gtk_main_quit_t gtk_main_quit, + const g_main_context_invoke_t g_main_context_invoke) + : _url(url), + _shmptr(shmptr), + _webview(webview), + _webkit_web_view_evaluate_javascript(webkit_web_view_evaluate_javascript), + _webkit_web_view_run_javascript(webkit_web_view_run_javascript), + _webkit_web_view_load_uri(webkit_web_view_load_uri), + _gtk_main_quit(gtk_main_quit), + _g_main_context_invoke(g_main_context_invoke) {} + + void evaluate(const char* const js) override + { + 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); - }; + void reload() override + { + _webkit_web_view_load_uri(WEBKIT_WEB_VIEW(_webview), _url); + } - terminateFn = [=](){ - d_stdout("terminateFn"); - static bool quit = true; - if (quit) + void terminate() override { - quit = false; - gtk_main_quit(); + if (running) + { + running = false; + webview_wake(&_shmptr->client.sem); + _gtk_main_quit(); + } } - }; - wakeFn = [=](WebViewRingBuffer* const rb){ - g_main_context_invoke(NULL, G_CALLBACK(gtk3_idle), rb); + void wake(WebViewRingBuffer* const rb) override + { + _g_main_context_invoke(NULL, G_CALLBACK(web_wake_idle), rb); + } }; + Gtk3WebFramework webFrameworkObj(url, + shmptr, + webview, + webkit_web_view_evaluate_javascript, + webkit_web_view_run_javascript, + webkit_web_view_load_uri, + gtk_main_quit, + g_main_context_invoke); + + webFramework = &webFrameworkObj; + // notify server we started ok webview_wake(&shmptr->server.sem); + d_stdout("WebView gtk3 main loop started"); + gtk_main(); - d_stdout("quit"); + + d_stdout("WebView gtk3 main loop quit"); dlclose(lib); return true; } -#if 0 // ----------------------------------------------------------------------------------------------------------- -// qt5webengine variant +// qt common code + +#define TRACE d_stdout("%04d: %s", __LINE__, __PRETTY_FUNCTION__); -static bool qt5webengine(const Window winId, const double scaleFactor, const char* const url) +class QObject { - void* lib; - if ((lib = dlopen("libQt5WebEngineWidgets.so.5", RTLD_NOW|RTLD_GLOBAL)) == nullptr || - (lib = dlopen("libQt5WebEngineWidgets.so", RTLD_NOW|RTLD_GLOBAL)) == nullptr) - return false; +public: + QObject(QObject* parent = nullptr) + { + static void (*m)(QObject*, QObject*) = reinterpret_cast(dlsym( + nullptr, "_ZN7QObjectC1EPS_")); + m(this, parent); + } - using QApplication__init_t = void (*)(QApplication*, int&, char**, int); - using QApplication_exec_t = void (*)(); - using QApplication_setAttribute_t = void (*)(Qt::ApplicationAttribute, bool); - using QString__init_t = void (*)(void*, const QChar*, ptrdiff_t); - using QUrl__init_t = void (*)(void*, const QString&, int /* QUrl::ParsingMode */); - using QWebEngineView__init_t = void (*)(QWebEngineView*, void*); - using QWebEngineView_move_t = void (*)(QWebEngineView*, const QPoint&); - using QWebEngineView_resize_t = void (*)(QWebEngineView*, const QSize&); - using QWebEngineView_setUrl_t = void (*)(QWebEngineView*, const QUrl&); - using QWebEngineView_show_t = void (*)(QWebEngineView*); - using QWebEngineView_winId_t = ulonglong (*)(QWebEngineView*); - using QWebEngineView_windowHandle_t = QWindow* (*)(QWebEngineView*); - using QWindow_fromWinId_t = QWindow* (*)(ulonglong); - using QWindow_setParent_t = void (*)(QWindow*, void*); + virtual const QMetaObject* metaObject() const + { + static const QMetaObject* (*m)(const QObject*) = reinterpret_cast(dlsym( + nullptr, "_ZNK7QObject10metaObjectEv")); + return m(this); + } - CPPSYM(QApplication__init_t, QApplication__init, _ZN12QApplicationC1ERiPPci) - CPPSYM(QApplication_exec_t, QApplication_exec, _ZN15QGuiApplication4execEv) - CPPSYM(QApplication_setAttribute_t, QApplication_setAttribute, _ZN16QCoreApplication12setAttributeEN2Qt20ApplicationAttributeEb) - CPPSYM(QString__init_t, QString__init, _ZN7QStringC2EPK5QChari) - CPPSYM(QUrl__init_t, QUrl__init, _ZN4QUrlC1ERK7QStringNS_11ParsingModeE) - CPPSYM(QWebEngineView__init_t, QWebEngineView__init, _ZN14QWebEngineViewC1EP7QWidget) - CPPSYM(QWebEngineView_move_t, QWebEngineView_move, _ZN7QWidget4moveERK6QPoint) - CPPSYM(QWebEngineView_resize_t, QWebEngineView_resize, _ZN7QWidget6resizeERK5QSize) - CPPSYM(QWebEngineView_setUrl_t, QWebEngineView_setUrl, _ZN14QWebEngineView6setUrlERK4QUrl) - CPPSYM(QWebEngineView_show_t, QWebEngineView_show, _ZN7QWidget4showEv) - CPPSYM(QWebEngineView_winId_t, QWebEngineView_winId, _ZNK7QWidget5winIdEv) - CPPSYM(QWebEngineView_windowHandle_t, QWebEngineView_windowHandle, _ZNK7QWidget12windowHandleEv) - CPPSYM(QWindow_fromWinId_t, QWindow_fromWinId, _ZN7QWindow9fromWinIdEy) - CPPSYM(QWindow_setParent_t, QWindow_setParent, _ZN7QWindow9setParentEPS_) + virtual void* qt_metacast(const char*) { return 0; } + virtual int qt_metacall(void* /* QMetaObject::Call */, int, void**) { return 0; } + virtual ~QObject() {} - unsetenv("QT_FONT_DPI"); - unsetenv("QT_SCREEN_SCALE_FACTORS"); - unsetenv("QT_USE_PHYSICAL_DPI"); - setenv("QT_AUTO_SCREEN_SCALE_FACTOR", "0", 1); + virtual bool event(QEvent* e) + { + static bool (*m)(QObject*, QEvent*) = reinterpret_cast(dlsym( + nullptr, "_ZN7QObject5eventEP6QEvent")); + return m(this, e); + } - char scale[8] = {}; - std::snprintf(scale, 7, "%.2f", scaleFactor); - setenv("QT_SCALE_FACTOR", scale, 1); + virtual bool eventFilter(QObject* watched, QEvent* event) + { + static bool (*m)(QObject*, QObject*, QEvent*) = reinterpret_cast(dlsym( + nullptr, "_ZN7QObject11eventFilterEPS_P6QEvent")); + return m(this, watched, event); + } - QApplication_setAttribute(Qt::AA_X11InitThreads, true); - QApplication_setAttribute(Qt::AA_EnableHighDpiScaling, true); - QApplication_setAttribute(Qt::AA_UseHighDpiPixmaps, true); + virtual void timerEvent(QTimerEvent* event) + { + static void (*m)(QObject*, QTimerEvent*) = reinterpret_cast(dlsym( + nullptr, "_ZN7QObject10timerEventEP11QTimerEvent")); + m(this, event); + } - static int argc = 0; - static char* argv[] = { nullptr }; + virtual void childEvent(QChildEvent* event) + { + static void (*m)(QObject*, QChildEvent*) = reinterpret_cast(dlsym( + nullptr, "_ZN7QObject10childEventEP11QChildEvent")); + m(this, event); + } - uint8_t _app[64]; // sizeof(QApplication) == 16 - QApplication* const app = reinterpret_cast(_app); - QApplication__init(app, argc, argv, 0); + virtual void customEvent(QEvent* event) + { + static void (*m)(QObject*, QEvent*) = reinterpret_cast(dlsym( + nullptr, "_ZN7QObject11customEventEP6QEvent")); + m(this, event); + } - uint8_t _qstrurl[32]; // sizeof(QString) == 8 - QString* const qstrurl(reinterpret_cast(_qstrurl)); + virtual void connectNotify(const QMetaMethod& signal) + { + static void (*m)(QObject*, const QMetaMethod&) = reinterpret_cast(dlsym( + nullptr, "_ZN7QObject13connectNotifyERK11QMetaMethod")); + m(this, signal); + } + virtual void disconnectNotify(const QMetaMethod& signal) { - const size_t url_len = std::strlen(url); - QChar* const url_qchar = new QChar[url_len + 1]; + static void (*m)(QObject*, const QMetaMethod&) = reinterpret_cast(dlsym( + nullptr, "_ZN7QObject16disconnectNotifyERK11QMetaMethod")); + m(this, signal); + } - for (size_t i = 0; i < url_len; ++i) - url_qchar[i] = QChar(url[i]); +private: + uint8_t _[8 * 2]; +}; - url_qchar[url_len] = 0; +class QWebChannelAbstractTransport : public QObject +{ +protected: + const QMetaObject* metaObject() const override + { + static const QMetaObject* (*m)(const QObject*) = reinterpret_cast(dlsym( + nullptr, "_ZNK28QWebChannelAbstractTransport10metaObjectEv")); + return m(this); + } + + void* qt_metacast(const char*) override { return 0; } + int qt_metacall(void* /* QMetaObject::Call */, int, void**) override { return 0; } + ~QWebChannelAbstractTransport() override {} - QString__init(qstrurl, url_qchar, url_len); +public: + QWebChannelAbstractTransport(QObject* parent = nullptr) + : QObject(parent) + { + static void (*m)(QObject*, QObject*) = reinterpret_cast(dlsym( + nullptr, "_ZN28QWebChannelAbstractTransportC1EP7QObject")); + m(this, parent); } - uint8_t _qurl[32]; // sizeof(QUrl) == 8 - QUrl* const qurl(reinterpret_cast(_qurl)); - QUrl__init(qurl, *qstrurl, 1 /* QUrl::StrictMode */); + virtual void sendMessage(const QJsonObject&) = 0; +}; - uint8_t _webview[128]; // sizeof(QWebEngineView) == 56 - QWebEngineView* const webview = reinterpret_cast(_webview); - QWebEngineView__init(webview, nullptr); +// ----------------------------------------------------------------------------------------------------------- - QWebEngineView_move(webview, QPoint(0, kVerticalOffset)); - QWebEngineView_resize(webview, QSize(DISTRHO_UI_DEFAULT_WIDTH, DISTRHO_UI_DEFAULT_HEIGHT - kVerticalOffset)); - QWebEngineView_winId(webview); - QWindow_setParent(QWebEngineView_windowHandle(webview), QWindow_fromWinId(winId)); - QWebEngineView_setUrl(webview, *qurl); - QWebEngineView_show(webview); +// QObject subclass for receiving events on main thread +class EventFilterQObject : public QWebChannelAbstractTransport +{ + QString qstrkey; + WebViewRingBuffer* const _rb; + bool isQt5; + +public: + EventFilterQObject(WebViewRingBuffer* const rb) + : QWebChannelAbstractTransport(), + _rb(rb), + isQt5(false) + { + void (*QString__init)(QString*, const QChar*, int) = + reinterpret_cast(dlsym(nullptr, "_ZN7QStringC2EPK5QCharx")); - reloadFn = [=](){ - QWebEngineView_setUrl(webview, *qurl); - }; + if (QString__init == nullptr) + { + isQt5 = true; + QString__init = reinterpret_cast(dlsym(nullptr, "_ZN7QStringC2EPK5QChari")); + } - terminateFn = [=](){ - // TODO - }; + const ushort key_qchar[] = { 'm', 0 }; + QString__init(&qstrkey, reinterpret_cast(key_qchar), 1); + } - QApplication_exec(); + void customEvent(QEvent*) override + { + web_wake_idle(_rb); + } - dlclose(lib); - return true; -} + void sendMessage(const QJsonObject& obj) override + { + static void (*QByteArray_clear)(QByteArray*) = + reinterpret_cast(dlsym(nullptr, "_ZN10QByteArray5clearEv")); + + static QJsonValue (*QJsonObject_value)(const QJsonObject*, const QString&) = + reinterpret_cast(dlsym(nullptr, "_ZNK11QJsonObject5valueERK7QString")); + + static void (*QJsonValue__deinit)(const QJsonValue*) = + reinterpret_cast(dlsym(nullptr, "_ZN10QJsonValueD1Ev")); + static QString (*QJsonValue_toString)(const QJsonValue*) = + reinterpret_cast(dlsym(nullptr, "_ZNK10QJsonValue8toStringEv")); + + static QString& (*QString_setRawData)(QString*, const QChar*, int) = + reinterpret_cast(dlsym(nullptr, "_ZN7QString10setRawDataEPK5QCharx")) ?: + reinterpret_cast(dlsym(nullptr, "_ZN7QString10setRawDataEPK5QChari")); + static QByteArray (*QString_toUtf8)(const QString*) = + reinterpret_cast(dlsym(nullptr, "_ZNK7QString6toUtf8Ev")) ?: + reinterpret_cast(dlsym(nullptr, "_ZN7QString13toUtf8_helperERKS_")); + + const QJsonValue json = QJsonObject_value(&obj, qstrkey); + QString qstrvalue = QJsonValue_toString(&json); + QByteArray data = QString_toUtf8(&qstrvalue); + + const uint8_t* const dptr = static_cast(*reinterpret_cast(data._)); + const intptr_t offset = isQt5 ? *reinterpret_cast(dptr + 16) : 16; + const char* const value = reinterpret_cast(dptr + offset); + + d_debug("js call received with data '%s'", value); + + const size_t len = std::strlen(value) + 1; + RingBufferControl rbctrl2; + rbctrl2.setRingBuffer(&_rb->server, false); + rbctrl2.writeUInt(kWebViewMessageCallback) && + rbctrl2.writeUInt(len) && + rbctrl2.writeCustomData(value, len); + rbctrl2.commitWrite(); + + // QByteArray and QString destructors are inlined and can't be called from here, call their next closest thing + QByteArray_clear(&data); + QString_setRawData(&qstrvalue, nullptr, 0); + + QJsonValue__deinit(&json); + } +}; -// ----------------------------------------------------------------------------------------------------------- -// qt6webengine variant (same as qt5 but `QString__init_t` has different arguments) +// -------------------------------------------------------------------------------------------------------------------- +// qt5webengine variant -static bool qt6webengine(const Window winId, const double scaleFactor, const char* const url) +static bool qtwebengine(const int qtVersion, + Display* const display, + const Window winId, + const int x, + const int y, + const uint width, + const uint height, + double scaleFactor, + const char* const url, + const char* const initialJS, + WebViewRingBuffer* const shmptr) { void* lib; - if ((lib = dlopen("libQt6WebEngineWidgets.so.6", RTLD_NOW|RTLD_GLOBAL)) == nullptr || - (lib = dlopen("libQt6WebEngineWidgets.so", RTLD_NOW|RTLD_GLOBAL)) == nullptr) + switch (qtVersion) + { + case 5: + if ((lib = dlopen("libQt5WebEngineWidgets.so.5", RTLD_NOW|RTLD_GLOBAL)) == nullptr && + (lib = dlopen("libQt5WebEngineWidgets.so", RTLD_NOW|RTLD_GLOBAL)) == nullptr) + { + d_stdout("WebView Qt5 platform not available: %s", dlerror()); + return false; + } + break; + case 6: + if ((lib = dlopen("libQt6WebEngineWidgets.so.6", RTLD_NOW|RTLD_GLOBAL)) == nullptr && + (lib = dlopen("libQt6WebEngineWidgets.so", RTLD_NOW|RTLD_GLOBAL)) == nullptr) + { + d_stdout("WebView Qt6 platform not available: %s", dlerror()); + return false; + } + break; + default: return false; + } - using QApplication__init_t = void (*)(QApplication*, int&, char**, int); - using QApplication_exec_t = void (*)(); - using QApplication_setAttribute_t = void (*)(Qt::ApplicationAttribute, bool); - using QString__init_t = void (*)(void*, const QChar*, long long); - using QUrl__init_t = void (*)(void*, const QString&, int /* QUrl::ParsingMode */); - using QWebEngineView__init_t = void (*)(QWebEngineView*, void*); - using QWebEngineView_move_t = void (*)(QWebEngineView*, const QPoint&); - using QWebEngineView_resize_t = void (*)(QWebEngineView*, const QSize&); - using QWebEngineView_setUrl_t = void (*)(QWebEngineView*, const QUrl&); - using QWebEngineView_show_t = void (*)(QWebEngineView*); - using QWebEngineView_winId_t = ulonglong (*)(QWebEngineView*); - using QWebEngineView_windowHandle_t = QWindow* (*)(QWebEngineView*); - using QWindow_fromWinId_t = QWindow* (*)(ulonglong); - using QWindow_setParent_t = void (*)(QWindow*, void*); + // Qt >= 6 uses int + void (*QByteArray__init)(QByteArray*, const char*, int) = + reinterpret_cast(dlsym(nullptr, "_ZN10QByteArrayC1EPKcx")) ?: + reinterpret_cast(dlsym(nullptr, "_ZN10QByteArrayC1EPKci")); + DISTRHO_SAFE_ASSERT_RETURN(QByteArray__init != nullptr, false); + + typedef void (*QString__init_t)(QString*, const QChar*, int); + const QString__init_t QString__init = + reinterpret_cast(dlsym(nullptr, "_ZN7QStringC2EPK5QCharx")) ?: + reinterpret_cast(dlsym(nullptr, "_ZN7QStringC2EPK5QChari")); + DISTRHO_SAFE_ASSERT_RETURN(QString__init != nullptr, false); + + void (*QWebEnginePage_setWebChannel)(QWebEnginePage*, QWebChannel*, uint) = + reinterpret_cast(dlsym( + nullptr, "_ZN14QWebEnginePage13setWebChannelEP11QWebChannelj")) ?: + reinterpret_cast(dlsym( + nullptr, "_ZN14QWebEnginePage13setWebChannelEP11QWebChannel")); + DISTRHO_SAFE_ASSERT_RETURN(QWebEnginePage_setWebChannel != nullptr, false); + + // Qt >= 6 has new function signature with lambdas +#ifdef DISTRHO_PROPER_CPP11_SUPPORT + typedef void (*QWebEnginePage_runJavaScript_t)(QWebEnginePage*, const QString&, uint, const std::function&); +#else + typedef void (*QWebEnginePage_runJavaScript_t)(QWebEnginePage*, const QString&, uint, const uintptr_t&); +#endif + typedef void (*QWebEnginePage_runJavaScript_compat_t)(QWebEnginePage*, const QString&); + + QWebEnginePage_runJavaScript_t QWebEnginePage_runJavaScript; + QWebEnginePage_runJavaScript_compat_t QWebEnginePage_runJavaScript_compat; + + if (qtVersion == 5) { + QWebEnginePage_runJavaScript = nullptr; + QWebEnginePage_runJavaScript_compat = reinterpret_cast(dlsym( + nullptr, "_ZN14QWebEnginePage13runJavaScriptERK7QString")); + DISTRHO_SAFE_ASSERT_RETURN(QWebEnginePage_runJavaScript_compat != nullptr, false); + } else { + QWebEnginePage_runJavaScript_compat = nullptr; + QWebEnginePage_runJavaScript = reinterpret_cast(dlsym( + nullptr, "_ZN14QWebEnginePage13runJavaScriptERK7QStringjRKSt8functionIFvRK8QVariantEE")); + DISTRHO_SAFE_ASSERT_RETURN(QWebEnginePage_runJavaScript != nullptr, false); + } + + typedef void (*QApplication__init_t)(QApplication*, int&, char**, int); + typedef void (*QApplication_exec_t)(); + typedef void (*QApplication_postEvent_t)(QObject*, QEvent*, int); + typedef void (*QApplication_quit_t)(); + typedef void (*QApplication_setAttribute_t)(int, bool); + typedef void (*QEvent__init_t)(QEvent*, int /* QEvent::Type */); + typedef QJsonValue (*QJsonObject_value_t)(const QJsonObject*, const QString &); + typedef QString (*QJsonValue_toString_t)(const QJsonValue*); + typedef void (*QUrl__init_t)(QUrl*, const QString&, int /* QUrl::ParsingMode */); + typedef void (*QWebChannel__init_t)(QWebChannel*, QObject*); + typedef void (*QWebChannel_registerObject_t)(QWebChannel*, const QString&, QObject*); + typedef void (*QWebEnginePage__init_t)(QWebEnginePage*, QWebEngineProfile*, QObject*); + typedef void (*QWebEnginePage_setBackgroundColor_t)(QWebEnginePage*, const QColor&); + typedef QWebChannel* (*QWebEnginePage_webChannel_t)(QWebEnginePage*); + typedef QWebEngineProfile* (*QWebEngineProfile_defaultProfile_t)(); + typedef void (*QWebEngineProfile_installUrlSchemeHandler_t)(QWebEngineProfile*, const QByteArray&, QWebEngineUrlSchemeHandler*); + typedef QWebEngineSettings* (*QWebEngineProfile_settings_t)(QWebEngineProfile*); + typedef QWebEngineScriptCollection* (*QWebEngineProfile_scripts_t)(QWebEngineProfile*); + typedef void (*QWebEngineScript__init_t)(QWebEngineScript*); + typedef void (*QWebEngineScript_setInjectionPoint_t)(QWebEngineScript*, int /* QWebEngineScript::InjectionPoint */); + typedef void (*QWebEngineScript_setRunsOnSubFrames_t)(QWebEngineScript*, bool); + typedef void (*QWebEngineScript_setSourceCode_t)(QWebEngineScript*, const QString &); + typedef void (*QWebEngineScript_setWorldId_t)(QWebEngineScript*, uint32_t); + typedef void (*QWebEngineScriptCollection_insert_t)(QWebEngineScriptCollection*, QWebEngineScript&); + typedef void (*QWebEngineSettings_setAttribute_t)(QWebEngineSettings*, int /* QWebEngineSettings::WebAttribute */, bool); + // typedef void (*QWebEngineUrlRequestJob_reply_t)(QWebEngineUrlRequestJob*, const QByteArray&, QIODevice*); + typedef void (*QWebEngineUrlScheme__init_t)(QWebEngineUrlScheme*, const QByteArray&); + typedef void (*QWebEngineUrlScheme_registerScheme_t)(QWebEngineUrlScheme&); + typedef void (*QWebEngineUrlScheme_setFlags_t)(QWebEngineUrlScheme*, int /* QWebEngineUrlScheme::Flags */); + typedef void (*QWebEngineUrlScheme_setSyntax_t)(QWebEngineUrlScheme*, int /* QWebEngineUrlScheme::Syntax */); + typedef void (*QWebEngineUrlSchemeHandler__init_t)(QObject*, QObject*); + typedef void (*QWebEngineView__init_t)(QWebEngineView*, QObject*); + typedef void (*QWebEngineView_move_t)(QWebEngineView*, const QPoint&); + typedef void (*QWebEngineView_resize_t)(QWebEngineView*, const QSize&); + typedef void (*QWebEngineView_setPage_t)(QWebEngineView*, QWebEnginePage*); + typedef void (*QWebEngineView_setUrl_t)(QWebEngineView*, const QUrl&); + typedef void (*QWebEngineView_show_t)(QWebEngineView*); + typedef ulonglong (*QWebEngineView_winId_t)(QWebEngineView*); + typedef QWindow* (*QWebEngineView_windowHandle_t)(QWebEngineView*); + typedef QWindow* (*QWindow_fromWinId_t)(ulonglong); + typedef void (*QWindow_setParent_t)(QWindow*, void*); CPPSYM(QApplication__init_t, QApplication__init, _ZN12QApplicationC1ERiPPci) CPPSYM(QApplication_exec_t, QApplication_exec, _ZN15QGuiApplication4execEv) + CPPSYM(QApplication_postEvent_t, QApplication_postEvent, _ZN16QCoreApplication9postEventEP7QObjectP6QEventi) + CPPSYM(QApplication_quit_t, QApplication_quit, _ZN16QCoreApplication4quitEv) CPPSYM(QApplication_setAttribute_t, QApplication_setAttribute, _ZN16QCoreApplication12setAttributeEN2Qt20ApplicationAttributeEb) - CPPSYM(QString__init_t, QString__init, _ZN7QStringC2EPK5QCharx) + CPPSYM(QEvent__init_t, QEvent__init, _ZN6QEventC1ENS_4TypeE) + CPPSYM(QJsonObject_value_t, QJsonObject_value, _ZNK11QJsonObject5valueERK7QString) + CPPSYM(QJsonValue_toString_t, QJsonValue_toString, _ZNK10QJsonValue8toStringEv) CPPSYM(QUrl__init_t, QUrl__init, _ZN4QUrlC1ERK7QStringNS_11ParsingModeE) + CPPSYM(QWebChannel__init_t, QWebChannel__init, _ZN11QWebChannelC1EP7QObject) + CPPSYM(QWebChannel_registerObject_t, QWebChannel_registerObject, _ZN11QWebChannel14registerObjectERK7QStringP7QObject) + CPPSYM(QWebEnginePage__init_t, QWebEnginePage__init, _ZN14QWebEnginePageC1EP17QWebEngineProfileP7QObject) + CPPSYM(QWebEnginePage_setBackgroundColor_t, QWebEnginePage_setBackgroundColor, _ZN14QWebEnginePage18setBackgroundColorERK6QColor) + CPPSYM(QWebEnginePage_webChannel_t, QWebEnginePage_webChannel, _ZNK14QWebEnginePage10webChannelEv) + CPPSYM(QWebEngineProfile_defaultProfile_t, QWebEngineProfile_defaultProfile, _ZN17QWebEngineProfile14defaultProfileEv) + CPPSYM(QWebEngineProfile_installUrlSchemeHandler_t, QWebEngineProfile_installUrlSchemeHandler, _ZN17QWebEngineProfile23installUrlSchemeHandlerERK10QByteArrayP26QWebEngineUrlSchemeHandler) + CPPSYM(QWebEngineProfile_settings_t, QWebEngineProfile_settings, _ZNK17QWebEngineProfile8settingsEv) + CPPSYM(QWebEngineProfile_scripts_t, QWebEngineProfile_scripts, _ZNK17QWebEngineProfile7scriptsEv) + CPPSYM(QWebEngineScript__init_t, QWebEngineScript__init, _ZN16QWebEngineScriptC1Ev) + CPPSYM(QWebEngineScript_setInjectionPoint_t, QWebEngineScript_setInjectionPoint, _ZN16QWebEngineScript17setInjectionPointENS_14InjectionPointE) + CPPSYM(QWebEngineScript_setRunsOnSubFrames_t, QWebEngineScript_setRunsOnSubFrames, _ZN16QWebEngineScript18setRunsOnSubFramesEb) + CPPSYM(QWebEngineScript_setSourceCode_t, QWebEngineScript_setSourceCode, _ZN16QWebEngineScript13setSourceCodeERK7QString) + CPPSYM(QWebEngineScript_setWorldId_t, QWebEngineScript_setWorldId, _ZN16QWebEngineScript10setWorldIdEj) + CPPSYM(QWebEngineScriptCollection_insert_t, QWebEngineScriptCollection_insert, _ZN26QWebEngineScriptCollection6insertERK16QWebEngineScript) + CPPSYM(QWebEngineSettings_setAttribute_t, QWebEngineSettings_setAttribute, _ZN18QWebEngineSettings12setAttributeENS_12WebAttributeEb) + // CPPSYM(QWebEngineUrlRequestJob_reply_t, QWebEngineUrlRequestJob_reply, _ZN23QWebEngineUrlRequestJob5replyERK10QByteArrayP9QIODevice) + CPPSYM(QWebEngineUrlScheme__init_t, QWebEngineUrlScheme__init, _ZN19QWebEngineUrlSchemeC1ERK10QByteArray) + CPPSYM(QWebEngineUrlScheme_registerScheme_t, QWebEngineUrlScheme_registerScheme, _ZN19QWebEngineUrlScheme14registerSchemeERKS_) + CPPSYM(QWebEngineUrlScheme_setFlags_t, QWebEngineUrlScheme_setFlags, _ZN19QWebEngineUrlScheme8setFlagsE6QFlagsINS_4FlagEE) + CPPSYM(QWebEngineUrlScheme_setSyntax_t, QWebEngineUrlScheme_setSyntax, _ZN19QWebEngineUrlScheme9setSyntaxENS_6SyntaxE) + CPPSYM(QWebEngineUrlSchemeHandler__init_t, QWebEngineUrlSchemeHandler__init, _ZN26QWebEngineUrlSchemeHandlerC1EP7QObject) CPPSYM(QWebEngineView__init_t, QWebEngineView__init, _ZN14QWebEngineViewC1EP7QWidget) CPPSYM(QWebEngineView_move_t, QWebEngineView_move, _ZN7QWidget4moveERK6QPoint) CPPSYM(QWebEngineView_resize_t, QWebEngineView_resize, _ZN7QWidget6resizeERK5QSize) + CPPSYM(QWebEngineView_setPage_t, QWebEngineView_setPage, _ZN14QWebEngineView7setPageEP14QWebEnginePage) CPPSYM(QWebEngineView_setUrl_t, QWebEngineView_setUrl, _ZN14QWebEngineView6setUrlERK4QUrl) CPPSYM(QWebEngineView_show_t, QWebEngineView_show, _ZN7QWidget4showEv) CPPSYM(QWebEngineView_winId_t, QWebEngineView_winId, _ZNK7QWidget5winIdEv) @@ -1287,67 +1617,300 @@ static bool qt6webengine(const Window winId, const double scaleFactor, const cha unsetenv("QT_FONT_DPI"); unsetenv("QT_SCREEN_SCALE_FACTORS"); unsetenv("QT_USE_PHYSICAL_DPI"); - setenv("QT_AUTO_SCREEN_SCALE_FACTOR", "0", 1); + setenv("QT_QPA_PLATFORM", "xcb", 1); + + if (qtVersion == 5) + { + setenv("QT_AUTO_SCREEN_SCALE_FACTOR", "0", 1); + } + else + { + setenv("QT_ENABLE_HIGHDPI_SCALING", "0", 1); + } char scale[8] = {}; std::snprintf(scale, 7, "%.2f", scaleFactor); setenv("QT_SCALE_FACTOR", scale, 1); - QApplication_setAttribute(Qt::AA_X11InitThreads, true); - QApplication_setAttribute(Qt::AA_EnableHighDpiScaling, true); - QApplication_setAttribute(Qt::AA_UseHighDpiPixmaps, true); + QByteArray urlSchemeName; + QByteArray__init(&urlSchemeName, "dpf", 3); + + constexpr const int urlSchemeFlags = 0 + | 0x1 /* QWebEngineUrlScheme::SecureScheme */ + | 0x2 /* QWebEngineUrlScheme::LocalScheme */ + | 0x4 /* QWebEngineUrlScheme::LocalAccessAllowed */ + | 0x8 /* QWebEngineUrlScheme::ServiceWorkersAllowed */ + | 0x40 /* QWebEngineUrlScheme::ContentSecurityPolicyIgnored */ + ; + QWebEngineUrlScheme urlScheme; + QWebEngineUrlScheme__init(&urlScheme, urlSchemeName); + QWebEngineUrlScheme_setSyntax(&urlScheme, 3 /* QWebEngineUrlScheme::Syntax::Path */); + QWebEngineUrlScheme_setFlags(&urlScheme, urlSchemeFlags); + QWebEngineUrlScheme_registerScheme(urlScheme); + + if (qtVersion == 5) + { + QApplication_setAttribute(10 /* Qt::AA_X11InitThreads */, true); + QApplication_setAttribute(13 /* Qt::AA_UseHighDpiPixmaps */, true); + QApplication_setAttribute(20 /* Qt::AA_EnableHighDpiScaling */, true); + } + + static int argc = 1; + static char argv0[] = "dpf-webview"; + static char* argv[] = { argv0, nullptr }; - static int argc = 0; - static char* argv[] = { nullptr }; + QApplication app; + QApplication__init(&app, argc, argv, 0); - uint8_t _app[64]; // sizeof(QApplication) == 16 - QApplication* const app = reinterpret_cast(_app); - QApplication__init(app, argc, argv, 0); + EventFilterQObject eventFilter(shmptr); + + QString qstrchannel, qstrmcode, qstrurl; + { + static constexpr const char* channel_src = "external"; + const size_t channel_len = std::strlen(channel_src); + ushort* const channel_qchar = new ushort[channel_len + 1]; - uint8_t _qstrurl[32]; // sizeof(QString) == 8 - QString* const qstrurl(reinterpret_cast(_qstrurl)); + for (size_t i = 0; i < channel_len; ++i) + channel_qchar[i] = channel_src[i]; + channel_qchar[channel_len] = 0; + + QString__init(&qstrchannel, reinterpret_cast(channel_qchar), channel_len); + + delete[] channel_qchar; + } + { + static constexpr const char* mcode_src = "\ + function postMessage(m){qt.webChannelTransport.send(JSON.stringify({\ + \"type\":6, \ + \"id\": \"WebSender\",\ + \"__QObject*__\": true,\ + \"object\": \"external\", \ + \"method\": \"sendMessage\",\ + \"args\":[{\"m\":m}], \ + }));}"; + const size_t mcode_len = std::strlen(mcode_src); + ushort* const mcode_qchar = new ushort[mcode_len + 1]; + + for (size_t i = 0; i < mcode_len; ++i) + mcode_qchar[i] = mcode_src[i]; + + mcode_qchar[mcode_len] = 0; + + QString__init(&qstrmcode, reinterpret_cast(mcode_qchar), mcode_len); + + delete[] mcode_qchar; + } { const size_t url_len = std::strlen(url); - QChar* const url_qchar = new QChar[url_len + 1]; + ushort* const url_qchar = new ushort[url_len + 1]; for (size_t i = 0; i < url_len; ++i) - url_qchar[i] = QChar(url[i]); + url_qchar[i] = url[i]; url_qchar[url_len] = 0; - QString__init(qstrurl, url_qchar, url_len); + QString__init(&qstrurl, reinterpret_cast(url_qchar), url_len); + + delete[] url_qchar; } - uint8_t _qurl[32]; // sizeof(QUrl) == 8 - QUrl* const qurl(reinterpret_cast(_qurl)); - QUrl__init(qurl, *qstrurl, 1 /* QUrl::StrictMode */); + QUrl qurl; + QUrl__init(&qurl, qstrurl, 1 /* QUrl::StrictMode */); - uint8_t _webview[128]; // sizeof(QWebEngineView) == 56 - QWebEngineView* const webview = reinterpret_cast(_webview); - QWebEngineView__init(webview, nullptr); + QWebEngineProfile* const profile = QWebEngineProfile_defaultProfile(); + QWebEngineScriptCollection* const scripts = QWebEngineProfile_scripts(profile); + QWebEngineSettings* const settings = QWebEngineProfile_settings(profile); - QWebEngineView_move(webview, QPoint(0, kVerticalOffset)); - QWebEngineView_resize(webview, QSize(DISTRHO_UI_DEFAULT_WIDTH, DISTRHO_UI_DEFAULT_HEIGHT - kVerticalOffset)); - QWebEngineView_winId(webview); - QWindow_setParent(QWebEngineView_windowHandle(webview), QWindow_fromWinId(winId)); - QWebEngineView_setUrl(webview, *qurl); - QWebEngineView_show(webview); + { + QWebEngineScript mscript; + QWebEngineScript__init(&mscript); + QWebEngineScript_setInjectionPoint(&mscript, 2 /* QWebEngineScript::DocumentCreation */); + QWebEngineScript_setRunsOnSubFrames(&mscript, true); + QWebEngineScript_setSourceCode(&mscript, qstrmcode); + QWebEngineScript_setWorldId(&mscript, 0 /* QWebEngineScript::MainWorld */); + QWebEngineScriptCollection_insert(scripts, mscript); + } - reloadFn = [=](){ - QWebEngineView_setUrl(webview, *qurl); - }; + if (initialJS != nullptr) + { + QString qstrcode; + { + const size_t code_len = std::strlen(initialJS); + ushort* const code_qchar = new ushort[code_len + 1]; + + for (size_t i = 0; i < code_len; ++i) + code_qchar[i] = initialJS[i]; + + code_qchar[code_len] = 0; + + QString__init(&qstrcode, reinterpret_cast(code_qchar), code_len); + } + + QWebEngineScript script; + QWebEngineScript__init(&script); + QWebEngineScript_setInjectionPoint(&script, 2 /* QWebEngineScript::DocumentCreation */); + QWebEngineScript_setRunsOnSubFrames(&script, true); + QWebEngineScript_setSourceCode(&script, qstrcode); + QWebEngineScript_setWorldId(&script, 0 /* QWebEngineScript::MainWorld */); + QWebEngineScriptCollection_insert(scripts, script); + } + + QWebEngineSettings_setAttribute(settings, 3 /* QWebEngineSettings::JavascriptCanAccessClipboard */, true); + QWebEngineSettings_setAttribute(settings, 6 /* QWebEngineSettings::LocalContentCanAccessRemoteUrls */, true); + QWebEngineSettings_setAttribute(settings, 9 /* QWebEngineSettings::LocalContentCanAccessFileUrls */, true); + QWebEngineSettings_setAttribute(settings, 28 /* QWebEngineSettings::JavascriptCanPaste */, true); + + QWebEngineView webview; + QWebEngineView__init(&webview, nullptr); + + QWebEnginePage page; + QWebEnginePage__init(&page, profile, reinterpret_cast(&webview)); + // QWebEnginePage_setBackgroundColor(&page, QColor{0,0,0,0}); + + QWebChannel channel; + QWebChannel__init(&channel, reinterpret_cast(&webview)); + QWebChannel_registerObject(&channel, qstrchannel, &eventFilter); + QWebEnginePage_setWebChannel(&page, &channel, 0); + + QWebEngineView_move(&webview, QPoint(x, y)); + QWebEngineView_resize(&webview, QSize(static_cast(width), static_cast(height))); + QWebEngineView_winId(&webview); + QWindow_setParent(QWebEngineView_windowHandle(&webview), QWindow_fromWinId(winId)); + + QWebEngineView_setPage(&webview, &page); + QWebEngineView_setUrl(&webview, qurl); + + // FIXME Qt6 seems to need some forcing.. + if (qtVersion >= 6) + { + XReparentWindow(display, QWebEngineView_winId(&webview), winId, x, y); + XFlush(display); + } + + QWebEngineView_show(&webview); + + struct QtWebFramework : WebFramework { + const int _qtVersion; + WebViewRingBuffer* const _shmptr; + const QUrl& _qurl; + QWebEnginePage& _page; + QWebEngineView& _webview; + EventFilterQObject& _eventFilter; + const QString__init_t _QString__init; + const QWebEnginePage_runJavaScript_compat_t _QWebEnginePage_runJavaScript_compat; + const QWebEnginePage_runJavaScript_t _QWebEnginePage_runJavaScript; + const QWebEngineView_setUrl_t _QWebEngineView_setUrl; + const QApplication_quit_t _QApplication_quit; + const QEvent__init_t _QEvent__init; + const QApplication_postEvent_t _QApplication_postEvent; + + QtWebFramework(const int qtVersion, + WebViewRingBuffer* const shmptr, + const QUrl& qurl, + QWebEnginePage& page, + QWebEngineView& webview, + EventFilterQObject& eventFilter, + const QString__init_t QString__init, + const QWebEnginePage_runJavaScript_compat_t QWebEnginePage_runJavaScript_compat, + const QWebEnginePage_runJavaScript_t QWebEnginePage_runJavaScript, + const QWebEngineView_setUrl_t QWebEngineView_setUrl, + const QApplication_quit_t QApplication_quit, + const QEvent__init_t QEvent__init, + const QApplication_postEvent_t QApplication_postEvent) + : _qtVersion(qtVersion), + _shmptr(shmptr), + _qurl(qurl), + _page(page), + _webview(webview), + _eventFilter(eventFilter), + _QString__init(QString__init), + _QWebEnginePage_runJavaScript_compat(QWebEnginePage_runJavaScript_compat), + _QWebEnginePage_runJavaScript(QWebEnginePage_runJavaScript), + _QWebEngineView_setUrl(QWebEngineView_setUrl), + _QApplication_quit(QApplication_quit), + _QEvent__init(QEvent__init), + _QApplication_postEvent(QApplication_postEvent) {} + + void evaluate(const char* const js) override + { + QString qstrjs; + { + const size_t js_len = std::strlen(js); + ushort* const js_qchar = new ushort[js_len + 1]; + + for (size_t i = 0; i < js_len; ++i) + js_qchar[i] = js[i]; + + js_qchar[js_len] = 0; + + _QString__init(&qstrjs, reinterpret_cast(js_qchar), js_len); + } + + if (_qtVersion == 5) + _QWebEnginePage_runJavaScript_compat(&_page, qstrjs); + else + _QWebEnginePage_runJavaScript(&_page, qstrjs, 0, + #ifdef DISTRHO_PROPER_CPP11_SUPPORT + {} + #else + 0 + #endif + ); + } - terminateFn = [=](){ - // TODO + void reload() override + { + _QWebEngineView_setUrl(&_webview, _qurl); + } + + void terminate() override + { + if (running) + { + running = false; + webview_wake(&_shmptr->client.sem); + _QApplication_quit(); + } + } + + void wake(WebViewRingBuffer*) override + { + // NOTE event pointer is deleted by Qt + QEvent* const qevent = new QEvent; + _QEvent__init(qevent, 1000 /* QEvent::User */); + _QApplication_postEvent(&_eventFilter, qevent, 1 /* Qt::HighEventPriority */); + } }; + QtWebFramework webFrameworkObj(qtVersion, + shmptr, + qurl, + page, + webview, + eventFilter, + QString__init, + QWebEnginePage_runJavaScript_compat, + QWebEnginePage_runJavaScript, + QWebEngineView_setUrl, + QApplication_quit, + QEvent__init, + QApplication_postEvent); + + webFramework = &webFrameworkObj; + + // notify server we started ok + webview_wake(&shmptr->server.sem); + + d_stdout("WebView Qt%d main loop started", qtVersion); + QApplication_exec(); + d_stdout("WebView Qt%d main loop quit", qtVersion); + dlclose(lib); return true; } -#endif // ----------------------------------------------------------------------------------------------------------- // startup via ld-linux @@ -1357,7 +1920,7 @@ static void signalHandler(const int sig) switch (sig) { case SIGTERM: - terminateFn(); + webFramework->terminate(); break; } } @@ -1366,13 +1929,10 @@ static void* threadHandler(void* const ptr) { WebViewRingBuffer* const shmptr = static_cast(ptr); - // TODO wait until page is loaded, or something better - d_sleep(1); - - while (shmptr->valid) + while (running && shmptr->valid) { - if (webview_timedwait(&shmptr->client.sem)) - wakeFn(shmptr); + if (webview_timedwait(&shmptr->client.sem) && running) + webFramework->wake(shmptr); } return nullptr; @@ -1380,14 +1940,14 @@ static void* threadHandler(void* const ptr) 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; } + d_stdout("starting... %d '%s' '%s'", argc, argv[1], argv[2]); + uselocale(newlocale(LC_NUMERIC_MASK, "C", nullptr)); Display* const display = XOpenDisplay(nullptr); @@ -1431,7 +1991,7 @@ int dpf_webview_start(const int argc, char* argv[]) { DISTRHO_SAFE_ASSERT_RETURN(rbctrl.readUInt() == kWebViewMessageInitData, 1); - hasInitialData = true; + hasInitialData = running = true; winId = rbctrl.readULong(); width = rbctrl.readUInt(); height = rbctrl.readUInt(); @@ -1454,20 +2014,30 @@ int dpf_webview_start(const int argc, char* argv[]) pthread_t thread; if (hasInitialData && pthread_create(&thread, nullptr, threadHandler, shmptr) == 0) { + d_stdout("WebView IPC in place, starting engine..."); + 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, initJS, shmptr); + if (! qtwebengine(5, display, winId, x, y, width, height, scaleFactor, url, initJS, shmptr) && + ! qtwebengine(6, display, winId, x, y, width, height, scaleFactor, url, initJS, shmptr) && + ! gtk3(display, winId, x, y, width, height, scaleFactor, url, initJS, shmptr)) + { + d_stderr("Failed to find usable WebView platform"); + } - shmptr->valid = false; + shmptr->valid = running = false; pthread_join(thread, nullptr); } + else + { + d_stderr("Failed to setup WebView IPC"); + } + std::free(initJS); munmap(shmptr, sizeof(WebViewRingBuffer)); close(shmfd); diff --git a/dpf/distrho/extra/WebViewImpl.hpp b/dpf/distrho/extra/WebViewImpl.hpp index 1b4f819..cd18591 100644 --- a/dpf/distrho/extra/WebViewImpl.hpp +++ b/dpf/distrho/extra/WebViewImpl.hpp @@ -86,6 +86,7 @@ struct WebViewOptions { Provided metrics must have scale factor pre-applied. + @p url: The URL to open, assumed to be in encoded form (e.g spaces converted to %20) @p windowId: The native window id to attach this view to (X11 Window, HWND or NSView*) @p scaleFactor: Scale factor in use @p options: Extra options, optional diff --git a/dpf/distrho/src/DistrhoDefines.h b/dpf/distrho/src/DistrhoDefines.h index 3b1d97a..4250f29 100644 --- a/dpf/distrho/src/DistrhoDefines.h +++ b/dpf/distrho/src/DistrhoDefines.h @@ -215,6 +215,12 @@ private: \ #define STRINGIFY2(s) #s #define STRINGIFY(s) STRINGIFY2(s) +#ifdef DISTRHO_PROPER_CPP11_SUPPORT +#define CPP_AGGREGATE_INIT(ClassName) ClassName +#else +#define CPP_AGGREGATE_INIT(ClassName) (ClassName) +#endif + /* Useful typedefs */ typedef unsigned char uchar; typedef unsigned short int ushort; diff --git a/dpf/distrho/src/DistrhoPluginAU.cpp b/dpf/distrho/src/DistrhoPluginAU.cpp index 3276c03..4262ca2 100644 --- a/dpf/distrho/src/DistrhoPluginAU.cpp +++ b/dpf/distrho/src/DistrhoPluginAU.cpp @@ -2038,7 +2038,7 @@ public: case 0x9: case 0xD: // unsupported - kAudioUnitErr_InvalidPropertyValue; + return kAudioUnitErr_InvalidPropertyValue; case 0x1: case 0x2: case 0x3: diff --git a/dpf/distrho/src/DistrhoPluginCLAP.cpp b/dpf/distrho/src/DistrhoPluginCLAP.cpp index 04933eb..498957e 100644 --- a/dpf/distrho/src/DistrhoPluginCLAP.cpp +++ b/dpf/distrho/src/DistrhoPluginCLAP.cpp @@ -171,12 +171,9 @@ struct ClapEventQueue ClapEventQueue() #if DISTRHO_PLUGIN_HAS_UI && DISTRHO_PLUGIN_WANT_MIDI_INPUT - : fNotesBuffer(StackBuffer_INIT) + : fNotesBuffer(CPP_AGGREGATE_INIT(SmallStackBuffer){0, 0, 0, false, {0}}) #endif { - #if DISTRHO_PLUGIN_HAS_UI && DISTRHO_PLUGIN_WANT_MIDI_INPUT && ! defined(DISTRHO_PROPER_CPP11_SUPPORT) - std::memset(&fNotesBuffer, 0, sizeof(fNotesBuffer)); - #endif #if DISTRHO_PLUGIN_WANT_PROGRAMS fCurrentProgram = 0; #endif diff --git a/dpf/distrho/src/DistrhoPluginVST2.cpp b/dpf/distrho/src/DistrhoPluginVST2.cpp index b9b9926..686341a 100644 --- a/dpf/distrho/src/DistrhoPluginVST2.cpp +++ b/dpf/distrho/src/DistrhoPluginVST2.cpp @@ -117,13 +117,10 @@ struct ParameterAndNotesHelper #if DISTRHO_PLUGIN_HAS_UI , parameterChecks(nullptr) #if DISTRHO_PLUGIN_WANT_MIDI_INPUT - , notesRingBuffer(StackBuffer_INIT) + , notesRingBuffer(CPP_AGGREGATE_INIT(SmallStackBuffer){0, 0, 0, false, {0}}) #endif #endif { - #if DISTRHO_PLUGIN_HAS_UI && DISTRHO_PLUGIN_WANT_MIDI_INPUT && ! defined(DISTRHO_PROPER_CPP11_SUPPORT) - std::memset(¬esRingBuffer, 0, sizeof(notesRingBuffer)); - #endif } virtual ~ParameterAndNotesHelper() diff --git a/dpf/distrho/src/DistrhoPluginVST3.cpp b/dpf/distrho/src/DistrhoPluginVST3.cpp index c1f2ae4..d26ba75 100644 --- a/dpf/distrho/src/DistrhoPluginVST3.cpp +++ b/dpf/distrho/src/DistrhoPluginVST3.cpp @@ -359,14 +359,14 @@ class PluginVst3 midiEvent.size = 3; midiEvent.data[0] = 0x90 | (eventStorage.noteOn.channel & 0xf); midiEvent.data[1] = eventStorage.noteOn.pitch; - midiEvent.data[2] = std::max(0, std::min(127, (int)(eventStorage.noteOn.velocity * 127))); + midiEvent.data[2] = std::max(0, std::min(127, d_roundToIntPositive(eventStorage.noteOn.velocity * 127))); midiEvent.data[3] = 0; break; case NoteOff: midiEvent.size = 3; midiEvent.data[0] = 0x80 | (eventStorage.noteOff.channel & 0xf); midiEvent.data[1] = eventStorage.noteOff.pitch; - midiEvent.data[2] = std::max(0, std::min(127, (int)(eventStorage.noteOff.velocity * 127))); + midiEvent.data[2] = std::max(0, std::min(127, d_roundToIntPositive(eventStorage.noteOff.velocity * 127))); midiEvent.data[3] = 0; break; /* TODO @@ -377,7 +377,7 @@ class PluginVst3 midiEvent.size = 3; midiEvent.data[0] = 0xA0 | (eventStorage.polyPressure.channel & 0xf); midiEvent.data[1] = eventStorage.polyPressure.pitch; - midiEvent.data[2] = std::max(0, std::min(127, (int)(eventStorage.polyPressure.pressure * 127))); + midiEvent.data[2] = std::max(0, std::min(127, d_roundToIntPositive(eventStorage.polyPressure.pressure * 127))); midiEvent.data[3] = 0; break; case CC_Normal: @@ -469,7 +469,7 @@ class PluginVst3 { case 128: eventStorage.type = CC_ChannelPressure; - eventStorage.midi[1] = std::max(0, std::min(127, (int)(normalized * 127))); + eventStorage.midi[1] = std::max(0, std::min(127, d_roundToIntPositive(normalized * 127))); eventStorage.midi[2] = 0; break; case 129: @@ -480,7 +480,7 @@ class PluginVst3 default: eventStorage.type = CC_Normal; eventStorage.midi[1] = cc; - eventStorage.midi[2] = std::max(0, std::min(127, (int)(normalized * 127))); + eventStorage.midi[2] = std::max(0, std::min(127, d_roundToIntPositive(normalized * 127))); break; } @@ -4459,7 +4459,8 @@ struct dpf_component : v3_component_cpp { dpf_component* const component = *static_cast(self); PluginVst3* const vst3 = component->vst3; - DISTRHO_SAFE_ASSERT_RETURN(vst3 != nullptr, V3_NOT_INITIALIZED); + // It must be called *before* "initialize". + DISTRHO_SAFE_ASSERT_RETURN(vst3 == nullptr, V3_NOT_INITIALIZED); // TODO return V3_NOT_IMPLEMENTED; diff --git a/dpf/distrho/src/DistrhoUI.cpp b/dpf/distrho/src/DistrhoUI.cpp index a4e8112..f0c8d25 100644 --- a/dpf/distrho/src/DistrhoUI.cpp +++ b/dpf/distrho/src/DistrhoUI.cpp @@ -119,7 +119,7 @@ static double getDesktopScaleFactor(const uintptr_t parentWindowHandle) { const HMONITOR hMon = parentWindowHandle != 0 ? MonitorFromWindow((HWND)parentWindowHandle, MONITOR_DEFAULTTOPRIMARY) - : MonitorFromPoint(POINT{0,0}, MONITOR_DEFAULTTOPRIMARY); + : MonitorFromPoint(POINT(), MONITOR_DEFAULTTOPRIMARY); GetScaleFactorForMonitor(hMon, &scaleFactor); } @@ -200,24 +200,30 @@ PluginWindow& UI::PrivateData::createNextWindow(UI* const ui, uint width, uint h path += "/resources"; } + path.urlEncode(); + // TODO convert win32 paths to web - // TODO encode paths (e.g. %20 for space) WebViewOptions opts; opts.initialJS = "" -"editParameter = function(index, started){ postMessage('editparam '+index+' '+(started ? 1 : 0)) };" -"setParameterValue = function(index, value){ postMessage('setparam '+index+' '+value) };" +"editParameter = function(index, started){ postMessage('editparam ' + index + ' ' + (started ? '1' : '0')) };" +"setParameterValue = function(index, value){ postMessage('setparam ' + index + ' ' + value) };" #if DISTRHO_PLUGIN_WANT_STATE -"setState = function(key, value){ postMessage('setstate '+key+' '+value) };" -"requestStateFile = function(key){ postMessage('reqstatefile '+key) };" +"setState = function(key, value){ postMessage('setstate ' + key + ' ' + value) };" +"requestStateFile = function(key){ postMessage('reqstatefile ' + key) };" #endif #if DISTRHO_PLUGIN_WANT_MIDI_INPUT -"sendNote = function(channel, note, velocity){ postMessage('sendnote '+channel+' '+note+' '+velocity) };" +"sendNote = function(channel, note, velocity){ postMessage('sendnote ' + channel + ' ' + note + ' ' + velocity) };" #endif ; opts.callback = webViewMessageCallback; opts.callbackPtr = uiData; - uiData->webview = webViewCreate("file://" + path + "/index.html", uiData->winId, width, height, scaleFactor, opts); + uiData->webview = webViewCreate("file://" + path + "/index.html", + uiData->winId != 0 ? uiData->winId : uiData->window->getNativeWindowHandle(), + width, + height, + scaleFactor, + opts); #endif } // If there are no callbacks, this is most likely a temporary window, so ignore idle callbacks @@ -268,7 +274,7 @@ void UI::PrivateData::webViewMessageCallback(void* const arg, char* const msg) char* const key = msg + 9; char* const sep = std::strchr(key, ' '); DISTRHO_SAFE_ASSERT_RETURN(sep != nullptr,); - *sep = 0; + *sep = '\0'; char* const value = sep + 1; uiData->setStateCallback(key, value);