16#include <QGuiApplication> 
   17#include <QQmlComponent> 
   21#include <QQuickRenderControl> 
   25#include <QOffscreenSurface> 
   26#include <QOpenGLContext> 
   27#include <QOpenGLFramebufferObject> 
   28#include <QQuickGraphicsDevice> 
   29#include <QQuickOpenGLUtils> 
   30#include <QQuickRenderTarget> 
   32#include <private/qeventpoint_p.h>  
   40    std::unique_ptr<QQuickWindow> 
m_view;
 
   44    std::unique_ptr<QOpenGLFramebufferObject> 
m_fbo;
 
   51    bool m_useBlit = 
false;
 
   52    bool m_visible = 
true;
 
   53    bool m_hasAlphaChannel = 
true;
 
   54    bool m_automaticRepaint = 
true;
 
   59    ulong lastMousePressTime = 0;
 
   60    Qt::MouseButton lastMousePressButton = Qt::NoButton;
 
 
   81    d->m_renderControl = std::make_unique<QQuickRenderControl>();
 
   83    d->m_view = std::make_unique<QQuickWindow>(d->m_renderControl.get());
 
   84    Q_ASSERT(d->m_view->setProperty(
"_KWIN_WINDOW_IS_OFFSCREEN", 
true) || 
true);
 
   85    d->m_view->setFlags(Qt::FramelessWindowHint);
 
   86    d->m_view->setColor(Qt::transparent);
 
   88    d->m_hasAlphaChannel = alpha;
 
   93    const bool usingGl = d->m_view->rendererInterface()->graphicsApi() == QSGRendererInterface::OpenGL;
 
   96        qCDebug(LIBKWINEFFECTS) << 
