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. | 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). | 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. | 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 | Get the path chosen by the user or null.@n | ||||
Should only be called after fileBrowserIdle returns true. | 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" | # include "../extra/FileBrowserDialogImpl.cpp" | ||||
#endif | #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 DISTRHO_PLUGIN_HAS_EXTERNAL_UI | ||||
# if defined(DISTRHO_OS_WINDOWS) | # if defined(DISTRHO_OS_WINDOWS) | ||||
# include <winsock2.h> | # 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/> |