File indexing completed on 2025-03-23 11:13:59
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"