File indexing completed on 2024-05-19 16:34:35

0001 /*
0002     KWin - the KDE window manager
0003     This file is part of the KDE project.
0004 
0005     SPDX-FileCopyrightText: 2019 David Edmundson <davidedmundson@kde.org>
0006 
0007     SPDX-License-Identifier: GPL-2.0-or-later
0008 */
0009 
0010 #include "kwinoffscreenquickview.h"
0011 
0012 #include "kwinglutils.h"
0013 #include "logging_p.h"
0014 #include "sharedqmlengine.h"
0015 
0016 #include <QGuiApplication>
0017 #include <QQmlComponent>
0018 #include <QQmlContext>
0019 #include <QQmlEngine>
0020 #include <QQuickItem>
0021 #include <QQuickRenderControl>
0022 #include <QQuickView>
0023 #include <QStyleHints>
0024 
0025 #include <QOffscreenSurface>
0026 #include <QOpenGLContext>
0027 #include <QOpenGLFramebufferObject>
0028 #include <QTimer>
0029 #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
0030 #include <QQuickGraphicsDevice>
0031 #include <QQuickOpenGLUtils>
0032 #include <QQuickRenderTarget>
0033 #include <private/qeventpoint_p.h> // for QMutableEventPoint
0034 #endif
0035 
0036 namespace KWin
0037 {
0038 
0039 class EffectQuickRenderControl : public QQuickRenderControl
0040 {
0041     Q_OBJECT
0042 
0043 public:
0044     explicit EffectQuickRenderControl(OffscreenQuickView *view, QWindow *renderWindow)
0045         : QQuickRenderControl(view)
0046         , m_view(view)
0047         , m_renderWindow(renderWindow)
0048     {
0049     }
0050 
0051     QWindow *renderWindow(QPoint *offset) override
0052     {
0053         if (offset) {
0054             if (m_renderWindow) {
0055                 *offset = m_renderWindow->mapFromGlobal(m_view->geometry().topLeft());
0056             } else {
0057                 *offset = QPoint(0, 0);
0058             }
0059         }
0060         return m_renderWindow;
0061     }
0062 
0063 private:
0064     OffscreenQuickView *m_view;
0065     QPointer<QWindow> m_renderWindow;
0066 };
0067 
0068 class Q_DECL_HIDDEN OffscreenQuickView::Private
0069 {
0070 public:
0071     std::unique_ptr<QQuickWindow> m_view;
0072     std::unique_ptr<QQuickRenderControl> m_renderControl;
0073     std::unique_ptr<QOffscreenSurface> m_offscreenSurface;
0074     std::unique_ptr<QOpenGLContext> m_glcontext;
0075     std::unique_ptr<QOpenGLFramebufferObject> m_fbo;
0076 
0077     std::unique_ptr<QTimer> m_repaintTimer;
0078     QImage m_image;
0079     std::unique_ptr<GLTexture> m_textureExport;
0080     // if we should capture a QImage after rendering into our BO.
0081     // Used for either software QtQuick rendering and nonGL kwin rendering
0082     bool m_useBlit = false;
0083     bool m_visible = true;
0084     bool m_automaticRepaint = true;
0085 
0086 #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
0087     QList<QTouchEvent::TouchPoint> touchPoints;
0088     Qt::TouchPointStates touchState;
0089     QTouchDevice *touchDevice;
0090 #else
0091     QList<QEventPoint> touchPoints;
0092     QPointingDevice *touchDevice;
0093 #endif
0094 
0095     ulong lastMousePressTime = 0;
0096     Qt::MouseButton lastMousePressButton = Qt::NoButton;
0097 
0098     void releaseResources();
0099 
0100     void updateTouchState(Qt::TouchPointState state, qint32 id, const QPointF &pos);
0101 };
0102 
0103 class Q_DECL_HIDDEN OffscreenQuickScene::Private
0104 {
0105 public:
0106     Private()
0107         : qmlEngine(SharedQmlEngine::engine())
0108     {
0109     }
0110 
0111     SharedQmlEngine::Ptr qmlEngine;
0112     std::unique_ptr<QQmlComponent> qmlComponent;
0113     std::unique_ptr<QQuickItem> quickItem;
0114 };
0115 
0116 OffscreenQuickView::OffscreenQuickView(QObject *parent)
0117     : OffscreenQuickView(parent, effects ? ExportMode::Texture : ExportMode::Image)
0118 {
0119 }
0120 
0121 OffscreenQuickView::OffscreenQuickView(QObject *parent, ExportMode exportMode)
0122     : OffscreenQuickView(parent, nullptr, exportMode)
0123 {
0124 }
0125 
0126 OffscreenQuickView::OffscreenQuickView(QObject *parent, QWindow *renderWindow)
0127     : OffscreenQuickView(parent, renderWindow, effects ? ExportMode::Texture : ExportMode::Image)
0128 {
0129 }
0130 
0131 OffscreenQuickView::OffscreenQuickView(QObject *parent, QWindow *renderWindow, ExportMode exportMode)
0132     : QObject(parent)
0133     , d(new OffscreenQuickView::Private)
0134 {
0135     d->m_renderControl = std::make_unique<EffectQuickRenderControl>(this, renderWindow);
0136 
0137     d->m_view = std::make_unique<QQuickWindow>(d->m_renderControl.get());
0138     d->m_view->setFlags(Qt::FramelessWindowHint);
0139     d->m_view->setColor(Qt::transparent);
0140 
0141     if (exportMode == ExportMode::Image) {
0142         d->m_useBlit = true;
0143     }
0144 
0145     const bool usingGl = d->m_view->rendererInterface()->graphicsApi() == QSGRendererInterface::OpenGL;
0146 
0147     if (!usingGl) {
0148         qCDebug(LIBKWINEFFECTS) << "QtQuick Software rendering mode detected";
0149         d->m_useBlit = true;
0150 #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
0151         d->m_renderControl->initialize(nullptr);
0152 #else
0153         d->m_renderControl->initialize();
0154 #endif
0155     } else {
0156         QSurfaceFormat format;
0157         format.setOption(QSurfaceFormat::ResetNotification);
0158         format.setDepthBufferSize(16);
0159         format.setStencilBufferSize(8);
0160 
0161         auto shareContext = QOpenGLContext::globalShareContext();
0162         d->m_glcontext.reset(new QOpenGLContext);
0163         d->m_glcontext->setShareContext(shareContext);
0164         d->m_glcontext->setFormat(format);
0165         d->m_glcontext->create();
0166 
0167         // and the offscreen surface
0168         d->m_offscreenSurface.reset(new QOffscreenSurface);
0169         d->m_offscreenSurface->setFormat(d->m_glcontext->format());
0170         d->m_offscreenSurface->create();
0171 
0172         d->m_glcontext->makeCurrent(d->m_offscreenSurface.get());
0173 #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
0174         d->m_renderControl->initialize(d->m_glcontext.get());
0175 #else
0176         d->m_view->setGraphicsDevice(QQuickGraphicsDevice::fromOpenGLContext(d->m_glcontext.get()));
0177         d->m_renderControl->initialize();
0178 #endif
0179         d->m_glcontext->doneCurrent();
0180 
0181         // On Wayland, contexts are implicitly shared and QOpenGLContext::globalShareContext() is null.
0182         if (shareContext && !d->m_glcontext->shareContext()) {
0183             qCDebug(LIBKWINEFFECTS) << "Failed to create a shared context, falling back to raster rendering";
0184             // still render via GL, but blit for presentation
0185             d->m_useBlit = true;
0186         }
0187     }
0188 
0189     auto updateSize = [this]() {
0190         contentItem()->setSize(d->m_view->size());
0191     };
0192     updateSize();
0193     connect(d->m_view.get(), &QWindow::widthChanged, this, updateSize);
0194     connect(d->m_view.get(), &QWindow::heightChanged, this, updateSize);
0195 
0196     d->m_repaintTimer = std::make_unique<QTimer>();
0197     d->m_repaintTimer->setSingleShot(true);
0198     d->m_repaintTimer->setInterval(10);
0199 
0200     connect(d->m_repaintTimer.get(), &QTimer::timeout, this, &OffscreenQuickView::update);
0201     connect(d->m_renderControl.get(), &QQuickRenderControl::renderRequested, this, &OffscreenQuickView::handleRenderRequested);
0202     connect(d->m_renderControl.get(), &QQuickRenderControl::sceneChanged, this, &OffscreenQuickView::handleSceneChanged);
0203 
0204 #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
0205     d->touchDevice = new QTouchDevice{};
0206     d->touchDevice->setCapabilities(QTouchDevice::Position);
0207     d->touchDevice->setType(QTouchDevice::TouchScreen);
0208     d->touchDevice->setMaximumTouchPoints(10);
0209 #else
0210     d->touchDevice = new QPointingDevice({}, {}, QInputDevice::DeviceType::TouchScreen, {}, QInputDevice::Capability::Position, 10, {});
0211 #endif
0212 }
0213 
0214 OffscreenQuickView::~OffscreenQuickView()
0215 {
0216     if (d->m_glcontext) {
0217         // close the view whilst we have an active GL context
0218         d->m_glcontext->makeCurrent(d->m_offscreenSurface.get());
0219     }
0220 
0221     // Always delete render control first.
0222     d->m_renderControl.reset();
0223     d->m_view.reset();
0224 }
0225 
0226 bool OffscreenQuickView::automaticRepaint() const
0227 {
0228     return d->m_automaticRepaint;
0229 }
0230 
0231 void OffscreenQuickView::setAutomaticRepaint(bool set)
0232 {
0233     if (d->m_automaticRepaint != set) {
0234         d->m_automaticRepaint = set;
0235 
0236         // If there's an in-flight update, disable it.
0237         if (!d->m_automaticRepaint) {
0238             d->m_repaintTimer->stop();
0239         }
0240     }
0241 }
0242 
0243 void OffscreenQuickView::handleSceneChanged()
0244 {
0245     if (d->m_automaticRepaint) {
0246         d->m_repaintTimer->start();
0247     }
0248     Q_EMIT sceneChanged();
0249 }
0250 
0251 void OffscreenQuickView::handleRenderRequested()
0252 {
0253     if (d->m_automaticRepaint) {
0254         d->m_repaintTimer->start();
0255     }
0256     Q_EMIT renderRequested();
0257 }
0258 
0259 void OffscreenQuickView::update()
0260 {
0261     if (!d->m_visible) {
0262         return;
0263     }
0264     if (d->m_view->size().isEmpty()) {
0265         return;
0266     }
0267 
0268     bool usingGl = d->m_glcontext != nullptr;
0269 
0270     if (usingGl) {
0271         if (!d->m_glcontext->makeCurrent(d->m_offscreenSurface.get())) {
0272             // probably a context loss event, kwin is about to reset all the effects anyway
0273             return;
0274         }
0275 
0276         const QSize nativeSize = d->m_view->size() * d->m_view->effectiveDevicePixelRatio();
0277         if (!d->m_fbo || d->m_fbo->size() != nativeSize) {
0278             d->m_textureExport.reset(nullptr);
0279             d->m_fbo.reset(new QOpenGLFramebufferObject(nativeSize, QOpenGLFramebufferObject::CombinedDepthStencil));
0280             if (!d->m_fbo->isValid()) {
0281                 d->m_fbo.reset();
0282                 d->m_glcontext->doneCurrent();
0283                 return;
0284             }
0285         }
0286 #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
0287         d->m_view->setRenderTarget(d->m_fbo.get());
0288 #else
0289         d->m_view->setRenderTarget(QQuickRenderTarget::fromOpenGLTexture(d->m_fbo->texture(), d->m_fbo->size()));
0290 #endif
0291     }
0292 
0293     d->m_renderControl->polishItems();
0294     d->m_renderControl->sync();
0295 
0296     d->m_renderControl->render();
0297     if (usingGl) {
0298 #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
0299         d->m_view->resetOpenGLState();
0300 #else
0301         QQuickOpenGLUtils::resetOpenGLState();
0302 #endif
0303     }
0304 
0305     if (d->m_useBlit) {
0306 #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
0307         d->m_image = d->m_renderControl->grab();
0308 #else
0309         d->m_image = d->m_view->grabWindow();
0310 #endif
0311     }
0312 
0313     if (usingGl) {
0314         QOpenGLFramebufferObject::bindDefault();
0315         d->m_glcontext->doneCurrent();
0316     }
0317     Q_EMIT repaintNeeded();
0318 }
0319 
0320 void OffscreenQuickView::forwardMouseEvent(QEvent *e)
0321 {
0322     if (!d->m_visible) {
0323         return;
0324     }
0325     switch (e->type()) {
0326     case QEvent::MouseMove:
0327     case QEvent::MouseButtonPress:
0328     case QEvent::MouseButtonRelease: {
0329         QMouseEvent *me = static_cast<QMouseEvent *>(e);
0330         const QPoint widgetPos = d->m_view->mapFromGlobal(me->pos());
0331         QMouseEvent cloneEvent(me->type(), widgetPos, me->pos(), me->button(), me->buttons(), me->modifiers());
0332         QCoreApplication::sendEvent(d->m_view.get(), &cloneEvent);
0333         e->setAccepted(cloneEvent.isAccepted());
0334 
0335         if (e->type() == QEvent::MouseButtonPress) {
0336             const ulong doubleClickInterval = static_cast<ulong>(QGuiApplication::styleHints()->mouseDoubleClickInterval());
0337             const bool doubleClick = (me->timestamp() - d->lastMousePressTime < doubleClickInterval) && me->button() == d->lastMousePressButton;
0338             d->lastMousePressTime = me->timestamp();
0339             d->lastMousePressButton = me->button();
0340             if (doubleClick) {
0341                 d->lastMousePressButton = Qt::NoButton;
0342                 QMouseEvent doubleClickEvent(QEvent::MouseButtonDblClick, me->localPos(), me->windowPos(), me->screenPos(), me->button(), me->buttons(), me->modifiers());
0343                 QCoreApplication::sendEvent(d->m_view.get(), &doubleClickEvent);
0344             }
0345         }
0346 
0347         return;
0348     }
0349     case QEvent::HoverEnter:
0350     case QEvent::HoverLeave:
0351     case QEvent::HoverMove: {
0352         QHoverEvent *he = static_cast<QHoverEvent *>(e);
0353         const QPointF widgetPos = d->m_view->mapFromGlobal(he->pos());
0354         const QPointF oldWidgetPos = d->m_view->mapFromGlobal(he->oldPos());
0355         QHoverEvent cloneEvent(he->type(), widgetPos, oldWidgetPos, he->modifiers());
0356         QCoreApplication::sendEvent(d->m_view.get(), &cloneEvent);
0357         e->setAccepted(cloneEvent.isAccepted());
0358         return;
0359     }
0360     case QEvent::Wheel: {
0361         QWheelEvent *we = static_cast<QWheelEvent *>(e);
0362         const QPointF widgetPos = d->m_view->mapFromGlobal(we->position().toPoint());
0363         QWheelEvent cloneEvent(widgetPos, we->globalPosition(), we->pixelDelta(), we->angleDelta(), we->buttons(),
0364                                we->modifiers(), we->phase(), we->inverted());
0365         QCoreApplication::sendEvent(d->m_view.get(), &cloneEvent);
0366         e->setAccepted(cloneEvent.isAccepted());
0367         return;
0368     }
0369     default:
0370         return;
0371     }
0372 }
0373 
0374 void OffscreenQuickView::forwardKeyEvent(QKeyEvent *keyEvent)
0375 {
0376     if (!d->m_visible) {
0377         return;
0378     }
0379     QCoreApplication::sendEvent(d->m_view.get(), keyEvent);
0380 }
0381 
0382 bool OffscreenQuickView::forwardTouchDown(qint32 id, const QPointF &pos, std::chrono::microseconds time)
0383 {
0384     d->updateTouchState(Qt::TouchPointPressed, id, pos);
0385 
0386 #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
0387     QTouchEvent event(QEvent::TouchBegin, d->touchDevice, Qt::NoModifier, d->touchState, d->touchPoints);
0388 #else
0389     QTouchEvent event(QEvent::TouchBegin, d->touchDevice, Qt::NoModifier, d->touchPoints);
0390 #endif
0391     event.setTimestamp(std::chrono::duration_cast<std::chrono::milliseconds>(time).count());
0392     QCoreApplication::sendEvent(d->m_view.get(), &event);
0393 
0394     return event.isAccepted();
0395 }
0396 
0397 bool OffscreenQuickView::forwardTouchMotion(qint32 id, const QPointF &pos, std::chrono::microseconds time)
0398 {
0399     d->updateTouchState(Qt::TouchPointMoved, id, pos);
0400 
0401 #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
0402     QTouchEvent event(QEvent::TouchUpdate, d->touchDevice, Qt::NoModifier, d->touchState, d->touchPoints);
0403 #else
0404     QTouchEvent event(QEvent::TouchUpdate, d->touchDevice, Qt::NoModifier, d->touchPoints);
0405 #endif
0406     event.setTimestamp(std::chrono::duration_cast<std::chrono::milliseconds>(time).count());
0407     QCoreApplication::sendEvent(d->m_view.get(), &event);
0408 
0409     return event.isAccepted();
0410 }
0411 
0412 bool OffscreenQuickView::forwardTouchUp(qint32 id, std::chrono::microseconds time)
0413 {
0414     d->updateTouchState(Qt::TouchPointReleased, id, QPointF{});
0415 
0416 #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
0417     QTouchEvent event(QEvent::TouchEnd, d->touchDevice, Qt::NoModifier, d->touchState, d->touchPoints);
0418 #else
0419     QTouchEvent event(QEvent::TouchEnd, d->touchDevice, Qt::NoModifier, d->touchPoints);
0420 #endif
0421     event.setTimestamp(std::chrono::duration_cast<std::chrono::milliseconds>(time).count());
0422     QCoreApplication::sendEvent(d->m_view.get(), &event);
0423 
0424     return event.isAccepted();
0425 }
0426 
0427 QRect OffscreenQuickView::geometry() const
0428 {
0429     return d->m_view->geometry();
0430 }
0431 
0432 void OffscreenQuickView::setOpacity(qreal opacity)
0433 {
0434     d->m_view->setOpacity(opacity);
0435 }
0436 
0437 qreal OffscreenQuickView::opacity() const
0438 {
0439     return d->m_view->opacity();
0440 }
0441 
0442 QQuickItem *OffscreenQuickView::contentItem() const
0443 {
0444     return d->m_view->contentItem();
0445 }
0446 
0447 QQuickWindow *OffscreenQuickView::window() const
0448 {
0449     return d->m_view.get();
0450 }
0451 
0452 void OffscreenQuickView::setVisible(bool visible)
0453 {
0454     if (d->m_visible == visible) {
0455         return;
0456     }
0457     d->m_visible = visible;
0458 
0459     if (visible) {
0460         Q_EMIT d->m_renderControl->renderRequested();
0461     } else {
0462         // deferred to not change GL context
0463         QTimer::singleShot(0, this, [this]() {
0464             d->releaseResources();
0465         });
0466     }
0467 }
0468 
0469 bool OffscreenQuickView::isVisible() const
0470 {
0471     return d->m_visible;
0472 }
0473 
0474 void OffscreenQuickView::show()
0475 {
0476     setVisible(true);
0477 }
0478 
0479 void OffscreenQuickView::hide()
0480 {
0481     setVisible(false);
0482 }
0483 
0484 GLTexture *OffscreenQuickView::bufferAsTexture()
0485 {
0486     if (d->m_useBlit) {
0487         if (d->m_image.isNull()) {
0488             return nullptr;
0489         }
0490         d->m_textureExport.reset(new GLTexture(d->m_image));
0491     } else {
0492         if (!d->m_fbo) {
0493             return nullptr;
0494         }
0495         if (!d->m_textureExport) {
0496             d->m_textureExport.reset(new GLTexture(d->m_fbo->texture(), d->m_fbo->format().internalTextureFormat(), d->m_fbo->size()));
0497         }
0498     }
0499     return d->m_textureExport.get();
0500 }
0501 
0502 QImage OffscreenQuickView::bufferAsImage() const
0503 {
0504     return d->m_image;
0505 }
0506 
0507 QSize OffscreenQuickView::size() const
0508 {
0509     return d->m_view->geometry().size();
0510 }
0511 
0512 void OffscreenQuickView::setGeometry(const QRect &rect)
0513 {
0514     const QRect oldGeometry = d->m_view->geometry();
0515     d->m_view->setGeometry(rect);
0516     Q_EMIT geometryChanged(oldGeometry, rect);
0517 }
0518 
0519 void OffscreenQuickView::Private::releaseResources()
0520 {
0521     if (m_glcontext) {
0522         m_glcontext->makeCurrent(m_offscreenSurface.get());
0523         m_view->releaseResources();
0524         m_glcontext->doneCurrent();
0525     } else {
0526         m_view->releaseResources();
0527     }
0528 }
0529 
0530 void OffscreenQuickView::Private::updateTouchState(Qt::TouchPointState state, qint32 id, const QPointF &pos)
0531 {
0532     // Remove the points that were previously in a released state, since they
0533     // are no longer relevant. Additionally, reset the state of all remaining
0534     // points to Stationary so we only have one touch point with a different
0535     // state.
0536     touchPoints.erase(std::remove_if(touchPoints.begin(), touchPoints.end(), [](QTouchEvent::TouchPoint &point) {
0537                           if (point.state() == Qt::TouchPointReleased) {
0538                               return true;
0539                           }
0540 #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
0541                           point.setState(Qt::TouchPointStationary);
0542 #else
0543                           QMutableEventPoint::setState(point, QEventPoint::Stationary);
0544 #endif
0545                           return false;
0546                       }),
0547                       touchPoints.end());
0548 
0549     // QtQuick Pointer Handlers incorrectly consider a touch point with ID 0
0550     // to be an invalid touch point. This has been fixed in Qt 6 but could not
0551     // be fixed for Qt 5. Instead, we offset kwin's internal IDs with this
0552     // offset to trick QtQuick into treating them as valid points.
0553     static const qint32 idOffset = 111;
0554 
0555     // Find the touch point that has changed. This is separate from the above
0556     // loop because removing the released touch points invalidates iterators.
0557     auto changed = std::find_if(touchPoints.begin(), touchPoints.end(), [id](const QTouchEvent::TouchPoint &point) {
0558         return point.id() == id + idOffset;
0559     });
0560 
0561     switch (state) {
0562     case Qt::TouchPointPressed: {
0563         if (changed != touchPoints.end()) {
0564             return;
0565         }
0566 
0567         QTouchEvent::TouchPoint point;
0568 #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
0569         point.setState(Qt::TouchPointPressed);
0570         point.setId(id + idOffset);
0571         point.setScreenPos(pos);
0572         point.setScenePos(m_view->mapFromGlobal(pos.toPoint()));
0573         point.setPos(m_view->mapFromGlobal(pos.toPoint()));
0574 #else
0575         QMutableEventPoint::setState(point, QEventPoint::Pressed);
0576         QMutableEventPoint::setId(point, id + idOffset);
0577         QMutableEventPoint::setGlobalPosition(point, pos);
0578         QMutableEventPoint::setScenePosition(point, m_view->mapFromGlobal(pos.toPoint()));
0579         QMutableEventPoint::setPosition(point, m_view->mapFromGlobal(pos.toPoint()));
0580 #endif
0581 
0582         touchPoints.append(point);
0583     } break;
0584     case Qt::TouchPointMoved: {
0585         if (changed == touchPoints.end()) {
0586             return;
0587         }
0588 
0589         auto &point = *changed;
0590 #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
0591         point.setLastPos(point.pos());
0592         point.setLastScenePos(point.scenePos());
0593         point.setLastScreenPos(point.screenPos());
0594         point.setState(Qt::TouchPointMoved);
0595         point.setScenePos(m_view->mapFromGlobal(pos.toPoint()));
0596         point.setPos(m_view->mapFromGlobal(pos.toPoint()));
0597         point.setScreenPos(pos);
0598 #else
0599         QMutableEventPoint::setGlobalLastPosition(point, point.globalPosition());
0600         QMutableEventPoint::setState(point, QEventPoint::Updated);
0601         QMutableEventPoint::setScenePosition(point, m_view->mapFromGlobal(pos.toPoint()));
0602         QMutableEventPoint::setPosition(point, m_view->mapFromGlobal(pos.toPoint()));
0603         QMutableEventPoint::setGlobalPosition(point, pos);
0604 #endif
0605     } break;
0606     case Qt::TouchPointReleased: {
0607         if (changed == touchPoints.end()) {
0608             return;
0609         }
0610 
0611         auto &point = *changed;
0612 #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
0613         point.setLastPos(point.pos());
0614         point.setLastScreenPos(point.screenPos());
0615         point.setState(Qt::TouchPointReleased);
0616 #else
0617         QMutableEventPoint::setGlobalLastPosition(point, point.globalPosition());
0618         QMutableEventPoint::setState(point, QEventPoint::Released);
0619 #endif
0620     } break;
0621     default:
0622         break;
0623     }
0624 
0625     // The touch state value is used in QTouchEvent and includes all the states
0626     // that the current touch points are in.
0627 #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
0628     touchState = std::accumulate(touchPoints.begin(), touchPoints.end(), Qt::TouchPointStates{}, [](auto init, const auto &point) {
0629         return init | point.state();
0630     });
0631 #endif
0632 }
0633 
0634 OffscreenQuickScene::OffscreenQuickScene(QObject *parent)
0635     : OffscreenQuickView(parent)
0636     , d(new OffscreenQuickScene::Private)
0637 {
0638 }
0639 
0640 OffscreenQuickScene::OffscreenQuickScene(QObject *parent, QWindow *renderWindow)
0641     : OffscreenQuickView(parent, renderWindow)
0642     , d(new OffscreenQuickScene::Private)
0643 {
0644 }
0645 
0646 OffscreenQuickScene::OffscreenQuickScene(QObject *parent, QWindow *renderWindow, ExportMode exportMode)
0647     : OffscreenQuickView(parent, renderWindow, exportMode)
0648     , d(new OffscreenQuickScene::Private)
0649 {
0650 }
0651 
0652 OffscreenQuickScene::OffscreenQuickScene(QObject *parent, OffscreenQuickView::ExportMode exportMode)
0653     : OffscreenQuickView(parent, exportMode)
0654     , d(new OffscreenQuickScene::Private)
0655 {
0656 }
0657 
0658 OffscreenQuickScene::~OffscreenQuickScene() = default;
0659 
0660 void OffscreenQuickScene::setSource(const QUrl &source)
0661 {
0662     setSource(source, QVariantMap());
0663 }
0664 
0665 void OffscreenQuickScene::setSource(const QUrl &source, const QVariantMap &initialProperties)
0666 {
0667     if (!d->qmlComponent) {
0668         d->qmlComponent.reset(new QQmlComponent(d->qmlEngine.get()));
0669     }
0670 
0671     d->qmlComponent->loadUrl(source);
0672     if (d->qmlComponent->isError()) {
0673         qCWarning(LIBKWINEFFECTS).nospace() << "Failed to load effect quick view " << source << ": " << d->qmlComponent->errors();
0674         d->qmlComponent.reset();
0675         return;
0676     }
0677 
0678     d->quickItem.reset();
0679 
0680     std::unique_ptr<QObject> qmlObject(d->qmlComponent->createWithInitialProperties(initialProperties));
0681     QQuickItem *item = qobject_cast<QQuickItem *>(qmlObject.get());
0682     if (!item) {
0683         qCWarning(LIBKWINEFFECTS) << "Root object of effect quick view" << source << "is not a QQuickItem";
0684         return;
0685     }
0686 
0687     qmlObject.release();
0688     d->quickItem.reset(item);
0689 
0690     item->setParentItem(contentItem());
0691 
0692     auto updateSize = [item, this]() {
0693         item->setSize(contentItem()->size());
0694     };
0695     updateSize();
0696     connect(contentItem(), &QQuickItem::widthChanged, item, updateSize);
0697     connect(contentItem(), &QQuickItem::heightChanged, item, updateSize);
0698 }
0699 
0700 QQmlContext *OffscreenQuickScene::rootContext() const
0701 {
0702     return d->qmlEngine->rootContext();
0703 }
0704 
0705 QQuickItem *OffscreenQuickScene::rootItem() const
0706 {
0707     return d->quickItem.get();
0708 }
0709 
0710 } // namespace KWin
0711 
0712 #include "kwinoffscreenquickview.moc"