[vlc-devel] [PATCH v2 16/16] qt: provide video integration for windows 7

Pierre Lamot pierre at videolabs.io
Fri Aug 14 18:43:50 CEST 2020


 this class is also used on windows when D3D isn't supported, because the dummy
 compositor uses QQuickWidget which requires opengl offscreen rendering (trough
 angle on windows)
---
 modules/gui/qt/Makefile.am                    |   7 +-
 modules/gui/qt/maininterface/compositor.cpp   |  10 +-
 .../gui/qt/maininterface/compositor_dummy.hpp |   8 +-
 .../gui/qt/maininterface/compositor_win7.cpp  | 323 ++++++++++++++++++
 .../gui/qt/maininterface/compositor_win7.hpp  |  85 +++++
 5 files changed, 425 insertions(+), 8 deletions(-)
 create mode 100644 modules/gui/qt/maininterface/compositor_win7.cpp
 create mode 100644 modules/gui/qt/maininterface/compositor_win7.hpp

diff --git a/modules/gui/qt/Makefile.am b/modules/gui/qt/Makefile.am
index 7b1a3a2236..35f0d34f5e 100644
--- a/modules/gui/qt/Makefile.am
+++ b/modules/gui/qt/Makefile.am
@@ -224,7 +224,9 @@ libqt_plugin_la_SOURCES = \
 if HAVE_WIN32
 libqt_plugin_la_SOURCES += \
 	gui/qt/maininterface/main_interface_win32.cpp \
-	gui/qt/maininterface/main_interface_win32.hpp
+	gui/qt/maininterface/main_interface_win32.hpp \
+	gui/qt/maininterface/compositor_win7.cpp \
+	gui/qt/maininterface/compositor_win7.hpp
 
 if HAVE_DCOMP
 libqt_plugin_la_SOURCES += \
@@ -343,7 +345,8 @@ nodist_libqt_plugin_la_SOURCES = \
 	gui/qt/widgets/native/searchlineedit.moc.cpp
 
 if HAVE_WIN32
-nodist_libqt_plugin_la_SOURCES += gui/qt/maininterface/main_interface_win32.moc.cpp
+nodist_libqt_plugin_la_SOURCES += gui/qt/maininterface/main_interface_win32.moc.cpp \
+	gui/qt/maininterface/compositor_win7.moc.cpp
 
 if HAVE_DCOMP
 nodist_libqt_plugin_la_SOURCES += \
diff --git a/modules/gui/qt/maininterface/compositor.cpp b/modules/gui/qt/maininterface/compositor.cpp
index 535a49ea8e..09df385b63 100644
--- a/modules/gui/qt/maininterface/compositor.cpp
+++ b/modules/gui/qt/maininterface/compositor.cpp
@@ -23,6 +23,7 @@
 #ifdef HAVE_DCOMP_H
 #  include "compositor_dcomp.hpp"
 #endif
+#  include "compositor_win7.hpp"
 #endif
 
 namespace vlc {
@@ -37,9 +38,14 @@ Compositor* Compositor::createCompositor(intf_thread_t *p_intf)
     ret = dcomp_compositor->init();
     if (ret)
         return dcomp_compositor;
-    else
-        delete dcomp_compositor;
+    delete dcomp_compositor;
+    msg_Dbg(p_intf, "failed to create DirectComposition backend, use fallback");
 #endif
+    CompositorWin7* win7_compositor = new CompositorWin7(p_intf);
+    if (win7_compositor->init())
+        return win7_compositor;
+    delete win7_compositor;
+    msg_Dbg(p_intf, "failed to create Win7 compositor backend, use fallback");
 #endif
     return new CompositorDummy(p_intf);
 }
diff --git a/modules/gui/qt/maininterface/compositor_dummy.hpp b/modules/gui/qt/maininterface/compositor_dummy.hpp
index bcaa9672a0..51647d1ac0 100644
--- a/modules/gui/qt/maininterface/compositor_dummy.hpp
+++ b/modules/gui/qt/maininterface/compositor_dummy.hpp
@@ -34,18 +34,18 @@ class CompositorDummy : public QObject, public Compositor
     Q_OBJECT
 public:
     CompositorDummy(intf_thread_t *p_intf, QObject* parent = nullptr);
-    ~CompositorDummy() = default;
+    virtual ~CompositorDummy() = default;
 
-    MainInterface *makeMainInterface() override;
+    virtual MainInterface *makeMainInterface() override;
     virtual void destroyMainInterface() override;
 
     bool setupVoutWindow(vout_window_t *p_wnd) override;
 
-private:
+protected:
 
     intf_thread_t *m_intf;
 
-    MainInterface* m_rootWindow;
+    MainInterface* m_rootWindow = nullptr;
 };
 
 }
diff --git a/modules/gui/qt/maininterface/compositor_win7.cpp b/modules/gui/qt/maininterface/compositor_win7.cpp
new file mode 100644
index 0000000000..dab17a6997
--- /dev/null
+++ b/modules/gui/qt/maininterface/compositor_win7.cpp
@@ -0,0 +1,323 @@
+/*****************************************************************************
+ * Copyright (C) 2020 VLC authors and VideoLAN
+ *
+ * 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 2 of the License, or
+ * (at your option) 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
+ *****************************************************************************/
+#include "compositor_win7.hpp"
+#include "main_interface_win32.hpp"
+#include "mainui.hpp"
+
+#include <d3d11.h>
+
+#include <dwmapi.h>
+#include <QLibrary>
+
+using namespace vlc;
+
+int CompositorWin7::window_enable(struct vout_window_t * p_wnd, const vout_window_cfg_t *)
+{
+    CompositorWin7* that = static_cast<CompositorWin7*>(p_wnd->sys);
+    msg_Dbg(that->m_intf, "window_enable");
+    that->m_qmlVideoSurfaceProvider->enable(p_wnd);
+    return VLC_SUCCESS;
+}
+
+void CompositorWin7::window_disable(struct vout_window_t * p_wnd)
+{
+    CompositorWin7* that = static_cast<CompositorWin7*>(p_wnd->sys);
+    that->m_qmlVideoSurfaceProvider->disable();
+    that->m_videoWindowHandler->disable();
+    msg_Dbg(that->m_intf, "window_disable");
+}
+
+void CompositorWin7::window_resize(struct vout_window_t * p_wnd, unsigned width, unsigned height)
+{
+    CompositorWin7* that = static_cast<CompositorWin7*>(p_wnd->sys);
+    msg_Dbg(that->m_intf, "window_resize %ux%u", width, height);
+    that->m_videoWindowHandler->requestResizeVideo(width, height);
+}
+
+void CompositorWin7::window_destroy(struct vout_window_t * p_wnd)
+{
+    CompositorWin7* that = static_cast<CompositorWin7*>(p_wnd->sys);
+    msg_Dbg(that->m_intf, "window_destroy");
+}
+
+void CompositorWin7::window_set_state(struct vout_window_t * p_wnd, unsigned state)
+{
+    CompositorWin7* that = static_cast<CompositorWin7*>(p_wnd->sys);
+    msg_Dbg(that->m_intf, "window_set_state");
+    that->m_videoWindowHandler->requestVideoState(static_cast<vout_window_state>(state));
+}
+
+void CompositorWin7::window_unset_fullscreen(struct vout_window_t * p_wnd)
+{
+    CompositorWin7* that = static_cast<CompositorWin7*>(p_wnd->sys);
+    msg_Dbg(that->m_intf, "window_unset_fullscreen");
+    that->m_videoWindowHandler->requestVideoWindowed();
+}
+
+void CompositorWin7::window_set_fullscreen(struct vout_window_t * p_wnd, const char *id)
+{
+    CompositorWin7* that = static_cast<CompositorWin7*>(p_wnd->sys);
+    msg_Dbg(that->m_intf, "window_set_fullscreen");
+    that->m_videoWindowHandler->requestVideoFullScreen(id);
+}
+
+
+CompositorWin7::CompositorWin7(intf_thread_t *p_intf, QObject* parent)
+    : CompositorDummy(p_intf, parent)
+{
+}
+
+CompositorWin7::~CompositorWin7()
+{
+    if (m_taskbarWidget)
+        qApp->removeNativeEventFilter(m_taskbarWidget);
+    if (m_nativeEventFilter)
+        qApp->removeNativeEventFilter(m_nativeEventFilter);
+    if (m_stable)
+        delete m_stable;
+}
+
+bool CompositorWin7::init()
+{
+    //check whether D3DCompiler is available. whitout it Angle won't work
+    QLibrary d3dCompilerDll;
+    for (int i = 47; i > 41; --i)
+    {
+        d3dCompilerDll.setFileName(QString("D3DCOMPILER_%1.dll").arg(i));
+        if (d3dCompilerDll.load())
+            break;
+    }
+
+    D3D_FEATURE_LEVEL requestedFeatureLevels[] = {
+        D3D_FEATURE_LEVEL_9_1,
+        D3D_FEATURE_LEVEL_9_2,
+        D3D_FEATURE_LEVEL_9_3,
+        D3D_FEATURE_LEVEL_10_0,
+        D3D_FEATURE_LEVEL_10_1,
+        D3D_FEATURE_LEVEL_11_1,
+        D3D_FEATURE_LEVEL_11_0,
+    };
+
+    HRESULT hr = D3D11CreateDevice(
+        nullptr,    // Adapter
+        D3D_DRIVER_TYPE_HARDWARE,
+        nullptr,    // Module
+        D3D11_CREATE_DEVICE_BGRA_SUPPORT,
+        requestedFeatureLevels,
+        ARRAY_SIZE(requestedFeatureLevels),
+        D3D11_SDK_VERSION,
+        nullptr, //D3D device
+        nullptr,    // Actual feature level
+        nullptr //D3D context
+        );
+
+    //no hw acceleration, manually select the software backend
+    //otherwise Qt will load angle and fail.
+    if (!d3dCompilerDll.isLoaded() || FAILED(hr))
+    {
+        msg_Info(m_intf, "no D3D support, use software backend");
+        QQuickWindow::setSceneGraphBackend(QSGRendererInterface::Software);
+    }
+
+    return true;
+}
+
+MainInterface* CompositorWin7::makeMainInterface()
+{
+    //Tool flag needs to be passed in the window constructor otherwise the
+    //window will still appears int the taskbar
+    MainInterfaceWin32* rootWindowW32 =  new MainInterfaceWin32(m_intf, nullptr, Qt::Tool | Qt::FramelessWindowHint );
+    m_rootWindow = rootWindowW32;
+    //m_rootWindow6>show() is not called on purpose
+
+    /*
+     * m_stable is not attached to the main interface because dialogs are attached to the mainInterface
+     * and showing them would raise the video widget above the interface
+     */
+    m_stable = new QWidget(nullptr, Qt::Tool | Qt::FramelessWindowHint);
+    m_stable->setContextMenuPolicy( Qt::PreventContextMenu );
+
+    QPalette plt = m_rootWindow->palette();
+    plt.setColor( QPalette::Window, Qt::black );
+    m_stable->setPalette( plt );
+    m_stable->setAutoFillBackground(true);
+    /* Force the widget to be native so that it gets a winId() */
+    m_stable->setAttribute( Qt::WA_NativeWindow, true );
+    m_stable->setAttribute( Qt::WA_PaintOnScreen, true );
+    m_stable->setMouseTracking( true );
+    m_stable->setWindowFlags( Qt::Tool | Qt::FramelessWindowHint | Qt::WindowDoesNotAcceptFocus );
+    m_stable->setAttribute( Qt::WA_ShowWithoutActivating );
+    m_stable->show();
+
+    m_videoWindowHWND = (HWND)m_stable->winId();
+
+    BOOL excluseFromPeek = TRUE;
+    DwmSetWindowAttribute(m_videoWindowHWND, DWMWA_EXCLUDED_FROM_PEEK, &excluseFromPeek, sizeof(excluseFromPeek));
+    DwmSetWindowAttribute(m_videoWindowHWND, DWMWA_DISALLOW_PEEK, &excluseFromPeek, sizeof(excluseFromPeek));
+
+    m_qmlVideoSurfaceProvider = std::make_unique<VideoSurfaceProvider>();
+    m_rootWindow->setVideoSurfaceProvider(m_qmlVideoSurfaceProvider.get());
+
+    m_qmlView = std::make_unique<QQuickView>();
+    m_qmlView->setResizeMode(QQuickView::SizeRootObjectToView);
+    m_qmlView->setClearBeforeRendering(true);
+    m_qmlView->setColor(QColor(Qt::transparent));
+    m_qmlView->setGeometry(m_rootWindow->geometry());
+    m_qmlView->setMinimumSize( m_rootWindow->minimumSize() );
+
+    m_qmlView->installEventFilter(this);
+    Win7NativeEventFilter* m_nativeEventFilter = new Win7NativeEventFilter(this);
+    qApp->installNativeEventFilter(m_nativeEventFilter);
+    connect(m_nativeEventFilter, &Win7NativeEventFilter::windowStyleChanged,
+            this, &CompositorWin7::resetVideoZOrder);
+
+    m_qmlView->show();
+
+    m_qmlWindowHWND = (HWND)m_qmlView->winId();
+
+    m_videoWindowHandler = std::make_unique<VideoWindowHandler>(m_intf, m_rootWindow);
+    m_videoWindowHandler->setWindow( m_qmlView.get() );
+
+    new InterfaceWindowHandlerWin32(m_intf, m_rootWindow, m_qmlView.get(), m_qmlView.get());
+
+    m_taskbarWidget = new WinTaskbarWidget(m_intf, m_qmlView.get(), this);
+    qApp->installNativeEventFilter(m_taskbarWidget);
+
+    MainUI* m_ui = new MainUI(m_intf, m_rootWindow, this);
+    m_ui->setup(m_qmlView->engine());
+
+
+    m_qmlView->setContent(QUrl(), m_ui->getComponent(), m_ui->createRootItem());
+
+    connect(m_rootWindow, &MainInterface::windowTitleChanged,
+            m_qmlView.get(), &QQuickView::setTitle);
+    connect(m_rootWindow, &MainInterface::windowIconChanged,
+            m_qmlView.get(), &QQuickView::setIcon);
+
+    return m_rootWindow;
+}
+
+bool CompositorWin7::setupVoutWindow(vout_window_t *p_wnd)
+{
+    BOOL isCompositionEnabled;
+    HRESULT hr = DwmIsCompositionEnabled(&isCompositionEnabled);
+
+    //composition is disabled, video can't be seen through the interface,
+    //so we fallback to a separate window.
+    if (FAILED(hr) || !isCompositionEnabled)
+        return false;
+
+    static const struct vout_window_operations ops = {
+        CompositorWin7::window_enable,
+        CompositorWin7::window_disable,
+        CompositorWin7::window_resize,
+        CompositorWin7::window_destroy,
+        CompositorWin7::window_set_state,
+        CompositorWin7::window_unset_fullscreen,
+        CompositorWin7::window_set_fullscreen,
+        nullptr, //window_set_title
+    };
+
+    p_wnd->sys = this;
+    p_wnd->type = VOUT_WINDOW_TYPE_HWND;
+    p_wnd->handle.hwnd = (HWND)m_stable->winId();
+    p_wnd->display.x11 = nullptr;
+    p_wnd->ops = &ops;
+    p_wnd->info.has_double_click = true;
+    return true;
+}
+
+bool CompositorWin7::eventFilter(QObject*, QEvent* ev)
+{
+    switch (ev->type())
+    {
+    case QEvent::Close:
+        m_rootWindow->close();
+        break;
+    case QEvent::Move:
+    case QEvent::Resize:
+    case QEvent::ApplicationStateChange:
+        m_stable->setGeometry(m_qmlView->geometry());
+        resetVideoZOrder();
+        break;
+    case QEvent::WindowStateChange:
+        if (m_qmlView->windowStates() & Qt::WindowMinimized)
+            m_stable->hide();
+        else
+        {
+            m_stable->show();
+            m_stable->setGeometry(m_qmlView->geometry());
+            resetVideoZOrder();
+        }
+        break;
+
+    case QEvent::FocusIn:
+        resetVideoZOrder();
+        break;
+    case QEvent::Show:
+        m_stable->show();
+        resetVideoZOrder();
+        break;
+    case QEvent::Hide:
+        m_stable->hide();
+        break;
+    default:
+        break;
+    }
+
+    return false;
+}
+
+void CompositorWin7::resetVideoZOrder()
+{
+    //Place the video wdiget right behind the interface
+    HWND bottomHWND = m_qmlWindowHWND;
+    HWND currentHWND = bottomHWND;
+    while (currentHWND != nullptr)
+    {
+        bottomHWND = currentHWND;
+        currentHWND = GetWindow(bottomHWND, GW_OWNER);
+    }
+
+    SetWindowPos(
+        m_videoWindowHWND,
+        bottomHWND,
+        0,0,0,0,
+        SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE
+    );
+}
+
+Win7NativeEventFilter::Win7NativeEventFilter(QObject* parent)
+    : QObject(parent)
+{
+}
+
+//parse native events that are not reported by Qt
+bool Win7NativeEventFilter::nativeEventFilter(const QByteArray&, void* message, long*)
+{
+    MSG * msg = static_cast<MSG*>( message );
+
+    switch( msg->message )
+    {
+        //style like "always on top" changed
+        case WM_STYLECHANGED:
+            emit windowStyleChanged();
+            break;
+    }
+    return false;
+}
diff --git a/modules/gui/qt/maininterface/compositor_win7.hpp b/modules/gui/qt/maininterface/compositor_win7.hpp
new file mode 100644
index 0000000000..a62650dc8b
--- /dev/null
+++ b/modules/gui/qt/maininterface/compositor_win7.hpp
@@ -0,0 +1,85 @@
+/*****************************************************************************
+ * Copyright (C) 2020 VLC authors and VideoLAN
+ *
+ * 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 2 of the License, or
+ * (at your option) 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
+ *****************************************************************************/
+#ifndef COMPOSITORDUMMYWIN32_H
+#define COMPOSITORDUMMYWIN32_H
+
+#include "compositor_dummy.hpp"
+#include "videosurface.hpp"
+#include "video_window_handler.hpp"
+#include <QAbstractNativeEventFilter>
+#include <memory>
+
+class WinTaskbarWidget;
+
+namespace vlc {
+
+class Win7NativeEventFilter : public QObject, public QAbstractNativeEventFilter {
+    Q_OBJECT
+public:
+    Win7NativeEventFilter( QObject* parent = nullptr );
+
+    bool nativeEventFilter(const QByteArray &, void *message, long* /* result */);
+signals:
+    void windowStyleChanged();
+};
+
+class CompositorWin7 : public CompositorDummy
+{
+    Q_OBJECT
+public:
+    CompositorWin7(intf_thread_t *p_intf, QObject* parent = nullptr);
+
+    virtual ~CompositorWin7();
+
+    bool init();
+
+    virtual MainInterface *makeMainInterface() override;
+    virtual bool setupVoutWindow(vout_window_t*) override;
+
+protected:
+    bool eventFilter(QObject *obj, QEvent *ev) override;
+
+private:
+    static int window_enable(struct vout_window_t *, const vout_window_cfg_t *);
+    static void window_disable(struct vout_window_t *);
+    static void window_resize(struct vout_window_t *, unsigned width, unsigned height);
+    static void window_destroy(struct vout_window_t *);
+    static void window_set_state(struct vout_window_t *, unsigned state);
+    static void window_unset_fullscreen(struct vout_window_t *);
+    static void window_set_fullscreen(struct vout_window_t *, const char *id);
+
+private slots:
+    void resetVideoZOrder();
+
+private:
+    QWidget* m_stable = nullptr;
+    std::unique_ptr<QQuickView> m_qmlView;
+    std::unique_ptr<VideoWindowHandler> m_videoWindowHandler;
+    std::unique_ptr<VideoSurfaceProvider> m_qmlVideoSurfaceProvider;
+    WinTaskbarWidget* m_taskbarWidget = nullptr;
+    Win7NativeEventFilter* m_nativeEventFilter = nullptr;
+
+    HWND m_qmlWindowHWND = nullptr;
+    HWND m_videoWindowHWND = nullptr;
+
+};
+
+}
+
+
+#endif // COMPOSITORDUMMYWIN32_H
-- 
2.25.1



More information about the vlc-devel mailing list