Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found

Target

Select target project
  • videolan/vlc
  • chouquette/vlc
  • bakiewicz.marek122/vlc
  • devnexen/vlc
  • rohanrajpal/vlc
  • blurrrb/vlc
  • gsoc/gsoc2019/darkapex/vlc
  • b1ue/vlc
  • fkuehne/vlc
  • magsoft/vlc
  • chub/vlc
  • cramiro9/vlc
  • robUx4/vlc
  • rom1v/vlc
  • akshayaky/vlc
  • tmk907/vlc
  • akymaster/vlc
  • govind.sharma/vlc
  • psilokos/vlc
  • xjbeta/vlc
  • jahan/vlc
  • 1480c1/vlc
  • amanchande/vlc
  • aaqib/vlc
  • rist/vlc
  • apol/vlc
  • mindfreeze/vlc
  • alexandre-janniaux/vlc
  • sandsmark/vlc
  • jagannatharjun/vlc
  • gsoc/gsoc2020/matiaslgonzalez/vlc
  • gsoc/gsoc2020/jagannatharjun/vlc
  • mstorsjo/vlc
  • gsoc/gsoc2020/vedenta/vlc
  • gsoc/gsoc2020/arnav-ishaan/vlc
  • gsoc/gsoc2020/andreduong/vlc
  • fuzun/vlc
  • gsoc/gsoc2020/vatsin/vlc
  • gsoc/gsoc2020/sagid/vlc
  • yaron/vlc
  • Phoenix/vlc
  • Garf/vlc
  • ePiratWorkarounds/vlc
  • tguillem/vlc
  • jnqnfe/vlc
  • mdc/vlc
  • Vedaa/vlc
  • rasa/vlc
  • quink/vlc
  • yealo/vlc
  • aleksey_ak/vlc
  • ePirat/vlc
  • ilya.yanok/vlc
  • asenat/vlc
  • m/vlc
  • bunjee/vlc
  • BLumia/vlc
  • sagudev/vlc
  • hamedmonji30/vlc
  • nullgemm/vlc
  • DivyamAhuja/vlc
  • thesamesam/vlc
  • dag7/vlc
  • snehil101/vlc
  • haasn/vlc
  • jbk/vlc
  • ValZapod/vlc
  • mfkl/vlc
  • WangChuan/vlc
  • core1024/vlc
  • GhostVaibhav/vlc
  • dfuhrmann/vlc
  • davide.prade/vlc
  • tmatth/vlc
  • Courmisch/vlc
  • zouya/vlc
  • hpi/vlc
  • EwoutH/vlc
  • aleung27/vlc
  • hengwu0/vlc
  • saladin/vlc
  • ashuio/vlc
  • richselwood/vlc
  • verma16Ayush/vlc
  • chemicalflash/vlc
  • PoignardAzur/vlc
  • huangjieNT/vlc
  • Blake-Haydon/vlc
  • AnuthaDev/vlc
  • gsoc/gsoc2021/mpd/vlc
  • nicolas_lequec/vlc
  • sambassaly/vlc
  • thresh/vlc
  • bonniegong/vlc
  • myaashish/vlc
  • stavros.vagionitis/vlc
  • ileoo/vlc
  • louis-santucci/vlc
  • cchristiansen/vlc
  • sabyasachi07/vlc
  • AbduAmeen/vlc
  • ashishb0410/vlc
  • urbanhusky/vlc
  • davidepietrasanta/vlc
  • riksleutelstad/vlc
  • jeremyVignelles/vlc
  • komh/vlc
  • iamjithinjohn/vlc
  • JohannesKauffmann/vlc2
  • kunglao/vlc
  • natzberg/vlc
  • jill/vlc
  • cwendling/vlc
  • adufou/vlc
  • ErwanAirone/vlc
  • HasinduDilshan10/vlc
  • vagrantc/vlc
  • rafiv/macos-bigsur-icon
  • Aymeriic/vlc
  • saranshg20/vlc
  • metzlove24/vlc
  • linkfanel/vlc
  • Ds886/vlc
  • metehan-arslan/vlc
  • Skantes/vlc
  • kgsandundananjaya96/vlc
  • mitchcapper/vlc
  • advaitgupta/vlc
  • StefanBruens/vlc
  • ratajs/vlc
  • T.M.F.B.3761/vlc
  • m222059/vlc
  • casemerrick/vlc
  • joshuaword2alt/vlc
  • sjwaddy/vlc
  • dima/vlc
  • Ybalrid/vlc
  • umxprime/vlc
  • eschmidt/vlc
  • vannieuwenhuysenmichelle/vlc
  • badcf00d/vlc
  • wesinator/vlc
  • louis/vlc
  • xqq/vlc
  • EmperorYP7/vlc
  • NicoLiam/vlc
  • loveleen/vlc
  • rofferom/vlc
  • rbultje/vlc
  • TheUnamed/vlc
  • pratiksharma341/vlc
  • Saurab17/vlc
  • purist.coder/vlc
  • Shuicheng/vlc
  • mdrrubel292/vlc
  • silverbleu00/vlc
  • metif12/vlc
  • asher-m/vlc
  • jeffk/vlc
  • Brandonbr1/vlc
  • beautyyuyanli/vlc
  • rego21/vlc
  • muyangren907/vlc
  • collectionbylawrencejason/vlc
  • evelez/vlc
  • GSMgeeth/vlc
  • Oneric/vlc
  • TJ5/vlc
  • XuanTung95/vlc
  • darrenjenny21/vlc
  • Trenly/vlc
  • RockyTDR/vlc
  • mjakubowski/vlc
  • caprica/vlc
  • ForteFrankie/vlc
  • seannamiller19/vlc
  • junlon2006/vlc
  • kiwiren6666/vlc
  • iuseiphonexs/vlc
  • fenngtun/vlc
  • Rajdutt999/vlc
  • typx/vlc
  • leon.vitanos/vlc
  • robertogarci0938/vlc
  • gsoc/gsoc2022/luc65r/vlc-mpd
  • skeller/vlc
  • MCJack123/vlc
  • luc65r/vlc-mpd
  • popov895/vlc
  • claucambra/vlc
  • brad/vlc
  • matthewmurua88/vlc
  • Tomas8874/vlc
  • philenotfound/vlc
  • makita-do3/vlc
  • LZXCorp/vlc
  • mar0x/vlc
  • senojetkennedy0102/vlc
  • shaneb243/vlc
  • ahmadbader/vlc
  • rajduttcse26/vlc-audio-filters
  • Juniorzito8415/vlc
  • achernyakov/vlc
  • lucasjetgroup/vlc
  • pupdoggy666/vlc
  • gmde9363/vlc
  • alexnwayne/vlc
  • bahareebrahimi781/vlc
  • hamad633666/vlc
  • umghof3112/vlc
  • joe0199771874/vlc
  • Octocats66666666/vlc
  • jjm_223/vlc
  • btech10110.19/vlc
  • sunnykfc028/vlc-audio-filters
  • loic/vlc
  • nguyenminhducmx1/vlc
  • JanekKrueger/vlc
  • bstubbington2/vlc
  • rcombs/vlc
  • Ordissimo/vlc
  • king7532/vlc
  • noobsauce101/vlc
  • schong0525/vlc
  • myQwil/vlc
  • apisbg91/vlc
  • geeboy0101017/vlc
  • kim.faughey/vlc
  • nurupo/vlc
  • yyusea/vlc
  • 0711235879.khco/vlc
  • ialo/vlc
  • iloveyeye2/vlc
  • gdtdftdqtd/vlc
  • leandroconsiglio/vlc
  • AndyHTML2012/vlc
  • ncz/vlc
  • lucenticus/vlc
  • knr1931/vlc
  • kjoonlee/vlc
  • chandrakant100/vlc-qt
  • johge42/vlc
  • polter/vlc
  • hexchain/vlc
  • Tushwrld/vlc
  • mztea928/vlc
  • jbelloncastro/vlc
  • alvinhochun/vlc
  • ghostpiratecrow/vlc
  • ujjwaltwitx/vlc
  • alexsonarin06/vlc
  • adrianbon76/vlc
  • altsod/vlc
  • damien.lucas44/vlc
  • dmytrivtaisa/vlc
  • utk202/vlc
  • aaxhrj/vlc
  • thomas.hermes/vlc
  • structurenewworldorder/vlc
  • slomo/vlc
  • wantlamy/vlc
  • musc.o3cminc/vlc
  • thebarshablog/vlc
  • kerrick/vlc
  • kratos142518/vlc
  • leogps/vlc
  • vacantron/vlc
  • luna_koly/vlc
  • Ratio2/vlc
  • anuoshemohammad/vlc
  • apsun/vlc
  • aaa1115910/vlc
  • alimotmoyo/vlc
  • Ambossmann/vlc
  • Sam-LearnsToCode/vlc
  • Chilledheart/vlc
  • Labnann/vlc
  • ktcoooot1/vlc
  • mohit-marathe/vlc
  • johnddx/vlc
  • manstabuk/vlc
  • Omar-ahmed314/vlc
  • vineethkm/vlc
  • 9Enemi86/vlc
  • radoslav.m.panteleev/vlc
  • ashishami2002/vlc
  • Corbax/vlc
  • firnasahmed/vlc
  • pelayarmalam4/vlc
  • c0ff330k/vlc
  • shikhindahikar/vlc
  • l342723951/vlc
  • christianschwandner/vlc
  • douniwan5788/vlc
  • 7damian7/vlc
  • ferdnyc/vlc
  • f.ales1/vlc
  • pandagby/vlc
  • BaaBaa/vlc
  • jewe37/vlc
  • w00drow/vlc
  • russelltg/vlc
  • ironicallygod/vlc
  • soumyaDghosh/vlc
  • linzihao1999/vlc
  • deyayush6/vlc
  • mibi88/vlc
  • newabdallah10/vlc
  • jhorbincolombia/vlc
  • rimvihaqueshupto/vlc
  • andrewkhon98/vlc
  • fab78/vlc
  • lapaz17/vlc
  • amanna13/vlc
  • mdakram28/vlc
  • 07jw1980/vlc
  • sohamgupta/vlc
  • Eson-Jia1/vlc
  • Sumou/vlc
  • vikram-kangotra/vlc
  • chalice191/vlc
  • olivercalder/vlc
  • aaasg4001/vlc
  • zipdox/vlc
  • kwizart/vlc
  • Dragon-S/vlc
  • jdemeule/vlc
  • gabriel_lt/vlc
  • locutusofborg/vlc
  • sammirata/vlc-librist
  • another/vlc
  • Benjamin_Loison/vlc
  • ahmedmoselhi/vlc
  • petergaal/vlc
  • huynhsontung/vlc
  • dariusmihut/vlc
  • tvermaashutosh/vlc
  • buti/vlc
  • Niram7777/vlc
  • rohan-here/vlc
  • balaji-sivasakthi/vlc
  • rlindner81/vlc
  • Kakadus/vlc
  • djain/vlc
  • ABBurmeister/vlc
  • craighuggins/vlc
  • orbea/vlc
  • maxos/vlc
  • aakarshmj/vlc
  • kblaschke/vlc
  • ankitm/vlc
  • advait-0/vlc
  • mohak2003/vlc
  • yselkowitz/vlc
  • AZM999/vlc-azm
  • andrey.turkin/vlc
  • Disha-Baghel/vlc
  • nowrep/vlc
  • Apeng/vlc
  • Choucroute_melba/vlc
  • autra/vlc
  • eclipseo/vlc
  • fhuber/vlc
  • olafhering/vlc
  • sdasda7777/vlc
  • 1div0/vlc
  • skosnits/vlc-extended-playlist-support
  • dnicolson/vlc
  • Timshel/vlc
  • octopols/vlc
  • MangalK/vlc
  • nima64/vlc
  • misawai/vlc
  • Alexander-Wilms/vlc
  • Maxime2/vlc-fork-for-visualizer
  • ww/vlc
  • jeske/vlc
  • sgross-emlix/vlc
  • morenonatural/vlc
  • freakingLovesVLC/vlc
  • borisgolovnev/vlc
  • mpromonet/vlc
  • diogo.simao-marques/vlc
  • masstock/vlc
  • pratikpatel8982/vlc
  • hugok79/vlc
  • longervision/vlc
  • abhiudaysurya/vlc
  • rishabhgarg/vlc
  • tumic/vlc
  • cart/vlc
  • shubham442/vlc
  • Aditya692005/vlc
  • sammirata/vlc4
  • syrykh/vlc
  • Vvorcun/macos-new-icon
  • AyaanshC/vlc
  • nasso/vlc
  • Quark/vlc
  • sebastinas/vlc
  • rhstone/vlc
  • talregev/vlc
  • Managor/vlc
  • abdsaber000/vlc
