DPF

DISTRHO Plugin Framework
Log | Files | Refs | Submodules | README | LICENSE

commit 79274a3da02792479f747fad38742b3cc4230b23
parent 30a467159f331352a3ea5e1f9cb71a5d21a4a89e
Author: falkTX <falktx@falktx.com>
Date:   Sun, 22 Aug 2021 22:35:20 +0100

Working ExternalWindow, start dummy/example X11 code

Signed-off-by: falkTX <falktx@falktx.com>

Diffstat:
Mdistrho/extra/ExternalWindow.hpp | 34++++++++++++++++++++++++++++++----
Mdistrho/src/DistrhoUI.cpp | 3++-
Mdistrho/src/DistrhoUIPrivateData.hpp | 85+++++++++++++++++++++----------------------------------------------------------
Mexamples/EmbedExternalUI/EmbedExternalExampleUI.cpp | 157++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
4 files changed, 210 insertions(+), 69 deletions(-)

diff --git a/distrho/extra/ExternalWindow.hpp b/distrho/extra/ExternalWindow.hpp @@ -92,7 +92,17 @@ public: virtual bool isRunning() const { - return false; + return isVisible(); + } + + virtual bool isQuiting() const + { + return !isVisible(); + } + + uintptr_t getTransientWindowId() const noexcept + { + return pData.transientWinId; } virtual void setTransientWindowId(uintptr_t winId) @@ -100,6 +110,7 @@ public: if (pData.transientWinId == winId) return; pData.transientWinId = winId; + transientWindowChanged(winId); } #if DISTRHO_PLUGIN_HAS_EMBED_UI @@ -153,6 +164,7 @@ public: if (pData.visible == visible) return; pData.visible = visible; + visibilityChanged(visible); } /** @@ -238,6 +250,7 @@ public: if (pData.title == title) return; pData.title = title; + titleChanged(title); } /** @@ -270,12 +283,25 @@ protected: (void)height; } - /* - uintptr_t getTransientWinId() const noexcept + virtual void titleChanged(const char* title) { - return transientWinId; + // unused, meant for custom implementations + return; (void)title; } + virtual void visibilityChanged(bool visible) + { + // unused, meant for custom implementations + return; (void)visible; + } + + virtual void transientWindowChanged(uintptr_t winId) + { + // unused, meant for custom implementations + return; (void)winId; + } + + /* bool isRunning() noexcept { if (pid <= 0) diff --git a/distrho/src/DistrhoUI.cpp b/distrho/src/DistrhoUI.cpp @@ -44,8 +44,8 @@ PluginWindow& UI::PrivateData::createNextWindow(UI* const ui, const uint width, const uint height) { UI::PrivateData* const pData = s_nextPrivateData; - pData->window = new PluginWindow(ui, pData->app, pData->winId, width, height, pData->scaleFactor); #if DISTRHO_PLUGIN_HAS_EXTERNAL_UI + pData->window = new PluginWindow(ui, pData->app); ExternalWindow::PrivateData ewData; ewData.parentWindowHandle = pData->winId; ewData.width = width; @@ -54,6 +54,7 @@ UI::PrivateData::createNextWindow(UI* const ui, const uint width, const uint hei ewData.title = DISTRHO_PLUGIN_NAME; return ewData; #else + pData->window = new PluginWindow(ui, pData->app, pData->winId, width, height, pData->scaleFactor); return pData->window.getObject(); #endif } diff --git a/distrho/src/DistrhoUIPrivateData.hpp b/distrho/src/DistrhoUIPrivateData.hpp @@ -61,13 +61,15 @@ struct PluginApplication void addIdleCallback(IdleCallback* const cb) { + DISTRHO_SAFE_ASSERT_RETURN(cb != nullptr,); + DISTRHO_SAFE_ASSERT_RETURN(idleCallback == nullptr,); + idleCallback = cb; } bool isQuiting() const noexcept { - // TODO - return false; + return ui->isQuiting(); } bool isStandalone() const noexcept @@ -77,23 +79,16 @@ struct PluginApplication void exec() { - // TODO while (ui->isRunning()) { - d_msleep(10); + d_msleep(30); idleCallback->idleCallback(); } } - void idle() - { - // TODO - } - - void quit() - { - // TODO - } + // these are not needed + void idle() {} + void quit() {} DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(PluginApplication) }; @@ -128,65 +123,29 @@ class PluginWindow UI* const ui; public: - explicit PluginWindow(UI* const uiPtr, - PluginApplication& app, - const uintptr_t parentWindowHandle, - uint, uint, // width and height - const double scaleFactor) + explicit PluginWindow(UI* const uiPtr, PluginApplication& app) : ui(uiPtr) { app.ui = ui; - ui->pData.parentWindowHandle = parentWindowHandle; - ui->pData.scaleFactor = scaleFactor; - } - - uint getWidth() const noexcept - { - return ui->pData.width; - } - - uint getHeight() const noexcept - { - return ui->getHeight(); - } - - double getScaleFactor() const noexcept - { - return ui->getScaleFactor(); } - bool isVisible() const noexcept - { - return ui->isRunning(); - } + // fetch cached data + uint getWidth() const noexcept { return ui->pData.width; } + uint getHeight() const noexcept { return ui->pData.height; } + double getScaleFactor() const noexcept { return ui->pData.scaleFactor; } + uintptr_t getNativeWindowHandle() const noexcept { return ui->pData.parentWindowHandle; } - uintptr_t getNativeWindowHandle() const noexcept - { - return ui->getNativeWindowHandle(); - } + // direct mappings + bool isVisible() const noexcept { return ui->isVisible(); } + void focus() { ui->focus(); } + void show() { ui->show(); } + void setTitle(const char* const title) { ui->setTitle(title); } + void setVisible(const bool visible) { ui->setVisible(visible); } + // custom void close() { - } - - void focus() - { - ui->focus(); - } - - void show() - { - ui->show(); - } - - void setTitle(const char* const title) - { - ui->setTitle(title); - } - - void setVisible(const bool visible) - { - ui->setVisible(visible); + ui->hide(); } DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(PluginWindow) diff --git a/examples/EmbedExternalUI/EmbedExternalExampleUI.cpp b/examples/EmbedExternalUI/EmbedExternalExampleUI.cpp @@ -14,19 +14,103 @@ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +// needed for IDE +#include "DistrhoPluginInfo.h" + #include "DistrhoUI.hpp" +#if defined(DISTRHO_OS_MAC) +#elif defined(DISTRHO_OS_WINDOWS) +#else +# include <sys/types.h> +# include <X11/Xatom.h> +# include <X11/Xlib.h> +# include <X11/Xutil.h> +# define X11Key_Escape 9 +#endif + START_NAMESPACE_DISTRHO // ----------------------------------------------------------------------------------------------------------- class EmbedExternalExampleUI : public UI { +#if defined(DISTRHO_OS_MAC) +#elif defined(DISTRHO_OS_WINDOWS) +#else + ::Display* fDisplay; + ::Window fWindow; +#endif + public: EmbedExternalExampleUI() - : UI(405, 256), + : UI(512, 256), + fDisplay(nullptr), + fWindow(0), fValue(0.0f) { +#if defined(DISTRHO_OS_MAC) +#elif defined(DISTRHO_OS_WINDOWS) +#else + fDisplay = XOpenDisplay(nullptr); + DISTRHO_SAFE_ASSERT_RETURN(fDisplay != nullptr,); + + const int screen = DefaultScreen(fDisplay); + const ::Window root = RootWindow(fDisplay, screen); + const ::Window parent = getParentWindowHandle() != 0 ? (::Window)getParentWindowHandle() : root; + + XSetWindowAttributes attr = {}; + attr.event_mask = KeyPressMask|KeyReleaseMask; + + fWindow = XCreateWindow(fDisplay, parent, + 0, 0, getWidth(), getHeight(), + 0, DefaultDepth(fDisplay, screen), InputOutput, DefaultVisual(fDisplay, screen), + CWColormap | CWEventMask, &attr); + 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 (parent == root) + { + // 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 + } + + ~EmbedExternalExampleUI() + { + if (fDisplay == nullptr) + return; + + if (fWindow != 0) + XDestroyWindow(fDisplay, fWindow); + + XCloseDisplay(fDisplay); } protected: @@ -48,6 +132,77 @@ protected: /* -------------------------------------------------------------------------------------------------------- * External Window overrides */ + void titleChanged(const char* const title) override + { + d_stdout("visibilityChanged %s", title); +#if defined(DISTRHO_OS_MAC) +#elif defined(DISTRHO_OS_WINDOWS) +#else + DISTRHO_SAFE_ASSERT_RETURN(fWindow != 0,); + XStoreName(fDisplay, fWindow, title); +#endif + } + + void transientWindowChanged(const uintptr_t winId) override + { + d_stdout("transientWindowChanged %lu", winId); +#if defined(DISTRHO_OS_MAC) +#elif defined(DISTRHO_OS_WINDOWS) +#else + DISTRHO_SAFE_ASSERT_RETURN(fWindow != 0,); + XSetTransientForHint(fDisplay, fWindow, (::Window)winId); +#endif + } + + void visibilityChanged(const bool visible) override + { + d_stdout("visibilityChanged %d", visible); +#if defined(DISTRHO_OS_MAC) +#elif defined(DISTRHO_OS_WINDOWS) +#else + DISTRHO_SAFE_ASSERT_RETURN(fWindow != 0,); + if (visible) + XMapRaised(fDisplay, fWindow); + else + XUnmapWindow(fDisplay, fWindow); +#endif + } + + void uiIdle() override + { + // d_stdout("uiIdle"); +#if defined(DISTRHO_OS_MAC) +#elif defined(DISTRHO_OS_WINDOWS) +#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 + } + // ------------------------------------------------------------------------------------------------------- private: