Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/VLC/modules/gui/qt/maininterface/   (VLC Media Plyer ©)  Datei vom 23.9.2026 mit Größe 16 kB image not shown  

Quelle  compositor.cpp   Sprache: C

 

/*****************************************************************************
 * 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.hpp"
#include "compositor_dummy.hpp"
#include "mainctx.hpp"
#include "video_window_handler.hpp"
#include "videosurface.hpp"
#include "interface_window_handler.hpp"
#include "mainui.hpp"

#ifdef _WIN32
#include "mainctx_win32.hpp"
#include "compositor_dcomp.hpp"
#include "compositor_win7.hpp"
#endif

#ifdef QT_HAS_WAYLAND_COMPOSITOR
#  include "compositor_wayland.hpp"
#endif

#ifdef QT_HAS_X11_COMPOSITOR
#  include "compositor_x11.hpp"
#endif

#include "maininterface/windoweffects_module.hpp"

#include "compositor_platform.hpp"

#include <vlc_window.h>
#include <vlc_modules.h>


using namespace vlc;

template<typename T>
static Compositor* instanciateCompositor(qt_intf_t *p_intf) {
    return new T(p_intf);
}

struct {
    const char* name;
    Compositor* (*instantiate)(qt_intf_t *p_intf);
} static compositorList[] = {
#if defined(_WIN32)
    {"dcomp", &instanciateCompositor<CompositorDirectComposition> },
#endif
#if defined(_WIN32) || defined(__APPLE__)
    {"platform", &instanciateCompositor<CompositorPlatform> },
#endif
#if defined(_WIN32)
    {"win7", &instanciateCompositor<CompositorWin7> },
#endif
#ifdef QT_HAS_WAYLAND_COMPOSITOR
    {"wayland", &instanciateCompositor<CompositorWayland> },
#endif
#ifdef QT_HAS_X11_COMPOSITOR
    {"x11", &instanciateCompositor<CompositorX11> },
#endif
    {"dummy", &instanciateCompositor<CompositorDummy> }
};

CompositorFactory::CompositorFactory(qt_intf_t *p_intf, const char* compositor)
    : m_intf(p_intf)
    , m_compositorName(compositor)
{
}

Compositor* CompositorFactory::createCompositor()
{
    for (; m_compositorIndex < ARRAY_SIZE(compositorList); m_compositorIndex++)
    {
        if (m_compositorName == "auto" || m_compositorName == compositorList[m_compositorIndex].name)
        {
            std::unique_ptr<Compositor> compositor {
                compositorList[m_compositorIndex].instantiate(m_intf)
            };

            if (compositor->init())
            {
                //avoid looping over the same compositor if the current ones fails further initialisation steps
                m_compositorIndex++;
                return compositor.release();
            }
        }
    }
    msg_Err(m_intf, "no suitable compositor found");
    return nullptr;
}

namespace {

struct CompositorWindow {
    /* The CompositorVideo instance that created the window */
    vlc::CompositorVideo *compositor;

    /* Callback setup from commonSetupVoutWindow. It usually comes from the main qt.cpp
     * file so as to handle the reference counting of Qt's resources. */

    void (*destroy_cb)(struct vlc_window *wnd);
};

}

extern "C"
{
static int windowEnableCb(vlc_window_t* p_wnd, const vlc_window_cfg_t * cfg)
{
    assert(p_wnd->sys);
    auto that = static_cast<CompositorWindow*>(p_wnd->sys);
    int ret = VLC_EGENERIC;
    QMetaObject::invokeMethod(that->compositor, [&](){
        ret = that->compositor->windowEnable(cfg);
    }, Qt::BlockingQueuedConnection);
    return ret;
}

static void windowDisableCb(vlc_window_t* p_wnd)
{
    assert(p_wnd->sys);
    auto that = static_cast<CompositorWindow*>(p_wnd->sys);
    QMetaObject::invokeMethod(that->compositor, [that](){
        that->compositor->windowDisable();
    }, Qt::BlockingQueuedConnection);
}

static void windowResizeCb(vlc_window_t* p_wnd, unsigned width, unsigned height)
{
    assert(p_wnd->sys);
    auto that = static_cast<CompositorWindow*>(p_wnd->sys);
    that->compositor->windowResize(width, height);
}

static void windowDestroyCb(struct vlc_window * p_wnd)
{
    assert(p_wnd->sys);
    auto that = static_cast<CompositorWindow*>(p_wnd->sys);
    that->compositor->windowDestroy();
    that->compositor = nullptr;

    if (that->destroy_cb != nullptr)
        that->destroy_cb(p_wnd);

    /* Destroy p_wnd->sys */
    delete that;
    p_wnd->sys = nullptr;
}

static void windowSetStateCb(vlc_window_t* p_wnd, unsigned state)
{
    assert(p_wnd->sys);
    auto that = static_cast<CompositorWindow*>(p_wnd->sys);
    that->compositor->windowSetState(state);
}

static void windowUnsetFullscreenCb(vlc_window_t* p_wnd)
{
    assert(p_wnd->sys);
    auto that = static_cast<CompositorWindow*>(p_wnd->sys);
    that->compositor->windowUnsetFullscreen();
}

static void windowSetFullscreenCb(vlc_window_t* p_wnd, const char *id)
{
    assert(p_wnd->sys);
    auto that = static_cast<CompositorWindow*>(p_wnd->sys);
    that->compositor->windowSetFullscreen(id);
}

}

