From 975e722a106edb0e5755f2a2fd2711f13b4812a2 Mon Sep 17 00:00:00 2001 From: falkTX Date: Tue, 25 Jan 2022 12:43:18 +0000 Subject: [PATCH] Initial tests for embedding outside widgets, working in X11 Signed-off-by: falkTX --- plugins/Cardinal/plugin.json | 9 ++ plugins/Cardinal/res/MPV.svg | 147 ++++++++++++++++++ plugins/Cardinal/src/EmbedWidget.cpp | 219 +++++++++++++++++++++++++++ plugins/Cardinal/src/EmbedWidget.hpp | 37 +++++ plugins/Cardinal/src/MPV.cpp | 133 ++++++++++++++++ plugins/Cardinal/src/plugin.hpp | 1 + plugins/Makefile | 2 + plugins/plugins.cpp | 2 + 8 files changed, 550 insertions(+) create mode 100644 plugins/Cardinal/res/MPV.svg create mode 100644 plugins/Cardinal/src/EmbedWidget.cpp create mode 100644 plugins/Cardinal/src/EmbedWidget.hpp create mode 100644 plugins/Cardinal/src/MPV.cpp 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 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + 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 } }