diff --git a/plugins/Cardinal/src/EmbedWidget.cpp b/plugins/Cardinal/src/EmbedWidget.cpp index 2484a82..3f0ee40 100644 --- a/plugins/Cardinal/src/EmbedWidget.cpp +++ b/plugins/Cardinal/src/EmbedWidget.cpp @@ -24,6 +24,7 @@ # include # include # include +# include #endif #include "EmbedWidget.hpp" @@ -55,6 +56,7 @@ struct EmbedWidget::PrivateData { int lastY = 0; uint lastWidth = 0; uint lastHeight = 0; + bool browserWasVisible = false; PrivateData(const Vec size) { @@ -65,6 +67,15 @@ struct EmbedWidget::PrivateData { display = XOpenDisplay(nullptr); DISTRHO_SAFE_ASSERT_RETURN(display != nullptr,); + int ignore = 0; + if (XShapeQueryExtension(display, &ignore, &ignore) == False) + { + XCloseDisplay(display); + display = nullptr; + async_dialog_message("XShape extension unsupported, cannot use embed widgets"); + return; + } + const ::Window rootWindow = RootWindow(display, DefaultScreen(display)); window = XCreateSimpleWindow(display, rootWindow, 0, 0, width, height, 0, 0, 0); @@ -120,10 +131,29 @@ struct EmbedWidget::PrivateData { DISTRHO_SAFE_ASSERT_RETURN(window != 0,); XReparentWindow(display, window, nativeWindowId, x, y); - XMapRaised(display, window); + setClipMask(x, y, width, height); + #endif + } + + void removeFromRack() + { + #ifdef HAVE_X11 + DISTRHO_SAFE_ASSERT_RETURN(window != 0,); + + const ::Window rootWindow = RootWindow(display, DefaultScreen(display)); + + XReparentWindow(display, window, rootWindow, 0, 0); XSync(display, False); + #endif + } + + void show() + { + #ifdef HAVE_X11 + DISTRHO_SAFE_ASSERT_RETURN(window != 0,); - d_stdout("this window is %lu", window); + XMapRaised(display, window); + XSync(display, False); #endif } @@ -132,10 +162,7 @@ struct EmbedWidget::PrivateData { #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 } @@ -144,28 +171,29 @@ struct EmbedWidget::PrivateData { int x, y; offsetToXY(rect.pos, x, y); + const bool browserVisible = APP->scene->browser->visible; 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); + const bool diffBrowser = browserWasVisible != browserVisible; + const bool diffPos = lastX != x || lastY != y; + const bool diffSize = lastWidth != width || lastHeight != height; + + if (diffBrowser) + browserWasVisible = browserVisible; - if (diffPos && diffSize) + /**/ 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) @@ -173,7 +201,6 @@ struct EmbedWidget::PrivateData { lastWidth = width; lastHeight = height; #ifdef HAVE_X11 - XResizeWindow(display, window, width, height); #endif } @@ -181,10 +208,74 @@ struct EmbedWidget::PrivateData { if (window == 0) return; + if (diffBrowser || diffPos || diffSize) + { + /**/ if (diffPos && diffSize) + XMoveResizeWindow(display, window, x, y, width, height); + else if (diffPos) + XMoveWindow(display, window, x, y); + else if (diffSize) + XResizeWindow(display, window, width, height); + + setClipMask(x, y, width, height); + } + for (XEvent event; XPending(display) > 0;) XNextEvent(display, &event); #endif } + + #ifdef HAVE_X11 + void setClipMask(const int x, const int y, const uint width, const uint height) + { + const size_t len = width*height/4; + uchar* const data = new uchar[len]; + + if (browserWasVisible) + { + // allow nothing + std::memset(data, 0xff, sizeof(uchar)*len); + } + else + { + // allow everything + std::memset(data, 0, sizeof(uchar)*len); + + // crop out menuBar + const int menuBarSize = APP->scene->menuBar->box.size.y * APP->window->pixelRatio; + const uint normy = (y < menuBarSize ? std::max(menuBarSize - y, 0) : 0); + for (uint i=0, j=0, d=0; i < width * height; ++i, ++j) + { + if (i >= normy * width) + break; + + if (i == 0) + { + } + else if ((j % width) == 0) + { + j = 0; + ++d; + } + else if ((j % 8) == 0) + { + ++d; + } + + DISTRHO_SAFE_ASSERT_BREAK(d < len); + + const uint v = (j % 8); + data[d] |= 1 << v; + } + } + + const ::Pixmap pixmap = XCreatePixmapFromBitmapData(display, window, (char*)data, width, height, 0, 1, 1); + delete[] data; + + XShapeCombineMask(display, window, ShapeBounding, 0, 0, pixmap, ShapeSet); + XFreePixmap(display, pixmap); + } + #endif }; EmbedWidget::EmbedWidget(const Vec size) @@ -203,6 +294,16 @@ void EmbedWidget::embedIntoRack(const uintptr_t nativeWindowId) pData->embedIntoRack(nativeWindowId, getAbsoluteRect()); } +void EmbedWidget::removeFromRack() +{ + pData->removeFromRack(); +} + +void EmbedWidget::show() +{ + pData->show(); +} + void EmbedWidget::hide() { pData->hide(); @@ -217,6 +318,14 @@ uintptr_t EmbedWidget::getNativeWindowId() const #endif } +void EmbedWidget::draw(const DrawArgs& args) +{ + nvgBeginPath(args.vg); + nvgRect(args.vg, 0, 0, box.size.x, box.size.y); + nvgFillColor(args.vg, nvgRGB(0, 0, 0)); + nvgFill(args.vg); +} + void EmbedWidget::step() { pData->step(getAbsoluteRect()); diff --git a/plugins/Cardinal/src/EmbedWidget.hpp b/plugins/Cardinal/src/EmbedWidget.hpp index 5bf2a52..61e5bb3 100644 --- a/plugins/Cardinal/src/EmbedWidget.hpp +++ b/plugins/Cardinal/src/EmbedWidget.hpp @@ -27,12 +27,15 @@ struct EmbedWidget : Widget { ~EmbedWidget() override; void embedIntoRack(uintptr_t nativeWindowId); + void removeFromRack(); + + void show(); void hide(); uintptr_t getNativeWindowId() const; private: - void draw(const DrawArgs&) override {} + void draw(const DrawArgs&) override; void step() override; Rect getAbsoluteRect(); diff --git a/plugins/Cardinal/src/MPV.cpp b/plugins/Cardinal/src/MPV.cpp index f9ca32f..1992ddc 100644 --- a/plugins/Cardinal/src/MPV.cpp +++ b/plugins/Cardinal/src/MPV.cpp @@ -59,6 +59,7 @@ struct CardinalEmbedWidget : ModuleWidget, ExternalWindow { CardinalPluginContext* const pcontext; EmbedWidget* embedWidget = nullptr; bool isEmbed = false; + bool videoIsLoaded = false; CardinalEmbedWidget(CardinalEmbedModule* const m) : ModuleWidget(), @@ -81,55 +82,64 @@ struct CardinalEmbedWidget : ModuleWidget, ExternalWindow { terminateAndWaitForExternalProcess(); } - void onContextCreate(const ContextCreateEvent& e) override + void onAdd(const AddEvent&) override { - ModuleWidget::onContextCreate(e); - widgetCreated(); - } + if (isEmbed) + return; - void onContextDestroy(const ContextDestroyEvent& e) override - { - widgetDestroyed(); - ModuleWidget::onContextDestroy(e); + ContextCreateEvent ce; + onContextCreate(ce); } - void onAdd(const AddEvent& e) override + void onRemove(const RemoveEvent&) override { - ModuleWidget::onAdd(e); - widgetCreated(); - } + if (!isEmbed) + return; - void onRemove(const RemoveEvent& e) override - { - widgetDestroyed(); - ModuleWidget::onRemove(e); + ContextDestroyEvent ce; + onContextDestroy(ce); } - void widgetCreated() + void onContextCreate(const ContextCreateEvent& e) override { - DISTRHO_SAFE_ASSERT_RETURN(pcontext != nullptr,); - DISTRHO_SAFE_ASSERT_RETURN(pcontext->nativeWindowId != 0,); + ModuleWidget::onContextCreate(e); - if (isEmbed) + if (module == nullptr) return; + DISTRHO_SAFE_ASSERT_RETURN(module != nullptr,); + DISTRHO_SAFE_ASSERT_RETURN(pcontext != nullptr,); + DISTRHO_SAFE_ASSERT_RETURN(pcontext->nativeWindowId != 0,); + DISTRHO_SAFE_ASSERT_RETURN(!isEmbed,); + isEmbed = true; embedWidget->embedIntoRack(pcontext->nativeWindowId); + embedWidget->show(); } - void widgetDestroyed() + void onContextDestroy(const ContextDestroyEvent& e) override { - DISTRHO_SAFE_ASSERT_RETURN(pcontext != nullptr,); + ModuleWidget::onContextDestroy(e); - if (! isEmbed) + if (module == nullptr) return; + DISTRHO_SAFE_ASSERT_RETURN(module != nullptr,); + DISTRHO_SAFE_ASSERT_RETURN(pcontext != nullptr,); + DISTRHO_SAFE_ASSERT_RETURN(isEmbed,); + isEmbed = false; embedWidget->hide(); + embedWidget->removeFromRack(); + terminateAndWaitForExternalProcess(); } void appendContextMenu(ui::Menu* const menu) override { + // embed player gets in the way, hide it + if (isEmbed) + embedWidget->hide(); + menu->addChild(new ui::MenuSeparator); struct LoadVideoFileItem : MenuItem { @@ -141,8 +151,19 @@ struct CardinalEmbedWidget : ModuleWidget, ExternalWindow { text = "Load video file..."; } + ~LoadVideoFileItem() override + { + d_stdout("submenu closed"); + if (self->isEmbed) + self->embedWidget->show(); + } + void onAction(const event::Action&) override { + // terminate old one + self->videoIsLoaded = false; + self->terminateAndWaitForExternalProcess(); + WeakPtr const self = this->self; async_dialog_filebrowser(false, nullptr, text.c_str(), [self](char* path) { @@ -157,7 +178,9 @@ struct CardinalEmbedWidget : ModuleWidget, ExternalWindow { const char* args[] = { "mpv", "--no-audio", winIdStr, path, nullptr }; - self->terminateAndWaitForExternalProcess(); + + self->videoIsLoaded = true; + self->embedWidget->show(); self->startExternalProcess(args); }