CompositorVideo::CompositorVideo(qt_intf_t *p_intf, QObject* parent)
    : QObject(parent)
    , m_intf(p_intf)
{
}

CompositorVideo::~CompositorVideo()
{
    if (m_windowEffectsModule)
    {
        if (m_windowEffectsModule->p_module)
            module_unneed(m_windowEffectsModule, m_windowEffectsModule->p_module);
        vlc_object_delete(m_windowEffectsModule);
    }
}

void CompositorVideo::commonSetupVoutWindow(vlc_window_t* p_wnd, VoutDestroyCb destroyCb)
{
    static const struct vlc_window_operations ops = {
        windowEnableCb,
        windowDisableCb,
        windowResizeCb,
        windowDestroyCb,
        windowSetStateCb,
        windowUnsetFullscreenCb,
        windowSetFullscreenCb,
        nullptr, //window_set_title
    };

    /* Allocate state that will be tracked by the window through vlc_window::sys */
    // NOTE: no std::nothrow here since no error code, forward the exception
    auto *compositor_window = new CompositorWindow;
    compositor_window->compositor = this;
    compositor_window->destroy_cb = destroyCb;

    /* We currently support only a single window in the compositor. */
    assert(m_wnd == nullptr);
    m_wnd = p_wnd;

    p_wnd->sys = compositor_window;
    p_wnd->ops = &ops;
    p_wnd->info.has_double_click = true;

    // These need to be connected here, since the compositor might not be ready when
    // these signals are emitted. VOut window might not be set, or worse, compositor's
    // internal preparations might not be completed yet:
    constexpr auto connType = static_cast<Qt::ConnectionType>(Qt::UniqueConnection | Qt::DirectConnection);
    connect(m_videoSurfaceProvider.get(), &VideoSurfaceProvider::surfacePropertiesChanged,
            this, &CompositorVideo::onSurfacePropertiesChanged, connType);
}

void CompositorVideo::windowDestroy()
{
    // Current thread may not be the thread where
    // m_videoSurfaceProvider belongs to, so do not delete
    // it here:
    disconnect(m_videoSurfaceProvider.get(), &VideoSurfaceProvider::surfacePropertiesChanged,
               this, &CompositorVideo::onSurfacePropertiesChanged);

    m_wnd = nullptr;
}

void CompositorVideo::windowResize(unsigned width, unsigned height)
{
    m_videoWindowHandler->requestResizeVideo(width, height);
}

void CompositorVideo::windowSetState(unsigned state)
{
    m_videoWindowHandler->requestVideoState(static_cast<vlc_window_state>(state));
}

void CompositorVideo::windowUnsetFullscreen()
{
    m_videoWindowHandler->requestVideoWindowed();
}

void CompositorVideo::windowSetFullscreen(const char *id)
{
    m_videoWindowHandler->requestVideoFullScreen(id);
}

void CompositorVideo::commonWindowEnable()
{
    m_videoSurfaceProvider->enable(m_wnd);
    m_videoSurfaceProvider->setVideoEmbed(true);
}

void CompositorVideo::commonWindowDisable()
{
    m_videoSurfaceProvider->setVideoEmbed(false);
    m_videoSurfaceProvider->disable();
    m_videoWindowHandler->disable();
}


