diff --git a/plugins/Cardinal/plugin.json b/plugins/Cardinal/plugin.json
index f31b06d..bd34e19 100644
--- a/plugins/Cardinal/plugin.json
+++ b/plugins/Cardinal/plugin.json
@@ -112,6 +112,15 @@
"Utility"
]
},
+ {
+ "slug": "MPV",
+ "disabled": false,
+ "name": "MPV",
+ "description": "An embed video player inside Cardinal",
+ "tags": [
+ "Visual"
+ ]
+ },
{
"slug": "TextEditor",
"disabled": false,
diff --git a/plugins/Cardinal/res/MPV.svg b/plugins/Cardinal/res/MPV.svg
new file mode 100644
index 0000000..af37f0f
--- /dev/null
+++ b/plugins/Cardinal/res/MPV.svg
@@ -0,0 +1,147 @@
+
+
+
+
diff --git a/plugins/Cardinal/src/EmbedWidget.cpp b/plugins/Cardinal/src/EmbedWidget.cpp
new file mode 100644
index 0000000..ce54acd
--- /dev/null
+++ b/plugins/Cardinal/src/EmbedWidget.cpp
@@ -0,0 +1,219 @@
+/*
+ * DISTRHO Cardinal Plugin
+ * Copyright (C) 2021-2022 Filipe Coelho
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 3 of
+ * the License, or any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * For a full copy of the GNU General Public License see the LICENSE file.
+ */
+
+#if defined(ARCH_LIN) && !defined(HEADLESS)
+# define HAVE_X11
+#endif
+
+#ifdef HAVE_X11
+# include
+# include
+# include
+# include
+#endif
+
+#include "EmbedWidget.hpp"
+
+static void offsetToXY(const Vec offset, int& x, int& y)
+{
+ if (offset.x > 0.0f)
+ x = static_cast(offset.x + 0.5f);
+ else if (offset.x < 0.0f)
+ x = static_cast(offset.x - 0.5f);
+ else
+ x = 0;
+
+ if (offset.y > 0.0f)
+ y = static_cast(offset.y + 0.5f);
+ else if (offset.y < 0.0f)
+ y = static_cast(offset.y - 0.5f);
+ else
+ y = 0;
+}
+
+struct EmbedWidget::PrivateData {
+ #ifdef HAVE_X11
+ ::Display* display = nullptr;
+ ::Window window = 0;
+ #endif
+
+ int lastX = 0;
+ int lastY = 0;
+ uint lastWidth = 0;
+ uint lastHeight = 0;
+
+ PrivateData(const Vec size)
+ {
+ const uint width = size.x;
+ const uint height = size.y;
+
+ #ifdef HAVE_X11
+ display = XOpenDisplay(nullptr);
+ DISTRHO_SAFE_ASSERT_RETURN(display != nullptr,);
+
+ const ::Window rootWindow = RootWindow(display, DefaultScreen(display));
+
+ window = XCreateSimpleWindow(display, rootWindow, 0, 0, width, height, 0, 0, 0);
+ DISTRHO_SAFE_ASSERT_RETURN(window != 0,);
+
+ XSizeHints sizeHints = {};
+ sizeHints.flags = PMinSize | PMaxSize;
+ sizeHints.min_width = width;
+ sizeHints.max_width = width;
+ sizeHints.min_height = height;
+ sizeHints.max_height = height;
+ XSetNormalHints(display, window, &sizeHints);
+ XStoreName(display, window, "EmbedWidget");
+ #endif
+
+ lastWidth = width;
+ lastHeight = height;
+ }
+
+ ~PrivateData()
+ {
+ #ifdef HAVE_X11
+ if (display == nullptr)
+ return;
+
+ if (window != 0)
+ XDestroyWindow(display, window);
+
+ XCloseDisplay(display);
+ #endif
+ }
+
+ void embedIntoRack(const uintptr_t nativeWindowId, const Rect rect)
+ {
+ int x, y;
+ offsetToXY(rect.pos, x, y);
+ lastX = x;
+ lastY = y;
+
+ const uint width = rect.size.x;
+ const uint height = rect.size.y;
+
+ if (lastWidth != width || lastHeight != height)
+ {
+ lastWidth = width;
+ lastHeight = height;
+ #ifdef HAVE_X11
+ XResizeWindow(display, window, width, height);
+ #endif
+ }
+
+ #ifdef HAVE_X11
+ DISTRHO_SAFE_ASSERT_RETURN(window != 0,);
+
+ XReparentWindow(display, window, nativeWindowId, x, y);
+ XMapRaised(display, window);
+ XSync(display, False);
+
+ d_stdout("this window is %lu", window);
+ #endif
+ }
+
+ void hide()
+ {
+ #ifdef HAVE_X11
+ DISTRHO_SAFE_ASSERT_RETURN(window != 0,);
+
+ const ::Window rootWindow = RootWindow(display, DefaultScreen(display));
+
+ XUnmapWindow(display, window);
+ XReparentWindow(display, window, rootWindow, 0, 0);
+ #endif
+ }
+
+ void step(const Rect rect)
+ {
+ int x, y;
+ offsetToXY(rect.pos, x, y);
+
+ const uint width = rect.size.x;
+ const uint height = rect.size.y;
+
+ const bool diffPos = (lastX != x || lastY != y);
+ const bool diffSize = (lastWidth != width || lastHeight != height);
+
+ if (diffPos && diffSize)
+ {
+ lastX = x;
+ lastY = y;
+ lastWidth = width;
+ lastHeight = height;
+ #ifdef HAVE_X11
+ XMoveResizeWindow(display, window, x, y, width, height);
+ #endif
+ }
+ else if (diffPos)
+ {
+ lastX = x;
+ lastY = y;
+ #ifdef HAVE_X11
+ XMoveWindow(display, window, x, y);
+ #endif
+ }
+ else if (diffSize)
+ {
+ lastWidth = width;
+ lastHeight = height;
+ #ifdef HAVE_X11
+ XResizeWindow(display, window, width, height);
+ #endif
+ }
+
+ #ifdef HAVE_X11
+ if (window == 0)
+ return;
+
+ for (XEvent event; XPending(display) > 0;)
+ XNextEvent(display, &event);
+ #endif
+ }
+};
+
+EmbedWidget::EmbedWidget(const Vec size)
+ : pData(new PrivateData(size))
+{
+ box.size = size;
+}
+
+EmbedWidget::~EmbedWidget()
+{
+ delete pData;
+}
+
+void EmbedWidget::embedIntoRack(const uintptr_t nativeWindowId)
+{
+ pData->embedIntoRack(nativeWindowId, getAbsoluteRect());
+}
+
+void EmbedWidget::hide()
+{
+ pData->hide();
+}
+
+void EmbedWidget::step()
+{
+ pData->step(getAbsoluteRect());
+}
+
+Rect EmbedWidget::getAbsoluteRect()
+{
+ return Rect(getAbsoluteOffset({}), box.size.mult(getAbsoluteZoom()));
+}
diff --git a/plugins/Cardinal/src/EmbedWidget.hpp b/plugins/Cardinal/src/EmbedWidget.hpp
new file mode 100644
index 0000000..9369754
--- /dev/null
+++ b/plugins/Cardinal/src/EmbedWidget.hpp
@@ -0,0 +1,37 @@
+/*
+ * DISTRHO Cardinal Plugin
+ * Copyright (C) 2021-2022 Filipe Coelho
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 3 of
+ * the License, or any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * For a full copy of the GNU General Public License see the LICENSE file.
+ */
+
+#pragma once
+
+#include "plugin.hpp"
+
+struct EmbedWidget : Widget {
+ struct PrivateData;
+ PrivateData* const pData;
+
+ EmbedWidget(Vec size);
+ ~EmbedWidget() override;
+
+ void embedIntoRack(uintptr_t nativeWindowId);
+ void hide();
+
+private:
+ void draw(const DrawArgs&) override {}
+ void step() override;
+
+ Rect getAbsoluteRect();
+};
diff --git a/plugins/Cardinal/src/MPV.cpp b/plugins/Cardinal/src/MPV.cpp
new file mode 100644
index 0000000..76e3694
--- /dev/null
+++ b/plugins/Cardinal/src/MPV.cpp
@@ -0,0 +1,133 @@
+/*
+ * DISTRHO Cardinal Plugin
+ * Copyright (C) 2021-2022 Filipe Coelho
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 3 of
+ * the License, or any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * For a full copy of the GNU General Public License see the LICENSE file.
+ */
+
+#include "plugincontext.hpp"
+
+#ifndef HEADLESS
+# include "EmbedWidget.hpp"
+#endif
+
+// --------------------------------------------------------------------------------------------------------------------
+
+struct CardinalEmbedModule : Module {
+ enum ParamIds {
+ NUM_PARAMS
+ };
+ enum InputIds {
+ NUM_INPUTS
+ };
+ enum OutputIds {
+ AUDIO_OUTPUT1,
+ AUDIO_OUTPUT2,
+ NUM_OUTPUTS
+ };
+ enum LightIds {
+ NUM_LIGHTS
+ };
+
+ CardinalPluginContext* const pcontext;
+
+ CardinalEmbedModule()
+ : pcontext(static_cast(APP))
+ {
+ config(NUM_PARAMS, NUM_INPUTS, NUM_OUTPUTS, NUM_LIGHTS);
+
+ configOutput(0, "Audio Left");
+ configOutput(1, "Audio Right");
+ }
+};
+
+// --------------------------------------------------------------------------------------------------------------------
+
+#ifndef HEADLESS
+struct CardinalEmbedWidget : ModuleWidget {
+ CardinalEmbedModule* const module;
+ CardinalPluginContext* const pcontext;
+ EmbedWidget* embedWidget = nullptr;
+ bool isEmbed = false;
+
+ CardinalEmbedWidget(CardinalEmbedModule* const m)
+ : ModuleWidget(),
+ module(m),
+ pcontext(m != nullptr ? m->pcontext : nullptr)
+ {
+ setModule(module);
+ setPanel(APP->window->loadSvg(asset::plugin(pluginInstance, "res/MPV.svg")));
+
+ if (m != nullptr)
+ {
+ embedWidget = new EmbedWidget(box.size);
+ addChild(embedWidget);
+ }
+ }
+
+ void onContextCreate(const ContextCreateEvent& e) override
+ {
+ ModuleWidget::onContextCreate(e);
+ widgetCreated();
+ }
+
+ void onContextDestroy(const ContextDestroyEvent& e) override
+ {
+ widgetDestroyed();
+ ModuleWidget::onContextDestroy(e);
+ }
+
+ void onAdd(const AddEvent& e) override
+ {
+ ModuleWidget::onAdd(e);
+ widgetCreated();
+ }
+
+ void onRemove(const RemoveEvent& e) override
+ {
+ widgetDestroyed();
+ ModuleWidget::onRemove(e);
+ }
+
+ void widgetCreated()
+ {
+ DISTRHO_SAFE_ASSERT_RETURN(pcontext != nullptr,);
+ DISTRHO_SAFE_ASSERT_RETURN(pcontext->nativeWindowId != 0,);
+
+ if (isEmbed)
+ return;
+
+ isEmbed = true;
+ embedWidget->embedIntoRack(pcontext->nativeWindowId);
+ }
+
+ void widgetDestroyed()
+ {
+ DISTRHO_SAFE_ASSERT_RETURN(pcontext != nullptr,);
+
+ if (! isEmbed)
+ return;
+
+ isEmbed = false;
+ embedWidget->hide();
+ }
+};
+#else
+typedef ModuleWidget CardinalEmbedWidget;
+#endif
+
+// --------------------------------------------------------------------------------------------------------------------
+
+Model* modelMPV = createModel("MPV");
+
+// --------------------------------------------------------------------------------------------------------------------
diff --git a/plugins/Cardinal/src/plugin.hpp b/plugins/Cardinal/src/plugin.hpp
index ab8332d..f811140 100644
--- a/plugins/Cardinal/src/plugin.hpp
+++ b/plugins/Cardinal/src/plugin.hpp
@@ -38,4 +38,5 @@ extern Model* modelHostMIDI;
extern Model* modelHostParameters;
extern Model* modelHostTime;
extern Model* modelIldaeil;
+extern Model* modelMPV;
extern Model* modelTextEditor;
diff --git a/plugins/Makefile b/plugins/Makefile
index 6c5fb8a..6a5076e 100644
--- a/plugins/Makefile
+++ b/plugins/Makefile
@@ -199,9 +199,11 @@ ifneq ($(STATIC_BUILD),true)
PLUGIN_FILES += Cardinal/src/AudioFile.cpp
PLUGIN_FILES += Cardinal/src/Carla.cpp
PLUGIN_FILES += Cardinal/src/Ildaeil.cpp
+PLUGIN_FILES += Cardinal/src/MPV.cpp
endif
ifneq ($(HEADLESS),true)
+PLUGIN_FILES += Cardinal/src/EmbedWidget.cpp
PLUGIN_FILES += Cardinal/src/ImGuiWidget.cpp
PLUGIN_FILES += Cardinal/src/ImGuiTextEditor.cpp
PLUGIN_FILES += $(wildcard Cardinal/src/DearImGui/*.cpp)
diff --git a/plugins/plugins.cpp b/plugins/plugins.cpp
index dafce3f..63d1be7 100644
--- a/plugins/plugins.cpp
+++ b/plugins/plugins.cpp
@@ -728,10 +728,12 @@ static void initStatic__Cardinal()
p->addModel(modelAudioFile);
p->addModel(modelCarla);
p->addModel(modelIldaeil);
+ p->addModel(modelMPV);
#else
spl.removeModule("AudioFile");
spl.removeModule("Carla");
spl.removeModule("Ildaeil");
+ spl.removeModule("MPV");
#endif
}
}