You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
625 lines
19 KiB
625 lines
19 KiB
/*****************************************************************************
|
|
* 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 <QWindow>
|
|
#include <QMainWindow>
|
|
#include <QThread>
|
|
#include <QSocketNotifier>
|
|
#ifndef QT_NO_ACCESSIBILITY
|
|
#include <QAccessible>
|
|
#endif
|
|
#ifndef QT_GUI_PRIVATE
|
|
#warning "qplatformnativeinterface is requried for x11 compositor"
|
|
#endif
|
|
#include <QtGui/qpa/qplatformnativeinterface.h>
|
|
|
|
#include <xcb/composite.h>
|
|
|
|
#include "compositor_x11_renderwindow.hpp"
|
|
#include "compositor_x11_renderclient.hpp"
|
|
#include "compositor_x11_uisurface.hpp"
|
|
#include "compositor_x11_utils.hpp"
|
|
|
|
#include <vlc_cxx_helpers.hpp>
|
|
|
|
#include "qt.hpp"
|
|
#include "mainctx.hpp"
|
|
|
|
#define _GTK_FRAME_EXTENTS "_GTK_FRAME_EXTENTS"
|
|
|
|
using namespace vlc;
|
|
|
|
namespace
|
|
{
|
|
|
|
void assertUint16Range(int value)
|
|
{
|
|
assert(value >= 0 && value <= std::numeric_limits<uint16_t>::max());
|
|
}
|
|
|
|
}
|
|
|
|
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 || m_hasExtendedFrame)
|
|
{
|
|
//clear screen
|
|
xcb_render_color_t clear = { 0x0000, 0x0000, 0x0000, 0x0000 };
|
|
|
|
|
|
xcb_rectangle_t rect = {0, 0
|
|
, static_cast<uint16_t>(m_renderSize.width())
|
|
, static_cast<uint16_t>(m_renderSize.height())};
|
|
|
|
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));
|
|
}
|
|
|
|
// paint function takes uint16
|
|
assertUint16Range(m_renderSize.width());
|
|
assertUint16Range(m_renderSize.height());
|
|
|
|
m_resizeRequested = true;
|
|
}
|
|
|
|
void RenderTask::requestRefresh()
|
|
{
|
|
emit requestRefreshInternal(m_refreshRequestId, QPrivateSignal());
|
|
}
|
|
|
|
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, QPrivateSignal());
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
void RenderTask::onExtendedFrameChanged(bool enabled)
|
|
{
|
|
m_hasExtendedFrame = 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;
|
|
}
|
|
|
|
//can't use QOverload with private signals
|
|
template<class T>
|
|
static auto privateOverload(void (QSocketNotifier::* s)( QSocketDescriptor,QSocketNotifier::Type, T) )
|
|
{
|
|
return s;
|
|
}
|
|
|
|
void X11DamageObserver::start()
|
|
{
|
|
//listen to the x11 socket instead of blocking
|
|
m_socketNotifier = new QSocketNotifier(m_connFd, QSocketNotifier::Read, this);
|
|
connect(m_socketNotifier, privateOverload(&QSocketNotifier::activated),
|
|
this, &X11DamageObserver::onEvent);
|
|
}
|
|
|
|
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, QWindow* parent)
|
|
: DummyRenderWindow(parent)
|
|
, m_intf(p_intf)
|
|
, m_conn(conn)
|
|
{
|
|
winId();
|
|
setVisible(true);
|
|
|
|
m_wid = winId();
|
|
|
|
m_lastVisibility = visibility();
|
|
|
|
connect(this, &QWindow::visibilityChanged, this, [this](QWindow::Visibility visibility) {
|
|
if (visibility == QWindow::Visibility::Minimized)
|
|
{
|
|
// Hidden is handled in `QWindow::hideEvent()`.
|
|
emit visiblityChanged(false);
|
|
}
|
|
else if (m_lastVisibility == QWindow::Visibility::Minimized && visibility != QWindow::Visibility::Hidden)
|
|
{
|
|
// Show is handled in `QWindow::showEvent()`.
|
|
// Expose is handled in `QWindow::exposeEvent()`. However, KWin X11 does not send expose event when
|
|
// a window is restored from minimized state.
|
|
resetClientPixmaps();
|
|
emit visiblityChanged(true);
|
|
emit requestUIRefresh();
|
|
}
|
|
|
|
m_lastVisibility = visibility;
|
|
});
|
|
}
|
|
|
|
CompositorX11RenderWindow::~CompositorX11RenderWindow()
|
|
{
|
|
stopRendering();
|
|
}
|
|
|
|
bool CompositorX11RenderWindow::init()
|
|
{
|
|
m_damageObserver = new X11DamageObserver(m_intf, m_conn);
|
|
bool ret = m_damageObserver->init();
|
|
if (!ret)
|
|
{
|
|
delete m_damageObserver;
|
|
m_damageObserver = nullptr;
|
|
msg_Warn(m_intf, "can't initialize X11 damage");
|
|
return false;
|
|
}
|
|
|
|
const auto nativeInterface = qGuiApp->nativeInterface<QNativeInterface::QX11Application>();
|
|
assert(nativeInterface);
|
|
xcb_connection_t* qtConn = nativeInterface->connection();
|
|
QPlatformNativeInterface *native = qApp->platformNativeInterface();
|
|
if (!native)
|
|
return true;
|
|
|
|
if (const int screen = reinterpret_cast<intptr_t>(native->nativeResourceForIntegration(QByteArrayLiteral("x11screen"))))
|
|
{
|
|
if (!wmScreenHasCompositor(qtConn, screen))
|
|
return true;
|
|
}
|
|
|
|
//_GTK_FRAME_EXTENTS should be available at least on Gnome/KDE/FXCE/Enlightement
|
|
const xcb_window_t rootWindow = reinterpret_cast<intptr_t>(native->nativeResourceForIntegration(QByteArrayLiteral("rootwindow")));
|
|
xcb_atom_t gtkExtendFrame = getInternAtom(qtConn, _GTK_FRAME_EXTENTS);
|
|
if (gtkExtendFrame != XCB_ATOM_NONE && wmNetSupport(qtConn, rootWindow, gtkExtendFrame))
|
|
{
|
|
m_supportExtendedFrame = true;
|
|
connect(m_intf->p_mi, &MainCtx::windowExtendedMarginChanged,
|
|
this, &CompositorX11RenderWindow::onWindowExtendedMarginChanged);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool CompositorX11RenderWindow::startRendering()
|
|
{
|
|
assert(m_interfaceWindow);
|
|
|
|
//Rendering thread
|
|
m_renderTask = new RenderTask(m_intf, m_conn, m_wid, 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::updated, 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);
|
|
connect(this, &CompositorX11RenderWindow::hasExtendedFrameChanged, m_renderTask, &RenderTask::onExtendedFrameChanged, Qt::BlockingQueuedConnection);
|
|
connect(this, &CompositorX11RenderWindow::acrylicChanged, m_renderTask, &RenderTask::onAcrylicChanged);
|
|
//pass initial values
|
|
m_renderTask->onInterfaceSurfaceChanged(m_interfaceClient.get());
|
|
m_renderTask->onVideoSurfaceChanged(m_videoClient.get());
|
|
m_renderTask->onWindowSizeChanged(size() * devicePixelRatio());
|
|
m_renderTask->onAcrylicChanged(m_hasAcrylic);
|
|
m_renderTask->onExtendedFrameChanged(m_hasExtendedFrame);
|
|
|
|
//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);
|
|
connect(m_renderThread, &QThread::finished, m_damageObserver, &QObject::deleteLater);
|
|
|
|
//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(qGuiApp->nativeInterface<QNativeInterface::QX11Application>()->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();
|
|
}
|
|
}
|
|
|
|
void CompositorX11RenderWindow::exposeEvent(QExposeEvent* event)
|
|
{
|
|
DummyRenderWindow::exposeEvent(event);
|
|
resetClientPixmaps();
|
|
emit requestUIRefresh();
|
|
}
|
|
|
|
void CompositorX11RenderWindow::resizeEvent(QResizeEvent* event)
|
|
{
|
|
DummyRenderWindow::resizeEvent(event);
|
|
resetClientPixmaps();
|
|
emit windowSizeChanged(event->size() * devicePixelRatio());
|
|
emit requestUIRefresh();
|
|
}
|
|
|
|
void CompositorX11RenderWindow::showEvent(QShowEvent* event)
|
|
{
|
|
DummyRenderWindow::showEvent(event);
|
|
resetClientPixmaps();
|
|
emit visiblityChanged(true);
|
|
emit requestUIRefresh();
|
|
}
|
|
|
|
void CompositorX11RenderWindow::hideEvent(QHideEvent* event)
|
|
{
|
|
DummyRenderWindow::hideEvent(event);
|
|
emit visiblityChanged(false);
|
|
}
|
|
|
|
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(qGuiApp->nativeInterface<QNativeInterface::QX11Application>()->connection());
|
|
//reset and recreate the clients surfaces
|
|
m_videoClient->resetPixmap();
|
|
m_videoClient->getPicture();
|
|
}
|
|
m_videoPosition.setSize(size * devicePixelRatio());
|
|
emit videoPositionChanged(m_videoPosition);
|
|
}
|
|
}
|
|
|
|
void CompositorX11RenderWindow::setAcrylic(bool value)
|
|
{
|
|
if (m_hasAcrylic == value)
|
|
return;
|
|
|
|
m_hasAcrylic = value;
|
|
|
|
emit acrylicChanged(value);
|
|
}
|
|
|
|
void CompositorX11RenderWindow::setVideoWindow( QWindow* window)
|
|
{
|
|
//ensure Qt x11 pending operation have been forwarded to the server
|
|
xcb_flush(qGuiApp->nativeInterface<QNativeInterface::QX11Application>()->connection());
|
|
m_videoClient = std::make_unique<CompositorX11RenderClient>(m_intf, m_conn, window->winId());
|
|
connect(window, &QWindow::widthChanged, m_videoClient.get(), &CompositorX11RenderClient::resetPixmap);
|
|
connect(window, &QWindow::heightChanged, m_videoClient.get(), &CompositorX11RenderClient::resetPixmap);
|
|
m_videoPosition = QRect(0,0,0,0);
|
|
m_videoWindow = window;
|
|
emit videoSurfaceChanged(m_videoClient.get());
|
|
}
|
|
|
|
void CompositorX11RenderWindow::enableVideoWindow()
|
|
{
|
|
//if we stop the rendering, m_videoWindow may be null
|
|
if (!m_videoWindow)
|
|
return;
|
|
|
|
emit registerVideoWindow(m_videoWindow->winId());
|
|
}
|
|
|
|
void CompositorX11RenderWindow::disableVideoWindow()
|
|
{
|
|
//if we stop the rendering, m_videoWindow may be null
|
|
if (!m_videoWindow)
|
|
return;
|
|
|
|
emit registerVideoWindow(0);
|
|
}
|
|
|
|
QQuickWindow* CompositorX11RenderWindow::getOffscreenWindow() const
|
|
{
|
|
if (!m_interfaceWindow)
|
|
return nullptr;
|
|
return m_interfaceWindow->getOffscreenWindow();
|
|
}
|
|
|
|
void CompositorX11RenderWindow::setInterfaceWindow(CompositorX11UISurface* window)
|
|
{
|
|
//ensure Qt x11 pending operation have been forwarded to the server
|
|
xcb_flush(qGuiApp->nativeInterface<QNativeInterface::QX11Application>()->connection());
|
|
m_interfaceClient = std::make_unique<CompositorX11RenderClient>(m_intf, m_conn, window->winId());
|
|
connect(window, &CompositorX11UISurface::requestPixmapReset, m_interfaceClient.get(), &CompositorX11RenderClient::resetPixmap);
|
|
m_interfaceWindow = window;
|
|
}
|
|
|
|
|
|
void CompositorX11RenderWindow::setHasExtendedFrame(bool hasExtendedFrame)
|
|
{
|
|
if (hasExtendedFrame == m_hasExtendedFrame)
|
|
return;
|
|
m_hasExtendedFrame = hasExtendedFrame;
|
|
emit hasExtendedFrameChanged(m_hasExtendedFrame);
|
|
}
|
|
|
|
void CompositorX11RenderWindow::onWindowExtendedMarginChanged(unsigned margin)
|
|
{
|
|
xcb_atom_t gtkExtendFrame = getInternAtom(m_conn, _GTK_FRAME_EXTENTS);
|
|
|
|
margin *= m_intf->p_mi->intfMainWindow()->devicePixelRatio();
|
|
|
|
uint32_t val[4] = {margin, margin, margin, margin};
|
|
xcb_change_property(m_conn, XCB_PROP_MODE_REPLACE, m_wid,
|
|
gtkExtendFrame, XCB_ATOM_CARDINAL, 32, 4, &val);
|
|
setHasExtendedFrame(margin != 0);
|
|
}
|
|
|
|
|