bool CompositorVideo::commonGUICreateImpl(QWindow* window, CompositorVideo::Flags flags)
{
    assert(m_mainCtx);
    assert(window);

    m_videoSurfaceProvider = std::make_unique<VideoSurfaceProvider>(canDoThreadedSurfaceUpdates());
    m_mainCtx->setVideoSurfaceProvider(m_videoSurfaceProvider.get());

    const QQuickWindow *const quickWindow = this->quickWindow();
    assert(quickWindow);
    assert(quickWindow->handle()); // Make sure `::create()` was called before
    assert(quickWindow->rendererInterface());
    // `ViewBlockingRectangle` only supports RHI with depth buffer (which is the default) or software mode:
    const bool hasDepthBufferOrSoftwareMode = (quickWindow->format().depthBufferSize() > 0) ||
                                              (quickWindow->rendererInterface()->graphicsApi() == QSGRendererInterface::Software);
    if (!hasDepthBufferOrSoftwareMode)
        qWarning() << this << ": interface window has no depth buffer, pip player and backdrop blur will not be functional!";

    if (hasDepthBufferOrSoftwareMode && (flags & CompositorVideo::CAN_SHOW_PIP) && var_InheritBool(m_intf, "qt-pip-mode"))
    {
        m_mainCtx->setCanShowVideoPIP(true);
    }
    m_mainCtx->setWindowSuportExtendedFrame(flags & CompositorVideo::HAS_EXTENDED_FRAME);
    if (hasDepthBufferOrSoftwareMode && (flags & CompositorVideo::HAS_ACRYLIC))
    {
        // FIXME: Normally window backdrop blur effect does not require depth buffer, provided that there is nothing between
        //        the window and the scene item that paints. Unfortunately this is not the case in multiple cases, due to
        //        various reasons. Because of that, we disable blur altogether when there is no depth buffer.

#ifndef _WIN32
        assert(qGuiApp);
        if (qGuiApp->platformName().startsWith(QLatin1String("wayland")) && Q_LIKELY(!window->isActive()))
        {
            connect(window, &QWindow::activeChanged, this, [this, window = QPointer(window)]() {
               setBlurBehind(window, true);
            }, Qt::SingleShotConnection);
        }
        else
#endif
        {
            setBlurBehind(window, true);
        }
    }
    m_videoWindowHandler = std::make_unique<VideoWindowHandler>(m_intf);
    m_videoWindowHandler->setWindow( window );

#ifdef _WIN32
    m_interfaceWindowHandler = std::make_unique<InterfaceWindowHandlerWin32>(m_intf, m_mainCtx, window);
#else
    m_interfaceWindowHandler = std::make_unique<InterfaceWindowHandler>(m_intf, m_mainCtx, window);
#endif

#ifdef _WIN32
    m_taskbarWidget = std::make_unique<WinTaskbarWidget>(m_intf, window);
    qApp->installNativeEventFilter(m_taskbarWidget.get());
#endif
    m_ui = std::make_unique<MainUI>(m_intf, m_mainCtx, window);
    return true;
}

void CompositorVideo::adjustBlurBehind()
{
    if (m_mainCtx && m_windowEffectsModule)
    {
        if (QWindow *const window = interfaceMainWindow())
        {
            assert(m_windowEffectsModule->isEffectAvailable(window, WindowEffectsModule::BlurBehind));
            const bool blurBehind = m_mainCtx->hasAcrylicSurface();
            if (blurBehind && m_mainCtx->windowSuportExtendedFrame() && m_mainCtx->useClientSideDecoration())
            {
                QRegion blurRegion;
                const QRegion maskRegion = window->mask();
                if (maskRegion.isEmpty())
                {
                    // If there is window mask, we assume that the content area is smaller than the
                    // window geometry. We also assume that in that case window has custom margins
                    // set (Wayland case). KWin 6.2 Wayland already considers window custom margins
                    // when applying the blur effect, but KWin X11 does not consider `_GTK_FRAME_EXTENTS`.
                    // Since the mask region represents the actual content area, and the outside region
                    // is included in the frame margins, we can set the blur region only if the mask is
                    // empty. This fixes blur backdrop applied to client shadows on X11:
                    const auto margin = m_mainCtx->windowExtendedMargin();
                    blurRegion = QRegion(margin, margin, window->width() - 2 * margin, window->height() - 2 * margin);
                }
                m_windowEffectsModule->setBlurBehind(window, true, blurRegion);
            }
            else
            {
                m_windowEffectsModule->setBlurBehind(window, blurBehind, {});
            }
        }
    }
}

