Browse Source

MPV: Use XShape to let menuBar show, hide if browser open

Signed-off-by: falkTX <falktx@falktx.com>
tags/22.02
falkTX 3 years ago
parent
commit
c3279f8f12
Signed by: falkTX <falktx@falktx.com> GPG Key ID: CDBAA37ABC74FBA0
3 changed files with 173 additions and 38 deletions
  1. +122
    -13
      plugins/Cardinal/src/EmbedWidget.cpp
  2. +4
    -1
      plugins/Cardinal/src/EmbedWidget.hpp
  3. +47
    -24
      plugins/Cardinal/src/MPV.cpp

+ 122
- 13
plugins/Cardinal/src/EmbedWidget.cpp View File

@@ -24,6 +24,7 @@
# include <X11/Xatom.h> # include <X11/Xatom.h>
# include <X11/Xlib.h> # include <X11/Xlib.h>
# include <X11/Xutil.h> # include <X11/Xutil.h>
# include <X11/extensions/shape.h>
#endif #endif


#include "EmbedWidget.hpp" #include "EmbedWidget.hpp"
@@ -55,6 +56,7 @@ struct EmbedWidget::PrivateData {
int lastY = 0; int lastY = 0;
uint lastWidth = 0; uint lastWidth = 0;
uint lastHeight = 0; uint lastHeight = 0;
bool browserWasVisible = false;


PrivateData(const Vec size) PrivateData(const Vec size)
{ {
@@ -65,6 +67,15 @@ struct EmbedWidget::PrivateData {
display = XOpenDisplay(nullptr); display = XOpenDisplay(nullptr);
DISTRHO_SAFE_ASSERT_RETURN(display != 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)); const ::Window rootWindow = RootWindow(display, DefaultScreen(display));


window = XCreateSimpleWindow(display, rootWindow, 0, 0, width, height, 0, 0, 0); window = XCreateSimpleWindow(display, rootWindow, 0, 0, width, height, 0, 0, 0);
@@ -120,10 +131,29 @@ struct EmbedWidget::PrivateData {
DISTRHO_SAFE_ASSERT_RETURN(window != 0,); DISTRHO_SAFE_ASSERT_RETURN(window != 0,);


XReparentWindow(display, window, nativeWindowId, x, y); 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); 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 #endif
} }


@@ -132,10 +162,7 @@ struct EmbedWidget::PrivateData {
#ifdef HAVE_X11 #ifdef HAVE_X11
DISTRHO_SAFE_ASSERT_RETURN(window != 0,); DISTRHO_SAFE_ASSERT_RETURN(window != 0,);


const ::Window rootWindow = RootWindow(display, DefaultScreen(display));

XUnmapWindow(display, window); XUnmapWindow(display, window);
XReparentWindow(display, window, rootWindow, 0, 0);
#endif #endif
} }


@@ -144,28 +171,29 @@ struct EmbedWidget::PrivateData {
int x, y; int x, y;
offsetToXY(rect.pos, x, y); offsetToXY(rect.pos, x, y);


const bool browserVisible = APP->scene->browser->visible;
const uint width = rect.size.x; const uint width = rect.size.x;
const uint height = rect.size.y; 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; lastX = x;
lastY = y; lastY = y;
lastWidth = width; lastWidth = width;
lastHeight = height; lastHeight = height;
#ifdef HAVE_X11
XMoveResizeWindow(display, window, x, y, width, height);
#endif
} }
else if (diffPos) else if (diffPos)
{ {
lastX = x; lastX = x;
lastY = y; lastY = y;
#ifdef HAVE_X11 #ifdef HAVE_X11
XMoveWindow(display, window, x, y);
#endif #endif
} }
else if (diffSize) else if (diffSize)
@@ -173,7 +201,6 @@ struct EmbedWidget::PrivateData {
lastWidth = width; lastWidth = width;
lastHeight = height; lastHeight = height;
#ifdef HAVE_X11 #ifdef HAVE_X11
XResizeWindow(display, window, width, height);
#endif #endif
} }


@@ -181,10 +208,74 @@ struct EmbedWidget::PrivateData {
if (window == 0) if (window == 0)
return; 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;) for (XEvent event; XPending(display) > 0;)
XNextEvent(display, &event); XNextEvent(display, &event);
#endif #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) EmbedWidget::EmbedWidget(const Vec size)
@@ -203,6 +294,16 @@ void EmbedWidget::embedIntoRack(const uintptr_t nativeWindowId)
pData->embedIntoRack(nativeWindowId, getAbsoluteRect()); pData->embedIntoRack(nativeWindowId, getAbsoluteRect());
} }


