MPV: Use XShape to let menuBar show, hide if browser open
Signed-off-by: falkTX <falktx@falktx.com>
This commit is contained in:
parent
54bc763d0d
commit
c3279f8f12
3 changed files with 180 additions and 45 deletions
|
|
@ -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);
|
||||||
|
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,);
|
||||||
|
|
||||||
XMapRaised(display, window);
|
XMapRaised(display, window);
|
||||||
XSync(display, False);
|
XSync(display, False);
|
||||||
|
|
||||||
d_stdout("this window is %lu", window);
|
|
||||||
#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 diffBrowser = browserWasVisible != browserVisible;
|
||||||
const bool diffSize = (lastWidth != width || lastHeight != height);
|
const bool diffPos = lastX != x || lastY != y;
|
||||||
|
const bool diffSize = lastWidth != width || lastHeight != height;
|
||||||
|
|
||||||
if (diffPos && diffSize)
|
if (diffBrowser)
|
||||||
|
browserWasVisible = browserVisible;
|
||||||
|
|
||||||
|
/**/ 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());
|
||||||
|
|
|
||||||
|
|
@ -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();
|
||||||
|
|
|
||||||
|
|
@ -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 onAdd(const AddEvent&) override
|
||||||
|
{
|
||||||
|
if (isEmbed)
|
||||||
|
return;
|
||||||
|
|
||||||
|
ContextCreateEvent ce;
|
||||||
|
onContextCreate(ce);
|
||||||
|
}
|
||||||
|
|
||||||
|
void onRemove(const RemoveEvent&) override
|
||||||
|
{
|
||||||
|
if (!isEmbed)
|
||||||
|
return;
|
||||||
|
|
||||||
|
ContextDestroyEvent ce;
|
||||||
|
onContextDestroy(ce);
|
||||||
|
}
|
||||||
|
|
||||||
void onContextCreate(const ContextCreateEvent& e) override
|
void onContextCreate(const ContextCreateEvent& e) override
|
||||||
{
|
{
|
||||||
ModuleWidget::onContextCreate(e);
|
ModuleWidget::onContextCreate(e);
|
||||||
widgetCreated();
|
|
||||||
|
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 onContextDestroy(const ContextDestroyEvent& e) override
|
void onContextDestroy(const ContextDestroyEvent& e) override
|
||||||
{
|
{
|
||||||
widgetDestroyed();
|
|
||||||
ModuleWidget::onContextDestroy(e);
|
ModuleWidget::onContextDestroy(e);
|
||||||
}
|
|
||||||
|
|
||||||
void onAdd(const AddEvent& e) override
|
if (module == nullptr)
|
||||||
{
|
|
||||||
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;
|
return;
|
||||||
|
|
||||||
isEmbed = true;
|
DISTRHO_SAFE_ASSERT_RETURN(module != nullptr,);
|
||||||
embedWidget->embedIntoRack(pcontext->nativeWindowId);
|
|
||||||
}
|
|
||||||
|
|
||||||
void widgetDestroyed()
|
|
||||||
{
|
|
||||||
DISTRHO_SAFE_ASSERT_RETURN(pcontext != nullptr,);
|
DISTRHO_SAFE_ASSERT_RETURN(pcontext != nullptr,);
|
||||||
|
DISTRHO_SAFE_ASSERT_RETURN(isEmbed,);
|
||||||
if (! isEmbed)
|
|
||||||
return;
|
|
||||||
|
|
||||||
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…
Add table
Add a link
Reference in a new issue