bool CompositorVideo::commonGUICreate(QWindow* window, QmlUISurface* qmlSurface, CompositorVideo::Flags flags)
{
    bool ret = commonGUICreateImpl(window, flags);
    if (!ret)
        return false;
    ret = m_ui->setup(qmlSurface->engine());
    if (! ret)
        return false;
    const auto rootItem = m_ui->createRootItem();
    if (Q_UNLIKELY(!rootItem))
        return false;
    qmlSurface->setContent(rootItem);
    return true;
}

bool CompositorVideo::commonGUICreate(QWindow* window, QQuickView* qmlView, CompositorVideo::Flags flags)
{
    bool ret = commonGUICreateImpl(window, flags);
    if (!ret)
        return false;
    ret = m_ui->setup(qmlView->engine());
    if (! ret)
        return false;
    const auto rootItem = m_ui->createRootItem();
    if (Q_UNLIKELY(!rootItem))
        return false;
    qmlView->setContent(QUrl(), nullptr, rootItem);
    return true;
}

void CompositorVideo::commonGUIDestroy()
{
    m_ui.reset();
#ifdef _WIN32
    qApp->removeNativeEventFilter(m_taskbarWidget.get());
    m_taskbarWidget.reset();
#endif
    m_interfaceWindowHandler.reset();
    m_mainCtx = nullptr;
}

void CompositorVideo::commonIntfDestroy()
{
    m_videoWindowHandler.reset();
    m_videoSurfaceProvider.reset();
    unloadGUI();
}

bool CompositorVideo::setBlurBehind(QWindow *window, const bool enable)
{
    assert(window);
    assert(m_intf);

    if (enable)
    {
        if (!var_InheritBool(m_intf, "qt-backdrop-blur"))
        {
            return false;
        }
    }

    if (m_failedToLoadWindowEffectsModule)
        return false;

    if (!m_windowEffectsModule)
    {
        m_windowEffectsModule = vlc_object_create<WindowEffectsModule>(m_intf);
        if (!m_windowEffectsModule) // do not set m_failedToLoadWindowEffectsModule here
            return false;
    }

    if (!m_windowEffectsModule->p_module)
    {
        m_windowEffectsModule->p_module = module_need(m_windowEffectsModule, "qtwindoweffects", nullptr, false);
        if (!m_windowEffectsModule->p_module)
        {
            msg_Dbg(m_intf, "A module providing window effects capability could not be instantiated. " \
                            "Native background blur effect will not be available. " \
                            "The application may compensate this with a simulated effect on certain platform(s).");
            m_failedToLoadWindowEffectsModule = true;
            vlc_object_delete(m_windowEffectsModule);
            m_windowEffectsModule = nullptr;
            return false;
        }
    }

    if (!m_windowEffectsModule->isEffectAvailable(window, WindowEffectsModule::BlurBehind))
        return false;

    m_mainCtx->setHasAcrylicSurface(enable);

    if (m_mainCtx->windowSuportExtendedFrame())
    {
        assert(m_mainCtx);

        if (enable)
        {
            connect(window, &QWindow::widthChanged, this, &CompositorVideo::adjustBlurBehind, Qt::UniqueConnection);
            connect(window, &QWindow::heightChanged, this, &CompositorVideo::adjustBlurBehind, Qt::UniqueConnection);
            connect(m_mainCtx, &MainCtx::windowExtendedMarginChanged, this, &CompositorVideo::adjustBlurBehind, Qt::UniqueConnection);
            connect(m_mainCtx, &MainCtx::useClientSideDecorationChanged, this, &CompositorVideo::adjustBlurBehind, Qt::UniqueConnection);
        }
        else
        {
            disconnect(window, &QWindow::widthChanged, this, &CompositorVideo::adjustBlurBehind);
            disconnect(window, &QWindow::heightChanged, this, &CompositorVideo::adjustBlurBehind);
            disconnect(m_mainCtx, &MainCtx::windowExtendedMarginChanged, this, &CompositorVideo::adjustBlurBehind);
            disconnect(m_mainCtx, &MainCtx::useClientSideDecorationChanged, this, &CompositorVideo::adjustBlurBehind);
        }

        adjustBlurBehind();
    }
    else
    {
        m_windowEffectsModule->setBlurBehind(window, enable, {});
    }

    return true;
}

QQuickWindow *Compositor::quickWindow() const
{
    return qobject_cast<QQuickWindow*>(interfaceMainWindow());
}

Messung
CHZG
8510010095

¤ Dauer der Verarbeitung: 0.6 Sekunden  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

PVS Prover

Isabelle Prover


Cephes Mathematical Library

Vienna Development Method

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.