void EmbedWidget::removeFromRack()
{
pData->removeFromRack();
}

void EmbedWidget::show()
{
pData->show();
}

void EmbedWidget::hide() void EmbedWidget::hide()
{ {
pData->hide(); pData->hide();
@@ -217,6 +318,14 @@ uintptr_t EmbedWidget::getNativeWindowId() const
#endif #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() void EmbedWidget::step()
{ {
pData->step(getAbsoluteRect()); pData->step(getAbsoluteRect());


+ 4
- 1
plugins/Cardinal/src/EmbedWidget.hpp View File

@@ -27,12 +27,15 @@ struct EmbedWidget : Widget {
~EmbedWidget() override; ~EmbedWidget() override;


void embedIntoRack(uintptr_t nativeWindowId); void embedIntoRack(uintptr_t nativeWindowId);
void removeFromRack();

void show();
void hide(); void hide();


uintptr_t getNativeWindowId() const; uintptr_t getNativeWindowId() const;


private: private:
void draw(const DrawArgs&) override {}
void draw(const DrawArgs&) override;
void step() override; void step() override;


Rect getAbsoluteRect(); Rect getAbsoluteRect();


+ 47
- 24
plugins/Cardinal/src/MPV.cpp View File

@@ -59,6 +59,7 @@ struct CardinalEmbedWidget : ModuleWidget, ExternalWindow {
CardinalPluginContext* const pcontext; CardinalPluginContext* const pcontext;
EmbedWidget* embedWidget = nullptr; EmbedWidget* embedWidget = nullptr;
bool isEmbed = false; bool isEmbed = false;
bool videoIsLoaded = false;


CardinalEmbedWidget(CardinalEmbedModule* const m) CardinalEmbedWidget(CardinalEmbedModule* const m)
: ModuleWidget(), : ModuleWidget(),
@@ -81,55 +82,64 @@ struct CardinalEmbedWidget : ModuleWidget, ExternalWindow {
terminateAndWaitForExternalProcess(); 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; 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; isEmbed = true;
embedWidget->embedIntoRack(pcontext->nativeWindowId); 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; return;


DISTRHO_SAFE_ASSERT_RETURN(module != nullptr,);
DISTRHO_SAFE_ASSERT_RETURN(pcontext != nullptr,);
DISTRHO_SAFE_ASSERT_RETURN(isEmbed,);

isEmbed = false; isEmbed = false;
embedWidget->hide(); embedWidget->hide();
embedWidget->removeFromRack();
terminateAndWaitForExternalProcess();
} }


void appendContextMenu(ui::Menu* const menu) override void appendContextMenu(ui::Menu* const menu) override
{ {
// embed player gets in the way, hide it
if (isEmbed)
embedWidget->hide();

menu->addChild(new ui::MenuSeparator); menu->addChild(new ui::MenuSeparator);


struct LoadVideoFileItem : MenuItem { struct LoadVideoFileItem : MenuItem {
@@ -141,8 +151,19 @@ struct CardinalEmbedWidget : ModuleWidget, ExternalWindow {
text = "Load video file..."; text = "Load video file...";
} }


~LoadVideoFileItem() override
{
d_stdout("submenu closed");
if (self->isEmbed)
self->embedWidget->show();
}

void onAction(const event::Action&) override void onAction(const event::Action&) override
{ {
// terminate old one
self->videoIsLoaded = false;
self->terminateAndWaitForExternalProcess();

WeakPtr<CardinalEmbedWidget> const self = this->self; WeakPtr<CardinalEmbedWidget> const self = this->self;
async_dialog_filebrowser(false, nullptr, text.c_str(), [self](char* path) async_dialog_filebrowser(false, nullptr, text.c_str(), [self](char* path)
{ {
@@ -157,7 +178,9 @@ struct CardinalEmbedWidget : ModuleWidget, ExternalWindow {
const char* args[] = { const char* args[] = {
"mpv", "--no-audio", winIdStr, path, nullptr "mpv", "--no-audio", winIdStr, path, nullptr
}; };
self->terminateAndWaitForExternalProcess();

self->videoIsLoaded = true;
self->embedWidget->show();
self->startExternalProcess(args); self->startExternalProcess(args);
} }




Loading…
Cancel
Save