"QtQuick Software rendering mode detected";
 
   98        d->m_renderControl->initialize();
 
  101        format.setOption(QSurfaceFormat::ResetNotification);
 
  102        format.setDepthBufferSize(16);
 
  103        format.setStencilBufferSize(8);
 
  105            format.setAlphaBufferSize(8);
 
  108        d->m_view->setFormat(
format);
 
  110        auto shareContext = QOpenGLContext::globalShareContext();
 
  111        d->m_glcontext = std::make_unique<QOpenGLContext>();
 
  112        d->m_glcontext->setShareContext(shareContext);
 
  113        d->m_glcontext->setFormat(
format);
 
  114        d->m_glcontext->create();
 
  117        d->m_offscreenSurface = std::make_unique<QOffscreenSurface>();
 
  118        d->m_offscreenSurface->setFormat(d->m_glcontext->format());
 
  119        d->m_offscreenSurface->create();
 
  121        d->m_glcontext->makeCurrent(d->m_offscreenSurface.get());
 
  122        d->m_view->setGraphicsDevice(QQuickGraphicsDevice::fromOpenGLContext(d->m_glcontext.get()));
 
  123        d->m_renderControl->initialize();
 
  124        d->m_glcontext->doneCurrent();
 
  127        if (shareContext && !d->m_glcontext->shareContext()) {
 
  128            qCDebug(LIBKWINEFFECTS) << 
"Failed to create a shared context, falling back to raster rendering";
 
  134    auto updateSize = [
this]() {
 
  138    connect(d->m_view.get(), &QWindow::widthChanged, 
this, updateSize);
 
  139    connect(d->m_view.get(), &QWindow::heightChanged, 
this, updateSize);
 
  141    d->m_repaintTimer = std::make_unique<QTimer>();
 
  142    d->m_repaintTimer->setSingleShot(
true);
 
  143    d->m_repaintTimer->setInterval(10);
 
  146    connect(d->m_renderControl.get(), &QQuickRenderControl::renderRequested, 
this, &OffscreenQuickView::handleRenderRequested);
 
  147    connect(d->m_renderControl.get(), &QQuickRenderControl::sceneChanged, 
this, &OffscreenQuickView::handleSceneChanged);
 
  149    d->touchDevice = 
new QPointingDevice(QStringLiteral(
"ForwardingTouchDevice"), {}, QInputDevice::DeviceType::TouchScreen, QPointingDevice::PointerType::Finger, QInputDevice::Capability::Position, 10, {});
 
 
  154    disconnect(d->m_renderControl.get(), &QQuickRenderControl::renderRequested, 
this, &OffscreenQuickView::handleRenderRequested);
 
  155    disconnect(d->m_renderControl.get(), &QQuickRenderControl::sceneChanged, 
this, &OffscreenQuickView::handleSceneChanged);
 
  157    if (d->m_glcontext) {
 
  159        d->m_glcontext->makeCurrent(d->m_offscreenSurface.get());
 
  163    d->m_renderControl.reset();
 
 
  168    return d->m_automaticRepaint;
 
 
  173    if (d->m_automaticRepaint != set) {
 
  174        d->m_automaticRepaint = set;
 
  177        if (!d->m_automaticRepaint) {
 
  178            d->m_repaintTimer->stop();
 
 
  183void OffscreenQuickView::handleSceneChanged()
 
  185    if (d->m_automaticRepaint) {
 
  186        d->m_repaintTimer->start();
 
  191void OffscreenQuickView::handleRenderRequested()
 
  193    if (d->m_automaticRepaint) {
 
  194        d->m_repaintTimer->start();
 
  204    if (d->m_view->size().isEmpty()) {
 
  208    bool usingGl = d->m_glcontext != 
nullptr;
 
  211        if (!d->m_glcontext->makeCurrent(d->m_offscreenSurface.get())) {
 
  216        const QSize nativeSize = d->m_view->size() * d->m_view->devicePixelRatio();
 
  217        if (!d->m_fbo || d->m_fbo->size() != nativeSize) {
 
  218            d->m_textureExport.reset(
nullptr);
 
  220            QOpenGLFramebufferObjectFormat fboFormat;
 
  221            fboFormat.setAttachment(QOpenGLFramebufferObject::CombinedDepthStencil);
 
  222            fboFormat.setInternalTextureFormat(GL_RGBA8);
 
  224            d->m_fbo = std::make_unique<QOpenGLFramebufferObject>(nativeSize, fboFormat);
 
  225            if (!d->m_fbo->isValid()) {
 
  227                d->m_glcontext->doneCurrent();
 
  232        QQuickRenderTarget renderTarget = QQuickRenderTarget::fromOpenGLTexture(d->m_fbo->texture(), d->m_fbo->size());
 
  233        renderTarget.setDevicePixelRatio(d->m_view->devicePixelRatio());
 
  235        d->m_view->setRenderTarget(renderTarget);
 
  238    d->m_renderControl->polishItems();
 
  239    d->m_renderControl->beginFrame();
 
  240    d->m_renderControl->sync();
 
  241    d->m_renderControl->render();
 
  242    d->m_renderControl->endFrame();
 
  245        QQuickOpenGLUtils::resetOpenGLState();
 
  250            d->m_image = d->m_fbo->toImage();
 
  251            d->m_image.setDevicePixelRatio(d->m_view->devicePixelRatio());
 
  253            d->m_image = d->m_view->grabWindow();
 
  258        QOpenGLFramebufferObject::bindDefault();
 
  259        d->m_glcontext->doneCurrent();
 
 
  270    case QEvent::MouseMove:
 
  271    case QEvent::MouseButtonPress:
 
  272    case QEvent::MouseButtonRelease: {
 
  273        QMouseEvent *me = 
static_cast<QMouseEvent *
>(e);
 
  274        const QPoint widgetPos = d->m_view->mapFromGlobal(me->pos());
 
  275        QMouseEvent cloneEvent(me->type(), widgetPos, me->pos(), me->button(), me->buttons(), me->modifiers());
 
  276        cloneEvent.setAccepted(
false);
 
  277        QCoreApplication::sendEvent(d->m_view.get(), &cloneEvent);
 
  278        e->setAccepted(cloneEvent.isAccepted());
 
  280        if (e->type() == QEvent::MouseButtonPress) {
 
  281            const ulong doubleClickInterval = 
static_cast<ulong
>(QGuiApplication::styleHints()->mouseDoubleClickInterval());
 
  282            const bool doubleClick = (me->timestamp() - d->lastMousePressTime < doubleClickInterval) && me->button() == d->lastMousePressButton;
 
  283            d->lastMousePressTime = me->timestamp();
 
  284            d->lastMousePressButton = me->button();
 
  286                d->lastMousePressButton = Qt::NoButton;
 
  287                QMouseEvent doubleClickEvent(QEvent::MouseButtonDblClick, me->localPos(), me->windowPos(), me->screenPos(), me->button(), me->buttons(), me->modifiers());
 
  288                QCoreApplication::sendEvent(d->m_view.get(), &doubleClickEvent);
 
  294    case QEvent::HoverEnter:
 
  295    case QEvent::HoverLeave:
 
  296    case QEvent::HoverMove: {
 
  297        QHoverEvent *he = 
static_cast<QHoverEvent *
>(e);
 
  298        const QPointF widgetPos = d->m_view->mapFromGlobal(he->pos());
 
  299        const QPointF oldWidgetPos = d->m_view->mapFromGlobal(he->oldPos());
 
  300        QHoverEvent cloneEvent(he->type(), widgetPos, oldWidgetPos, he->modifiers());
 
  301        cloneEvent.setAccepted(
false);
 
  302        QCoreApplication::sendEvent(d->m_view.get(), &cloneEvent);
 
  303        e->setAccepted(cloneEvent.isAccepted());
 
  306    case QEvent::Wheel: {
 
  307        QWheelEvent *we = 
static_cast<QWheelEvent *
>(e);
 
  308        const QPointF widgetPos = d->m_view->mapFromGlobal(we->position().toPoint());
 
  309        QWheelEvent cloneEvent(widgetPos, we->globalPosition(), we->pixelDelta(), we->angleDelta(), we->buttons(),
 
  310                               we->modifiers(), we->phase(), we->inverted());
 
  311        cloneEvent.setAccepted(
false);
 
  312        QCoreApplication::sendEvent(d->m_view.get(), &cloneEvent);
 
  313        e->setAccepted(cloneEvent.isAccepted());
 
 
  326    QCoreApplication::sendEvent(d->m_view.get(), keyEvent);
 
 
  331    d->updateTouchState(Qt::TouchPointPressed, 
id, pos);
 
  333    QTouchEvent event(QEvent::TouchBegin, d->touchDevice, Qt::NoModifier, d->touchPoints);
 
  334    event.setTimestamp(std::chrono::duration_cast<std::chrono::milliseconds>(time).count());
 
  335    event.setAccepted(
false);
 
  336    QCoreApplication::sendEvent(d->m_view.get(), &event);
 
  338    return event.isAccepted();
 
 
  343    d->updateTouchState(Qt::TouchPointMoved, 
id, pos);
 
  345    QTouchEvent event(QEvent::TouchUpdate, d->touchDevice, Qt::NoModifier, d->touchPoints);
 
  346    event.setTimestamp(std::chrono::duration_cast<std::chrono::milliseconds>(time).count());
 
  347    event.setAccepted(
false);
 
  348    QCoreApplication::sendEvent(d->m_view.get(), &event);
 
  350    return event.isAccepted();
 
 
  355    d->updateTouchState(Qt::TouchPointReleased, 
id, QPointF{});
 
  357    QTouchEvent event(QEvent::TouchEnd, d->touchDevice, Qt::NoModifier, d->touchPoints);
 
  358    event.setTimestamp(std::chrono::duration_cast<std::chrono::milliseconds>(time).count());
 
  359    event.setAccepted(
false);
 
  360    QCoreApplication::sendEvent(d->m_view.get(), &event);
 
  362    return event.isAccepted();
 
 
  367    return d->m_view->geometry();
 
 
  372    d->m_view->setOpacity(
opacity);
 
 
  377    return d->m_view->opacity();
 
 
  382    return d->m_hasAlphaChannel;
 
 
  387    return d->m_view->contentItem();
 
 
  392    return d->m_view.get();
 
 
  397    if (d->m_visible == visible) {
 
  400    d->m_visible = visible;
 
  403        Q_EMIT d->m_renderControl->renderRequested();
 
  406        QTimer::singleShot(0, 
this, [
this]() {
 
  407            d->releaseResources();
 
 
  435        if (!d->m_textureExport) {
 
  439    return d->m_textureExport.get();
 
 
  449    return d->m_view->geometry().size();
 
 
  454    const QRect oldGeometry = d->m_view->geometry();
 
  455    d->m_view->setGeometry(rect);
 
  457    d->m_view->setScreen(QGuiApplication::screenAt(rect.center()));
 
 
  461void OffscreenQuickView::Private::releaseResources()
 
  464        m_glcontext->makeCurrent(m_offscreenSurface.get());
 
  465        m_view->releaseResources();
 
  466        m_glcontext->doneCurrent();
 
  468        m_view->releaseResources();
 
  472void OffscreenQuickView::Private::updateTouchState(Qt::TouchPointState state, qint32 
id, 
const QPointF &pos)
 
  478    touchPoints.erase(std::remove_if(touchPoints.begin(), touchPoints.end(), [](QTouchEvent::TouchPoint &point) {
 
  479                          if (point.state() == QEventPoint::Released) {
 
  482                          QMutableEventPoint::setState(point, QEventPoint::Stationary);
 
  491    static const qint32 idOffset = 111;
 
  495    auto changed = std::find_if(touchPoints.begin(), touchPoints.end(), [
id](
const QTouchEvent::TouchPoint &point) {
 
  496        return point.id() == id + idOffset;
 
  500    case Qt::TouchPointPressed: {
 
  501        if (changed != touchPoints.end()) {
 
  505        QTouchEvent::TouchPoint point;
 
  506        QMutableEventPoint::setState(point, QEventPoint::Pressed);
 
  507        QMutableEventPoint::setId(point, 
id + idOffset);
 
  508        QMutableEventPoint::setGlobalPosition(point, pos);
 
  509        QMutableEventPoint::setScenePosition(point, m_view->mapFromGlobal(pos.toPoint()));
 
  510        QMutableEventPoint::setPosition(point, m_view->mapFromGlobal(pos.toPoint()));
 
  512        touchPoints.append(point);
 
  514    case Qt::TouchPointMoved: {
 
  515        if (changed == touchPoints.end()) {
 
  519        auto &point = *changed;
 
  520        QMutableEventPoint::setGlobalLastPosition(point, point.globalPosition());
 
  521        QMutableEventPoint::setState(point, QEventPoint::Updated);
 
  522        QMutableEventPoint::setScenePosition(point, m_view->mapFromGlobal(pos.toPoint()));
 
  523        QMutableEventPoint::setPosition(point, m_view->mapFromGlobal(pos.toPoint()));
 
  524        QMutableEventPoint::setGlobalPosition(point, pos);
 
  526    case Qt::TouchPointReleased: {
 
  527        if (changed == touchPoints.end()) {
 
  531        auto &point = *changed;
 
  532        QMutableEventPoint::setGlobalLastPosition(point, point.globalPosition());
 
  533        QMutableEventPoint::setState(point, QEventPoint::Released);
 
  555    if (!d->qmlComponent) {
 
  559    d->qmlComponent->loadUrl(source);
 
  560    if (d->qmlComponent->isError()) {
 
  561        qCWarning(LIBKWINEFFECTS).nospace() << 
"Failed to load effect quick view " << source << 
": " << d->qmlComponent->errors();
 
  562        d->qmlComponent.reset();
 
  566    d->quickItem.reset();
 
  568    std::unique_ptr<QObject> qmlObject(d->qmlComponent->createWithInitialProperties(initialProperties));
 
  569    QQuickItem *item = qobject_cast<QQuickItem *>(qmlObject.get());
 
  571        qCWarning(LIBKWINEFFECTS) << 
"Root object of effect quick view" << source << 
"is not a QQuickItem";
 
  576    d->quickItem.reset(item);
 
  580    auto updateSize = [item, 
this]() {
 
  584    connect(
contentItem(), &QQuickItem::widthChanged, item, updateSize);
 
  585    connect(
contentItem(), &QQuickItem::heightChanged, item, updateSize);
 
 
  590    return d->quickItem.get();
 
 
  595#include "moc_offscreenquickview.cpp" 
QQmlEngine * qmlEngine() const
static std::unique_ptr< GLTexture > createNonOwningWrapper(GLuint textureId, GLenum internalFormat, const QSize &size)
static std::unique_ptr< GLTexture > upload(const QImage &image)
QQuickItem * rootItem() const
std::unique_ptr< QQmlComponent > qmlComponent
std::unique_ptr< QQuickItem > quickItem
void setSource(const QUrl &source)
The KwinQuickView class provides a convenient API for exporting QtQuick scenes as buffers that can be...
std::unique_ptr< QQuickRenderControl > m_renderControl
QQuickWindow * window() const
OffscreenQuickView(ExportMode exportMode=ExportMode::Texture, bool alpha=true)
QQuickItem * contentItem() const
void setVisible(bool visible)
Marks the window as visible/invisible This can be used to release resources used by the window The de...
std::unique_ptr< QOpenGLFramebufferObject > m_fbo
std::unique_ptr< QQuickWindow > m_view
QImage bufferAsImage() const
std::unique_ptr< QOffscreenSurface > m_offscreenSurface
void setOpacity(qreal opacity)
bool forwardTouchDown(qint32 id, const QPointF &pos, std::chrono::microseconds time)
std::unique_ptr< QTimer > m_repaintTimer
std::unique_ptr< GLTexture > m_textureExport
void updateTouchState(Qt::TouchPointState state, qint32 id, const QPointF &pos)
bool automaticRepaint() const
void setGeometry(const QRect &rect)
bool forwardTouchUp(qint32 id, std::chrono::microseconds time)
bool forwardTouchMotion(qint32 id, const QPointF &pos, std::chrono::microseconds time)
std::unique_ptr< QOpenGLContext > m_glcontext
GLTexture * bufferAsTexture()
QPointingDevice * touchDevice
void setAutomaticRepaint(bool set)
QList< QEventPoint > touchPoints
void forwardKeyEvent(QKeyEvent *keyEvent)
bool hasAlphaChannel() const
void forwardMouseEvent(QEvent *mouseEvent)
void geometryChanged(const QRect &oldGeometry, const QRect &newGeometry)