Browse Source

Initial implementation for webviews, test it in ext-embed example

Signed-off-by: falkTX <falktx@falktx.com>
pull/457/head
falkTX 1 year ago
parent
commit
90ee1072f8
Signed by: falkTX <falktx@falktx.com> GPG Key ID: CDBAA37ABC74FBA0
20 changed files with 2826 additions and 462 deletions
  1. +8
    -7
      .github/workflows/makefile.yml
  2. +1
    -0
      CMakeLists.txt
  3. +20
    -0
      Makefile.base.mk
  4. +5
    -1
      Makefile.plugins.mk
  5. +4
    -4
      distrho/DistrhoUI.hpp
  6. +14
    -5
      distrho/DistrhoUI_macOS.mm
  7. +276
    -0
      distrho/extra/ChildProcess.hpp
  8. +127
    -0
      distrho/extra/Time.hpp
  9. +28
    -0
      distrho/extra/WebView.hpp
  10. +1540
    -0
      distrho/extra/WebViewImpl.cpp
  11. +126
    -0
      distrho/extra/WebViewImpl.hpp
  12. +32
    -9
      distrho/src/DistrhoPluginChecks.h
  13. +19
    -9
      distrho/src/DistrhoPluginJACK.cpp
  14. +1
    -1
      distrho/src/DistrhoPluginVST2.cpp
  15. +54
    -32
      distrho/src/DistrhoUI.cpp
  16. +8
    -1
      examples/EmbedExternalUI/DistrhoPluginInfo.h
  17. +7
    -7
      examples/EmbedExternalUI/EmbedExternalExamplePlugin.cpp
  18. +50
    -386
      examples/EmbedExternalUI/EmbedExternalExampleUI.cpp
  19. +3
    -0
      examples/EmbedExternalUI/Makefile
  20. +503
    -0
      examples/EmbedExternalUI/NativeWindow.hpp

+ 8
- 7
.github/workflows/makefile.yml View File

@@ -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
- 0
CMakeLists.txt View File

@@ -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



+ 20
- 0
Makefile.base.mk View File

@@ -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



+ 5
- 1
Makefile.plugins.mk View File

@@ -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)


+ 4
- 4
distrho/DistrhoUI.hpp View File

@@ -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

/* --------------------------------------------------------------------------------------------------------


+ 14
- 5
distrho/DistrhoUI_macOS.mm View File

@@ -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

+ 276
- 0
distrho/extra/ChildProcess.hpp View File

@@ -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

+ 127
- 0
distrho/extra/Time.hpp View File

@@ -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

+ 28
- 0
distrho/extra/WebView.hpp View File

@@ -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

+ 1540
- 0
distrho/extra/WebViewImpl.cpp
File diff suppressed because it is too large
View File


+ 126
- 0
distrho/extra/WebViewImpl.hpp View File

@@ -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);

// --------------------------------------------------------------------------------------------------------------------

+ 32
- 9
distrho/src/DistrhoPluginChecks.h View File

@@ -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



+ 19
- 9
distrho/src/DistrhoPluginJACK.cpp View File

@@ -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;
}



+ 1
- 1
distrho/src/DistrhoPluginVST2.cpp View File

@@ -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");



+ 54
- 32
distrho/src/DistrhoUI.cpp View File

@@ -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) */



+ 8
- 1
examples/EmbedExternalUI/DistrhoPluginInfo.h View File

@@ -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,


+ 7
- 7
examples/EmbedExternalUI/EmbedExternalExamplePlugin.cpp View File

@@ -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";


+ 50
- 386
examples/EmbedExternalUI/EmbedExternalExampleUI.cpp View File

@@ -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);
}

// -------------------------------------------------------------------------------------------------------


+ 3
- 0
examples/EmbedExternalUI/Makefile View File

@@ -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

# --------------------------------------------------------------


+ 503
- 0
examples/EmbedExternalUI/NativeWindow.hpp View File

@@ -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

Loading…
Cancel
Save