404 results
Show changes
Commits on Source (3)
  • Pierre Lamot's avatar
    cfb14b4d
  • Pierre Lamot's avatar
    snap: add libxdamage dependency · 990f7ccf
    Pierre Lamot authored and Jean-Baptiste Kempf's avatar Jean-Baptiste Kempf committed
    990f7ccf
  • Pierre Lamot's avatar
    qt: add X11 video compositor · 96166bad
    Pierre Lamot authored and Jean-Baptiste Kempf's avatar Jean-Baptiste Kempf committed
    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
    96166bad
Showing
with 1968 additions and 4 deletions
......@@ -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
......
......@@ -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
......
......@@ -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
......
......@@ -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 \
......
......@@ -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");
......
......@@ -54,7 +54,8 @@ public:
{
DummyCompositor,
Win7Compositor,
DirectCompositionCompositor
DirectCompositionCompositor,
X11Compositor
};
typedef void (*VoutDestroyCb)(vout_window_t *p_wnd);
......
/*****************************************************************************
* 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;
}
/*****************************************************************************
* 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
/*****************************************************************************
* 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();
}
/*****************************************************************************
* 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
/*****************************************************************************
* 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;
}
/*****************************************************************************
* 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
/*****************************************************************************
* 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;
}
/*****************************************************************************
* 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
/*****************************************************************************
* 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;
}
}
/*****************************************************************************
* 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 */
......@@ -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)
......