Signed-off-by: falkTX <falktx@falktx.com>web-ui
@@ -0,0 +1,63 @@ | |||
/* | |||
* DISTRHO Plugin Framework (DPF) | |||
* Copyright (C) 2012-2024 Filipe Coelho <falktx@falktx.com> | |||
* | |||
* 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. | |||
*/ | |||
#ifndef DGL_WEBVIEW_HPP_INCLUDED | |||
#define DGL_WEBVIEW_HPP_INCLUDED | |||
#include "TopLevelWidget.hpp" | |||
// -------------------------------------------------------------------------------------------------------------------- | |||
START_NAMESPACE_DISTRHO | |||
struct WebViewData; | |||
typedef WebViewData* WebViewHandle; | |||
END_NAMESPACE_DISTRHO | |||
START_NAMESPACE_DGL | |||
// -------------------------------------------------------------------------------------------------------------------- | |||
// WebViewWidget | |||
class WebViewWidget : public TopLevelWidget | |||
{ | |||
public: | |||
/** | |||
Constructor for a WebViewWidget. | |||
*/ | |||
explicit WebViewWidget(Window& windowToMapTo); | |||
/** | |||
Destructor. | |||
*/ | |||
~WebViewWidget() override; | |||
protected: | |||
void onResize(const ResizeEvent& ev) override; | |||
private: | |||
const DISTRHO_NAMESPACE::WebViewHandle webview; | |||
void onDisplay() {} | |||
DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(WebViewWidget) | |||
}; | |||
// -------------------------------------------------------------------------------------------------------------------- | |||
END_NAMESPACE_DGL | |||
#endif // DGL_WEBVIEW_HPP_INCLUDED |
@@ -0,0 +1,81 @@ | |||
/* | |||
* DISTRHO Plugin Framework (DPF) | |||
* Copyright (C) 2012-2024 Filipe Coelho <falktx@falktx.com> | |||
* | |||
* 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. | |||
*/ | |||
#include "../Web.hpp" | |||
#include "../distrho/extra/WebView.hpp" | |||
#include "TopLevelWidgetPrivateData.hpp" | |||
#include "WindowPrivateData.hpp" | |||
START_NAMESPACE_DGL | |||
// ----------------------------------------------------------------------- | |||
WebViewWidget::WebViewWidget(Window& windowToMapTo) | |||
: TopLevelWidget(windowToMapTo), | |||
webview(addWebView(windowToMapTo.getNativeWindowHandle(), | |||
0, 0, | |||
windowToMapTo.getWidth(), | |||
windowToMapTo.getHeight(), | |||
windowToMapTo.getScaleFactor())) {} | |||
WebViewWidget::~WebViewWidget() | |||
{ | |||
if (webview != nullptr) | |||
destroyWebView(webview); | |||
} | |||
void WebViewWidget::onResize(const ResizeEvent& ev) | |||
{ | |||
TopLevelWidget::onResize(ev); | |||
if (webview != nullptr) | |||
resizeWebView(webview, 0, 0, ev.size.getWidth(), ev.size.getHeight()); | |||
} | |||
// ----------------------------------------------------------------------- | |||
static void notImplemented(const char* const name) | |||
{ | |||
d_stderr2("web function not implemented: %s", name); | |||
} | |||
// ----------------------------------------------------------------------- | |||
void TopLevelWidget::PrivateData::display() | |||
{ | |||
} | |||
// ----------------------------------------------------------------------- | |||
void Window::PrivateData::renderToPicture(const char*, const GraphicsContext&, uint, uint) | |||
{ | |||
notImplemented("Window::PrivateData::renderToPicture"); | |||
} | |||
// ----------------------------------------------------------------------- | |||
const GraphicsContext& Window::PrivateData::getGraphicsContext() const noexcept | |||
{ | |||
return (const GraphicsContext&)graphicsContext; | |||
} | |||
// ----------------------------------------------------------------------- | |||
END_NAMESPACE_DGL | |||
// ----------------------------------------------------------------------- |
@@ -0,0 +1,276 @@ | |||
// SPDX-FileCopyrightText: 2023-2024 MOD Audio UG | |||
// SPDX-License-Identifier: AGPL-3.0-or-later | |||
#pragma once | |||
#include "extra/Sleep.hpp" | |||
#include "Time.hpp" | |||
#ifdef DISTRHO_OS_WINDOWS | |||
# include <string> | |||
# include <winsock2.h> | |||
# include <windows.h> | |||
#else | |||
# include <cerrno> | |||
# include <ctime> | |||
# include <signal.h> | |||
# include <sys/wait.h> | |||
#endif | |||
START_NAMESPACE_DISTRHO | |||
// ----------------------------------------------------------------------------------------------------------- | |||
class ChildProcess | |||
{ | |||
#ifdef _WIN32 | |||
PROCESS_INFORMATION pinfo = { INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE, 0, 0 }; | |||
#else | |||
pid_t pid = -1; | |||
#endif | |||
public: | |||
ChildProcess() | |||
{ | |||
} | |||
~ChildProcess() | |||
{ | |||
stop(); | |||
} | |||
#ifdef _WIN32 | |||
bool start(const char* const args[], const WCHAR* const envp) | |||
#else | |||
bool start(const char* const args[], char* const* const envp = nullptr) | |||
#endif | |||
{ | |||
#ifdef _WIN32 | |||
std::string cmd; | |||
for (uint i = 0; args[i] != nullptr; ++i) | |||
{ | |||
if (i != 0) | |||
cmd += " "; | |||
if (args[i][0] != '"' && std::strchr(args[i], ' ') != nullptr) | |||
{ | |||
cmd += "\""; | |||
cmd += args[i]; | |||
cmd += "\""; | |||
} | |||
else | |||
{ | |||
cmd += args[i]; | |||
} | |||
} | |||
wchar_t wcmd[PATH_MAX]; | |||
if (MultiByteToWideChar(CP_UTF8, 0, cmd.data(), -1, wcmd, PATH_MAX) <= 0) | |||
return false; | |||
STARTUPINFOW si = {}; | |||
si.cb = sizeof(si); | |||
d_stdout("will start process with args '%s'", cmd.data()); | |||
return CreateProcessW(nullptr, // lpApplicationName | |||
wcmd, // lpCommandLine | |||
nullptr, // lpProcessAttributes | |||
nullptr, // lpThreadAttributes | |||
TRUE, // bInheritHandles | |||
CREATE_NO_WINDOW | CREATE_UNICODE_ENVIRONMENT, // dwCreationFlags | |||
const_cast<LPWSTR>(envp), // lpEnvironment | |||
nullptr, // lpCurrentDirectory | |||
&si, // lpStartupInfo | |||
&pinfo) != FALSE; | |||
#else | |||
const pid_t ret = pid = vfork(); | |||
switch (ret) | |||
{ | |||
// child process | |||
case 0: | |||
if (envp != nullptr) | |||
execve(args[0], const_cast<char* const*>(args), envp); | |||
else | |||
execvp(args[0], const_cast<char* const*>(args)); | |||
d_stderr2("exec failed: %d:%s", errno, std::strerror(errno)); | |||
_exit(1); | |||
break; | |||
// error | |||
case -1: | |||
d_stderr2("vfork() failed: %d:%s", errno, std::strerror(errno)); | |||
break; | |||
} | |||
return ret > 0; | |||
#endif | |||
} | |||
void stop(const uint32_t timeoutInMilliseconds = 2000) | |||
{ | |||
const uint32_t timeout = d_gettime_ms() + timeoutInMilliseconds; | |||
bool sendTerminate = true; | |||
#ifdef _WIN32 | |||
if (pinfo.hProcess == INVALID_HANDLE_VALUE) | |||
return; | |||
const PROCESS_INFORMATION opinfo = pinfo; | |||
pinfo = { INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE, 0, 0 }; | |||
for (DWORD exitCode;;) | |||
{ | |||
if (GetExitCodeProcess(opinfo.hProcess, &exitCode) == FALSE || | |||
exitCode != STILL_ACTIVE || | |||
WaitForSingleObject(opinfo.hProcess, 0) != WAIT_TIMEOUT) | |||
{ | |||
CloseHandle(opinfo.hThread); | |||
CloseHandle(opinfo.hProcess); | |||
return; | |||
} | |||
if (sendTerminate) | |||
{ | |||
sendTerminate = false; | |||
TerminateProcess(opinfo.hProcess, ERROR_BROKEN_PIPE); | |||
} | |||
if (d_gettime_ms() < timeout) | |||
{ | |||
d_msleep(5); | |||
continue; | |||
} | |||
d_stderr("ChildProcess::stop() - timed out"); | |||
TerminateProcess(opinfo.hProcess, 9); | |||
d_msleep(5); | |||
CloseHandle(opinfo.hThread); | |||
CloseHandle(opinfo.hProcess); | |||
break; | |||
} | |||
#else | |||
if (pid <= 0) | |||
return; | |||
const pid_t opid = pid; | |||
pid = -1; | |||
for (pid_t ret;;) | |||
{ | |||
try { | |||
ret = ::waitpid(opid, nullptr, WNOHANG); | |||
} DISTRHO_SAFE_EXCEPTION_BREAK("waitpid"); | |||
switch (ret) | |||
{ | |||
case -1: | |||
if (errno == ECHILD) | |||
{ | |||
// success, child doesn't exist | |||
return; | |||
} | |||
else | |||
{ | |||
d_stderr("ChildProcess::stop() - waitpid failed: %d:%s", errno, std::strerror(errno)); | |||
return; | |||
} | |||
break; | |||
case 0: | |||
if (sendTerminate) | |||
{ | |||
sendTerminate = false; | |||
kill(opid, SIGTERM); | |||
} | |||
if (d_gettime_ms() < timeout) | |||
{ | |||
d_msleep(5); | |||
continue; | |||
} | |||
d_stderr("ChildProcess::stop() - timed out"); | |||
kill(opid, SIGKILL); | |||
waitpid(opid, nullptr, WNOHANG); | |||
break; | |||
default: | |||
if (ret == opid) | |||
{ | |||
// success | |||
return; | |||
} | |||
else | |||
{ | |||
d_stderr("ChildProcess::stop() - got wrong pid %i (requested was %i)", int(ret), int(opid)); | |||
return; | |||
} | |||
} | |||
break; | |||
} | |||
#endif | |||
} | |||
bool isRunning() | |||
{ | |||
#ifdef _WIN32 | |||
if (pinfo.hProcess == INVALID_HANDLE_VALUE) | |||
return false; | |||
DWORD exitCode; | |||
if (GetExitCodeProcess(pinfo.hProcess, &exitCode) == FALSE || | |||
exitCode != STILL_ACTIVE || | |||
WaitForSingleObject(pinfo.hProcess, 0) != WAIT_TIMEOUT) | |||
{ | |||
const PROCESS_INFORMATION opinfo = pinfo; | |||
pinfo = { INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE, 0, 0 }; | |||
CloseHandle(opinfo.hThread); | |||
CloseHandle(opinfo.hProcess); | |||
return false; | |||
} | |||
return true; | |||
#else | |||
if (pid <= 0) | |||
return false; | |||
const pid_t ret = ::waitpid(pid, nullptr, WNOHANG); | |||
if (ret == pid || (ret == -1 && errno == ECHILD)) | |||
{ | |||
pid = 0; | |||
return false; | |||
} | |||
return true; | |||
#endif | |||
} | |||
#ifndef _WIN32 | |||
void signal(const int sig) | |||
{ | |||
if (pid > 0) | |||
kill(pid, sig); | |||
} | |||
#endif | |||
void terminate() | |||
{ | |||
#ifdef _WIN32 | |||
if (pinfo.hProcess != INVALID_HANDLE_VALUE) | |||
TerminateProcess(pinfo.hProcess, 15); | |||
#else | |||
if (pid > 0) | |||
kill(pid, SIGTERM); | |||
#endif | |||
} | |||
DISTRHO_DECLARE_NON_COPYABLE(ChildProcess) | |||
}; | |||
// ----------------------------------------------------------------------------------------------------------- | |||
END_NAMESPACE_DISTRHO |
@@ -109,17 +109,17 @@ FileBrowserHandle fileBrowserCreate(bool isEmbed, | |||
in which case this idle function must not be called anymore for this handle. | |||
You can then call fileBrowserGetPath to know the selected file (or null if cancelled). | |||
*/ | |||
bool fileBrowserIdle(const FileBrowserHandle handle); | |||
bool fileBrowserIdle(FileBrowserHandle handle); | |||
/** | |||
Close and free the file browser dialog, handle must not be used afterwards. | |||
*/ | |||
void fileBrowserClose(const FileBrowserHandle handle); | |||
void fileBrowserClose(FileBrowserHandle handle); | |||
/** | |||
Get the path chosen by the user or null.@n | |||
Should only be called after fileBrowserIdle returns true. | |||
*/ | |||
const char* fileBrowserGetPath(const FileBrowserHandle handle); | |||
const char* fileBrowserGetPath(FileBrowserHandle handle); | |||
// -------------------------------------------------------------------------------------------------------------------- |
@@ -0,0 +1,127 @@ | |||
/* | |||
* DISTRHO Plugin Framework (DPF) | |||
* Copyright (C) 2012-2024 Filipe Coelho <falktx@falktx.com> | |||
* | |||
* 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. | |||
*/ | |||
#ifndef DISTRHO_TIME_HPP_INCLUDED | |||
#define DISTRHO_TIME_HPP_INCLUDED | |||
#include "DistrhoUtils.hpp" | |||
#ifdef DISTRHO_OS_WINDOWS | |||
# include <winsock2.h> | |||
# include <windows.h> | |||
# include <mmsystem.h> | |||
#else | |||
# include <ctime> | |||
#endif | |||
START_NAMESPACE_DISTRHO | |||
// ----------------------------------------------------------------------------------------------------------- | |||
// d_gettime_* | |||
/* | |||
* Get a monotonically-increasing time in milliseconds. | |||
*/ | |||
static inline | |||
uint32_t d_gettime_ms() noexcept | |||
{ | |||
#if defined(DISTRHO_OS_MAC) | |||
static const time_t s = clock_gettime_nsec_np(CLOCK_UPTIME_RAW) / 1000000; | |||
return (clock_gettime_nsec_np(CLOCK_UPTIME_RAW) / 1000000) - s; | |||
#elif defined(DISTRHO_OS_WINDOWS) | |||
return static_cast<uint32_t>(timeGetTime()); | |||
#else | |||
static struct { | |||
timespec ts; | |||
int r; | |||
uint32_t ms; | |||
} s = { {}, clock_gettime(CLOCK_MONOTONIC, &s.ts), static_cast<uint32_t>(s.ts.tv_sec * 1000 + | |||
s.ts.tv_nsec / 1000000) }; | |||
timespec ts; | |||
clock_gettime(CLOCK_MONOTONIC, &ts); | |||
return (ts.tv_sec * 1000 + ts.tv_nsec / 1000000) - s.ms; | |||
#endif | |||
} | |||
/* | |||
* Get a monotonically-increasing time in microseconds. | |||
*/ | |||
static inline | |||
uint64_t d_gettime_us() noexcept | |||
{ | |||
#if defined(DISTRHO_OS_MAC) | |||
static const uint64_t s = clock_gettime_nsec_np(CLOCK_UPTIME_RAW) / 1000; | |||
return (clock_gettime_nsec_np(CLOCK_UPTIME_RAW) / 1000) - s; | |||
#elif defined(DISTRHO_OS_WINDOWS) | |||
static struct { | |||
LARGE_INTEGER freq; | |||
LARGE_INTEGER counter; | |||
BOOL r1, r2; | |||
} s = { {}, {}, QueryPerformanceFrequency(&s.freq), QueryPerformanceCounter(&s.counter) }; | |||
LARGE_INTEGER counter; | |||
QueryPerformanceCounter(&counter); | |||
return (counter.QuadPart - s.counter.QuadPart) * 1000000 / s.freq.QuadPart; | |||
#else | |||
static struct { | |||
timespec ts; | |||
int r; | |||
uint64_t us; | |||
} s = { {}, clock_gettime(CLOCK_MONOTONIC, &s.ts), static_cast<uint64_t>(s.ts.tv_sec * 1000000 + | |||
s.ts.tv_nsec / 1000) }; | |||
timespec ts; | |||
clock_gettime(CLOCK_MONOTONIC, &ts); | |||
return (ts.tv_sec * 1000000 + ts.tv_nsec / 1000) - s.us; | |||
#endif | |||
} | |||
/* | |||
* Get a monotonically-increasing time in nanoseconds. | |||
*/ | |||
static inline | |||
uint64_t d_gettime_ns() noexcept | |||
{ | |||
#if defined(DISTRHO_OS_MAC) | |||
static const uint64_t s = clock_gettime_nsec_np(CLOCK_UPTIME_RAW); | |||
return clock_gettime_nsec_np(CLOCK_UPTIME_RAW) - s; | |||
#elif defined(DISTRHO_OS_WINDOWS) | |||
static struct { | |||
LARGE_INTEGER freq; | |||
LARGE_INTEGER counter; | |||
BOOL r1, r2; | |||
} s = { {}, {}, QueryPerformanceFrequency(&s.freq), QueryPerformanceCounter(&s.counter) }; | |||
LARGE_INTEGER counter; | |||
QueryPerformanceCounter(&counter); | |||
return (counter.QuadPart - s.counter.QuadPart) * 1000000000ULL / s.freq.QuadPart; | |||
#else | |||
static struct { | |||
timespec ts; | |||
int r; | |||
uint64_t ns; | |||
} s = { {}, clock_gettime(CLOCK_MONOTONIC, &s.ts), static_cast<uint64_t>(s.ts.tv_sec * 1000000000ULL + | |||
s.ts.tv_nsec) }; | |||
timespec ts; | |||
clock_gettime(CLOCK_MONOTONIC, &ts); | |||
return (ts.tv_sec * 1000000000ULL + ts.tv_nsec) - s.ns; | |||
#endif | |||
} | |||
// ----------------------------------------------------------------------------------------------------------- | |||
END_NAMESPACE_DISTRHO | |||
#endif // DISTRHO_TIME_HPP_INCLUDED |
@@ -0,0 +1,28 @@ | |||
/* | |||
* DISTRHO Plugin Framework (DPF) | |||
* Copyright (C) 2012-2024 Filipe Coelho <falktx@falktx.com> | |||
* | |||
* 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. | |||
*/ | |||
#ifndef DISTRHO_WEBVIEW_HPP_INCLUDED | |||
#define DISTRHO_WEBVIEW_HPP_INCLUDED | |||
#include "../DistrhoUtils.hpp" | |||
START_NAMESPACE_DISTRHO | |||
#include "WebViewImpl.hpp" | |||
END_NAMESPACE_DISTRHO | |||
#endif // DISTRHO_WEBVIEW_HPP_INCLUDED |
@@ -0,0 +1,585 @@ | |||
/* | |||
* DISTRHO Plugin Framework (DPF) | |||
* Copyright (C) 2012-2024 Filipe Coelho <falktx@falktx.com> | |||
* | |||
* 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. | |||
*/ | |||
#if !defined(DISTRHO_WEBVIEW_HPP_INCLUDED) && !defined(DGL_WEBVIEW_HPP_INCLUDED) | |||
# error bad include | |||
#endif | |||
#if !defined(WEBVIEW_DISTRHO_NAMESPACE) && !defined(WEBVIEW_DGL_NAMESPACE) | |||
# error bad usage | |||
#endif | |||
// #define QT_NO_VERSION_TAGGING | |||
// #include <QtCore/QChar> | |||
// #include <QtCore/QPoint> | |||
// #include <QtCore/QSize> | |||
// #undef signals | |||
#include "ChildProcess.hpp" | |||
#include "String.hpp" | |||
#include <clocale> | |||
#include <cstdio> | |||
#include <dlfcn.h> | |||
#include <functional> | |||
#include <linux/limits.h> | |||
#include <X11/Xlib.h> | |||
#ifdef WEBVIEW_DGL_NAMESPACE | |||
START_NAMESPACE_DGL | |||
using DISTRHO_NAMESPACE::String; | |||
#else | |||
START_NAMESPACE_DISTRHO | |||
#endif | |||
// ----------------------------------------------------------------------------------------------------------- | |||
struct WebViewData { | |||
ChildProcess p; | |||
::Display* display; | |||
::Window childWindow; | |||
::Window ourWindow; | |||
}; | |||
// ----------------------------------------------------------------------------------------------------------- | |||
static void getFilenameFromFunctionPtr(char filename[PATH_MAX], const void* const ptr) | |||
{ | |||
Dl_info info = {}; | |||
dladdr(ptr, &info); | |||
if (info.dli_fname[0] == '.') | |||
{ | |||
getcwd(filename, PATH_MAX - 1); | |||
std::strncat(filename, info.dli_fname + 1, PATH_MAX - 1); | |||
} | |||
else if (info.dli_fname[0] != '/') | |||
{ | |||
getcwd(filename, PATH_MAX - 1); | |||
std::strncat(filename, "/", PATH_MAX - 1); | |||
std::strncat(filename, info.dli_fname, PATH_MAX - 1); | |||
} | |||
else | |||
{ | |||
std::strncpy(filename, info.dli_fname, PATH_MAX - 1); | |||
} | |||
} | |||
WebViewHandle addWebView(const uintptr_t parentWindowId, | |||
const int x, | |||
const int y, | |||
const uint width, | |||
const uint height, | |||
const double scaleFactor) | |||
{ | |||
char ldlinux[PATH_MAX] = {}; | |||
getFilenameFromFunctionPtr(ldlinux, dlsym(nullptr, "_rtld_global")); | |||
char filename[PATH_MAX] = {}; | |||
getFilenameFromFunctionPtr(filename, reinterpret_cast<const void*>(addWebView)); | |||
d_stdout("ld-linux is '%s'", ldlinux); | |||
d_stdout("filename is '%s'", filename); | |||
::Display* const display = XOpenDisplay(nullptr); | |||
DISTRHO_SAFE_ASSERT_RETURN(display != nullptr, nullptr); | |||
// set up custom child environment | |||
uint envsize = 0; | |||
while (environ[envsize] != nullptr) | |||
++envsize; | |||
char** const envp = new char*[envsize + 5]; | |||
{ | |||
uint e = 0; | |||
for (uint i = 0; i < envsize; ++i) | |||
{ | |||
if (std::strncmp(environ[i], "LD_PRELOAD=", 11) == 0) | |||
continue; | |||
if (std::strncmp(environ[i], "LD_LIBRARY_PATH=", 16) == 0) | |||
continue; | |||
envp[e++] = strdup(environ[i]); | |||
} | |||
envp[e++] = strdup("LANG=en_US.UTF-8"); | |||
envp[e++] = ("DPF_WEBVIEW_SCALE_FACTOR=" + String(scaleFactor)).getAndReleaseBuffer(); | |||
envp[e++] = ("DPF_WEBVIEW_WIN_ID=" +String(parentWindowId)).getAndReleaseBuffer(); | |||
for (uint i = e; i < envsize + 5; ++i) | |||
envp[e++] = nullptr; | |||
} | |||
WebViewData* const handle = new WebViewData(); | |||
handle->display = display; | |||
handle->childWindow = 0; | |||
handle->ourWindow = parentWindowId; | |||
const char* const args[] = { ldlinux, filename, "dpf-ld-linux-webview", nullptr }; | |||
handle->p.start(args, envp); | |||
for (uint i = 0; envp[i] != nullptr; ++i) | |||
std::free(envp[i]); | |||
delete[] envp; | |||
return handle; | |||
} | |||
void destroyWebView(const WebViewHandle handle) | |||
{ | |||
XCloseDisplay(handle->display); | |||
delete handle; | |||
} | |||
void reloadWebView(const WebViewHandle handle, uint) | |||
{ | |||
handle->p.signal(SIGUSR1); | |||
} | |||
void resizeWebView(const WebViewHandle handle, int x, int y, uint width, uint height) | |||
{ | |||
if (handle->childWindow == 0) | |||
{ | |||
::Window rootWindow, parentWindow; | |||
::Window* childWindows = nullptr; | |||
uint numChildren = 0; | |||
XFlush(handle->display); | |||
XQueryTree(handle->display, handle->ourWindow, &rootWindow, &parentWindow, &childWindows, &numChildren); | |||
if (numChildren == 0 || childWindows == nullptr) | |||
return; | |||
handle->childWindow = childWindows[0]; | |||
XFree(childWindows); | |||
} | |||
XMoveResizeWindow(handle->display, handle->childWindow, x, y, width, height); | |||
XFlush(handle->display); | |||
} | |||
// ----------------------------------------------------------------------------------------------------------- | |||
static std::function<void()> reloadFn; | |||
// ----------------------------------------------------------------------------------------------------------- | |||
struct GtkContainer; | |||
struct GtkPlug; | |||
struct GtkWidget; | |||
struct GtkWindow; | |||
struct WebKitSettings; | |||
struct WebKitWebView; | |||
#define GTK_CONTAINER(p) reinterpret_cast<GtkContainer*>(p) | |||
#define GTK_PLUG(p) reinterpret_cast<GtkPlug*>(p) | |||
#define GTK_WINDOW(p) reinterpret_cast<GtkWindow*>(p) | |||
#define WEBKIT_WEB_VIEW(p) reinterpret_cast<WebKitWebView*>(p) | |||
// struct QApplication; | |||
// struct QUrl; | |||
// struct QWebEngineView; | |||
// struct QWindow; | |||
// ----------------------------------------------------------------------------------------------------------- | |||
#define JOIN(A, B) A ## B | |||
#define AUTOSYM(S) \ | |||
using JOIN(gtk3_, S) = decltype(&S); \ | |||
JOIN(gtk3_, S) S = reinterpret_cast<JOIN(gtk3_, S)>(dlsym(nullptr, #S)); \ | |||
DISTRHO_SAFE_ASSERT_RETURN(S != nullptr, false); | |||
#define CSYM(S, NAME) \ | |||
S NAME = reinterpret_cast<S>(dlsym(nullptr, #NAME)); \ | |||
DISTRHO_SAFE_ASSERT_RETURN(NAME != nullptr, false); | |||
#define CPPSYM(S, NAME, SN) \ | |||
S NAME = reinterpret_cast<S>(dlsym(nullptr, #SN)); \ | |||
DISTRHO_SAFE_ASSERT_RETURN(NAME != nullptr, false); | |||
// ----------------------------------------------------------------------------------------------------------- | |||
// gtk3 variant | |||
static bool gtk3(Display* const display, | |||
const Window winId, | |||
const uint x, | |||
const uint y, | |||
const uint width, | |||
const uint height, | |||
double scaleFactor, | |||
const char* const url) | |||
{ | |||
void* lib; | |||
if ((lib = dlopen("libwebkit2gtk-4.0.so.37", RTLD_NOW|RTLD_GLOBAL)) == nullptr || | |||
(lib = dlopen("libwebkit2gtk-4.0.so", RTLD_NOW|RTLD_GLOBAL)) == nullptr) | |||
return false; | |||
using gdk_set_allowed_backends_t = void (*)(const char*); | |||
using gtk_container_add_t = void (*)(GtkContainer*, GtkWidget*); | |||
using gtk_init_check_t = bool (*)(int*, char***); | |||
using gtk_main_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_hardware_acceleration_policy_t = void (*)(WebKitSettings*, int); | |||
using webkit_settings_set_javascript_can_access_clipboard_t = void (*)(WebKitSettings*, bool); | |||
using webkit_web_view_load_uri_t = void (*)(WebKitWebView*, const char*); | |||
using webkit_web_view_new_with_settings_t = GtkWidget* (*)(WebKitSettings*); | |||
CSYM(gdk_set_allowed_backends_t, gdk_set_allowed_backends) | |||
CSYM(gtk_container_add_t, gtk_container_add) | |||
CSYM(gtk_init_check_t, gtk_init_check) | |||
CSYM(gtk_main_t, gtk_main) | |||
CSYM(gtk_plug_get_id_t, gtk_plug_get_id) | |||
CSYM(gtk_plug_new_t, gtk_plug_new) | |||
CSYM(gtk_widget_show_all_t, gtk_widget_show_all) | |||
CSYM(gtk_window_move_t, gtk_window_move) | |||
CSYM(gtk_window_set_default_size_t, gtk_window_set_default_size) | |||
CSYM(webkit_settings_new_t, webkit_settings_new) | |||
CSYM(webkit_settings_set_hardware_acceleration_policy_t, webkit_settings_set_hardware_acceleration_policy) | |||
CSYM(webkit_settings_set_javascript_can_access_clipboard_t, webkit_settings_set_javascript_can_access_clipboard) | |||
CSYM(webkit_web_view_load_uri_t, webkit_web_view_load_uri) | |||
CSYM(webkit_web_view_new_with_settings_t, webkit_web_view_new_with_settings) | |||
const int gdkScale = std::fmod(scaleFactor, 1.0) >= 0.75 | |||
? static_cast<int>(scaleFactor + 0.5) | |||
: static_cast<int>(scaleFactor); | |||
if (gdkScale != 1) | |||
{ | |||
char scale[8] = {}; | |||
std::snprintf(scale, 7, "%d", gdkScale); | |||
setenv("GDK_SCALE", scale, 1); | |||
std::snprintf(scale, 7, "%.2f", (1.0 / scaleFactor) * 1.2); | |||
setenv("GDK_DPI_SCALE", scale, 1); | |||
} | |||
else if (scaleFactor > 1.0) | |||
{ | |||
char scale[8] = {}; | |||
std::snprintf(scale, 7, "%.2f", (1.0 / scaleFactor) * 1.4); | |||
setenv("GDK_DPI_SCALE", scale, 1); | |||
} | |||
scaleFactor /= gdkScale; | |||
gdk_set_allowed_backends("x11"); | |||
if (! gtk_init_check (nullptr, nullptr)) | |||
return false; | |||
GtkWidget* const window = gtk_plug_new(winId); | |||
DISTRHO_SAFE_ASSERT_RETURN(window != nullptr, false); | |||
gtk_window_set_default_size(GTK_WINDOW(window), | |||
(width - x) * scaleFactor, | |||
(height - y) * scaleFactor); | |||
gtk_window_move(GTK_WINDOW(window), x * scaleFactor, y * scaleFactor); | |||
WebKitSettings* const settings = webkit_settings_new(); | |||
DISTRHO_SAFE_ASSERT_RETURN(settings != nullptr, false); | |||
webkit_settings_set_javascript_can_access_clipboard(settings, true); | |||
webkit_settings_set_hardware_acceleration_policy(settings, 2 /* WEBKIT_HARDWARE_ACCELERATION_POLICY_NEVER */); | |||
GtkWidget* const webview = webkit_web_view_new_with_settings(settings); | |||
DISTRHO_SAFE_ASSERT_RETURN(webview != nullptr, false); | |||
webkit_web_view_load_uri(WEBKIT_WEB_VIEW (webview), url); | |||
gtk_container_add(GTK_CONTAINER(window), webview); | |||
gtk_widget_show_all(window); | |||
Window wid = gtk_plug_get_id(GTK_PLUG(window)); | |||
XMapWindow(display, wid); | |||
XFlush(display); | |||
reloadFn = [=](){ | |||
webkit_web_view_load_uri(WEBKIT_WEB_VIEW (webview), url); | |||
}; | |||
gtk_main(); | |||
dlclose(lib); | |||
return true; | |||
} | |||
#if 0 | |||
// ----------------------------------------------------------------------------------------------------------- | |||
// qt5webengine variant | |||
static bool qt5webengine(const Window winId, const double scaleFactor, const char* const url) | |||
{ | |||
void* lib; | |||
if ((lib = dlopen("libQt5WebEngineWidgets.so.5", RTLD_NOW|RTLD_GLOBAL)) == nullptr || | |||
(lib = dlopen("libQt5WebEngineWidgets.so", RTLD_NOW|RTLD_GLOBAL)) == nullptr) | |||
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*, 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*); | |||
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_) | |||
unsetenv("QT_FONT_DPI"); | |||
unsetenv("QT_SCREEN_SCALE_FACTORS"); | |||
unsetenv("QT_USE_PHYSICAL_DPI"); | |||
setenv("QT_AUTO_SCREEN_SCALE_FACTOR", "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); | |||
static int argc = 0; | |||
static char* argv[] = { nullptr }; | |||
uint8_t _app[64]; // sizeof(QApplication) == 16 | |||
QApplication* const app = reinterpret_cast<QApplication*>(_app); | |||
QApplication__init(app, argc, argv, 0); | |||
uint8_t _qstrurl[32]; // sizeof(QString) == 8 | |||
QString* const qstrurl(reinterpret_cast<QString*>(_qstrurl)); | |||
{ | |||
const size_t url_len = std::strlen(url); | |||
QChar* const url_qchar = new QChar[url_len + 1]; | |||
for (size_t i = 0; i < url_len; ++i) | |||
url_qchar[i] = QChar(url[i]); | |||
url_qchar[url_len] = 0; | |||
QString__init(qstrurl, url_qchar, url_len); | |||
} | |||
uint8_t _qurl[32]; // sizeof(QUrl) == 8 | |||
QUrl* const qurl(reinterpret_cast<QUrl*>(_qurl)); | |||
QUrl__init(qurl, *qstrurl, 1 /* QUrl::StrictMode */); | |||
uint8_t _webview[128]; // sizeof(QWebEngineView) == 56 | |||
QWebEngineView* const webview = reinterpret_cast<QWebEngineView*>(_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); | |||
reloadFn = [=](){ | |||
QWebEngineView_setUrl(webview, *qurl); | |||
}; | |||
QApplication_exec(); | |||
dlclose(lib); | |||
return true; | |||
} | |||
// ----------------------------------------------------------------------------------------------------------- | |||
// qt6webengine variant (same as qt5 but `QString__init_t` has different arguments) | |||
static bool qt6webengine(const Window winId, const double scaleFactor, const char* const url) | |||
{ | |||
void* lib; | |||
if ((lib = dlopen("libQt6WebEngineWidgets.so.6", RTLD_NOW|RTLD_GLOBAL)) == nullptr || | |||
(lib = dlopen("libQt6WebEngineWidgets.so", RTLD_NOW|RTLD_GLOBAL)) == nullptr) | |||
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*); | |||
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, _ZN7QStringC2EPK5QCharx) | |||
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_) | |||
unsetenv("QT_FONT_DPI"); | |||
unsetenv("QT_SCREEN_SCALE_FACTORS"); | |||
unsetenv("QT_USE_PHYSICAL_DPI"); | |||
setenv("QT_AUTO_SCREEN_SCALE_FACTOR", "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); | |||
static int argc = 0; | |||
static char* argv[] = { nullptr }; | |||
uint8_t _app[64]; // sizeof(QApplication) == 16 | |||
QApplication* const app = reinterpret_cast<QApplication*>(_app); | |||
QApplication__init(app, argc, argv, 0); | |||
uint8_t _qstrurl[32]; // sizeof(QString) == 8 | |||
QString* const qstrurl(reinterpret_cast<QString*>(_qstrurl)); | |||
{ | |||
const size_t url_len = std::strlen(url); | |||
QChar* const url_qchar = new QChar[url_len + 1]; | |||
for (size_t i = 0; i < url_len; ++i) | |||
url_qchar[i] = QChar(url[i]); | |||
url_qchar[url_len] = 0; | |||
QString__init(qstrurl, url_qchar, url_len); | |||
} | |||
uint8_t _qurl[32]; // sizeof(QUrl) == 8 | |||
QUrl* const qurl(reinterpret_cast<QUrl*>(_qurl)); | |||
QUrl__init(qurl, *qstrurl, 1 /* QUrl::StrictMode */); | |||
uint8_t _webview[128]; // sizeof(QWebEngineView) == 56 | |||
QWebEngineView* const webview = reinterpret_cast<QWebEngineView*>(_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); | |||
reloadFn = [=](){ | |||
QWebEngineView_setUrl(webview, *qurl); | |||
}; | |||
QApplication_exec(); | |||
dlclose(lib); | |||
return true; | |||
} | |||
#endif | |||
// ----------------------------------------------------------------------------------------------------------- | |||
// startup via ld-linux | |||
static void signalHandler(const int sig) | |||
{ | |||
DISTRHO_SAFE_ASSERT_RETURN(sig == SIGUSR1,); | |||
reloadFn(); | |||
} | |||
int dpf_webview_start(int /* argc */, char** /* argv[] */) | |||
{ | |||
uselocale(newlocale(LC_NUMERIC_MASK, "C", nullptr)); | |||
const char* const envScaleFactor = std::getenv("DPF_WEBVIEW_SCALE_FACTOR"); | |||
DISTRHO_SAFE_ASSERT_RETURN(envScaleFactor != nullptr, 1); | |||
const char* const envWinId = std::getenv("DPF_WEBVIEW_WIN_ID"); | |||
DISTRHO_SAFE_ASSERT_RETURN(envWinId != nullptr, 1); | |||
const Window winId = std::strtoul(envWinId, nullptr, 10); | |||
DISTRHO_SAFE_ASSERT_RETURN(winId != 0, 1); | |||
const double scaleFactor = std::atof(envScaleFactor); | |||
DISTRHO_SAFE_ASSERT_RETURN(scaleFactor > 0.0, 1); | |||
Display* const display = XOpenDisplay(nullptr); | |||
DISTRHO_SAFE_ASSERT_RETURN(display != nullptr, 1); | |||
// const char* url = "file:///home/falktx/"; | |||
const char* url = "https://mastodon.falktx.com/"; | |||
struct sigaction sig = {}; | |||
sig.sa_handler = signalHandler; | |||
sig.sa_flags = SA_RESTART; | |||
sigemptyset(&sig.sa_mask); | |||
sigaction(SIGUSR1, &sig, nullptr); | |||
// qt5webengine(winId, scaleFactor, url) || | |||
// qt6webengine(winId, scaleFactor, url) || | |||
gtk3(display, winId, 0, 0, 600, 400, scaleFactor, url); | |||
XCloseDisplay(display); | |||
return 0; | |||
} | |||
// -------------------------------------------------------------------------------------------------------------------- | |||
#ifdef WEBVIEW_DGL_NAMESPACE | |||
END_NAMESPACE_DGL | |||
#else | |||
END_NAMESPACE_DISTRHO | |||
#endif | |||
#undef WEBVIEW_DISTRHO_NAMESPACE | |||
#undef WEBVIEW_DGL_NAMESPACE | |||
#undef WEBVIEW_NAMESPACE | |||
#undef fileBrowserSetPathNamespaced | |||
#undef fileBrowserSetPathFuncName |
@@ -0,0 +1,34 @@ | |||
/* | |||
* DISTRHO Plugin Framework (DPF) | |||
* Copyright (C) 2012-2024 Filipe Coelho <falktx@falktx.com> | |||
* | |||
* 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. | |||
*/ | |||
#if !defined(DISTRHO_WEBVIEW_HPP_INCLUDED) && !defined(DGL_WEBVIEW_HPP_INCLUDED) | |||
# error bad include | |||
#endif | |||
// -------------------------------------------------------------------------------------------------------------------- | |||
// Web View stuff | |||
struct WebViewData; | |||
typedef WebViewData* WebViewHandle; | |||
// ----------------------------------------------------------------------------------------------------------- | |||
WebViewHandle addWebView(uintptr_t parentWinId, int x, int y, uint width, uint height,double scaleFactor); | |||
void destroyWebView(WebViewHandle webview); | |||
void reloadWebView(WebViewHandle webview, uint port); | |||
void resizeWebView(WebViewHandle webview, int x, int y, uint width, uint height); | |||
// ----------------------------------------------------------------------------------------------------------- |
@@ -53,6 +53,16 @@ END_NAMESPACE_DISTRHO | |||
# include "../extra/FileBrowserDialogImpl.cpp" | |||
#endif | |||
#if DISTRHO_UI_USE_WEBVIEW && !defined(DISTRHO_OS_MAC) | |||
# define DISTRHO_WEBVIEW_HPP_INCLUDED | |||
# define WEBVIEW_NAMESPACE DISTRHO_NAMESPACE | |||
# define WEBVIEW_DISTRHO_NAMESPACE | |||
START_NAMESPACE_DISTRHO | |||
# include "../extra/WebViewImpl.hpp" | |||
END_NAMESPACE_DISTRHO | |||
# include "../extra/WebViewImpl.cpp" | |||
#endif | |||
#if DISTRHO_PLUGIN_HAS_EXTERNAL_UI | |||
# if defined(DISTRHO_OS_WINDOWS) | |||
# include <winsock2.h> | |||
@@ -0,0 +1,12 @@ | |||
# CMake file for DISTRHO Plugins # | |||
# ------------------------------ # | |||
dpf_add_plugin(d_meters | |||
TARGETS jack dssi lv2 vst2 vst3 clap | |||
FILES_DSP | |||
ExamplePluginMeters.cpp | |||
FILES_UI | |||
ExampleUIMeters.cpp) | |||
target_include_directories( | |||
d_meters PUBLIC ".") |
@@ -0,0 +1,44 @@ | |||
/* | |||
* DISTRHO Plugin Framework (DPF) | |||
* Copyright (C) 2012-2024 Filipe Coelho <falktx@falktx.com> | |||
* | |||
* 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. | |||
*/ | |||
#ifndef DISTRHO_PLUGIN_INFO_H_INCLUDED | |||
#define DISTRHO_PLUGIN_INFO_H_INCLUDED | |||
#define DISTRHO_PLUGIN_BRAND "DISTRHO" | |||
#define DISTRHO_PLUGIN_NAME "Web Meters" | |||
#define DISTRHO_PLUGIN_URI "http://distrho.sf.net/examples/WebMeters" | |||
#define DISTRHO_PLUGIN_CLAP_ID "studio.kx.distrho.examples.webmeters" | |||
#define DISTRHO_PLUGIN_BRAND_ID Dstr | |||
#define DISTRHO_PLUGIN_UNIQUE_ID wMtr | |||
#define DISTRHO_PLUGIN_HAS_UI 1 | |||
#define DISTRHO_PLUGIN_IS_RT_SAFE 1 | |||
#define DISTRHO_PLUGIN_NUM_INPUTS 2 | |||
#define DISTRHO_PLUGIN_NUM_OUTPUTS 2 | |||
#define DISTRHO_PLUGIN_WANT_STATE 1 | |||
#define DISTRHO_UI_FILE_BROWSER 0 | |||
#define DISTRHO_UI_USER_RESIZABLE 1 | |||
#define DISTRHO_UI_USE_WEBVIEW 1 | |||
#define METER_COLOR_GREEN 0 | |||
#define METER_COLOR_BLUE 1 | |||
#define DISTRHO_UI_DEFAULT_WIDTH 600 | |||
#define DISTRHO_UI_DEFAULT_HEIGHT 400 | |||
#define kVerticalOffset 0 | |||
#endif // DISTRHO_PLUGIN_INFO_H_INCLUDED |
@@ -0,0 +1,288 @@ | |||
/* | |||
* DISTRHO Plugin Framework (DPF) | |||
* Copyright (C) 2012-2024 Filipe Coelho <falktx@falktx.com> | |||
* | |||
* 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. | |||
*/ | |||
#include "DistrhoPlugin.hpp" | |||
START_NAMESPACE_DISTRHO | |||
// ----------------------------------------------------------------------------------------------------------- | |||
/** | |||
Plugin to demonstrate parameter outputs using meters. | |||
*/ | |||
class ExamplePluginMeters : public Plugin | |||
{ | |||
public: | |||
ExamplePluginMeters() | |||
: Plugin(3, 0, 0), // 3 parameters, 0 programs, 0 states | |||
fColor(0.0f), | |||
fOutLeft(0.0f), | |||
fOutRight(0.0f), | |||
fNeedsReset(true) | |||
{ | |||
} | |||
protected: | |||
/* -------------------------------------------------------------------------------------------------------- | |||
* Information */ | |||
/** | |||
Get the plugin label. | |||
A plugin label follows the same rules as Parameter::symbol, with the exception that it can start with numbers. | |||
*/ | |||
const char* getLabel() const override | |||
{ | |||
return "meters"; | |||
} | |||
/** | |||
Get an extensive comment/description about the plugin. | |||
*/ | |||
const char* getDescription() const override | |||
{ | |||
return "Plugin to demonstrate parameter outputs using meters."; | |||
} | |||
/** | |||
Get the plugin author/maker. | |||
*/ | |||
const char* getMaker() const override | |||
{ | |||
return "DISTRHO"; | |||
} | |||
/** | |||
Get the plugin homepage. | |||
*/ | |||
const char* getHomePage() const override | |||
{ | |||
return "https://github.com/DISTRHO/DPF"; | |||
} | |||
/** | |||
Get the plugin license name (a single line of text). | |||
For commercial plugins this should return some short copyright information. | |||
*/ | |||
const char* getLicense() const override | |||
{ | |||
return "ISC"; | |||
} | |||
/** | |||
Get the plugin version, in hexadecimal. | |||
*/ | |||
uint32_t getVersion() const override | |||
{ | |||
return d_version(1, 0, 0); | |||
} | |||
/* -------------------------------------------------------------------------------------------------------- | |||
* Init */ | |||
/** | |||
Initialize the audio port @a index.@n | |||
This function will be called once, shortly after the plugin is created. | |||
*/ | |||
void initAudioPort(bool input, uint32_t index, AudioPort& port) override | |||
{ | |||
// treat meter audio ports as stereo | |||
port.groupId = kPortGroupStereo; | |||
// everything else is as default | |||
Plugin::initAudioPort(input, index, port); | |||
} | |||
/** | |||
Initialize the parameter @a index.@n | |||
This function will be called once, shortly after the plugin is created. | |||
*/ | |||
void initParameter(uint32_t index, Parameter& parameter) override | |||
{ | |||
/** | |||
All parameters in this plugin have the same ranges. | |||
*/ | |||
parameter.ranges.min = 0.0f; | |||
parameter.ranges.max = 1.0f; | |||
parameter.ranges.def = 0.0f; | |||
/** | |||
Set parameter data. | |||
*/ | |||
switch (index) | |||
{ | |||
case 0: | |||
parameter.hints = kParameterIsAutomatable|kParameterIsInteger; | |||
parameter.name = "color"; | |||
parameter.symbol = "color"; | |||
parameter.enumValues.count = 2; | |||
parameter.enumValues.restrictedMode = true; | |||
{ | |||
ParameterEnumerationValue* const values = new ParameterEnumerationValue[2]; | |||
parameter.enumValues.values = values; | |||
values[0].label = "Green"; | |||
values[0].value = METER_COLOR_GREEN; | |||
values[1].label = "Blue"; | |||
values[1].value = METER_COLOR_BLUE; | |||
} | |||
break; | |||
case 1: | |||
parameter.hints = kParameterIsAutomatable|kParameterIsOutput; | |||
parameter.name = "out-left"; | |||
parameter.symbol = "out_left"; | |||
break; | |||
case 2: | |||
parameter.hints = kParameterIsAutomatable|kParameterIsOutput; | |||
parameter.name = "out-right"; | |||
parameter.symbol = "out_right"; | |||
break; | |||
} | |||
} | |||
/** | |||
Set a state key and default value. | |||
This function will be called once, shortly after the plugin is created. | |||
*/ | |||
void initState(uint32_t, String&, String&) override | |||
{ | |||
// we are using states but don't want them saved in the host | |||
} | |||
/* -------------------------------------------------------------------------------------------------------- | |||
* Internal data */ | |||
/** | |||
Get the current value of a parameter. | |||
*/ | |||
float getParameterValue(uint32_t index) const override | |||
{ | |||
switch (index) | |||
{ | |||
case 0: return fColor; | |||
case 1: return fOutLeft; | |||
case 2: return fOutRight; | |||
} | |||
return 0.0f; | |||
} | |||
/** | |||
Change a parameter value. | |||
*/ | |||
void setParameterValue(uint32_t index, float value) override | |||
{ | |||
// this is only called for input paramters, and we only have one of those. | |||
if (index != 0) return; | |||
fColor = value; | |||
} | |||
/** | |||
Change an internal state. | |||
*/ | |||
void setState(const char* key, const char*) override | |||
{ | |||
if (std::strcmp(key, "reset") != 0) | |||
return; | |||
fNeedsReset = true; | |||
} | |||
/* -------------------------------------------------------------------------------------------------------- | |||
* Process */ | |||
/** | |||
Run/process function for plugins without MIDI input. | |||
*/ | |||
void run(const float** inputs, float** outputs, uint32_t frames) override | |||
{ | |||
float tmp; | |||
float tmpLeft = 0.0f; | |||
float tmpRight = 0.0f; | |||
for (uint32_t i=0; i<frames; ++i) | |||
{ | |||
// left | |||
tmp = std::abs(inputs[0][i]); | |||
if (tmp > tmpLeft) | |||
tmpLeft = tmp; | |||
// right | |||
tmp = std::abs(inputs[1][i]); | |||
if (tmp > tmpRight) | |||
tmpRight = tmp; | |||
} | |||
if (tmpLeft > 1.0f) | |||
tmpLeft = 1.0f; | |||
if (tmpRight > 1.0f) | |||
tmpRight = 1.0f; | |||
if (fNeedsReset) | |||
{ | |||
fOutLeft = tmpLeft; | |||
fOutRight = tmpRight; | |||
fNeedsReset = false; | |||
} | |||
else | |||
{ | |||
if (tmpLeft > fOutLeft) | |||
fOutLeft = tmpLeft; | |||
if (tmpRight > fOutRight) | |||
fOutRight = tmpRight; | |||
} | |||
// copy inputs over outputs if needed | |||
if (outputs[0] != inputs[0]) | |||
std::memcpy(outputs[0], inputs[0], sizeof(float)*frames); | |||
if (outputs[1] != inputs[1]) | |||
std::memcpy(outputs[1], inputs[1], sizeof(float)*frames); | |||
} | |||
// ------------------------------------------------------------------------------------------------------- | |||
private: | |||
/** | |||
Parameters. | |||
*/ | |||
float fColor, fOutLeft, fOutRight; | |||
/** | |||
Boolean used to reset meter values. | |||
The UI will send a "reset" message which sets this as true. | |||
*/ | |||
volatile bool fNeedsReset; | |||
/** | |||
Set our plugin class as non-copyable and add a leak detector just in case. | |||
*/ | |||
DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(ExamplePluginMeters) | |||
}; | |||
/* ------------------------------------------------------------------------------------------------------------ | |||
* Plugin entry point, called by DPF to create a new plugin instance. */ | |||
Plugin* createPlugin() | |||
{ | |||
return new ExamplePluginMeters(); | |||
} | |||
// ----------------------------------------------------------------------------------------------------------- | |||
END_NAMESPACE_DISTRHO |
@@ -0,0 +1,72 @@ | |||
/* | |||
* DISTRHO Plugin Framework (DPF) | |||
* Copyright (C) 2012-2019 Filipe Coelho <falktx@falktx.com> | |||
* | |||
* 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. | |||
*/ | |||
#include "DistrhoUI.hpp" | |||
START_NAMESPACE_DISTRHO | |||
// ----------------------------------------------------------------------------------------------------------- | |||
class ExampleUIMeters : public UI | |||
{ | |||
public: | |||
ExampleUIMeters() | |||
: UI(600, 400) | |||
{ | |||
setGeometryConstraints(600, 400, false); | |||
} | |||
protected: | |||
/* -------------------------------------------------------------------------------------------------------- | |||
* DSP/Plugin Callbacks */ | |||
/** | |||
A parameter has changed on the plugin side. | |||
This is called by the host to inform the UI about parameter changes. | |||
*/ | |||
void parameterChanged(uint32_t index, float value) override | |||
{ | |||
} | |||
/** | |||
A state has changed on the plugin side. | |||
This is called by the host to inform the UI about state changes. | |||
*/ | |||
void stateChanged(const char*, const char*) override | |||
{ | |||
// nothing here | |||
} | |||
// ------------------------------------------------------------------------------------------------------- | |||
private: | |||
/** | |||
Set our UI class as non-copyable and add a leak detector just in case. | |||
*/ | |||
DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(ExampleUIMeters) | |||
}; | |||
/* ------------------------------------------------------------------------------------------------------------ | |||
* UI entry point, called by DPF to create a new UI instance. */ | |||
UI* createUI() | |||
{ | |||
return new ExampleUIMeters(); | |||
} | |||
// ----------------------------------------------------------------------------------------------------------- | |||
END_NAMESPACE_DISTRHO |
@@ -0,0 +1,50 @@ | |||
#!/usr/bin/make -f | |||
# Makefile for DISTRHO Plugins # | |||
# ---------------------------- # | |||
# Created by falkTX | |||
# | |||
# -------------------------------------------------------------- | |||
# Project name, used for binaries | |||
NAME = d_web | |||
# -------------------------------------------------------------- | |||
# Files to build | |||
FILES_DSP = \ | |||
ExamplePluginWebMeters.cpp | |||
FILES_UI = \ | |||
ExampleUIWebMeters.cpp | |||
# -------------------------------------------------------------- | |||
# Do some magic | |||
UI_TYPE = web | |||
include ../../Makefile.plugins.mk | |||
# -------------------------------------------------------------- | |||
# Enable all possible plugin types | |||
ifeq ($(HAVE_OPENGL),true) | |||
TARGETS += jack | |||
ifneq ($(MACOS_OR_WINDOWS),true) | |||
ifeq ($(HAVE_LIBLO),true) | |||
TARGETS += dssi | |||
endif # HAVE_LIBLO | |||
endif # MACOS_OR_WINDOWS | |||
TARGETS += lv2_sep | |||
TARGETS += vst2 | |||
TARGETS += vst3 | |||
TARGETS += clap | |||
TARGETS += au | |||
endif # HAVE_OPENGL | |||
all: $(TARGETS) | |||
# -------------------------------------------------------------- |
@@ -0,0 +1,8 @@ | |||
# Meters example | |||
This example will show how parameter outputs can be used for UI meters in DPF.<br/> | |||
The plugin will inspect the host audio buffer but it won't change it in any way.<br/> | |||
In this example the UI will display a simple meter based on the plugin's parameter outputs.<br/> | |||
In order to make drawing easier the UI uses NanoVG instead of raw OpenGL.<br/> | |||
Please see the Parameters and States examples before studying this one.<br/> |