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