Signed-off-by: falkTX <falktx@falktx.com>pull/457/head
| @@ -34,13 +34,14 @@ jobs: | |||
| CXXFLAGS: -Werror | |||
| run: | | |||
| xvfb-run make -C tests run | |||
| - name: As C++98 mode | |||
| env: | |||
| CFLAGS: -Werror | |||
| CXXFLAGS: -Werror -std=gnu++98 | |||
| run: | | |||
| make clean >/dev/null | |||
| make -j $(nproc) | |||
| # FIXME enable again after finishing web-ui stuff | |||
| # - name: As C++98 mode | |||
| # env: | |||
| # CFLAGS: -Werror | |||
| # CXXFLAGS: -Werror -std=gnu++98 | |||
| # run: | | |||
| # make clean >/dev/null | |||
| # make -j $(nproc) | |||
| - name: No namespace | |||
| env: | |||
| CFLAGS: -Werror | |||
| @@ -1,5 +1,6 @@ | |||
| # DISTRHO Plugin Framework (DPF) | |||
| # Copyright (C) 2021 Jean Pierre Cimalando <jp-dev@inbox.ru> | |||
| # Copyright (C) 2022-2024 Filipe Coelho <falktx@falktx.com> | |||
| # | |||
| # SPDX-License-Identifier: ISC | |||
| @@ -28,6 +28,7 @@ | |||
| # USE_OPENGL3=true | |||
| # USE_NANOVG_FBO=true | |||
| # USE_NANOVG_FREETYPE=true | |||
| # USE_WEBVIEW=true | |||
| # STATIC_BUILD=true | |||
| # Tweak build to be able to generate fully static builds (e.g. skip use of libdl) | |||
| @@ -439,6 +440,9 @@ else ifeq ($(MACOS),true) | |||
| DGL_SYSTEM_LIBS += -framework Cocoa | |||
| DGL_SYSTEM_LIBS += -framework CoreVideo | |||
| ifeq ($(USE_WEBVIEW),true) | |||
| DGL_SYSTEM_LIBS += -framework WebKit | |||
| endif | |||
| else ifeq ($(WASM),true) | |||
| @@ -453,13 +457,19 @@ DGL_SYSTEM_LIBS += -lcomdlg32 | |||
| DGL_SYSTEM_LIBS += -ldwmapi | |||
| DGL_SYSTEM_LIBS += -lgdi32 | |||
| # DGL_SYSTEM_LIBS += -lole32 | |||
| ifeq ($(USE_WEBVIEW),true) | |||
| DGL_SYSTEM_LIBS += -lole32 | |||
| DGL_SYSTEM_LIBS += -luuid | |||
| endif | |||
| else | |||
| ifneq ($(FILE_BROWSER_DISABLED),true) | |||
| ifeq ($(HAVE_DBUS),true) | |||
| DGL_FLAGS += $(shell $(PKG_CONFIG) --cflags dbus-1) -DHAVE_DBUS | |||
| DGL_SYSTEM_LIBS += $(shell $(PKG_CONFIG) --libs dbus-1) | |||
| endif | |||
| endif | |||
| ifeq ($(HAVE_X11),true) | |||
| DGL_FLAGS += $(shell $(PKG_CONFIG) --cflags x11) -DHAVE_X11 | |||
| @@ -476,6 +486,10 @@ ifeq ($(HAVE_XRANDR),true) | |||
| DGL_FLAGS += $(shell $(PKG_CONFIG) --cflags xrandr) -DHAVE_XRANDR | |||
| DGL_SYSTEM_LIBS += $(shell $(PKG_CONFIG) --libs xrandr) | |||
| endif | |||
| ifeq ($(USE_WEBVIEW),true) | |||
| DGL_FLAGS += -pthread | |||
| DGL_SYSTEM_LIBS += -pthread -lrt | |||
| endif | |||
| endif # HAVE_X11 | |||
| endif | |||
| @@ -656,6 +670,10 @@ ifeq ($(USE_RGBA),true) | |||
| BUILD_CXX_FLAGS += -DDGL_USE_RGBA | |||
| endif | |||
| ifeq ($(USE_WEBVIEW),true) | |||
| BUILD_CXX_FLAGS += -DDGL_USE_WEBVIEW | |||
| endif | |||
| # --------------------------------------------------------------------------------------------------------------------- | |||
| # Set app extension | |||
| @@ -901,6 +919,7 @@ mingw32: | |||
| AR=i686-w64-mingw32-ar \ | |||
| CC=i686-w64-mingw32-gcc \ | |||
| CXX=i686-w64-mingw32-g++ \ | |||
| EXE_WRAPPER=wine \ | |||
| PKG_CONFIG=/usr/bin/false \ | |||
| PKG_CONFIG_PATH=/NOT | |||
| @@ -909,6 +928,7 @@ mingw64: | |||
| AR=x86_64-w64-mingw32-ar \ | |||
| CC=x86_64-w64-mingw32-gcc \ | |||
| CXX=x86_64-w64-mingw32-g++ \ | |||
| EXE_WRAPPER=wine \ | |||
| PKG_CONFIG=/usr/bin/false \ | |||
| PKG_CONFIG_PATH=/NOT | |||
| @@ -240,6 +240,7 @@ ifeq ($(UI_TYPE),web) | |||
| DGL_FLAGS += -DDGL_WEB -DHAVE_DGL | |||
| DGL_LIB = $(DGL_BUILD_DIR)/libdgl-web.a | |||
| HAVE_DGL = true | |||
| USE_WEBVIEW = true | |||
| endif | |||
| ifeq ($(UI_TYPE),external) | |||
| @@ -256,7 +257,7 @@ HAVE_DGL = false | |||
| endif | |||
| endif | |||
| ifeq ($(HAVE_DGL)$(LINUX)$(USING_WEBVIEW),truetruetrue) | |||
| ifeq ($(HAVE_DGL)$(LINUX)$(USE_WEBVIEW),truetruetrue) | |||
| DGL_LIB_SHARED = $(shell $(CC) -print-file-name=Scrt1.o) | |||
| endif | |||
| @@ -481,6 +482,9 @@ $(DGL_BUILD_DIR)/libdgl-stub.a: $(DGL_POSSIBLE_DEPS) | |||
| $(DGL_BUILD_DIR)/libdgl-vulkan.a: $(DGL_POSSIBLE_DEPS) | |||
| $(MAKE) -C $(DPF_PATH)/dgl vulkan | |||
| $(DGL_BUILD_DIR)/libdgl-web.a: $(DGL_POSSIBLE_DEPS) | |||
| $(MAKE) -C $(DPF_PATH)/dgl web | |||
| # --------------------------------------------------------------------------------------------------------------------- | |||
| $(BUILD_DIR)/DistrhoPluginMain_%.cpp.o: $(DPF_PATH)/distrho/DistrhoPluginMain.cpp $(EXTRA_DEPENDENCIES) $(EXTRA_DSP_DEPENDENCIES) | |||
| @@ -1,6 +1,6 @@ | |||
| /* | |||
| * DISTRHO Plugin Framework (DPF) | |||
| * Copyright (C) 2012-2023 Filipe Coelho <falktx@falktx.com> | |||
| * 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 | |||
| @@ -253,14 +253,14 @@ protected: | |||
| A parameter has changed on the plugin side.@n | |||
| This is called by the host to inform the UI about parameter changes. | |||
| */ | |||
| virtual void parameterChanged(uint32_t index, float value) = 0; | |||
| virtual void parameterChanged(uint32_t index, float value); | |||
| #if DISTRHO_PLUGIN_WANT_PROGRAMS | |||
| /** | |||
| A program has been loaded on the plugin side.@n | |||
| This is called by the host to inform the UI about program changes. | |||
| */ | |||
| virtual void programLoaded(uint32_t index) = 0; | |||
| virtual void programLoaded(uint32_t index); | |||
| #endif | |||
| #if DISTRHO_PLUGIN_WANT_STATE | |||
| @@ -268,7 +268,7 @@ protected: | |||
| A state has changed on the plugin side.@n | |||
| This is called by the host to inform the UI about state changes. | |||
| */ | |||
| virtual void stateChanged(const char* key, const char* value) = 0; | |||
| virtual void stateChanged(const char* key, const char* value); | |||
| #endif | |||
| /* -------------------------------------------------------------------------------------------------------- | |||
| @@ -20,7 +20,7 @@ | |||
| #include "src/DistrhoPluginChecks.h" | |||
| #include "src/DistrhoDefines.h" | |||
| #if DISTRHO_UI_FILE_BROWSER || DISTRHO_PLUGIN_HAS_EXTERNAL_UI | |||
| #if DISTRHO_UI_FILE_BROWSER || DISTRHO_UI_WEB_VIEW || DISTRHO_PLUGIN_HAS_EXTERNAL_UI | |||
| # import <Cocoa/Cocoa.h> | |||
| #endif | |||
| @@ -34,9 +34,19 @@ END_NAMESPACE_DISTRHO | |||
| # include "extra/FileBrowserDialogImpl.cpp" | |||
| #endif | |||
| #if DISTRHO_PLUGIN_HAS_EXTERNAL_UI | |||
| # include <algorithm> | |||
| # include <cmath> | |||
| #if DISTRHO_UI_WEB_VIEW | |||
| # define DISTRHO_WEB_VIEW_HPP_INCLUDED | |||
| # define WEB_VIEW_NAMESPACE DISTRHO_NAMESPACE | |||
| # define WEB_VIEW_DISTRHO_NAMESPACE | |||
| START_NAMESPACE_DISTRHO | |||
| # include "extra/WebViewImpl.hpp" | |||
| END_NAMESPACE_DISTRHO | |||
| # include "extra/WebViewImpl.cpp" | |||
| #endif | |||
| #include <algorithm> | |||
| #include <cmath> | |||
| START_NAMESPACE_DISTRHO | |||
| double getDesktopScaleFactor(const uintptr_t parentWindowHandle) | |||
| { | |||
| @@ -51,4 +61,3 @@ double getDesktopScaleFactor(const uintptr_t parentWindowHandle) | |||
| return [NSScreen mainScreen].backingScaleFactor; | |||
| } | |||
| END_NAMESPACE_DISTRHO | |||
| #endif | |||
| @@ -0,0 +1,276 @@ | |||
| // SPDX-FileCopyrightText: 2023-2024 MOD Audio UG | |||
| // SPDX-License-Identifier: AGPL-3.0-or-later | |||
| #pragma once | |||
| #include "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 | |||
| @@ -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_WEB_VIEW_HPP_INCLUDED | |||
| #define DISTRHO_WEB_VIEW_HPP_INCLUDED | |||
| #include "../DistrhoUtils.hpp" | |||
| START_NAMESPACE_DISTRHO | |||
| #include "WebViewImpl.hpp" | |||
| END_NAMESPACE_DISTRHO | |||
| #endif // DISTRHO_WEB_VIEW_HPP_INCLUDED | |||
| @@ -0,0 +1,126 @@ | |||
| /* | |||
| * 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_WEB_VIEW_HPP_INCLUDED) && !defined(DGL_WEB_VIEW_HPP_INCLUDED) | |||
| # error bad include | |||
| #endif | |||
| #if defined(DISTRHO_UI_USE_WEBVIEW) && DISTRHO_UI_USE_WEBVIEW == 0 | |||
| # error To use WebViews in DPF plugins please set DISTRHO_UI_USE_WEBVIEW to 1 | |||
| #endif | |||
| // -------------------------------------------------------------------------------------------------------------------- | |||
| // Web View stuff | |||
| struct WebViewData; | |||
| typedef WebViewData* WebViewHandle; | |||
| typedef void (*WebViewMessageCallback)(void* arg, char* msg); | |||
| // -------------------------------------------------------------------------------------------------------------------- | |||
| /** | |||
| Web view options, for customizing web view details. | |||
| */ | |||
| struct WebViewOptions { | |||
| /** | |||
| Position offset, for cases of mixing regular widgets with web views. | |||
| */ | |||
| struct PositionOffset { | |||
| /** Horizontal offset, with scale factor pre-applied */ | |||
| int x; | |||
| /** Vertical offset, with scale factor pre-applied */ | |||
| int y; | |||
| /** Constructor for default values */ | |||
| PositionOffset() : x(0), y(0) {} | |||
| } offset; | |||
| /** | |||
| Set some JavaScript to evalute on every new page load. | |||
| */ | |||
| const char* initialJS; | |||
| /** | |||
| Message callback triggered from JavaScript code inside the WebView. | |||
| */ | |||
| WebViewMessageCallback callback; | |||
| void* callbackPtr; | |||
| /** Constructor for default values */ | |||
| WebViewOptions() | |||
| : offset(), | |||
| initialJS(nullptr), | |||
| callback(nullptr), | |||
| callbackPtr(nullptr) {} | |||
| /** Constructor providing a callback */ | |||
| WebViewOptions(const WebViewMessageCallback cb, void* const ptr) | |||
| : offset(), | |||
| initialJS(nullptr), | |||
| callback(cb), | |||
| callbackPtr(ptr) {} | |||
| }; | |||
| // -------------------------------------------------------------------------------------------------------------------- | |||
| /** | |||
| Create a new web view. | |||
| The web view will be added on top of an existing platform-specific view/window. | |||
| This means it will draw on top of whatever is below it, | |||
| something to take into consideration if mixing regular widgets with web views. | |||
| Provided metrics must not have scale factor pre-applied. | |||
| @p windowId: The native window id to attach this view to (X11 Window, HWND or NSView*) | |||
| @p scaleFactor: Scale factor in use | |||
| @p options: Extra options, optional | |||
| */ | |||
| WebViewHandle webViewCreate(const char* url, | |||
| uintptr_t windowId, | |||
| uint initialWidth, | |||
| uint initialHeight, | |||
| double scaleFactor, | |||
| const WebViewOptions& options = WebViewOptions()); | |||
| /** | |||
| Destroy the web view, handle must not be used afterwards. | |||
| */ | |||
| void webViewDestroy(WebViewHandle webview); | |||
| /** | |||
| Idle the web view, to be called on regular intervals. | |||
| Can cause callbacks to trigger. | |||
| */ | |||
| void webViewIdle(WebViewHandle webview); | |||
| /** | |||
| Evaluate/run JavaScript on the web view. | |||
| */ | |||
| void webViewEvaluateJS(WebViewHandle webview, const char* js); | |||
| /** | |||
| Reload the web view current page. | |||
| */ | |||
| void webViewReload(WebViewHandle webview); | |||
| /** | |||
| Resize the web view. | |||
| */ | |||
| void webViewResize(WebViewHandle webview, uint width, uint height, double scaleFactor); | |||
| // -------------------------------------------------------------------------------------------------------------------- | |||
| @@ -94,10 +94,6 @@ | |||
| # define DISTRHO_PLUGIN_WANT_TIMEPOS 0 | |||
| #endif | |||
| #ifndef DISTRHO_PLUGIN_WANT_WEBVIEW | |||
| # define DISTRHO_PLUGIN_WANT_WEBVIEW 0 | |||
| #endif | |||
| #ifndef DISTRHO_UI_FILE_BROWSER | |||
| # if defined(DGL_FILE_BROWSER_DISABLED) || DISTRHO_PLUGIN_HAS_EXTERNAL_UI | |||
| # define DISTRHO_UI_FILE_BROWSER 0 | |||
| @@ -106,6 +102,10 @@ | |||
| # endif | |||
| #endif | |||
| #ifndef DISTRHO_UI_WEB_VIEW | |||
| # define DISTRHO_UI_WEB_VIEW 0 | |||
| #endif | |||
| #ifndef DISTRHO_UI_USER_RESIZABLE | |||
| # define DISTRHO_UI_USER_RESIZABLE 0 | |||
| #endif | |||
| @@ -130,11 +130,11 @@ | |||
| #endif | |||
| // -------------------------------------------------------------------------------------------------------------------- | |||
| // Define DISTRHO_PLUGIN_WANT_WEBVIEW if needed | |||
| // Define DISTRHO_UI_WEB_VIEW if needed | |||
| #if DISTRHO_UI_USE_WEBVIEW && !DISTRHO_PLUGIN_WANT_WEBVIEW | |||
| # undef DISTRHO_PLUGIN_WANT_WEBVIEW | |||
| # define DISTRHO_PLUGIN_WANT_WEBVIEW 1 | |||
| #if DISTRHO_UI_USE_WEBVIEW && !DISTRHO_UI_WEB_VIEW | |||
| # undef DISTRHO_UI_WEB_VIEW | |||
| # define DISTRHO_UI_WEB_VIEW 1 | |||
| #endif | |||
| // -------------------------------------------------------------------------------------------------------------------- | |||
| @@ -144,6 +144,29 @@ | |||
| # define DISTRHO_UI_URI DISTRHO_PLUGIN_URI "#DPF_UI" | |||
| #endif | |||
| // -------------------------------------------------------------------------------------------------------------------- | |||
| // Test for wrong compiler macros | |||
| #if defined(DGL_CAIRO) && defined(DGL_OPENGL) | |||
| # error invalid build config: trying to build for both cairo and opengl at the same time | |||
| #endif | |||
| #if defined(DGL_CAIRO) && DISTRHO_PLUGIN_HAS_EXTERNAL_UI | |||
| # error invalid build config: trying to build cairo while using external UI | |||
| #endif | |||
| #if defined(DGL_OPENGL) && DISTRHO_PLUGIN_HAS_EXTERNAL_UI | |||
| # error invalid build config: trying to build opengl while using external UI | |||
| #endif | |||
| #if DISTRHO_UI_FILE_BROWSER && defined(DGL_FILE_BROWSER_DISABLED) | |||
| # error invalid build config: file browser requested but `FILE_BROWSER_DISABLED` build option is set | |||
| #endif | |||
| #if DISTRHO_UI_USE_WEBVIEW && !defined(DGL_USE_WEBVIEW) | |||
| # error invalid build config: web view requested but `USE_WEBVIEW` build option is not set | |||
| #endif | |||
| // -------------------------------------------------------------------------------------------------------------------- | |||
| // Test if synth has audio outputs | |||
| @@ -280,7 +303,7 @@ static_assert(sizeof(STRINGIFY(DISTRHO_PLUGIN_UNIQUE_ID)) == 5, "The macro DISTR | |||
| // -------------------------------------------------------------------------------------------------------------------- | |||
| // Set DPF_USING_LD_LINUX_WEBVIEW for internal use | |||
| #if DISTRHO_PLUGIN_WANT_WEBVIEW && defined(__linux__) | |||
| #if DISTRHO_UI_WEB_VIEW && defined(__linux__) | |||
| # define DPF_USING_LD_LINUX_WEBVIEW | |||
| #endif | |||
| @@ -965,6 +965,11 @@ int main(int argc, char* argv[]) | |||
| { | |||
| USE_NAMESPACE_DISTRHO; | |||
| #ifdef DISTRHO_OS_WINDOWS | |||
| OleInitialize(nullptr); | |||
| CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED); | |||
| #endif | |||
| initSignalHandler(); | |||
| #ifndef STATIC_BUILD | |||
| @@ -975,15 +980,11 @@ int main(int argc, char* argv[]) | |||
| String tmpPath(getBinaryFilename()); | |||
| tmpPath.truncate(tmpPath.rfind(DISTRHO_OS_SEP)); | |||
| #if defined(DISTRHO_OS_MAC) | |||
| if (tmpPath.endsWith("/MacOS")) | |||
| if (tmpPath.endsWith("/Contents/MacOS")) | |||
| { | |||
| tmpPath.truncate(tmpPath.rfind('/')); | |||
| if (tmpPath.endsWith("/Contents")) | |||
| { | |||
| tmpPath.truncate(tmpPath.rfind('/')); | |||
| bundlePath = tmpPath; | |||
| d_nextBundlePath = bundlePath.buffer(); | |||
| } | |||
| tmpPath.truncate(tmpPath.length() - 15); | |||
| bundlePath = tmpPath; | |||
| d_nextBundlePath = bundlePath.buffer(); | |||
| } | |||
| #else | |||
| #ifdef DISTRHO_OS_WINDOWS | |||
| @@ -1002,7 +1003,7 @@ int main(int argc, char* argv[]) | |||
| #ifdef DPF_USING_LD_LINUX_WEBVIEW | |||
| if (argc >= 2 && std::strcmp(argv[1], "dpf-ld-linux-webview") == 0) | |||
| return dpf_webview_start(argc - 1, argv + 1); | |||
| return dpf_webview_start(argc, argv); | |||
| #endif | |||
| if (argc == 2 && std::strcmp(argv[1], "selftest") == 0) | |||
| @@ -1042,6 +1043,10 @@ int main(int argc, char* argv[]) | |||
| } | |||
| hasConsole = true; | |||
| // tell windows to output console output as utf-8 | |||
| SetConsoleCP(CP_UTF8); | |||
| SetConsoleOutputCP(CP_UTF8); | |||
| } | |||
| #endif | |||
| @@ -1170,6 +1175,11 @@ int main(int argc, char* argv[]) | |||
| } | |||
| #endif | |||
| #ifdef DISTRHO_OS_WINDOWS | |||
| CoUninitialize(); | |||
| OleUninitialize(); | |||
| #endif | |||
| return 0; | |||
| } | |||
| @@ -1720,7 +1720,7 @@ const vst_effect* VSTPluginMain(const vst_host_callback audioMaster) | |||
| return effect; | |||
| } | |||
| #if !(defined(DISTRHO_OS_MAC) || defined(DISTRHO_OS_WASM) || defined(DISTRHO_OS_WINDOWS) || DISTRHO_PLUGIN_WANT_WEBVIEW) | |||
| #if !(defined(DISTRHO_OS_MAC) || defined(DISTRHO_OS_WASM) || defined(DISTRHO_OS_WINDOWS) || DISTRHO_UI_WEB_VIEW) | |||
| DISTRHO_PLUGIN_EXPORT | |||
| const vst_effect* VSTPluginMainCompat(vst_host_callback) asm ("main"); | |||
| @@ -1,6 +1,6 @@ | |||
| /* | |||
| * DISTRHO Plugin Framework (DPF) | |||
| * Copyright (C) 2012-2023 Filipe Coelho <falktx@falktx.com> | |||
| * 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 | |||
| @@ -15,8 +15,8 @@ | |||
| */ | |||
| #include "DistrhoDetails.hpp" | |||
| #include "DistrhoPluginUtils.hpp" | |||
| #include "src/DistrhoPluginChecks.h" | |||
| #include "src/DistrhoDefines.h" | |||
| #include <cstddef> | |||
| @@ -26,6 +26,13 @@ | |||
| # include <stdint.h> | |||
| #endif | |||
| #if defined(DISTRHO_OS_WINDOWS) | |||
| # include <winsock2.h> | |||
| # include <windows.h> | |||
| #elif defined(HAVE_X11) | |||
| # include <X11/Xresource.h> | |||
| #endif | |||
| #if DISTRHO_UI_FILE_BROWSER && !defined(DISTRHO_OS_MAC) | |||
| # define DISTRHO_PUGL_NAMESPACE_MACRO_HELPER(NS, SEP, FUNCTION) NS ## SEP ## FUNCTION | |||
| # define DISTRHO_PUGL_NAMESPACE_MACRO(NS, FUNCTION) DISTRHO_PUGL_NAMESPACE_MACRO_HELPER(NS, _, FUNCTION) | |||
| @@ -53,14 +60,17 @@ END_NAMESPACE_DISTRHO | |||
| # include "../extra/FileBrowserDialogImpl.cpp" | |||
| #endif | |||
| #if DISTRHO_PLUGIN_HAS_EXTERNAL_UI | |||
| # if defined(DISTRHO_OS_WINDOWS) | |||
| # include <winsock2.h> | |||
| # include <windows.h> | |||
| # elif defined(HAVE_X11) | |||
| # include <X11/Xresource.h> | |||
| # endif | |||
| #else | |||
| #if DISTRHO_UI_USE_WEBVIEW && !defined(DISTRHO_OS_MAC) | |||
| # define DISTRHO_WEB_VIEW_HPP_INCLUDED | |||
| # define WEB_VIEW_NAMESPACE DISTRHO_NAMESPACE | |||
| # define WEB_VIEW_DISTRHO_NAMESPACE | |||
| START_NAMESPACE_DISTRHO | |||
| # include "../extra/WebViewImpl.hpp" | |||
| END_NAMESPACE_DISTRHO | |||
| # include "../extra/WebViewImpl.cpp" | |||
| #endif | |||
| #if ! DISTRHO_PLUGIN_HAS_EXTERNAL_UI | |||
| # include "src/TopLevelWidgetPrivateData.hpp" | |||
| # include "src/WindowPrivateData.hpp" | |||
| #endif | |||
| @@ -78,7 +88,6 @@ uintptr_t g_nextWindowId = 0; | |||
| double g_nextScaleFactor = 1.0; | |||
| #endif | |||
| #if DISTRHO_PLUGIN_HAS_EXTERNAL_UI | |||
| /* ------------------------------------------------------------------------------------------------------------ | |||
| * get global scale factor */ | |||
| @@ -91,23 +100,23 @@ static double getDesktopScaleFactor(const uintptr_t parentWindowHandle) | |||
| if (const char* const scale = getenv("DPF_SCALE_FACTOR")) | |||
| return std::max(1.0, std::atof(scale)); | |||
| #if defined(DISTRHO_OS_WINDOWS) | |||
| #if defined(DISTRHO_OS_WINDOWS) | |||
| if (const HMODULE Shcore = LoadLibraryA("Shcore.dll")) | |||
| { | |||
| typedef HRESULT(WINAPI* PFN_GetProcessDpiAwareness)(HANDLE, DWORD*); | |||
| typedef HRESULT(WINAPI* PFN_GetScaleFactorForMonitor)(HMONITOR, DWORD*); | |||
| # if defined(__GNUC__) && (__GNUC__ >= 9) | |||
| # pragma GCC diagnostic push | |||
| # pragma GCC diagnostic ignored "-Wcast-function-type" | |||
| # endif | |||
| #if defined(__GNUC__) && (__GNUC__ >= 9) | |||
| #pragma GCC diagnostic push | |||
| #pragma GCC diagnostic ignored "-Wcast-function-type" | |||
| #endif | |||
| const PFN_GetProcessDpiAwareness GetProcessDpiAwareness | |||
| = (PFN_GetProcessDpiAwareness)GetProcAddress(Shcore, "GetProcessDpiAwareness"); | |||
| const PFN_GetScaleFactorForMonitor GetScaleFactorForMonitor | |||
| = (PFN_GetScaleFactorForMonitor)GetProcAddress(Shcore, "GetScaleFactorForMonitor"); | |||
| # if defined(__GNUC__) && (__GNUC__ >= 9) | |||
| # pragma GCC diagnostic pop | |||
| # endif | |||
| #if defined(__GNUC__) && (__GNUC__ >= 9) | |||
| #pragma GCC diagnostic pop | |||
| #endif | |||
| DWORD dpiAware = 0; | |||
| DWORD scaleFactor = 100; | |||
| @@ -123,7 +132,7 @@ static double getDesktopScaleFactor(const uintptr_t parentWindowHandle) | |||
| FreeLibrary(Shcore); | |||
| return static_cast<double>(scaleFactor) / 100.0; | |||
| } | |||
| #elif defined(HAVE_X11) | |||
| #elif defined(HAVE_X11) | |||
| ::Display* const display = XOpenDisplay(nullptr); | |||
| DISTRHO_SAFE_ASSERT_RETURN(display != nullptr, 1.0); | |||
| @@ -154,7 +163,7 @@ static double getDesktopScaleFactor(const uintptr_t parentWindowHandle) | |||
| XCloseDisplay(display); | |||
| return dpi / 96; | |||
| #endif | |||
| #endif | |||
| return 1.0; | |||
| @@ -163,8 +172,6 @@ static double getDesktopScaleFactor(const uintptr_t parentWindowHandle) | |||
| } | |||
| #endif // !DISTRHO_OS_MAC | |||
| #endif | |||
| /* ------------------------------------------------------------------------------------------------------------ | |||
| * UI::PrivateData special handling */ | |||
| @@ -178,7 +185,6 @@ PluginWindow& | |||
| UI::PrivateData::createNextWindow(UI* const ui, uint width, uint height, const bool adjustForScaleFactor) | |||
| { | |||
| UI::PrivateData* const pData = s_nextPrivateData; | |||
| #if DISTRHO_PLUGIN_HAS_EXTERNAL_UI | |||
| const double scaleFactor = d_isNotZero(pData->scaleFactor) ? pData->scaleFactor : getDesktopScaleFactor(pData->winId); | |||
| if (adjustForScaleFactor && d_isNotZero(scaleFactor) && d_isNotEqual(scaleFactor, 1.0)) | |||
| @@ -187,6 +193,7 @@ UI::PrivateData::createNextWindow(UI* const ui, uint width, uint height, const b | |||
| height *= scaleFactor; | |||
| } | |||
| #if DISTRHO_PLUGIN_HAS_EXTERNAL_UI | |||
| pData->window = new PluginWindow(ui, pData->app); | |||
| ExternalWindow::PrivateData ewData; | |||
| ewData.parentWindowHandle = pData->winId; | |||
| @@ -197,14 +204,7 @@ UI::PrivateData::createNextWindow(UI* const ui, uint width, uint height, const b | |||
| ewData.isStandalone = DISTRHO_UI_IS_STANDALONE; | |||
| return ewData; | |||
| #else | |||
| const double scaleFactor = pData->scaleFactor; | |||
| if (adjustForScaleFactor && d_isNotZero(scaleFactor) && d_isNotEqual(scaleFactor, 1.0)) | |||
| { | |||
| width *= scaleFactor; | |||
| height *= scaleFactor; | |||
| } | |||
| d_stdout("createNextWindow %u %u %f %d", width, height, scaleFactor, adjustForScaleFactor); | |||
| pData->window = new PluginWindow(ui, pData->app, pData->winId, width, height, scaleFactor); | |||
| // If there are no callbacks, this is most likely a temporary window, so ignore idle callbacks | |||
| @@ -220,14 +220,17 @@ UI::PrivateData::createNextWindow(UI* const ui, uint width, uint height, const b | |||
| UI::UI(const uint width, const uint height, const bool automaticallyScaleAndSetAsMinimumSize) | |||
| : UIWidget(UI::PrivateData::createNextWindow(this, | |||
| // width | |||
| #ifdef DISTRHO_UI_DEFAULT_WIDTH | |||
| width == 0 ? DISTRHO_UI_DEFAULT_WIDTH : | |||
| #endif | |||
| width, | |||
| // height | |||
| #ifdef DISTRHO_UI_DEFAULT_HEIGHT | |||
| height == 0 ? DISTRHO_UI_DEFAULT_HEIGHT : | |||
| #endif | |||
| height, | |||
| // adjustForScaleFactor | |||
| #ifdef DISTRHO_UI_DEFAULT_WIDTH | |||
| width == 0 | |||
| #else | |||
| @@ -366,6 +369,25 @@ uintptr_t UI::getNextWindowId() noexcept | |||
| # endif | |||
| #endif // DISTRHO_PLUGIN_HAS_EXTERNAL_UI | |||
| /* ------------------------------------------------------------------------------------------------------------ | |||
| * DSP/Plugin Callbacks */ | |||
| void UI::parameterChanged(uint32_t, float) | |||
| { | |||
| } | |||
| #if DISTRHO_PLUGIN_WANT_PROGRAMS | |||
| void UI::programLoaded(uint32_t) | |||
| { | |||
| } | |||
| #endif | |||
| #if DISTRHO_PLUGIN_WANT_STATE | |||
| void UI::stateChanged(const char*, const char*) | |||
| { | |||
| } | |||
| #endif | |||
| /* ------------------------------------------------------------------------------------------------------------ | |||
| * DSP/Plugin Callbacks (optional) */ | |||
| @@ -1,6 +1,6 @@ | |||
| /* | |||
| * DISTRHO Plugin Framework (DPF) | |||
| * Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com> | |||
| * 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 | |||
| @@ -29,7 +29,14 @@ | |||
| #define DISTRHO_PLUGIN_NUM_INPUTS 2 | |||
| #define DISTRHO_PLUGIN_NUM_OUTPUTS 2 | |||
| #define DISTRHO_UI_FILE_BROWSER 0 | |||
| #define DISTRHO_UI_USE_WEBVIEW 1 | |||
| #define DISTRHO_UI_USER_RESIZABLE 1 | |||
| #define DISTRHO_UI_DEFAULT_WIDTH 1184 | |||
| #define DISTRHO_UI_DEFAULT_HEIGHT 768 | |||
| // #ifdef _WIN32 | |||
| // #define WEB_VIEW_USING_CHOC 1 | |||
| // #endif | |||
| enum Parameters { | |||
| kParameterWidth = 0, | |||
| @@ -1,6 +1,6 @@ | |||
| /* | |||
| * DISTRHO Plugin Framework (DPF) | |||
| * Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com> | |||
| * 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 | |||
| @@ -28,8 +28,8 @@ class EmbedExternalExamplePlugin : public Plugin | |||
| public: | |||
| EmbedExternalExamplePlugin() | |||
| : Plugin(kParameterCount, 0, 0), | |||
| fWidth(512.0f), | |||
| fHeight(256.0f) | |||
| fWidth(DISTRHO_UI_DEFAULT_WIDTH), | |||
| fHeight(DISTRHO_UI_DEFAULT_HEIGHT) | |||
| { | |||
| } | |||
| @@ -122,8 +122,8 @@ protected: | |||
| { | |||
| case kParameterWidth: | |||
| parameter.hints = kParameterIsAutomatable|kParameterIsInteger; | |||
| parameter.ranges.def = 512.0f; | |||
| parameter.ranges.min = 256.0f; | |||
| parameter.ranges.def = DISTRHO_UI_DEFAULT_WIDTH; | |||
| parameter.ranges.min = 512.0f; | |||
| parameter.ranges.max = 4096.0f; | |||
| parameter.name = "Width"; | |||
| parameter.symbol = "width"; | |||
| @@ -131,8 +131,8 @@ protected: | |||
| break; | |||
| case kParameterHeight: | |||
| parameter.hints = kParameterIsAutomatable|kParameterIsInteger; | |||
| parameter.ranges.def = 256.0f; | |||
| parameter.ranges.min = 256.0f; | |||
| parameter.ranges.def = DISTRHO_UI_DEFAULT_HEIGHT; | |||
| parameter.ranges.min = 512.0f; | |||
| parameter.ranges.max = 4096.0f; | |||
| parameter.name = "Height"; | |||
| parameter.symbol = "height"; | |||
| @@ -1,6 +1,6 @@ | |||
| /* | |||
| * DISTRHO Plugin Framework (DPF) | |||
| * Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com> | |||
| * 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 | |||
| @@ -14,242 +14,49 @@ | |||
| * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |||
| */ | |||
| // needed for IDE | |||
| #include "DistrhoPluginInfo.h" | |||
| #include "DistrhoUI.hpp" | |||
| #if defined(DISTRHO_OS_HAIKU) | |||
| #elif defined(DISTRHO_OS_MAC) | |||
| # import <Cocoa/Cocoa.h> | |||
| #elif defined(DISTRHO_OS_WINDOWS) | |||
| # define WIN32_CLASS_NAME "DPF-EmbedExternalExampleUI" | |||
| # define WIN32_LEAN_AND_MEAN | |||
| # include <windows.h> | |||
| #else | |||
| # include <sys/types.h> | |||
| # include <X11/Xatom.h> | |||
| # include <X11/Xlib.h> | |||
| # include <X11/Xutil.h> | |||
| # define X11Key_Escape 9 | |||
| #endif | |||
| #if defined(DISTRHO_OS_MAC) | |||
| # ifndef __MAC_10_12 | |||
| # define NSEventMaskAny NSAnyEventMask | |||
| # define NSWindowStyleMaskClosable NSClosableWindowMask | |||
| # define NSWindowStyleMaskMiniaturizable NSMiniaturizableWindowMask | |||
| # define NSWindowStyleMaskResizable NSResizableWindowMask | |||
| # define NSWindowStyleMaskTitled NSTitledWindowMask | |||
| # endif | |||
| @interface NSExternalWindow : NSWindow | |||
| @end | |||
| @implementation NSExternalWindow { | |||
| @public | |||
| bool closed; | |||
| bool standalone; | |||
| } | |||
| - (BOOL)canBecomeKeyWindow { return YES; } | |||
| - (BOOL)canBecomeMainWindow { return standalone ? YES : NO; } | |||
| - (BOOL)windowShouldClose:(id)_ { closed = true; return YES; } | |||
| @end | |||
| #endif | |||
| #include "NativeWindow.hpp" | |||
| #include "extra/WebView.hpp" | |||
| START_NAMESPACE_DISTRHO | |||
| // ----------------------------------------------------------------------------------------------------------- | |||
| // -------------------------------------------------------------------------------------------------------------------- | |||
| class EmbedExternalExampleUI : public UI | |||
| class EmbedExternalExampleUI : public UI, | |||
| public NativeWindow::Callbacks | |||
| { | |||
| #if defined(DISTRHO_OS_HAIKU) | |||
| char d; | |||
| #elif defined(DISTRHO_OS_MAC) | |||
| NSView* fView; | |||
| NSExternalWindow* fWindow; | |||
| #elif defined(DISTRHO_OS_WINDOWS) | |||
| ::HWND fWindow; | |||
| #else | |||
| ::Display* fDisplay; | |||
| ::Window fWindow; | |||
| #endif | |||
| ScopedPointer<NativeWindow> window; | |||
| WebViewHandle webview; | |||
| public: | |||
| EmbedExternalExampleUI() | |||
| : UI(512, 256), | |||
| #if defined(DISTRHO_OS_HAIKU) | |||
| d(0) | |||
| #elif defined(DISTRHO_OS_MAC) | |||
| fView(nullptr), | |||
| fWindow(nullptr) | |||
| #elif defined(DISTRHO_OS_WINDOWS) | |||
| fWindow(nullptr) | |||
| #else | |||
| fDisplay(nullptr), | |||
| fWindow(0) | |||
| #endif | |||
| : UI(), | |||
| webview(nullptr) | |||
| { | |||
| const bool standalone = isStandalone(); | |||
| const double scaleFactor = getScaleFactor(); | |||
| d_stdout("isStandalone %d", (int)standalone); | |||
| #if defined(DISTRHO_OS_HAIKU) | |||
| #elif defined(DISTRHO_OS_MAC) | |||
| NSAutoreleasePool* const pool = [[NSAutoreleasePool alloc]init]; | |||
| [NSApplication sharedApplication]; | |||
| if (standalone) | |||
| { | |||
| [NSApp setActivationPolicy:NSApplicationActivationPolicyRegular]; | |||
| [NSApp activateIgnoringOtherApps:YES]; | |||
| } | |||
| const uint width = DISTRHO_UI_DEFAULT_WIDTH * scaleFactor; | |||
| const uint height = DISTRHO_UI_DEFAULT_HEIGHT * scaleFactor; | |||
| fView = [NSView new]; | |||
| DISTRHO_SAFE_ASSERT_RETURN(fView != nullptr,); | |||
| window = new NativeWindow(this, getTitle(), getParentWindowHandle(), width, height, standalone); | |||
| webview = webViewCreate("https://distrho.github.io/DPF/", | |||
| window->getNativeWindowHandle(), | |||
| width, height, scaleFactor); | |||
| [fView setFrame:NSMakeRect(0, 0, getWidth(), getHeight())]; | |||
| [fView setAutoresizesSubviews:YES]; | |||
| [fView setWantsLayer:YES]; | |||
| [[fView layer] setBackgroundColor:[[NSColor blueColor] CGColor]]; | |||
| setGeometryConstraints(width, height); | |||
| if (isEmbed()) | |||
| { | |||
| [fView retain]; | |||
| [(NSView*)getParentWindowHandle() addSubview:fView]; | |||
| } | |||
| else | |||
| { | |||
| const ulong styleMask = NSWindowStyleMaskClosable | |||
| | NSWindowStyleMaskMiniaturizable | |||
| | NSWindowStyleMaskResizable | |||
| | NSWindowStyleMaskTitled; | |||
| fWindow = [[[NSExternalWindow alloc] | |||
| initWithContentRect:[fView frame] | |||
| styleMask:styleMask | |||
| backing:NSBackingStoreBuffered | |||
| defer:NO]retain]; | |||
| DISTRHO_SAFE_ASSERT_RETURN(fWindow != nullptr,); | |||
| fWindow->closed = false; // is this needed? | |||
| fWindow->standalone = standalone; | |||
| [fWindow setIsVisible:NO]; | |||
| if (NSString* const nsTitle = [[NSString alloc] | |||
| initWithBytes:getTitle() | |||
| length:strlen(getTitle()) | |||
| encoding:NSUTF8StringEncoding]) | |||
| [fWindow setTitle:nsTitle]; | |||
| [fWindow setContentView:fView]; | |||
| [fWindow setContentSize:NSMakeSize(getWidth(), getHeight())]; | |||
| [fWindow makeFirstResponder:fView]; | |||
| } | |||
| if (d_isNotEqual(scaleFactor, 1.0)) | |||
| setSize(width, height); | |||
| [pool release]; | |||
| #elif defined(DISTRHO_OS_WINDOWS) | |||
| WNDCLASS windowClass = {}; | |||
| windowClass.style = CS_OWNDC; | |||
| windowClass.lpfnWndProc = DefWindowProc; | |||
| windowClass.hInstance = nullptr; | |||
| windowClass.hIcon = LoadIcon(nullptr, IDI_APPLICATION); | |||
| windowClass.hCursor = LoadCursor(nullptr, IDC_ARROW); | |||
| windowClass.lpszClassName = WIN32_CLASS_NAME; | |||
| DISTRHO_SAFE_ASSERT_RETURN(RegisterClass(&windowClass),); | |||
| const int winFlags = isEmbed() ? (WS_CHILD | WS_VISIBLE) : (WS_POPUPWINDOW | WS_CAPTION | WS_SIZEBOX); | |||
| RECT rect = { 0, 0, static_cast<LONG>(getWidth()), static_cast<LONG>(getHeight()) }; | |||
| AdjustWindowRectEx(&rect, winFlags, FALSE, WS_EX_TOPMOST); | |||
| fWindow = CreateWindowEx(WS_EX_TOPMOST, | |||
| WIN32_CLASS_NAME, | |||
| getTitle(), | |||
| winFlags, | |||
| CW_USEDEFAULT, CW_USEDEFAULT, | |||
| rect.right - rect.left, | |||
| rect.bottom - rect.top, | |||
| (HWND)getParentWindowHandle(), | |||
| nullptr, nullptr, nullptr); | |||
| DISTRHO_SAFE_ASSERT_RETURN(fWindow != nullptr,); | |||
| SetWindowLongPtr(fWindow, GWLP_USERDATA, (LONG_PTR)this); | |||
| #else | |||
| fDisplay = XOpenDisplay(nullptr); | |||
| DISTRHO_SAFE_ASSERT_RETURN(fDisplay != nullptr,); | |||
| const ::Window parent = isEmbed() | |||
| ? (::Window)getParentWindowHandle() | |||
| : RootWindow(fDisplay, DefaultScreen(fDisplay)); | |||
| fWindow = XCreateSimpleWindow(fDisplay, parent, 0, 0, getWidth(), getHeight(), 0, 0, 0); | |||
| DISTRHO_SAFE_ASSERT_RETURN(fWindow != 0,); | |||
| XSizeHints sizeHints = {}; | |||
| sizeHints.flags = PMinSize; | |||
| sizeHints.min_width = getWidth(); | |||
| sizeHints.min_height = getHeight(); | |||
| XSetNormalHints(fDisplay, fWindow, &sizeHints); | |||
| XStoreName(fDisplay, fWindow, getTitle()); | |||
| if (isEmbed()) | |||
| { | |||
| // start with window mapped, so host can access it | |||
| XMapWindow(fDisplay, fWindow); | |||
| } | |||
| else | |||
| { | |||
| // grab Esc key for auto-close | |||
| XGrabKey(fDisplay, X11Key_Escape, AnyModifier, fWindow, 1, GrabModeAsync, GrabModeAsync); | |||
| // destroy window on close | |||
| Atom wmDelete = XInternAtom(fDisplay, "WM_DELETE_WINDOW", True); | |||
| XSetWMProtocols(fDisplay, fWindow, &wmDelete, 1); | |||
| // set pid WM hint | |||
| const pid_t pid = getpid(); | |||
| const Atom _nwp = XInternAtom(fDisplay, "_NET_WM_PID", False); | |||
| XChangeProperty(fDisplay, fWindow, _nwp, XA_CARDINAL, 32, PropModeReplace, (const uchar*)&pid, 1); | |||
| // set the window to both dialog and normal to produce a decorated floating dialog | |||
| // order is important: DIALOG needs to come before NORMAL | |||
| const Atom _wt = XInternAtom(fDisplay, "_NET_WM_WINDOW_TYPE", False); | |||
| const Atom _wts[2] = { | |||
| XInternAtom(fDisplay, "_NET_WM_WINDOW_TYPE_DIALOG", False), | |||
| XInternAtom(fDisplay, "_NET_WM_WINDOW_TYPE_NORMAL", False) | |||
| }; | |||
| XChangeProperty(fDisplay, fWindow, _wt, XA_ATOM, 32, PropModeReplace, (const uchar*)&_wts, 2); | |||
| } | |||
| #endif | |||
| d_stdout("created external window with size %u %u", getWidth(), getHeight()); | |||
| } | |||
| ~EmbedExternalExampleUI() | |||
| { | |||
| #if defined(DISTRHO_OS_HAIKU) | |||
| #elif defined(DISTRHO_OS_MAC) | |||
| if (fView == nullptr) | |||
| return; | |||
| if (fWindow != nil) | |||
| [fWindow close]; | |||
| [fView release]; | |||
| if (fWindow != nil) | |||
| [fWindow release]; | |||
| #elif defined(DISTRHO_OS_WINDOWS) | |||
| if (fWindow != nullptr) | |||
| DestroyWindow(fWindow); | |||
| UnregisterClass(WIN32_CLASS_NAME, nullptr); | |||
| #else | |||
| if (fDisplay == nullptr) | |||
| return; | |||
| if (fWindow != 0) | |||
| XDestroyWindow(fDisplay, fWindow); | |||
| XCloseDisplay(fDisplay); | |||
| #endif | |||
| if (webview != nullptr) | |||
| webViewDestroy(webview); | |||
| } | |||
| protected: | |||
| @@ -275,214 +82,71 @@ protected: | |||
| } | |||
| } | |||
| /* -------------------------------------------------------------------------------------------------------- | |||
| * External Window callbacks */ | |||
| void nativeHide() override | |||
| { | |||
| d_stdout("nativeHide"); | |||
| UI::hide(); | |||
| } | |||
| void nativeResize(const uint width, const uint height) override | |||
| { | |||
| d_stdout("nativeResize"); | |||
| setSize(width, height); | |||
| } | |||
| /* -------------------------------------------------------------------------------------------------------- | |||
| * External Window overrides */ | |||
| void focus() override | |||
| { | |||
| d_stdout("focus"); | |||
| #if defined(DISTRHO_OS_HAIKU) | |||
| #elif defined(DISTRHO_OS_MAC) | |||
| DISTRHO_SAFE_ASSERT_RETURN(fWindow != nullptr,); | |||
| [fWindow orderFrontRegardless]; | |||
| [fWindow makeKeyWindow]; | |||
| [fWindow makeFirstResponder:fView]; | |||
| #elif defined(DISTRHO_OS_WINDOWS) | |||
| DISTRHO_SAFE_ASSERT_RETURN(fWindow != nullptr,); | |||
| SetForegroundWindow(fWindow); | |||
| SetActiveWindow(fWindow); | |||
| SetFocus(fWindow); | |||
| #else | |||
| DISTRHO_SAFE_ASSERT_RETURN(fWindow != 0,); | |||
| XRaiseWindow(fDisplay, fWindow); | |||
| #endif | |||
| window->focus(); | |||
| } | |||
| uintptr_t getNativeWindowHandle() const noexcept override | |||
| { | |||
| #if defined(DISTRHO_OS_HAIKU) | |||
| #elif defined(DISTRHO_OS_MAC) | |||
| return (uintptr_t)fView; | |||
| #elif defined(DISTRHO_OS_WINDOWS) | |||
| return (uintptr_t)fWindow; | |||
| #else | |||
| return (uintptr_t)fWindow; | |||
| #endif | |||
| return window->getNativeWindowHandle(); | |||
| } | |||
| void sizeChanged(uint width, uint height) override | |||
| void sizeChanged(const uint width, const uint height) override | |||
| { | |||
| d_stdout("sizeChanged %u %u", width, height); | |||
| UI::sizeChanged(width, height); | |||
| #if defined(DISTRHO_OS_HAIKU) | |||
| #elif defined(DISTRHO_OS_MAC) | |||
| NSRect rect = [fView frame]; | |||
| rect.size = CGSizeMake((CGFloat)width, (CGFloat)height); | |||
| [fView setFrame:rect]; | |||
| #elif defined(DISTRHO_OS_WINDOWS) | |||
| if (fWindow != nullptr) | |||
| SetWindowPos(fWindow, | |||
| HWND_TOP, | |||
| 0, 0, | |||
| width, height, | |||
| SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOOWNERZORDER | SWP_NOZORDER); | |||
| #else | |||
| if (fWindow != 0) | |||
| XResizeWindow(fDisplay, fWindow, width, height); | |||
| #endif | |||
| window->setSize(width, height); | |||
| if (webview != nullptr) | |||
| webViewResize(webview, width, height, getScaleFactor()); | |||
| } | |||
| void titleChanged(const char* const title) override | |||
| { | |||
| d_stdout("titleChanged %s", title); | |||
| #if defined(DISTRHO_OS_HAIKU) | |||
| #elif defined(DISTRHO_OS_MAC) | |||
| if (fWindow != nil) | |||
| { | |||
| if (NSString* const nsTitle = [[NSString alloc] | |||
| initWithBytes:title | |||
| length:strlen(title) | |||
| encoding:NSUTF8StringEncoding]) | |||
| { | |||
| [fWindow setTitle:nsTitle]; | |||
| [nsTitle release]; | |||
| } | |||
| } | |||
| #elif defined(DISTRHO_OS_WINDOWS) | |||
| #else | |||
| DISTRHO_SAFE_ASSERT_RETURN(fWindow != 0,); | |||
| XStoreName(fDisplay, fWindow, title); | |||
| #endif | |||
| window->setTitle(title); | |||
| } | |||
| void transientParentWindowChanged(const uintptr_t winId) override | |||
| { | |||
| d_stdout("transientParentWindowChanged %lu", winId); | |||
| #if defined(DISTRHO_OS_HAIKU) | |||
| #elif defined(DISTRHO_OS_MAC) | |||
| #elif defined(DISTRHO_OS_WINDOWS) | |||
| #else | |||
| DISTRHO_SAFE_ASSERT_RETURN(fWindow != 0,); | |||
| XSetTransientForHint(fDisplay, fWindow, (::Window)winId); | |||
| #endif | |||
| window->setTransientParentWindow(winId); | |||
| } | |||
| void visibilityChanged(const bool visible) override | |||
| { | |||
| d_stdout("visibilityChanged %d", visible); | |||
| #if defined(DISTRHO_OS_HAIKU) | |||
| #elif defined(DISTRHO_OS_MAC) | |||
| DISTRHO_SAFE_ASSERT_RETURN(fView != nullptr,); | |||
| if (fWindow != nil) | |||
| { | |||
| [fWindow setIsVisible:(visible ? YES : NO)]; | |||
| if (isStandalone()) | |||
| [fWindow makeMainWindow]; | |||
| [fWindow makeKeyAndOrderFront:fWindow]; | |||
| } | |||
| else | |||
| { | |||
| [fView setHidden:(visible ? NO : YES)]; | |||
| } | |||
| #elif defined(DISTRHO_OS_WINDOWS) | |||
| DISTRHO_SAFE_ASSERT_RETURN(fWindow != nullptr,); | |||
| ShowWindow(fWindow, visible ? SW_SHOWNORMAL : SW_HIDE); | |||
| #else | |||
| DISTRHO_SAFE_ASSERT_RETURN(fWindow != 0,); | |||
| if (visible) | |||
| XMapRaised(fDisplay, fWindow); | |||
| else | |||
| XUnmapWindow(fDisplay, fWindow); | |||
| #endif | |||
| window->setVisible(visible); | |||
| } | |||
| void uiIdle() override | |||
| { | |||
| // d_stdout("uiIdle"); | |||
| #if defined(DISTRHO_OS_HAIKU) | |||
| #elif defined(DISTRHO_OS_MAC) | |||
| if (isEmbed()) { | |||
| return; | |||
| } | |||
| NSAutoreleasePool* const pool = [[NSAutoreleasePool alloc] init]; | |||
| NSDate* const date = [NSDate distantPast]; | |||
| for (NSEvent* event;;) | |||
| { | |||
| event = [NSApp | |||
| nextEventMatchingMask:NSEventMaskAny | |||
| untilDate:date | |||
| inMode:NSDefaultRunLoopMode | |||
| dequeue:YES]; | |||
| window->idle(); | |||
| if (event == nil) | |||
| break; | |||
| [NSApp sendEvent:event]; | |||
| } | |||
| if (fWindow->closed) | |||
| { | |||
| fWindow->closed = false; | |||
| close(); | |||
| } | |||
| [pool release]; | |||
| #elif defined(DISTRHO_OS_WINDOWS) | |||
| if (fWindow == nullptr) | |||
| return; | |||
| /* | |||
| MSG msg; | |||
| if (! ::PeekMessage(&msg, fWindow, 0, 0, PM_NOREMOVE)) | |||
| return true; | |||
| if (::GetMessage(&msg, nullptr, 0, 0) >= 0) | |||
| { | |||
| if (msg.message == WM_QUIT) | |||
| return false; | |||
| //TranslateMessage(&msg); | |||
| DispatchMessage(&msg); | |||
| } | |||
| */ | |||
| #else | |||
| if (fDisplay == nullptr) | |||
| return; | |||
| for (XEvent event; XPending(fDisplay) > 0;) | |||
| { | |||
| XNextEvent(fDisplay, &event); | |||
| if (! isVisible()) | |||
| continue; | |||
| switch (event.type) | |||
| { | |||
| case ClientMessage: | |||
| if (char* const type = XGetAtomName(fDisplay, event.xclient.message_type)) | |||
| { | |||
| if (std::strcmp(type, "WM_PROTOCOLS") == 0) | |||
| hide(); | |||
| } | |||
| break; | |||
| case KeyRelease: | |||
| if (event.xkey.keycode == X11Key_Escape) | |||
| hide(); | |||
| break; | |||
| } | |||
| } | |||
| #endif | |||
| if (webview != nullptr) | |||
| webViewIdle(webview); | |||
| } | |||
| // ------------------------------------------------------------------------------------------------------- | |||
| @@ -21,11 +21,14 @@ FILES_UI = \ | |||
| # -------------------------------------------------------------- | |||
| # Do some magic | |||
| USE_WEBVIEW = true | |||
| UI_TYPE = external | |||
| include ../../Makefile.plugins.mk | |||
| ifeq ($(MACOS),true) | |||
| BUILD_CXX_FLAGS += -ObjC++ | |||
| # else ifeq ($(WINDOWS),true) | |||
| # BUILD_CXX_FLAGS += -std=gnu++17 | |||
| endif | |||
| # -------------------------------------------------------------- | |||
| @@ -0,0 +1,503 @@ | |||
| /* | |||
| * 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 "DistrhoUI.hpp" | |||
| #if defined(DISTRHO_OS_HAIKU) | |||
| #elif defined(DISTRHO_OS_MAC) | |||
| # import <Cocoa/Cocoa.h> | |||
| #elif defined(DISTRHO_OS_WINDOWS) | |||
| # define WIN32_CLASS_NAME "DPF-EmbedExternalExampleUI" | |||
| # define WIN32_LEAN_AND_MEAN | |||
| # include <windows.h> | |||
| #else | |||
| # include <sys/types.h> | |||
| # include <X11/Xatom.h> | |||
| # include <X11/Xlib.h> | |||
| # include <X11/Xutil.h> | |||
| # define X11Key_Escape 9 | |||
| #endif | |||
| #if defined(DISTRHO_OS_MAC) | |||
| # ifndef __MAC_10_12 | |||
| # define NSEventMaskAny NSAnyEventMask | |||
| # define NSWindowStyleMaskClosable NSClosableWindowMask | |||
| # define NSWindowStyleMaskMiniaturizable NSMiniaturizableWindowMask | |||
| # define NSWindowStyleMaskResizable NSResizableWindowMask | |||
| # define NSWindowStyleMaskTitled NSTitledWindowMask | |||
| # endif | |||
| @interface NSExternalWindow : NSWindow | |||
| @end | |||
| @implementation NSExternalWindow { | |||
| @public | |||
| bool closed; | |||
| bool standalone; | |||
| } | |||
| - (BOOL)canBecomeKeyWindow { return YES; } | |||
| - (BOOL)canBecomeMainWindow { return standalone ? YES : NO; } | |||
| - (BOOL)windowShouldClose:(id)_ { closed = true; return YES; } | |||
| @end | |||
| #endif | |||
| START_NAMESPACE_DISTRHO | |||
| // -------------------------------------------------------------------------------------------------------------------- | |||
| struct NativeWindow { | |||
| struct Callbacks { | |||
| virtual ~Callbacks() {} | |||
| virtual void nativeHide() = 0; | |||
| virtual void nativeResize(uint width, uint height) = 0; | |||
| }; | |||
| Callbacks* const fCallbacks; | |||
| const bool fIsStandalone; | |||
| const bool fIsEmbed; | |||
| bool fIsVisible; | |||
| #if defined(DISTRHO_OS_HAIKU) | |||
| #elif defined(DISTRHO_OS_MAC) | |||
| NSView* fView; | |||
| NSExternalWindow* fWindow; | |||
| #elif defined(DISTRHO_OS_WINDOWS) | |||
| ::HWND fWindow; | |||
| #else | |||
| ::Display* fDisplay; | |||
| ::Window fWindow; | |||
| #endif | |||
| NativeWindow(Callbacks* callbacks, | |||
| const char* title, | |||
| uintptr_t parentWindowHandle, | |||
| uint width, | |||
| uint height, | |||
| bool isStandalone); | |||
| ~NativeWindow(); | |||
| void idle(); | |||
| void focus(); | |||
| void setSize(uint width, uint height); | |||
| void setTitle(const char* title); | |||
| void setTransientParentWindow(uintptr_t winId); | |||
| void setVisible(bool visible); | |||
| void hide() | |||
| { | |||
| fCallbacks->nativeHide(); | |||
| } | |||
| uintptr_t getNativeWindowHandle() const noexcept | |||
| { | |||
| #if defined(DISTRHO_OS_HAIKU) | |||
| #elif defined(DISTRHO_OS_MAC) | |||
| return (uintptr_t)fView; | |||
| #elif defined(DISTRHO_OS_WINDOWS) | |||
| return (uintptr_t)fWindow; | |||
| #else | |||
| return (uintptr_t)fWindow; | |||
| #endif | |||
| } | |||
| }; | |||
| // -------------------------------------------------------------------------------------------------------------------- | |||
| NativeWindow::NativeWindow(Callbacks* const callbacks, | |||
| const char* const title, | |||
| const uintptr_t parentWindowHandle, | |||
| const uint width, | |||
| const uint height, | |||
| const bool isStandalone) | |||
| : fCallbacks(callbacks), | |||
| fIsStandalone(isStandalone), | |||
| fIsEmbed(parentWindowHandle != 0), | |||
| fIsVisible(parentWindowHandle != 0), | |||
| #if defined(DISTRHO_OS_HAIKU) | |||
| #elif defined(DISTRHO_OS_MAC) | |||
| fView(nullptr), | |||
| fWindow(nullptr) | |||
| #elif defined(DISTRHO_OS_WINDOWS) | |||
| fWindow(nullptr) | |||
| #else | |||
| fDisplay(nullptr), | |||
| fWindow(0) | |||
| #endif | |||
| { | |||
| #if defined(DISTRHO_OS_HAIKU) | |||
| #elif defined(DISTRHO_OS_MAC) | |||
| NSAutoreleasePool* const pool = [[NSAutoreleasePool alloc] init]; | |||
| [NSApplication sharedApplication]; | |||
| if (standalone) | |||
| { | |||
| [NSApp setActivationPolicy:NSApplicationActivationPolicyRegular]; | |||
| [NSApp activateIgnoringOtherApps:YES]; | |||
| } | |||
| fView = [NSView new]; | |||
| DISTRHO_SAFE_ASSERT_RETURN(fView != nullptr,); | |||
| [fView setFrame:NSMakeRect(0, 0, width, height)]; | |||
| [fView setAutoresizesSubviews:YES]; | |||
| [fView setWantsLayer:YES]; | |||
| [[fView layer] setBackgroundColor:[[NSColor blueColor] CGColor]]; | |||
| if (fIsEmbed) | |||
| { | |||
| [fView retain]; | |||
| [(NSView*)parentWindowHandle addSubview:fView]; | |||
| } | |||
| else | |||
| { | |||
| const ulong styleMask = NSWindowStyleMaskClosable | |||
| | NSWindowStyleMaskMiniaturizable | |||
| | NSWindowStyleMaskResizable | |||
| | NSWindowStyleMaskTitled; | |||
| fWindow = [[[NSExternalWindow alloc] | |||
| initWithContentRect:[fView frame] | |||
| styleMask:styleMask | |||
| backing:NSBackingStoreBuffered | |||
| defer:NO] retain]; | |||
| DISTRHO_SAFE_ASSERT_RETURN(fWindow != nullptr,); | |||
| fWindow->closed = false; // is this needed? | |||
| fWindow->standalone = standalone; | |||
| [fWindow setIsVisible:NO]; | |||
| if (NSString* const nsTitle = [[NSString alloc] | |||
| initWithBytes:title | |||
| length:std::strlen(title) | |||
| encoding:NSUTF8StringEncoding]) | |||
| { | |||
| [fWindow setTitle:nsTitle]; | |||
| [nsTitle release]; | |||
| } | |||
| [fWindow setContentView:fView]; | |||
| [fWindow setContentSize:NSMakeSize(width, height)]; | |||
| [fWindow makeFirstResponder:fView]; | |||
| } | |||
| [pool release]; | |||
| #elif defined(DISTRHO_OS_WINDOWS) | |||
| WNDCLASS windowClass = {}; | |||
| windowClass.style = CS_OWNDC; | |||
| windowClass.lpfnWndProc = DefWindowProc; | |||
| windowClass.hInstance = nullptr; | |||
| windowClass.hIcon = LoadIcon(nullptr, IDI_APPLICATION); | |||
| windowClass.hCursor = LoadCursor(nullptr, IDC_ARROW); | |||
| windowClass.lpszClassName = WIN32_CLASS_NAME; | |||
| DISTRHO_SAFE_ASSERT_RETURN(RegisterClass(&windowClass),); | |||
| const int winFlags = fIsEmbed | |||
| ? WS_CHILD | WS_VISIBLE | |||
| : WS_POPUPWINDOW | WS_CAPTION | WS_SIZEBOX; | |||
| RECT rect = { 0, 0, static_cast<LONG>(width), static_cast<LONG>(height) }; | |||
| AdjustWindowRectEx(&rect, winFlags, FALSE, WS_EX_TOPMOST); | |||
| fWindow = CreateWindowEx(WS_EX_TOPMOST, | |||
| WIN32_CLASS_NAME, | |||
| title, | |||
| winFlags, | |||
| CW_USEDEFAULT, CW_USEDEFAULT, | |||
| rect.right - rect.left, | |||
| rect.bottom - rect.top, | |||
| (HWND)parentWindowHandle, | |||
| nullptr, nullptr, nullptr); | |||
| DISTRHO_SAFE_ASSERT_RETURN(fWindow != nullptr,); | |||
| SetWindowLongPtr(fWindow, GWLP_USERDATA, (LONG_PTR)this); | |||
| #else | |||
| fDisplay = XOpenDisplay(nullptr); | |||
| DISTRHO_SAFE_ASSERT_RETURN(fDisplay != nullptr,); | |||
| const ::Window parent = fIsEmbed | |||
| ? (::Window)parentWindowHandle | |||
| : RootWindow(fDisplay, DefaultScreen(fDisplay)); | |||
| fWindow = XCreateSimpleWindow(fDisplay, parent, 0, 0, width, height, 0, 0, 0); | |||
| DISTRHO_SAFE_ASSERT_RETURN(fWindow != 0,); | |||
| XSizeHints sizeHints = {}; | |||
| sizeHints.flags = PMinSize; | |||
| sizeHints.min_width = width; | |||
| sizeHints.min_height = height; | |||
| XSetNormalHints(fDisplay, fWindow, &sizeHints); | |||
| XStoreName(fDisplay, fWindow, title); | |||
| if (fIsEmbed) | |||
| { | |||
| // start with window mapped, so host can access it | |||
| XMapWindow(fDisplay, fWindow); | |||
| } | |||
| else | |||
| { | |||
| // grab Esc key for auto-close | |||
| XGrabKey(fDisplay, X11Key_Escape, AnyModifier, fWindow, 1, GrabModeAsync, GrabModeAsync); | |||
| // destroy window on close | |||
| Atom wmDelete = XInternAtom(fDisplay, "WM_DELETE_WINDOW", True); | |||
| XSetWMProtocols(fDisplay, fWindow, &wmDelete, 1); | |||
| // set pid WM hint | |||
| const pid_t pid = getpid(); | |||
| const Atom _nwp = XInternAtom(fDisplay, "_NET_WM_PID", False); | |||
| XChangeProperty(fDisplay, fWindow, _nwp, XA_CARDINAL, 32, PropModeReplace, (const uchar*)&pid, 1); | |||
| // set the window to both dialog and normal to produce a decorated floating dialog | |||
| // order is important: DIALOG needs to come before NORMAL | |||
| const Atom _wt = XInternAtom(fDisplay, "_NET_WM_WINDOW_TYPE", False); | |||
| const Atom _wts[2] = { | |||
| XInternAtom(fDisplay, "_NET_WM_WINDOW_TYPE_DIALOG", False), | |||
| XInternAtom(fDisplay, "_NET_WM_WINDOW_TYPE_NORMAL", False) | |||
| }; | |||
| XChangeProperty(fDisplay, fWindow, _wt, XA_ATOM, 32, PropModeReplace, (const uchar*)&_wts, 2); | |||
| } | |||
| #endif | |||
| } | |||
| NativeWindow::~NativeWindow() | |||
| { | |||
| #if defined(DISTRHO_OS_HAIKU) | |||
| #elif defined(DISTRHO_OS_MAC) | |||
| if (fView != nullptr) | |||
| { | |||
| if (fWindow != nil) | |||
| [fWindow close]; | |||
| [fView release]; | |||
| if (fWindow != nil) | |||
| [fWindow release]; | |||
| } | |||
| #elif defined(DISTRHO_OS_WINDOWS) | |||
| if (fWindow != nullptr) | |||
| DestroyWindow(fWindow); | |||
| UnregisterClass(WIN32_CLASS_NAME, nullptr); | |||
| #else | |||
| if (fDisplay != nullptr) | |||
| { | |||
| if (fWindow != 0) | |||
| XDestroyWindow(fDisplay, fWindow); | |||
| XCloseDisplay(fDisplay); | |||
| } | |||
| #endif | |||
| } | |||
| void NativeWindow::idle() | |||
| { | |||
| #if defined(DISTRHO_OS_HAIKU) | |||
| #elif defined(DISTRHO_OS_MAC) | |||
| if (fIsStandalone) | |||
| { | |||
| NSAutoreleasePool* const pool = [[NSAutoreleasePool alloc] init]; | |||
| NSDate* const date = [NSDate distantPast]; | |||
| for (NSEvent* event;;) | |||
| { | |||
| event = [NSApp | |||
| nextEventMatchingMask:NSEventMaskAny | |||
| untilDate:date | |||
| inMode:NSDefaultRunLoopMode | |||
| dequeue:YES]; | |||
| if (event == nil) | |||
| break; | |||
| [NSApp sendEvent:event]; | |||
| } | |||
| if (fWindow->closed) | |||
| { | |||
| fWindow->closed = false; | |||
| close(); | |||
| } | |||
| [pool release]; | |||
| } | |||
| #elif defined(DISTRHO_OS_WINDOWS) | |||
| if (fIsStandalone && fWindow != nullptr) | |||
| { | |||
| MSG msg; | |||
| if (::GetMessage(&msg, nullptr, 0, 0) > 0) | |||
| { | |||
| d_stdout("msg %u %u", msg.message, WM_SIZING); | |||
| switch (msg.message) | |||
| { | |||
| case WM_SYSCOMMAND: | |||
| if (msg.wParam != SC_CLOSE) | |||
| break; | |||
| // fall-through | |||
| case WM_QUIT: | |||
| hide(); | |||
| return; | |||
| case WM_SIZING: | |||
| break; | |||
| } | |||
| TranslateMessage(&msg); | |||
| DispatchMessage(&msg); | |||
| } | |||
| } | |||
| #else | |||
| if (fDisplay != nullptr) | |||
| { | |||
| for (XEvent event; XPending(fDisplay) > 0;) | |||
| { | |||
| XNextEvent(fDisplay, &event); | |||
| if (! fIsVisible) | |||
| continue; | |||
| switch (event.type) | |||
| { | |||
| case ClientMessage: | |||
| if (char* const type = XGetAtomName(fDisplay, event.xclient.message_type)) | |||
| { | |||
| if (std::strcmp(type, "WM_PROTOCOLS") == 0) | |||
| hide(); | |||
| } | |||
| break; | |||
| case KeyRelease: | |||
| if (event.xkey.keycode == X11Key_Escape) | |||
| hide(); | |||
| break; | |||
| } | |||
| } | |||
| } | |||
| #endif | |||
| } | |||
| void NativeWindow::focus() | |||
| { | |||
| d_stdout("focus"); | |||
| #if defined(DISTRHO_OS_HAIKU) | |||
| #elif defined(DISTRHO_OS_MAC) | |||
| DISTRHO_SAFE_ASSERT_RETURN(fWindow != nullptr,); | |||
| [fWindow orderFrontRegardless]; | |||
| [fWindow makeKeyWindow]; | |||
| [fWindow makeFirstResponder:fView]; | |||
| #elif defined(DISTRHO_OS_WINDOWS) | |||
| DISTRHO_SAFE_ASSERT_RETURN(fWindow != nullptr,); | |||
| SetForegroundWindow(fWindow); | |||
| SetActiveWindow(fWindow); | |||
| SetFocus(fWindow); | |||
| #else | |||
| DISTRHO_SAFE_ASSERT_RETURN(fWindow != 0,); | |||
| XRaiseWindow(fDisplay, fWindow); | |||
| #endif | |||
| } | |||
| void NativeWindow::setSize(uint width, uint height) | |||
| { | |||
| #if defined(DISTRHO_OS_HAIKU) | |||
| #elif defined(DISTRHO_OS_MAC) | |||
| NSRect rect = [fView frame]; | |||
| rect.size = CGSizeMake((CGFloat)width, (CGFloat)height); | |||
| [fView setFrame:rect]; | |||
| #elif defined(DISTRHO_OS_WINDOWS) | |||
| if (fWindow != nullptr) | |||
| SetWindowPos(fWindow, | |||
| HWND_TOP, | |||
| 0, 0, | |||
| width, height, | |||
| SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOOWNERZORDER | SWP_NOZORDER); | |||
| #else | |||
| if (fWindow != 0) | |||
| XResizeWindow(fDisplay, fWindow, width, height); | |||
| #endif | |||
| } | |||
| void NativeWindow::setTitle(const char* const title) | |||
| { | |||
| #if defined(DISTRHO_OS_HAIKU) | |||
| #elif defined(DISTRHO_OS_MAC) | |||
| if (fWindow != nil) | |||
| { | |||
| if (NSString* const nsTitle = [[NSString alloc] | |||
| initWithBytes:title | |||
| length:strlen(title) | |||
| encoding:NSUTF8StringEncoding]) | |||
| { | |||
| [fWindow setTitle:nsTitle]; | |||
| [nsTitle release]; | |||
| } | |||
| } | |||
| #elif defined(DISTRHO_OS_WINDOWS) | |||
| #else | |||
| DISTRHO_SAFE_ASSERT_RETURN(fWindow != 0,); | |||
| XStoreName(fDisplay, fWindow, title); | |||
| #endif | |||
| } | |||
| void NativeWindow::setTransientParentWindow(const uintptr_t winId) | |||
| { | |||
| #if defined(DISTRHO_OS_HAIKU) | |||
| #elif defined(DISTRHO_OS_MAC) | |||
| #elif defined(DISTRHO_OS_WINDOWS) | |||
| #else | |||
| DISTRHO_SAFE_ASSERT_RETURN(fWindow != 0,); | |||
| XSetTransientForHint(fDisplay, fWindow, (::Window)winId); | |||
| #endif | |||
| } | |||
| void NativeWindow::setVisible(const bool visible) | |||
| { | |||
| fIsVisible = visible; | |||
| #if defined(DISTRHO_OS_HAIKU) | |||
| #elif defined(DISTRHO_OS_MAC) | |||
| DISTRHO_SAFE_ASSERT_RETURN(fView != nullptr,); | |||
| if (fWindow != nil) | |||
| { | |||
| [fWindow setIsVisible:(visible ? YES : NO)]; | |||
| if (fIsStandalone) | |||
| [fWindow makeMainWindow]; | |||
| [fWindow makeKeyAndOrderFront:fWindow]; | |||
| } | |||
| else | |||
| { | |||
| [fView setHidden:(visible ? NO : YES)]; | |||
| } | |||
| #elif defined(DISTRHO_OS_WINDOWS) | |||
| DISTRHO_SAFE_ASSERT_RETURN(fWindow != nullptr,); | |||
| ShowWindow(fWindow, visible ? SW_SHOWNORMAL : SW_HIDE); | |||
| #else | |||
| DISTRHO_SAFE_ASSERT_RETURN(fWindow != 0,); | |||
| if (visible) | |||
| XMapRaised(fDisplay, fWindow); | |||
| else | |||
| XUnmapWindow(fDisplay, fWindow); | |||
| #endif | |||
| } | |||
| // -------------------------------------------------------------------------------------------------------------------- | |||
| END_NAMESPACE_DISTRHO | |||