[vlc-commits] [Git][videolan/vlc][master] 3 commits: qt: destroy the current compositor when it's unable to create an interface
Jean-Baptiste Kempf (@jbk)
gitlab at videolan.org
Fri Oct 22 07:29:30 UTC 2021
Jean-Baptiste Kempf pushed to branch master at VideoLAN / VLC
Commits:
cfb14b4d by Pierre Lamot at 2021-10-22T06:34:30+00:00
qt: destroy the current compositor when it's unable to create an interface
- - - - -
990f7ccf by Pierre Lamot at 2021-10-22T06:34:30+00:00
snap: add libxdamage dependency
- - - - -
96166bad by Pierre Lamot at 2021-10-22T06:34:30+00:00
qt: add X11 video compositor
The composition works as follow:
* Both the interface and the video are rendered in an offscreen window (using
X11 composite extension)
* We register to damage event (x11 damage extension) to get notified when the
content of the offscreen video window change. When we receive a damage event
we ask the rendering part of the composition to refresh
* The interface is rendered in the offscreen surface using a RenderControl, when
the interface do render, the composition is asked to refresh
* A dedicated thread is spawned to do the rendering, upon a refresh event it
will take the pictures from the offscreen surface and blend them into the
actual window using X11 render extension. Using a separated thread from Qt
ensure that the rendering of the video will not be stalled if Qt thread is
busy.
* The damage events are listened on a separate X11 connection and on a separate
thread than Qt main thread (here the rendering thread). This allows to receive
theses events independently from Qt (in case the Qt thread is stalled). Note
that it is not possible to peek in qt X11 event queue from a non gui thread as
the QX11Info::peekEventQueue is no longer thread safe since 5.12.
fixes: #25627, #22155
- - - - -
17 changed files:
- configure.ac
- extras/ci/gitlab-ci.yml
- extras/package/snap/snapcraft.yaml
- modules/gui/qt/Makefile.am
- modules/gui/qt/maininterface/compositor.cpp
- modules/gui/qt/maininterface/compositor.hpp
- + modules/gui/qt/maininterface/compositor_x11.cpp
- + modules/gui/qt/maininterface/compositor_x11.hpp
- + modules/gui/qt/maininterface/compositor_x11_renderclient.cpp
- + modules/gui/qt/maininterface/compositor_x11_renderclient.hpp
- + modules/gui/qt/maininterface/compositor_x11_renderwindow.cpp
- + modules/gui/qt/maininterface/compositor_x11_renderwindow.hpp
- + modules/gui/qt/maininterface/compositor_x11_uisurface.cpp
- + modules/gui/qt/maininterface/compositor_x11_uisurface.hpp
- + modules/gui/qt/maininterface/compositor_x11_utils.cpp
- + modules/gui/qt/maininterface/compositor_x11_utils.hpp
- modules/gui/qt/qt.cpp
Changes:
=====================================
configure.ac
=====================================
@@ -3266,6 +3266,7 @@ AC_ARG_ENABLE([xcb],
have_xcb="no"
have_xkbcommon_x11="no"
have_xcb_keysyms="no"
+have_xcb_damage="no"
AS_IF([test "${enable_xcb}" != "no"], [
xcb_err=""
@@ -3305,12 +3306,19 @@ AS_IF([test "${enable_xcb}" != "no"], [
AC_MSG_WARN([${XCB_KEYSYMS_PKG_ERRORS}. Global hotkeys are disabled.])
])
+ dnl xcb-damage
+ PKG_CHECK_MODULES([XCB_DAMAGE], [xcb-damage], [
+ have_xcb_damage="yes"
+ ], [
+ AC_MSG_WARN([${XCB_DAMAGE_PKG_ERRORS}. Qt X11 composition disabled.])
+ ])
+
have_xcb="yes"
])
AM_CONDITIONAL([HAVE_XCB], [test "${have_xcb}" = "yes"])
AM_CONDITIONAL([HAVE_XKBCOMMON_X11], [test "${have_xkbcommon_x11}" = "yes"])
AM_CONDITIONAL([HAVE_XCB_KEYSYMS], [test "${have_xcb_keysyms}" = "yes"])
-
+AM_CONDITIONAL([HAVE_XCB_DAMAGE], [test "${have_xcb_damage}" = "yes"])
dnl
dnl VDPAU needs X11
=====================================
extras/ci/gitlab-ci.yml
=====================================
@@ -19,7 +19,7 @@ variables:
VLC_UWP_LLVM_IMAGE: registry.videolan.org/vlc-debian-llvm-uwp:20211020111246
VLC_DEBIAN_IMAGE: registry.videolan.org/vlc-debian-unstable:20210803114245
VLC_ANDROID_IMAGE: registry.videolan.org/vlc-debian-android:20210730131708
- VLC_SNAP_IMAGE: registry.videolan.org/vlc-ubuntu-focal:20211006143413
+ VLC_SNAP_IMAGE: registry.videolan.org/vlc-ubuntu-focal:20211020115724
VLC_RASPBIAN_IMAGE: registry.videolan.org/vlc-ubuntu-raspberry:20211006142322
VLC_WASM_EMSCRIPTEN: registry.videolan.org/vlc-debian-wasm-emscripten:20210915101305
=====================================
extras/package/snap/snapcraft.yaml
=====================================
@@ -144,6 +144,7 @@ parts:
- libvdpau-dev
- libx11-dev
- libxcb-composite0-dev
+ - libxcb-damage0-dev
- libxcb-keysyms1-dev
- libxcb-randr0-dev
- libxcb-shm0-dev
@@ -194,6 +195,7 @@ parts:
- libvdpau1
- libx11-6
- libxcb-composite0
+ - libxcb-damage0
- libxcb-keysyms1
- libxcb-randr0
- libxcb-shm0
=====================================
modules/gui/qt/Makefile.am
=====================================
@@ -36,9 +36,17 @@ if HAVE_QT5_X11
libqt_plugin_la_CXXFLAGS += $(QT5_X11_CFLAGS) -DQT5_HAS_X11
libqt_plugin_la_LIBADD += $(QT5_X11_LIBS) $(X_LIBS) $(X_PRE_LIB) -lX11
endif
+
if HAVE_XCB
-libqt_plugin_la_CXXFLAGS += -DQT5_HAS_XCB
+libqt_plugin_la_CXXFLAGS += -DQT5_HAS_XCB $(XCB_CFLAGS)
+libqt_plugin_la_LIBADD += $(XCB_LIBS)
+if HAVE_XCB_DAMAGE
+libqt_plugin_la_CXXFLAGS += -DQT5_HAS_X11_COMPOSITOR \
+ $(XCB_RENDER_CFLAGS) $(XCB_COMPOSITE_CFLAGS) $(XLCB_DAMAGE_CFLAGS)
+libqt_plugin_la_LIBADD += $(XCB_RENDER_LIBS) $(XCB_COMPOSITE_LIBS) $(XCB_DAMAGE_LIBS)
+endif
endif
+
if HAVE_WAYLAND
libqt_plugin_la_CPPFLAGS += -DQT5_HAS_WAYLAND \
-DQPNI_HEADER=\<$(QT_VERSION)/QtGui/qpa/qplatformnativeinterface.h\>
@@ -283,6 +291,23 @@ libqt_plugin_la_SOURCES += \
gui/qt/maininterface/compositor_dcomp_uisurface.cpp \
gui/qt/maininterface/compositor_dcomp_uisurface.hpp
endif
+
+endif
+
+if HAVE_XCB
+if HAVE_XCB_DAMAGE
+libqt_plugin_la_SOURCES += \
+ gui/qt/maininterface/compositor_x11.cpp \
+ gui/qt/maininterface/compositor_x11.hpp \
+ gui/qt/maininterface/compositor_x11_renderclient.cpp \
+ gui/qt/maininterface/compositor_x11_renderclient.hpp \
+ gui/qt/maininterface/compositor_x11_renderwindow.cpp \
+ gui/qt/maininterface/compositor_x11_renderwindow.hpp \
+ gui/qt/maininterface/compositor_x11_uisurface.cpp \
+ gui/qt/maininterface/compositor_x11_uisurface.hpp \
+ gui/qt/maininterface/compositor_x11_utils.cpp \
+ gui/qt/maininterface/compositor_x11_utils.hpp
+endif
endif
# Meta-object compilation
@@ -423,6 +448,17 @@ endif
endif
+if HAVE_XCB
+if HAVE_XCB_DAMAGE
+nodist_libqt_plugin_la_SOURCES += \
+ gui/qt/maininterface/compositor_x11.moc.cpp \
+ gui/qt/maininterface/compositor_x11_renderclient.moc.cpp \
+ gui/qt/maininterface/compositor_x11_renderwindow.moc.cpp \
+ gui/qt/maininterface/compositor_x11_uisurface.moc.cpp \
+ gui/qt/maininterface/compositor_x11_utils.moc.cpp
+endif
+endif
+
nodist_libqt_plugin_la_SOURCES += \
gui/qt/dialogs/extended/ui_equalizer.h \
gui/qt/dialogs/extended/ui_video_effects.h \
=====================================
modules/gui/qt/maininterface/compositor.cpp
=====================================
@@ -32,6 +32,10 @@
# include "compositor_win7.hpp"
#endif
+#ifdef QT5_HAS_XCB
+# include "compositor_x11.hpp"
+#endif
+
using namespace vlc;
template<typename T>
@@ -54,6 +58,9 @@ struct {
{"dcomp", &instanciateCompositor<CompositorDirectComposition>, &preInit<CompositorDirectComposition> },
#endif
{"win7", &instanciateCompositor<CompositorWin7>, &preInit<CompositorWin7> },
+#endif
+#ifdef QT5_HAS_X11_COMPOSITOR
+ {"x11", &instanciateCompositor<CompositorX11>, &preInit<CompositorX11> },
#endif
{"dummy", &instanciateCompositor<CompositorDummy>, &preInit<CompositorDummy> }
};
@@ -85,7 +92,11 @@ Compositor* CompositorFactory::createCompositor()
{
Compositor* compositor = compositorList[m_compositorIndex].instanciate(m_intf);
if (compositor->init())
+ {
+ //avoid looping over the same compositor if the current ones fails further initialisation steps
+ m_compositorIndex++;
return compositor;
+ }
}
}
msg_Err(m_intf, "no suitable compositor found");
=====================================
modules/gui/qt/maininterface/compositor.hpp
=====================================
@@ -54,7 +54,8 @@ public:
{
DummyCompositor,
Win7Compositor,
- DirectCompositionCompositor
+ DirectCompositionCompositor,
+ X11Compositor
};
typedef void (*VoutDestroyCb)(vout_window_t *p_wnd);
=====================================
modules/gui/qt/maininterface/compositor_x11.cpp
=====================================
@@ -0,0 +1,212 @@
+/*****************************************************************************
+ * 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 <QX11Info>
+#include <QByteArray>
+#include <QHBoxLayout>
+#include <QWidget>
+#include <QScreen>
+#include "compositor_x11.hpp"
+#include "compositor_x11_renderwindow.hpp"
+#include "compositor_x11_uisurface.hpp"
+#include "main_interface.hpp"
+#include "interface_window_handler.hpp"
+#include "video_window_handler.hpp"
+#include "mainui.hpp"
+
+using namespace vlc;
+
+int CompositorX11::windowEnable(const vout_window_cfg_t *)
+{
+ commonWindowEnable();
+ m_renderWindow->enableVideoWindow();
+ return VLC_SUCCESS;
+}
+
+void CompositorX11::windowDisable()
+{
+ m_renderWindow->disableVideoWindow();
+ commonWindowDisable();
+}
+
+CompositorX11::CompositorX11(qt_intf_t *p_intf, QObject *parent)
+ : CompositorVideo(p_intf, parent)
+{
+}
+
+CompositorX11::~CompositorX11()
+{
+ if (m_conn)
+ xcb_disconnect(m_conn);
+}
+
+bool CompositorX11::preInit(qt_intf_t*)
+{
+ return true;
+}
+
+static bool checkExtensionPresent(qt_intf_t* intf, xcb_connection_t *conn, const char* extension)
+{
+ bool ret = queryExtension(conn, extension, nullptr, nullptr);
+ if (! ret)
+ msg_Warn(intf, "X11 extension %s is missing", extension);
+ return ret;
+}
+
+#define REGISTER_XCB_EXTENSION(c, extension, minor, major) \
+ do { \
+ xcb_ ## extension ##_query_version_cookie_t cookie = xcb_## extension ##_query_version(c, minor, major); \
+ xcb_generic_error_t* error = nullptr; \
+ auto reply = wrap_cptr(xcb_## extension ##_query_version_reply(c, cookie, &error)); \
+ if (error) { \
+ msg_Warn(m_intf, "X11 extension %s is too old", #extension); \
+ free(error); \
+ return false; \
+ } \
+ } while(0)
+
+
+bool CompositorX11::init()
+{
+ if (!QX11Info::isPlatformX11())
+ {
+ msg_Info(m_intf, "not running an X11 platform");
+ return false;
+ }
+
+ //open a separated X11 connection from Qt to be able to receive
+ //damage events independently from Qt (even when Qt's main loop is stalled)
+ m_conn = xcb_connect(nullptr, nullptr);
+ if (xcb_connection_has_error(m_conn) != 0)
+ {
+ msg_Warn(m_intf, "can't open X11 connection");
+ return false;
+ }
+
+ //check X11 extensions
+ if (!checkExtensionPresent(m_intf, m_conn, "DAMAGE"))
+ return false;
+ REGISTER_XCB_EXTENSION(m_conn, damage, XCB_DAMAGE_MAJOR_VERSION, XCB_DAMAGE_MINOR_VERSION);
+
+ if (!checkExtensionPresent(m_intf, m_conn, "RENDER"))
+ return false;
+ //0.11 required for Blend mode operators
+ static_assert (XCB_RENDER_MAJOR_VERSION == 0 && XCB_RENDER_MINOR_VERSION >= 11,
+ "X11 Render version is too old, 0.11+ is required");
+ REGISTER_XCB_EXTENSION(m_conn, render, XCB_RENDER_MAJOR_VERSION, XCB_RENDER_MINOR_VERSION);
+
+ if (!checkExtensionPresent(m_intf, m_conn, "Composite"))
+ return false;
+ //0.2 is required for NameWindowPixmap
+ static_assert (XCB_COMPOSITE_MAJOR_VERSION == 0 && XCB_COMPOSITE_MINOR_VERSION >= 2,
+ "X11 Composite version is too old, 0.2+ is required");
+ REGISTER_XCB_EXTENSION(m_conn, composite, XCB_COMPOSITE_MAJOR_VERSION, XCB_COMPOSITE_MINOR_VERSION);
+
+ // check whether we're running under "XWAYLAND"
+ auto screens = qApp->screens();
+ bool isXWayland = std::any_of(screens.begin(), screens.end(), [](QScreen* screen){
+ return screen->name().startsWith("XWAYLAND");
+ });
+
+ if (isXWayland)
+ {
+ // if the X11 server is XWayland, test if it is a broken version
+ const xcb_setup_t* setup = xcb_get_setup(m_conn);
+
+ if (setup == nullptr)
+ {
+ msg_Info(m_intf, "error checking for XWayland version");
+ return false;
+ }
+
+ if (setup->release_number < 12100000u)
+ {
+ msg_Info(m_intf, "running a broken XWayland version, disabling X11 composition");
+ return false;
+ }
+ }
+
+ return true;
+}
+
+bool CompositorX11::makeMainInterface(MainInterface* mainInterface)
+{
+ m_mainInterface = mainInterface;
+
+ m_videoWidget = std::make_unique<DummyNativeWidget>();
+ m_videoWidget->setWindowFlag(Qt::WindowType::BypassWindowManagerHint);
+ m_videoWidget->setWindowFlag(Qt::WindowType::WindowTransparentForInput);
+ m_videoWidget->winId();
+ m_videoWidget->show();
+
+ bool useCSD = m_mainInterface->useClientSideDecoration();
+ m_renderWindow = std::make_unique<vlc::CompositorX11RenderWindow>(m_intf, m_conn, useCSD);
+ if (!m_renderWindow->init())
+ return false;
+
+ m_interfaceWindow = m_renderWindow->getWindow();
+
+ m_qmlView = std::make_unique<CompositorX11UISurface>(m_interfaceWindow);
+ m_qmlView->setFlag(Qt::WindowType::BypassWindowManagerHint);
+ m_qmlView->setFlag(Qt::WindowType::WindowTransparentForInput);
+ m_qmlView->winId();
+ m_qmlView->show();
+
+ CompositorVideo::Flags flags = CompositorVideo::CAN_SHOW_PIP;
+ if (m_renderWindow->hasAcrylic())
+ flags |= CompositorVideo::HAS_ACRYLIC;
+ commonGUICreate(m_interfaceWindow, m_qmlView.get(), flags);
+
+ m_renderWindow->setInterfaceWindow(m_qmlView.get());
+ m_renderWindow->setVideoWindow(m_videoWidget->windowHandle());
+
+ m_renderWindow->startRendering();
+
+ return true;
+}
+
+void CompositorX11::destroyMainInterface()
+{
+ commonIntfDestroy();
+ m_videoWidget.reset();
+ m_renderWindow.reset();
+}
+
+void CompositorX11::unloadGUI()
+{
+ m_renderWindow->stopRendering();
+ commonGUIDestroy();
+ m_qmlView.reset();
+}
+
+void CompositorX11::onSurfacePositionChanged(const QPointF& position)
+{
+ m_renderWindow->setVideoPosition(position.toPoint());
+}
+
+void CompositorX11::onSurfaceSizeChanged(const QSizeF& size)
+{
+ m_renderWindow->setVideoSize((size / m_videoWidget->window()->devicePixelRatioF()).toSize());
+}
+
+bool CompositorX11::setupVoutWindow(vout_window_t* p_wnd, VoutDestroyCb destroyCb)
+{
+ p_wnd->type = VOUT_WINDOW_TYPE_XID;
+ p_wnd->handle.xid = m_videoWidget->winId();
+ commonSetupVoutWindow(p_wnd, destroyCb);
+ return true;
+}
=====================================
modules/gui/qt/maininterface/compositor_x11.hpp
=====================================
@@ -0,0 +1,77 @@
+/*****************************************************************************
+ * 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 COMPOSITORX11_HPP
+#define COMPOSITORX11_HPP
+
+#include <QObject>
+#include <QWidget>
+#include "compositor.hpp"
+#include "videosurface.hpp"
+#include <memory>
+
+#include <xcb/xcb.h>
+
+class QMainWindow;
+class MainUI;
+class InterfaceWindowHandler;
+class VideoWindowHandler;
+
+namespace vlc {
+
+class CompositorX11RenderWindow;
+class CompositorX11UISurface;
+class CompositorX11 : public CompositorVideo
+{
+ Q_OBJECT
+public:
+ explicit CompositorX11(qt_intf_t *p_intf, QObject *parent = nullptr);
+ virtual ~CompositorX11();
+
+ static bool preInit(qt_intf_t *);
+ bool init() override;
+
+ bool makeMainInterface(MainInterface*) override;
+ void destroyMainInterface() override;
+ void unloadGUI() override;
+
+ bool setupVoutWindow(vout_window_t *p_wnd, VoutDestroyCb destroyCb) override;
+
+ inline Type type() const override { return X11Compositor; };
+
+ inline QWindow* interfaceMainWindow() const override { return m_interfaceWindow; };
+
+private:
+ int windowEnable(const vout_window_cfg_t *) override;
+ void windowDisable() override;
+
+private slots:
+ void onSurfacePositionChanged(const QPointF& position) override;
+ void onSurfaceSizeChanged(const QSizeF& size) override;
+
+private:
+ QWindow* m_interfaceWindow = nullptr;
+ xcb_connection_t* m_conn = nullptr;
+
+ std::unique_ptr<QWidget> m_videoWidget;
+ std::unique_ptr<CompositorX11UISurface> m_qmlView;
+ std::unique_ptr<CompositorX11RenderWindow> m_renderWindow;
+};
+
+}
+
+#endif // COMPOSITORX11_HPP
=====================================
modules/gui/qt/maininterface/compositor_x11_renderclient.cpp
=====================================
@@ -0,0 +1,122 @@
+/*****************************************************************************
+ * Copyright (C) 2021 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 <vlc_cxx_helpers.hpp>
+#include "compositor_x11_renderclient.hpp"
+#include <X11/extensions/Xdamage.h>
+
+#define _NET_WM_BYPASS_COMPOSITOR_NAME "_NET_WM_BYPASS_COMPOSITOR"
+
+using namespace vlc;
+
+CompositorX11RenderClient::CompositorX11RenderClient(qt_intf_t* p_intf, xcb_connection_t* conn, QWindow* window, QObject *parent)
+ : QObject(parent)
+ , m_intf(p_intf)
+ , m_window(window)
+ , m_conn(conn)
+ , m_wid(window->winId())
+ , m_pixmap(m_conn)
+ , m_picture(m_conn)
+{
+ xcb_generic_error_t* err = nullptr;
+ xcb_get_window_attributes_cookie_t attrCookie = xcb_get_window_attributes(m_conn, m_wid);
+ auto attrReply = wrap_cptr(xcb_get_window_attributes_reply(m_conn, attrCookie, &err));
+ if (err)
+ {
+ msg_Info(m_intf, "can't get window attr");
+ free(err);
+ return;
+ }
+
+ xcb_visualid_t visual = attrReply->visual;
+ bool ret = findVisualFormat(m_conn, visual, &m_format, nullptr);
+ if (!ret)
+ {
+ msg_Info(m_intf, "can't find visual format");
+ return;
+ }
+
+ xcb_void_cookie_t cookie = xcb_composite_redirect_window_checked(m_conn, m_wid, XCB_COMPOSITE_REDIRECT_MANUAL);
+ err = xcb_request_check(m_conn, cookie);
+ if (err)
+ {
+ msg_Warn(m_intf, " can't redirect window %u.%u : %u", err->major_code, err->minor_code, err->error_code);
+ free(err);
+ return;
+ }
+
+ xcb_atom_t _NET_WM_BYPASS_COMPOSITOR = getInternAtom(m_conn, _NET_WM_BYPASS_COMPOSITOR_NAME);
+ if (_NET_WM_BYPASS_COMPOSITOR != XCB_ATOM_NONE)
+ {
+ uint32_t val = 1;
+ xcb_change_property(m_conn, XCB_PROP_MODE_REPLACE, m_wid,
+ _NET_WM_BYPASS_COMPOSITOR, XCB_ATOM_CARDINAL, 32, 1, &val);
+ }
+
+ connect(window, &QWindow::widthChanged, this, &CompositorX11RenderClient::resetPixmap);
+ connect(window, &QWindow::heightChanged, this, &CompositorX11RenderClient::resetPixmap);
+}
+
+CompositorX11RenderClient::~CompositorX11RenderClient()
+{
+ m_pixmap.reset();
+ m_picture.reset();
+}
+
+xcb_drawable_t CompositorX11RenderClient::getWindowXid() const
+{
+ return m_window->winId();
+}
+
+void CompositorX11RenderClient::createPicture()
+{
+ xcb_void_cookie_t voidCookie;
+ auto err = wrap_cptr<xcb_generic_error_t>(nullptr);
+
+ m_pixmap.generateId();
+ voidCookie = xcb_composite_name_window_pixmap_checked(m_conn, m_wid, m_pixmap.get());
+ err.reset(xcb_request_check(m_conn, voidCookie));
+ if (err)
+ {
+ msg_Warn(m_intf, "can't create name window pixmap");
+ m_pixmap.reset();
+ return;
+ }
+
+ m_picture.generateId();
+ voidCookie = xcb_render_create_picture_checked(m_conn, m_picture.get(), m_pixmap.get(), m_format, 0, 0);
+ err.reset(xcb_request_check(m_conn, voidCookie));
+ if (err)
+ {
+ msg_Warn(m_intf, "can't create name window picture");
+ m_pixmap.reset();
+ m_picture.reset();
+ }
+}
+
+xcb_render_picture_t CompositorX11RenderClient::getPicture()
+{
+ if (!m_picture)
+ createPicture();
+ return m_picture.get();
+}
+
+void CompositorX11RenderClient::resetPixmap()
+{
+ m_pixmap.reset();
+ m_picture.reset();
+}
=====================================
modules/gui/qt/maininterface/compositor_x11_renderclient.hpp
=====================================
@@ -0,0 +1,72 @@
+/*****************************************************************************
+ * Copyright (C) 2021 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 COMPOSITOR_X11_RENDERCLIENT_HPP
+#define COMPOSITOR_X11_RENDERCLIENT_HPP
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <QObject>
+#include <QX11Info>
+#include <QWindow>
+
+#include <vlc_common.h>
+
+#include "qt.hpp"
+
+#include "compositor_x11_utils.hpp"
+
+namespace vlc {
+
+class CompositorX11RenderClient : public QObject
+{
+ Q_OBJECT
+public:
+ CompositorX11RenderClient(
+ qt_intf_t* p_intf, xcb_connection_t* conn,
+ QWindow* window,
+ QObject* parent = nullptr);
+
+ ~CompositorX11RenderClient();
+
+ bool registerDamageEvent(Display* dpy);
+
+ xcb_drawable_t getWindowXid() const;
+
+ void createPicture();
+ xcb_render_picture_t getPicture();
+
+public slots:
+ void resetPixmap();
+
+private:
+ qt_intf_t* m_intf;
+ QWindow* m_window = nullptr;
+
+ xcb_connection_t* m_conn = 0;
+ xcb_window_t m_wid = 0;
+ PixmapPtr m_pixmap;
+ PicturePtr m_picture;
+
+ xcb_render_pictformat_t m_format;
+};
+
+}
+
+#endif // RENDERCLIENT_HPP
=====================================
modules/gui/qt/maininterface/compositor_x11_renderwindow.cpp
=====================================
@@ -0,0 +1,578 @@
+/*****************************************************************************
+ * Copyright (C) 2021 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 <math.h>
+#include <QtEvents>
+#include <QCoreApplication>
+#include <QWindow>
+#include <QQuickItem>
+#include <QX11Info>
+#include <QMainWindow>
+#include <QApplication>
+#include <QTimer>
+#include <QThread>
+#include <QSocketNotifier>
+
+#include <xcb/composite.h>
+
+#include "compositor_x11_renderwindow.hpp"
+#include "compositor_x11_renderclient.hpp"
+#include "compositor_x11_uisurface.hpp"
+
+#include <vlc_cxx_helpers.hpp>
+
+#include "util/qvlcapp.hpp"
+#include "qt.hpp"
+
+//blur behind for KDE
+#define _KDE_NET_WM_BLUR_BEHIND_REGION_NAME "_KDE_NET_WM_BLUR_BEHIND_REGION"
+
+using namespace vlc;
+
+RenderTask::RenderTask(qt_intf_t *intf, xcb_connection_t* conn, xcb_drawable_t wid,
+ QMutex& pictureLock, QObject *parent)
+ : QObject(parent)
+ , m_intf(intf)
+ , m_conn(conn)
+ , m_pictureLock(pictureLock)
+ , m_drawingarea(m_conn)
+ , m_wid(wid)
+{
+ assert(conn);
+ assert(m_intf);
+ assert(m_wid);
+ connect(this, &RenderTask::requestRefreshInternal, this, &RenderTask::render);
+}
+
+RenderTask::~RenderTask()
+{
+}
+
+void RenderTask::render(unsigned int requestId)
+{
+ if (requestId != m_refreshRequestId)
+ return;
+ if (!m_visible)
+ return;
+
+ assert(m_interfaceClient != nullptr);
+
+ xcb_flush(m_conn);
+ xcb_render_picture_t drawingarea = getBackTexture();
+
+ if (m_hasAcrylic)
+ {
+ //clear screen
+ xcb_render_color_t clear = { 0x0000, 0x0000, 0x0000, 0x0000 };
+ xcb_rectangle_t rect = {0, 0, 0xFFFF, 0xFFFF};
+ xcb_render_fill_rectangles(m_conn, XCB_RENDER_PICT_OP_SRC, drawingarea,
+ clear, 1, &rect);
+ }
+
+
+ {
+ QMutexLocker lock(&m_pictureLock);
+ if (m_videoEmbed)
+ {
+ assert(m_videoClient);
+ xcb_render_picture_t pic = m_videoClient->getPicture();
+ if (pic)
+ {
+ xcb_render_composite(m_conn, XCB_RENDER_PICT_OP_SRC,
+ pic, 0, drawingarea,
+ 0,0,0,0,
+ m_videoPosition.x(),m_videoPosition.y(),
+ m_videoPosition.width(), m_videoPosition.height());
+ }
+ }
+
+ xcb_render_picture_t pic = m_interfaceClient->getPicture();
+ if (pic)
+ {
+ xcb_render_composite(m_conn, XCB_RENDER_PICT_OP_OVER,
+ pic, 0, drawingarea,
+ 0,0,0,0,
+ 0, 0, m_interfaceSize.width(), m_interfaceSize.height());
+ }
+
+ } //picture lock scope
+
+ xcb_clear_area(m_conn, 0, m_wid,
+ 0, 0, 0, 0);
+
+ m_refreshRequestId++;
+}
+
+void RenderTask::onWindowSizeChanged(const QSize& newSize)
+{
+ if (m_renderSize.isValid()
+ && newSize.width() <= m_renderSize.width()
+ && newSize.height() <= m_renderSize.height())
+ return;
+
+ if (!m_renderSize.isValid())
+ {
+ m_renderSize = newSize;
+ }
+ else
+ {
+ m_renderSize.setWidth(vlc_align(newSize.width(), 128));
+ m_renderSize.setHeight(vlc_align(newSize.height(), 128));
+ }
+ m_resizeRequested = true;
+}
+
+void RenderTask::requestRefresh()
+{
+ emit requestRefreshInternal(m_refreshRequestId, {});
+}
+
+void RenderTask::onInterfaceSurfaceChanged(CompositorX11RenderClient* surface)
+{
+ m_interfaceClient = surface;
+}
+
+void RenderTask::onVideoSurfaceChanged(CompositorX11RenderClient* surface)
+{
+ m_videoClient = surface;
+}
+
+void RenderTask::onRegisterVideoWindow(unsigned int surface)
+{
+ m_videoEmbed = (surface != 0);
+}
+
+void RenderTask::onVideoPositionChanged(const QRect& position)
+{
+ if (m_videoPosition == position)
+ return;
+ m_videoPosition = position;
+ emit requestRefreshInternal(m_refreshRequestId, {});
+}
+
+void RenderTask::onInterfaceSizeChanged(const QSize& size)
+{
+ if (m_interfaceSize == size)
+ return;
+ m_interfaceSize = size;
+}
+
+void RenderTask::onVisibilityChanged(bool visible)
+{
+ m_visible = visible;
+}
+
+void RenderTask::onAcrylicChanged(bool enabled)
+{
+ m_hasAcrylic = enabled;
+}
+
+xcb_render_picture_t RenderTask::getBackTexture()
+{
+ if (m_drawingarea && !m_resizeRequested)
+ return m_drawingarea.get();
+
+ xcb_void_cookie_t voidCookie;
+ auto err = wrap_cptr<xcb_generic_error_t>(nullptr);
+ xcb_generic_error_t* rawerror = NULL;
+
+ xcb_get_window_attributes_cookie_t attrCookie = xcb_get_window_attributes(m_conn, m_wid);
+
+ auto attrReply = wrap_cptr(xcb_get_window_attributes_reply(m_conn, attrCookie, &rawerror));
+ if (rawerror)
+ {
+ msg_Warn(m_intf, " error: getting window attributes xcb_get_window_attributes_reply %u", rawerror->error_code);
+ free(rawerror);
+ return 0;
+ }
+
+ xcb_visualid_t visual = attrReply->visual;
+
+ uint8_t depth;
+ xcb_render_pictformat_t fmt;
+ findVisualFormat(m_conn, visual, &fmt, &depth);
+
+ PixmapPtr background{ m_conn};
+ background.generateId();
+ voidCookie = xcb_create_pixmap_checked(m_conn, depth, background.get(), m_wid, m_renderSize.width(), m_renderSize.height());
+ err.reset(xcb_request_check(m_conn, voidCookie));
+ if (err)
+ {
+ msg_Warn(m_intf, " error: creating xcb_create_pixmap %u", err->error_code);
+ return 0;
+ }
+
+ uint32_t attributeList[] = {background.get()};
+ voidCookie = xcb_change_window_attributes_checked(m_conn, m_wid, XCB_CW_BACK_PIXMAP, attributeList);
+ err.reset(xcb_request_check(m_conn, voidCookie));
+ if (err)
+ {
+ msg_Warn(m_intf, " error: xcb_change_window_attributes_checked %u", err->error_code);
+ return 0;
+ }
+
+ m_drawingarea.generateId();
+ xcb_render_create_picture_checked(m_conn, m_drawingarea.get(), background.get(), fmt, 0, nullptr);
+ err.reset(xcb_request_check(m_conn, voidCookie));
+ if (err)
+ {
+ msg_Warn(m_intf, " error: xcb_change_window_attributes_checked %u", err->error_code);
+ return 0;
+ }
+
+ m_resizeRequested = false;
+ return m_drawingarea.get();
+}
+
+//X11 damage listenner
+
+X11DamageObserver::X11DamageObserver(qt_intf_t* intf, xcb_connection_t* conn, QObject* parent)
+ : QObject(parent)
+ , m_intf(intf)
+ , m_conn(conn)
+{
+}
+
+bool X11DamageObserver::init()
+{
+ const xcb_query_extension_reply_t *reply = xcb_get_extension_data(m_conn, &xcb_damage_id);
+ if (!reply || !reply->present)
+ return false;
+ m_xdamageBaseEvent = reply->first_event;
+ m_connFd = xcb_get_file_descriptor(m_conn);
+ return true;
+}
+
+#if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0)
+//can't use QOverload with private signals
+template<class T>
+static auto privateOverload(void (QSocketNotifier::* s)( QSocketDescriptor,QSocketNotifier::Type, T) )
+{
+ return s;
+}
+#endif
+
+void X11DamageObserver::start()
+{
+ //listen to the x11 socket instead of blocking
+ m_socketNotifier = new QSocketNotifier(m_connFd, QSocketNotifier::Read, this);
+#if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0)
+ connect(m_socketNotifier, privateOverload(&QSocketNotifier::activated),
+ this, &X11DamageObserver::onEvent);
+#else
+ connect(m_socketNotifier, &QSocketNotifier::activated, this, &X11DamageObserver::onEvent);
+#endif
+}
+
+bool X11DamageObserver::onRegisterSurfaceDamage(unsigned int wid)
+{
+ if (m_dammage != 0)
+ {
+ xcb_damage_destroy(m_conn, m_dammage);
+ m_dammage = 0;
+ }
+ if (wid != 0)
+ {
+ m_dammage = xcb_generate_id(m_conn);
+ xcb_void_cookie_t cookie = xcb_damage_create_checked(m_conn, m_dammage, wid, XCB_DAMAGE_REPORT_LEVEL_RAW_RECTANGLES);
+ auto err = wrap_cptr(xcb_request_check(m_conn, cookie));
+ if (err)
+ {
+ msg_Warn(m_intf, "error while registering damage on surface");
+ return false;
+ }
+ }
+
+ return true;
+}
+
+void X11DamageObserver::onEvent()
+{
+ bool isRefreshNeeded = false;
+ auto event = wrap_cptr<xcb_generic_event_t>(nullptr);
+ while ((event = wrap_cptr(xcb_poll_for_event(m_conn))) != nullptr)
+ {
+ if (event->response_type == m_xdamageBaseEvent + XCB_DAMAGE_NOTIFY)
+ {
+ xcb_damage_notify_event_t* damageEvent = reinterpret_cast<xcb_damage_notify_event_t*>(event.get());
+ if (damageEvent->damage != m_dammage)
+ continue;
+ isRefreshNeeded = true;
+ }
+ }
+
+ if (isRefreshNeeded)
+ emit needRefresh();
+}
+
+//// CompositorX11RenderWindow
+
+CompositorX11RenderWindow::CompositorX11RenderWindow(qt_intf_t* p_intf, xcb_connection_t* conn, bool useCDS, QObject* parent)
+ : QObject(parent)
+ , m_intf(p_intf)
+ , m_conn(conn)
+{
+ m_rootWidget = new QMainWindow();
+ m_rootWidget->setAttribute(Qt::WA_NativeWindow);
+ m_rootWidget->setAttribute(Qt::WA_OpaquePaintEvent);
+ m_rootWidget->setAttribute(Qt::WA_NoSystemBackground);
+ m_rootWidget->setAttribute(Qt::WA_TranslucentBackground);
+
+ if (useCDS)
+ m_rootWidget->setWindowFlag(Qt::FramelessWindowHint);
+
+ m_stable = new DummyNativeWidget(m_rootWidget);
+ m_stable->winId();
+
+ m_rootWidget->setCentralWidget(m_stable);
+
+ m_rootWidget->winId();
+ m_rootWidget->show();
+
+ m_window = m_rootWidget->window()->windowHandle();
+ m_wid = m_window->winId();
+
+ m_damageObserver = new X11DamageObserver(m_intf, m_conn);
+}
+
+CompositorX11RenderWindow::~CompositorX11RenderWindow()
+{
+ stopRendering();
+
+ if (m_rootWidget)
+ {
+ m_rootWidget->removeEventFilter(this);
+ m_window->removeEventFilter(this);
+
+ if (m_rootWidget)
+ delete m_rootWidget;
+ }
+}
+
+bool CompositorX11RenderWindow::init()
+{
+ bool ret = m_damageObserver->init();
+ if (!ret)
+ {
+ msg_Warn(m_intf, "can't initialize X11 damage");
+ return false;
+ }
+
+ xcb_connection_t* qtConn = QX11Info::connection();
+
+ //check if KDE "acrylic" effect is available
+ xcb_atom_t blurBehindAtom = getInternAtom(qtConn, _KDE_NET_WM_BLUR_BEHIND_REGION_NAME);
+ if (blurBehindAtom != XCB_ATOM_NONE)
+ {
+ uint32_t val = 0;
+ xcb_change_property(qtConn, XCB_PROP_MODE_REPLACE, m_wid,
+ blurBehindAtom, XCB_ATOM_CARDINAL, 32, 1, &val);
+ m_hasAcrylic = true;
+ }
+
+ //install event filters
+ m_rootWidget->installEventFilter(this);
+ m_window->installEventFilter(this);
+
+ return true;
+}
+
+bool CompositorX11RenderWindow::startRendering()
+{
+ assert(m_interfaceWindow);
+
+ //Rendering thread
+ m_renderTask = new RenderTask(m_intf, m_conn, m_stable->effectiveWinId(), m_pictureLock);
+ m_renderThread = new QThread(this);
+
+ m_renderTask->moveToThread(m_renderThread);
+ connect(m_renderThread, &QThread::finished, m_renderTask, &QObject::deleteLater);
+
+ connect(m_interfaceWindow, &CompositorX11UISurface::afterRendering, m_renderTask, &RenderTask::requestRefresh);
+ connect(m_interfaceWindow, &CompositorX11UISurface::sizeChanged, m_renderTask, &RenderTask::onInterfaceSizeChanged);
+
+ connect(this, &CompositorX11RenderWindow::windowSizeChanged, m_renderTask, &RenderTask::onWindowSizeChanged);
+ connect(this, &CompositorX11RenderWindow::requestUIRefresh, m_renderTask, &RenderTask::requestRefresh);
+ connect(this, &CompositorX11RenderWindow::visiblityChanged, m_renderTask, &RenderTask::onVisibilityChanged);
+ connect(this, &CompositorX11RenderWindow::videoPositionChanged, m_renderTask, &RenderTask::onVideoPositionChanged);
+ connect(this, &CompositorX11RenderWindow::registerVideoWindow, m_renderTask, &RenderTask::onRegisterVideoWindow);
+ connect(this, &CompositorX11RenderWindow::videoSurfaceChanged, m_renderTask, &RenderTask::onVideoSurfaceChanged, Qt::BlockingQueuedConnection);
+
+ //pass initial values
+ m_renderTask->onInterfaceSurfaceChanged(m_interfaceClient.get());
+ m_renderTask->onVideoSurfaceChanged(m_videoClient.get());
+ m_renderTask->onWindowSizeChanged(m_rootWidget->size() * m_rootWidget->devicePixelRatioF());
+ m_renderTask->onAcrylicChanged(m_hasAcrylic);
+
+ //use the same thread as the rendering thread, neither tasks are blocking.
+ m_damageObserver->moveToThread(m_renderThread);
+ connect(m_renderThread, &QThread::started, m_damageObserver, &X11DamageObserver::start);
+ connect(this, &CompositorX11RenderWindow::registerVideoWindow, m_damageObserver, &X11DamageObserver::onRegisterSurfaceDamage);
+ connect(m_damageObserver, &X11DamageObserver::needRefresh, m_renderTask, &RenderTask::requestRefresh);
+
+ //start the rendering thread
+ m_renderThread->start();
+
+ return true;
+}
+
+void CompositorX11RenderWindow::stopRendering()
+{
+ if (m_renderThread)
+ {
+ m_renderThread->quit();
+ m_renderThread->wait();
+ delete m_renderThread;
+ m_renderThread = nullptr;
+ }
+ m_videoClient.reset();
+ m_videoWindow = nullptr;
+ m_interfaceClient.reset();
+ m_interfaceWindow = nullptr;
+}
+
+void CompositorX11RenderWindow::resetClientPixmaps()
+{
+ QMutexLocker lock(&m_pictureLock);
+ xcb_flush(QX11Info::connection());
+ //reset and recreate the clients surfaces
+ if (m_interfaceClient)
+ {
+ m_interfaceClient->resetPixmap();
+ m_interfaceClient->getPicture();
+ }
+ if (m_videoClient)
+ {
+ m_videoClient->resetPixmap();
+ m_interfaceClient->getPicture();
+ }
+}
+
+bool CompositorX11RenderWindow::eventFilter(QObject* obj, QEvent* event)
+{
+ bool ret = false;
+ bool needRefresh = false;
+
+ //event on the window
+ if (obj == m_window)
+ {
+ //window may get resized without the widget knowing about it
+ if (event->type() == QEvent::Resize)
+ {
+ auto resizeEvent = static_cast<QResizeEvent*>(event);
+ if (m_interfaceWindow)
+ m_interfaceWindow->handleWindowEvent(event);
+ resetClientPixmaps();
+ emit windowSizeChanged(resizeEvent->size() * m_rootWidget->devicePixelRatioF());
+ needRefresh = true;
+ }
+ }
+ else
+ {
+ assert(obj == m_rootWidget);
+ if (event->type() == QEvent::Resize)
+ return false;
+
+ if (m_interfaceWindow)
+ ret = m_interfaceWindow->handleWindowEvent(event);
+
+ switch (event->type())
+ {
+ case QEvent::Expose:
+ {
+ resetClientPixmaps();
+ needRefresh = true;
+ break;
+ }
+ case QEvent::Show:
+ {
+ resetClientPixmaps();
+ needRefresh = true;
+ emit visiblityChanged(true);
+ break;
+ }
+ case QEvent::Hide:
+ emit visiblityChanged(false);
+ break;
+ default:
+ break;
+ }
+ }
+
+ if (needRefresh)
+ emit requestUIRefresh();
+
+ return ret;
+}
+
+void CompositorX11RenderWindow::setVideoPosition(const QPoint& position)
+{
+ if (m_videoWindow && m_videoClient)
+ {
+ m_videoPosition.moveTopLeft(position);
+ emit videoPositionChanged(m_videoPosition);
+ }
+}
+
+void CompositorX11RenderWindow::setVideoSize(const QSize& size)
+{
+ if (m_videoWindow && m_videoClient)
+ {
+ m_videoWindow->resize(size);
+ {
+ QMutexLocker lock(&m_pictureLock);
+ xcb_flush(QX11Info::connection());
+ //reset and recreate the clients surfaces
+ m_videoClient->resetPixmap();
+ m_videoClient->getPicture();
+ }
+ m_videoPosition.setSize(size * m_rootWidget->devicePixelRatioF());
+ emit videoPositionChanged(m_videoPosition);
+ }
+}
+
+void CompositorX11RenderWindow::setVideoWindow( QWindow* window)
+{
+ window->setParent(m_window);
+ //ensure Qt x11 pending operation have been forwarded to the server
+ xcb_flush(QX11Info::connection());
+ m_videoClient = std::make_unique<CompositorX11RenderClient>(m_intf, m_conn, window);
+ m_videoPosition = QRect(0,0,0,0);
+ m_videoWindow = window;
+ emit videoSurfaceChanged(m_videoClient.get());
+}
+
+void CompositorX11RenderWindow::enableVideoWindow()
+{
+ emit registerVideoWindow(m_videoWindow->winId());
+}
+
+void CompositorX11RenderWindow::disableVideoWindow()
+{
+ emit registerVideoWindow(0);
+}
+
+void CompositorX11RenderWindow::setInterfaceWindow(CompositorX11UISurface* window)
+{
+ assert(m_window);
+ window->setParent(m_window);
+ //ensure Qt x11 pending operation have been forwarded to the server
+ xcb_flush(QX11Info::connection());
+ m_interfaceClient = std::make_unique<CompositorX11RenderClient>(m_intf, m_conn, window);
+ m_interfaceWindow = window;
+
+}
=====================================
modules/gui/qt/maininterface/compositor_x11_renderwindow.hpp
=====================================
@@ -0,0 +1,212 @@
+/*****************************************************************************
+ * Copyright (C) 2021 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 COMPOSITOR_X11_RENDERWINDOW_HPP
+#define COMPOSITOR_X11_RENDERWINDOW_HPP
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <memory>
+#include <set>
+
+#include <QObject>
+#include <QWidget>
+#include <QMutex>
+
+#include <xcb/xcb.h>
+#include <xcb/render.h>
+#include <xcb/damage.h>
+
+#include <vlc_common.h>
+#include <vlc_interface.h>
+
+#include "qt.hpp"
+
+#include "compositor_x11_utils.hpp"
+
+class QMainWindow;
+class QSocketNotifier;
+
+namespace vlc {
+
+class CompositorX11RenderClient;
+class CompositorX11UISurface;
+
+/**
+ * @brief The RenderTask class does the actual rendering into the window
+ * it grab the offscreen surface from the interface and the video and blends
+ * them into the output surface. It will refresh the composition when either the
+ * interface refresh or the video surface is updated
+ */
+class RenderTask : public QObject
+{
+ Q_OBJECT
+public:
+ explicit RenderTask(qt_intf_t* intf,
+ xcb_connection_t* conn,
+ xcb_drawable_t wid,
+ QMutex& pictureLock,
+ QObject* parent = nullptr);
+ ~RenderTask();
+
+public slots:
+ void render(unsigned int requestId);
+ void onWindowSizeChanged(const QSize& newSize);
+
+ void requestRefresh();
+
+ void onInterfaceSurfaceChanged(CompositorX11RenderClient*);
+ void onVideoSurfaceChanged(CompositorX11RenderClient*);
+ void onRegisterVideoWindow(unsigned int surface);
+
+ void onVideoPositionChanged(const QRect& position);
+ void onInterfaceSizeChanged(const QSize& size);
+
+ void onVisibilityChanged(bool visible);
+
+ void onAcrylicChanged(bool enabled);
+
+signals:
+ void requestRefreshInternal(unsigned int requestId, QPrivateSignal priv);
+
+private:
+ xcb_render_picture_t getBackTexture();
+
+ qt_intf_t* m_intf = nullptr;
+ xcb_connection_t* m_conn = nullptr;
+
+ QMutex& m_pictureLock;
+
+ PicturePtr m_drawingarea;
+ QSize m_renderSize;
+ bool m_resizeRequested = true;
+
+ xcb_drawable_t m_wid = 0;
+ unsigned int m_refreshRequestId = 0;
+ QRect m_videoPosition;
+ QSize m_interfaceSize;
+
+ CompositorX11RenderClient* m_videoClient = nullptr;
+ bool m_videoEmbed = false;
+ CompositorX11RenderClient* m_interfaceClient = nullptr;
+
+ bool m_hasAcrylic = false;
+ bool m_visible = true;
+};
+
+/**
+ * @brief The X11DamageObserver class allows to register and listen
+ * damages on a X11 surface. This is performed on a separate X11 conection
+ * from Qt, as we want to be able to be able to continue refreshing the composition
+ * when Qt main thread is stalled.
+ */
+class X11DamageObserver : public QObject
+{
+ Q_OBJECT
+public:
+ X11DamageObserver(qt_intf_t* intf, xcb_connection_t* conn, QObject* parent = nullptr);
+
+ bool init();
+ void start();
+
+public slots:
+ bool onRegisterSurfaceDamage(unsigned int surface);
+
+signals:
+ void needRefresh();
+
+private:
+ void onEvent();
+
+public:
+ qt_intf_t* m_intf = nullptr;
+
+ xcb_connection_t* m_conn = nullptr;
+ int m_connFd = 0;
+ xcb_damage_damage_t m_dammage = 0;
+ uint8_t m_xdamageBaseEvent = 0;
+
+ QSocketNotifier* m_socketNotifier = nullptr;
+};
+
+class CompositorX11RenderWindow : public QObject
+{
+ Q_OBJECT
+public:
+ explicit CompositorX11RenderWindow(qt_intf_t* p_intf, xcb_connection_t* conn, bool useCDS, QObject* parent = nullptr);
+ ~CompositorX11RenderWindow();
+
+ bool init();
+
+ bool startRendering();
+ void stopRendering();
+
+ bool eventFilter(QObject *, QEvent *event) override;
+
+ void setVideoPosition(const QPoint& position);
+ void setVideoSize(const QSize& size);
+
+ inline QWindow* getWindow() const { return m_window; }
+
+ inline bool hasAcrylic() const { return m_hasAcrylic; }
+
+ void setVideoWindow(QWindow* window);
+ void setInterfaceWindow(CompositorX11UISurface* window);
+
+ void enableVideoWindow();
+ void disableVideoWindow();
+
+signals:
+ void windowSizeChanged(const QSize& newSize);
+ void requestUIRefresh();
+ void videoPositionChanged(const QRect& position);
+ void videoSurfaceChanged(CompositorX11RenderClient*);
+ void visiblityChanged(bool visible);
+ void registerVideoWindow(unsigned int xid);
+
+private:
+ void resetClientPixmaps();
+
+ qt_intf_t* m_intf = nullptr;
+ xcb_connection_t* m_conn = nullptr;
+
+ QMainWindow* m_rootWidget = nullptr;
+ QWidget* m_stable = nullptr;
+
+ QThread* m_renderThread = nullptr;
+ RenderTask* m_renderTask = nullptr;
+ X11DamageObserver* m_damageObserver = nullptr;
+ QMutex m_pictureLock;
+
+ QWindow* m_window = nullptr;
+ xcb_window_t m_wid = 0;
+
+ bool m_hasAcrylic = false;
+
+ QWindow* m_videoWindow = nullptr;
+ std::unique_ptr<CompositorX11RenderClient> m_videoClient;
+ QRect m_videoPosition;
+
+ CompositorX11UISurface* m_interfaceWindow = nullptr;
+ std::unique_ptr<CompositorX11RenderClient> m_interfaceClient;
+};
+
+}
+
+#endif // RENDERWINDOW_HPP
=====================================
modules/gui/qt/maininterface/compositor_x11_uisurface.cpp
=====================================
@@ -0,0 +1,305 @@
+/*****************************************************************************
+ * Copyright (C) 2021 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 <QOpenGLContext>
+#include <QPainter>
+#include <QOpenGLPaintDevice>
+#include <QQmlEngine>
+#include <QQuickWindow>
+#include <QQuickItem>
+
+#include "compositor_x11_uisurface.hpp"
+
+using namespace vlc;
+
+CompositorX11UISurface::CompositorX11UISurface(QWindow* window, QScreen* screen)
+ : QWindow(screen)
+{
+ setSurfaceType(QWindow::OpenGLSurface);
+
+ QSurfaceFormat format;
+ // Qt Quick may need a depth and stencil buffer. Always make sure these are available.
+ format.setDepthBufferSize(8);
+ format.setStencilBufferSize(8);
+ format.setAlphaBufferSize(8);
+
+ setFormat(format);
+
+ m_context = new QOpenGLContext();
+ m_context->setScreen(this->screen());
+ m_context->setFormat(format);
+ m_context->create();
+
+ m_uiRenderControl = new CompositorX11RenderControl(window);
+
+ m_uiWindow = new QQuickWindow(m_uiRenderControl);
+ m_uiWindow->setDefaultAlphaBuffer(true);
+ m_uiWindow->setFormat(format);
+ m_uiWindow->setColor(Qt::transparent);
+ m_uiWindow->setClearBeforeRendering(true);
+
+ m_qmlEngine = new QQmlEngine();
+ if (!m_qmlEngine->incubationController())
+ m_qmlEngine->setIncubationController(m_uiWindow->incubationController());
+
+ connect(m_uiWindow, &QQuickWindow::sceneGraphInitialized, this, &CompositorX11UISurface::createFbo);
+ connect(m_uiWindow, &QQuickWindow::sceneGraphInvalidated, this, &CompositorX11UISurface::destroyFbo);
+ connect(m_uiWindow, &QQuickWindow::beforeRendering, this, &CompositorX11UISurface::beforeRendering);
+ connect(m_uiWindow, &QQuickWindow::afterRendering, this, &CompositorX11UISurface::afterRendering);
+
+ connect(m_uiRenderControl, &QQuickRenderControl::renderRequested, this, &CompositorX11UISurface::requestUpdate);
+ connect(m_uiRenderControl, &QQuickRenderControl::sceneChanged, this, &CompositorX11UISurface::requestUpdate);
+}
+
+CompositorX11UISurface::~CompositorX11UISurface()
+{
+ if (m_rootItem)
+ delete m_rootItem;
+ if (m_uiWindow)
+ delete m_uiWindow;
+ if (m_uiRenderControl)
+ delete m_uiRenderControl;
+ if (m_context)
+ delete m_context;
+ if (m_qmlEngine)
+ delete m_qmlEngine;
+}
+
+
+void CompositorX11UISurface::setContent(QQmlComponent*, QQuickItem* rootItem)
+{
+ m_rootItem = rootItem;
+
+ QQuickItem* contentItem = m_uiWindow->contentItem();
+
+ m_rootItem->setParentItem(contentItem);
+
+ updateSizes();
+}
+
+void CompositorX11UISurface::createFbo()
+{
+ //write to the immediate context
+ QSize fboSize = size() * devicePixelRatio();
+ m_uiWindow->setRenderTarget(0, fboSize);
+ emit sizeChanged(fboSize);
+}
+
+void CompositorX11UISurface::destroyFbo()
+{
+}
+
+void CompositorX11UISurface::render()
+{
+ if (!isExposed())
+ return;
+
+ m_context->makeCurrent(this);
+
+ m_uiRenderControl->polishItems();
+ m_uiRenderControl->sync();
+ m_uiRenderControl->render();
+
+ m_uiWindow->resetOpenGLState();
+
+ m_context->functions()->glFlush();
+ m_context->swapBuffers(this);
+}
+
+void CompositorX11UISurface::updateSizes()
+{
+ qreal dpr = devicePixelRatio();
+ QSize windowSize = size();
+
+ m_onscreenSize = windowSize * dpr;
+
+ // Behave like SizeRootObjectToView.
+ m_rootItem->setSize(windowSize);
+ m_uiWindow->resize(windowSize);
+}
+
+bool CompositorX11UISurface::event(QEvent *event)
+{
+ switch (event->type())
+ {
+ case QEvent::UpdateRequest:
+ render();
+ return true;
+ default:
+ return QWindow::event(event);
+ }
+}
+
+
+static void remapInputMethodQueryEvent(QObject *object, QInputMethodQueryEvent *e)
+{
+ auto item = qobject_cast<QQuickItem *>(object);
+ if (!item)
+ return;
+ // Remap all QRectF values.
+ for (auto query : {Qt::ImCursorRectangle, Qt::ImAnchorRectangle, Qt::ImInputItemClipRectangle})
+ {
+ if (e->queries() & query)
+ {
+ auto value = e->value(query);
+ if (value.canConvert<QRectF>())
+ e->setValue(query, item->mapRectToScene(value.toRectF()));
+ }
+ }
+ // Remap all QPointF values.
+ if (e->queries() & Qt::ImCursorPosition)
+ {
+ auto value = e->value(Qt::ImCursorPosition);
+ if (value.canConvert<QPointF>())
+ e->setValue(Qt::ImCursorPosition, item->mapToScene(value.toPointF()));
+ }
+}
+
+bool CompositorX11UISurface::handleWindowEvent(QEvent *event)
+{
+ switch (event->type())
+ {
+
+ case QEvent::Move:
+ {
+ QPoint windowPosition = mapToGlobal(QPoint(0,0));
+ if (m_uiWindow->position() != windowPosition)
+ m_uiWindow->setPosition(windowPosition);
+ break;
+ }
+
+ case QEvent::Resize:
+ {
+ QResizeEvent* resizeEvent = static_cast<QResizeEvent*>(event);
+ m_uiWindow->resize(resizeEvent->size());
+ resize( resizeEvent->size() );
+ resizeFbo();
+ break;
+ }
+
+ case QEvent::WindowActivate:
+ case QEvent::WindowDeactivate:
+ case QEvent::Leave:
+ {
+ return QCoreApplication::sendEvent(m_uiWindow, event);
+ }
+
+ case QEvent::Enter:
+ {
+ QEnterEvent *enterEvent = static_cast<QEnterEvent *>(event);
+ QEnterEvent mappedEvent(enterEvent->localPos(), enterEvent->windowPos(),
+ enterEvent->screenPos());
+ bool ret = QCoreApplication::sendEvent(m_uiWindow, &mappedEvent);
+ event->setAccepted(mappedEvent.isAccepted());
+ return ret;
+ }
+
+ case QEvent::InputMethod:
+ return QCoreApplication::sendEvent(m_uiWindow->focusObject(), event);
+
+ case QEvent::InputMethodQuery:
+ {
+ bool eventResult = QCoreApplication::sendEvent(m_uiWindow->focusObject(), event);
+ // The result in focusObject are based on offscreenWindow. But
+ // the inputMethodTransform won't get updated because the focus
+ // is on QQuickWidget. We need to remap the value based on the
+ // widget.
+ remapInputMethodQueryEvent(m_uiWindow->focusObject(), static_cast<QInputMethodQueryEvent *>(event));
+ return eventResult;
+ }
+
+ case QEvent::MouseButtonPress:
+ case QEvent::MouseButtonRelease:
+ case QEvent::MouseButtonDblClick:
+ case QEvent::MouseMove:
+ {
+ QMouseEvent* mouseEvent = static_cast<QMouseEvent*>(event);
+ QMouseEvent mappedEvent(mouseEvent->type(), mouseEvent->localPos(),
+ mouseEvent->localPos(), mouseEvent->screenPos(),
+ mouseEvent->button(), mouseEvent->buttons(),
+ mouseEvent->modifiers(), mouseEvent->source());
+ QCoreApplication::sendEvent(m_uiWindow, &mappedEvent);
+ return true;
+ }
+
+ case QEvent::Wheel:
+ case QEvent::HoverEnter:
+ case QEvent::HoverLeave:
+ case QEvent::HoverMove:
+ case QEvent::DragEnter:
+ case QEvent::DragMove:
+ case QEvent::DragLeave:
+ case QEvent::DragResponse:
+ case QEvent::Drop:
+ return QCoreApplication::sendEvent(m_uiWindow, event);
+
+ case QEvent::KeyPress:
+ case QEvent::KeyRelease:
+ {
+ return QCoreApplication::sendEvent(m_uiWindow, event);
+ }
+
+ case QEvent::ScreenChangeInternal:
+ m_uiWindow->setScreen(screen());
+ break;
+
+ default:
+ break;
+ }
+ return false;
+}
+
+void CompositorX11UISurface::resizeFbo()
+{
+ if (m_rootItem && m_context->makeCurrent(this))
+ {
+ createFbo();
+ m_context->doneCurrent();
+ updateSizes();
+ }
+}
+
+void CompositorX11UISurface::resizeEvent(QResizeEvent *)
+{
+ if (m_onscreenSize != size() * devicePixelRatio())
+ resizeFbo();
+}
+
+void CompositorX11UISurface::exposeEvent(QExposeEvent *)
+{
+ if (isExposed())
+ {
+ m_context->makeCurrent(this);
+ m_uiRenderControl->initialize(m_context);
+ m_context->doneCurrent();
+
+ requestUpdate();
+ }
+}
+
+void CompositorX11UISurface::handleScreenChange()
+{
+ m_uiWindow->setGeometry(0, 0, width(), height());
+ requestUpdate();
+}
+
+QWindow* CompositorX11RenderControl::renderWindow(QPoint* offset)
+{
+ if (offset)
+ *offset = QPoint(0, 0);
+ return m_window;
+}
=====================================
modules/gui/qt/maininterface/compositor_x11_uisurface.hpp
=====================================
@@ -0,0 +1,95 @@
+/*****************************************************************************
+ * Copyright (C) 2021 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 COMPOSITOR_X11_UISURFACE_HPP
+#define COMPOSITOR_X11_UISURFACE_HPP
+
+#include <QObject>
+#include <QWindow>
+#include <QOpenGLFunctions>
+#include <QQuickRenderControl>
+#include "compositor.hpp"
+
+class QOpenGLPaintDevice;
+class QQuickWindow;
+class QQmlEngine;
+class QQmlComponent;
+class QQuickItem;
+
+namespace vlc {
+
+class CompositorX11RenderControl : public QQuickRenderControl {
+ Q_OBJECT
+public:
+ CompositorX11RenderControl(QWindow* window, QObject* parent = nullptr)
+ : QQuickRenderControl(parent)
+ , m_window(window)
+ {}
+
+ QWindow *renderWindow(QPoint * offset) override;
+
+private:
+ QWindow* m_window = nullptr;
+};
+
+class CompositorX11UISurface : public QWindow , public CompositorVideo::QmlUISurface
+{
+ Q_OBJECT
+public:
+ explicit CompositorX11UISurface(QWindow* window, QScreen *screen = nullptr);
+ ~CompositorX11UISurface();
+
+ virtual void render();
+
+ bool handleWindowEvent(QEvent *event);
+
+ //QmlUISurface API
+ void setContent(QQmlComponent*, QQuickItem* rootItem) override;
+ QQmlEngine* engine() const override { return m_qmlEngine; }
+
+signals:
+ void beforeRendering();
+ void afterRendering();
+ void sizeChanged(const QSize& size);
+
+protected:
+ bool event(QEvent *event) override;
+
+ void resizeEvent(QResizeEvent *) override;
+ void exposeEvent(QExposeEvent *) override;
+ void handleScreenChange();
+
+
+ void updateSizes();
+
+ void createFbo();
+ void destroyFbo();
+ void resizeFbo();
+
+private:
+ QQuickItem* m_rootItem = nullptr;
+ QOpenGLContext *m_context = nullptr;
+ QQuickWindow* m_uiWindow = nullptr;
+ QQmlEngine* m_qmlEngine = nullptr;
+ CompositorX11RenderControl* m_uiRenderControl = nullptr;
+
+ QSize m_onscreenSize;
+};
+
+}
+
+#endif // COMPOSITOR_X11_UISURFACE_HPP
=====================================
modules/gui/qt/maininterface/compositor_x11_utils.cpp
=====================================
@@ -0,0 +1,109 @@
+/*****************************************************************************
+ * Copyright (C) 2021 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 <vlc_cxx_helpers.hpp>
+#include "compositor_x11_utils.hpp"
+#include <cstring>
+#include <QDebug>
+
+namespace vlc {
+
+
+DummyNativeWidget::DummyNativeWidget(QWidget* parent, Qt::WindowFlags f)
+ : QWidget(parent, f)
+{
+ setAttribute(Qt::WA_NativeWindow, true);
+ setAttribute(Qt::WA_OpaquePaintEvent, true);
+ setAttribute(Qt::WA_PaintOnScreen, true);
+}
+
+DummyNativeWidget::~DummyNativeWidget()
+{
+
+}
+
+QPaintEngine* DummyNativeWidget::paintEngine() const
+{
+ return nullptr;
+}
+
+bool queryExtension(xcb_connection_t* conn, const char* name, uint8_t* first_event_out, uint8_t* first_error_out)
+{
+ xcb_query_extension_cookie_t cookie = xcb_query_extension(conn, (uint16_t)strlen(name), name);
+ xcb_generic_error_t* error = NULL;
+ auto reply = wrap_cptr(xcb_query_extension_reply(conn, cookie, &error));
+ auto errorPtr = wrap_cptr(error);
+ if (errorPtr || !reply)
+ return false;
+ if (!reply->present)
+ return false;
+
+ if (first_event_out)
+ *first_event_out = reply->first_event;
+ if (first_error_out)
+ *first_error_out = reply->first_error;
+ return true;
+}
+
+bool findVisualFormat(xcb_connection_t* conn, xcb_visualid_t visual,
+ xcb_render_pictformat_t* formatOut = nullptr,
+ uint8_t* depthOut = nullptr
+ )
+{
+ xcb_render_query_pict_formats_cookie_t pictFormatC = xcb_render_query_pict_formats(conn);
+ auto pictFormatR = wrap_cptr(xcb_render_query_pict_formats_reply(conn, pictFormatC, nullptr));
+
+ if (!pictFormatR)
+ return false;
+
+ auto screenIt = xcb_render_query_pict_formats_screens_iterator(pictFormatR.get());
+ for (; screenIt.rem > 0; xcb_render_pictscreen_next(&screenIt))
+ {
+ xcb_render_pictscreen_t* pictScreen = screenIt.data;
+ auto depthIt = xcb_render_pictscreen_depths_iterator(pictScreen);
+ for (; depthIt.rem > 0; xcb_render_pictdepth_next(&depthIt))
+ {
+ xcb_render_pictdepth_t* pictDepth = depthIt.data;
+ auto visualIt = xcb_render_pictdepth_visuals_iterator(pictDepth);
+ for (; visualIt.rem > 0; xcb_render_pictvisual_next(&visualIt))
+ {
+ xcb_render_pictvisual_t* pictVisual = visualIt.data;
+ if (pictVisual->visual == visual)
+ {
+ if (formatOut)
+ *formatOut = pictVisual->format;
+ if (depthOut)
+ *depthOut = pictDepth->depth;
+ return true;
+ }
+ }
+ }
+ }
+
+ return false;
+}
+
+xcb_atom_t getInternAtom(xcb_connection_t* conn, const char* atomName)
+{
+ xcb_intern_atom_cookie_t atomCookie = xcb_intern_atom(conn, 1, strlen(atomName), atomName);
+ auto atomReply = wrap_cptr(xcb_intern_atom_reply(conn, atomCookie, nullptr));
+ if (!atomReply)
+ return 0;
+ return atomReply->atom;
+}
+
+}
=====================================
modules/gui/qt/maininterface/compositor_x11_utils.hpp
=====================================
@@ -0,0 +1,114 @@
+/*****************************************************************************
+ * Copyright (C) 2021 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 COMPOSITOR_X11_UTILS_HPP
+#define COMPOSITOR_X11_UTILS_HPP
+#include <memory>
+
+#include <QWidget>
+
+#include <xcb/xcb.h>
+#include <xcb/render.h>
+#include <xcb/composite.h>
+
+
+namespace vlc {
+
+class DummyNativeWidget : public QWidget
+{
+ Q_OBJECT
+public:
+ DummyNativeWidget(QWidget* parent = nullptr, Qt::WindowFlags f = Qt::WindowFlags());
+ virtual ~DummyNativeWidget();
+
+ //override paintEnging to suppress warning
+ QPaintEngine* paintEngine() const override;
+};
+
+template<typename T, typename R, R RELEASE>
+class X11Resource {
+public:
+ X11Resource() = delete;
+
+ explicit X11Resource(xcb_connection_t* conn, T _xid = 0)
+ : m_conn(conn)
+ , xid(_xid)
+ {}
+
+ X11Resource(const X11Resource &other) = delete;
+ X11Resource(X11Resource &&other)
+ : m_conn (other.m_conn)
+ , xid (other.xid)
+ {
+ other.xid = 0;
+ }
+
+ ~X11Resource()
+ {
+ if (!m_conn)
+ return;
+ if (xid)
+ RELEASE(m_conn, xid);
+ }
+
+ void generateId() {
+ reset(xcb_generate_id(m_conn));
+ }
+
+ X11Resource &operator=(const X11Resource &other) = delete;
+ X11Resource &operator=(X11Resource &&other) noexcept
+ {
+ reset(other.xid);
+ other.xid = 0;
+ return *this;
+ }
+ X11Resource &operator=(T value) noexcept
+ {
+ reset(value);
+ return *this;
+ }
+
+ void reset(T newval = 0) {
+ if (xid)
+ RELEASE(m_conn, xid);
+ xid = newval;
+ }
+
+ operator bool() noexcept
+ {
+ return xid != 0;
+ }
+
+ T get() const { return xid; }
+
+ xcb_connection_t* m_conn;
+ T xid = 0;
+};
+
+using PixmapPtr = X11Resource<xcb_pixmap_t, decltype(&xcb_free_pixmap), xcb_free_pixmap>;
+using PicturePtr = X11Resource<xcb_render_picture_t, decltype(&xcb_render_free_picture), xcb_render_free_picture>;
+using WindowPtr = X11Resource<xcb_window_t, decltype(&xcb_destroy_window), xcb_destroy_window>;
+
+bool queryExtension(xcb_connection_t* conn, const char* name, uint8_t* first_event_out, uint8_t* first_error_out);
+
+bool findVisualFormat(xcb_connection_t* conn, xcb_visualid_t visual, xcb_render_pictformat_t* fmtOut, uint8_t* depthOut);
+
+xcb_atom_t getInternAtom(xcb_connection_t* conn, const char* atomName);
+}
+
+
+#endif /* COMPOSITOR_X11_UTILS_HPP */
=====================================
modules/gui/qt/qt.cpp
=====================================
@@ -266,6 +266,9 @@ static const char *const compositor_vlc[] = {
"dcomp"
#endif
"win7",
+#endif
+#ifdef QT5_HAS_X11_COMPOSITOR
+ "x11",
#endif
"dummy"
};
@@ -276,6 +279,9 @@ static const char *const compositor_user[] = {
"Direct Composition",
#endif
"Windows 7",
+#endif
+#ifdef QT5_HAS_X11_COMPOSITOR
+ N_("X11"),
#endif
N_("Dummy"),
};
@@ -772,7 +778,11 @@ static void *Thread( void *obj )
break;
ret = p_intf->p_compositor->makeMainInterface(p_intf->p_mi);
if (!ret)
+ {
p_intf->p_compositor->destroyMainInterface();
+ delete p_intf->p_compositor;
+ p_intf->p_compositor = nullptr;
+ }
} while(!ret);
if (!ret)
View it on GitLab: https://code.videolan.org/videolan/vlc/-/compare/d5102812dab5e94b1c8da2b71faf8db7601063d3...96166bad04b04bc676aefb1a6909bc31558f93e8
--
View it on GitLab: https://code.videolan.org/videolan/vlc/-/compare/d5102812dab5e94b1c8da2b71faf8db7601063d3...96166bad04b04bc676aefb1a6909bc31558f93e8
You're receiving this email because of your account on code.videolan.org.
More information about the vlc-commits
mailing list