1 | // Copyright (C) 2020 The Qt Company Ltd. |
2 | // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only |
3 | |
4 | #include "qquickwindow.h" |
5 | #include "qquickwindow_p.h" |
6 | |
7 | #include "qquickitem.h" |
8 | #include "qquickitem_p.h" |
9 | #include "qquickevents_p_p.h" |
10 | #include "qquickgraphicsdevice_p.h" |
11 | |
12 | #include <QtQuick/private/qsgrenderer_p.h> |
13 | #include <QtQuick/private/qsgplaintexture_p.h> |
14 | #include <QtQuick/private/qquickpointerhandler_p.h> |
15 | #include <private/qsgrenderloop_p.h> |
16 | #include <private/qsgrhisupport_p.h> |
17 | #include <private/qquickrendercontrol_p.h> |
18 | #include <private/qquickanimatorcontroller_p.h> |
19 | #include <private/qquickprofiler_p.h> |
20 | #include <private/qquicktextinterface_p.h> |
21 | |
22 | #include <private/qguiapplication_p.h> |
23 | |
24 | #include <private/qabstractanimation_p.h> |
25 | |
26 | #include <QtGui/qpainter.h> |
27 | #include <QtGui/qevent.h> |
28 | #include <QtGui/qmatrix4x4.h> |
29 | #include <QtGui/private/qevent_p.h> |
30 | #include <QtGui/private/qpointingdevice_p.h> |
31 | #include <QtCore/qvarlengtharray.h> |
32 | #include <QtCore/qabstractanimation.h> |
33 | #include <QtCore/QLibraryInfo> |
34 | #include <QtCore/QRunnable> |
35 | #include <QtQml/qqmlincubator.h> |
36 | #include <QtQml/qqmlinfo.h> |
37 | #include <QtQml/private/qqmlmetatype_p.h> |
38 | |
39 | #include <QtQuick/private/qquickpixmapcache_p.h> |
40 | |
41 | #include <private/qqmldebugserviceinterfaces_p.h> |
42 | #include <private/qqmldebugconnector_p.h> |
43 | #include <private/qsgdefaultrendercontext_p.h> |
44 | #if QT_CONFIG(opengl) |
45 | #include <private/qopengl_p.h> |
46 | #include <QOpenGLContext> |
47 | #endif |
48 | #ifndef QT_NO_DEBUG_STREAM |
49 | #include <private/qdebug_p.h> |
50 | #endif |
51 | |
52 | #include <rhi/qrhi.h> |
53 | |
54 | QT_BEGIN_NAMESPACE |
55 | |
56 | Q_DECLARE_LOGGING_CATEGORY(lcHoverTrace) |
57 | Q_DECLARE_LOGGING_CATEGORY(lcMouse) |
58 | Q_DECLARE_LOGGING_CATEGORY(lcTouch) |
59 | Q_DECLARE_LOGGING_CATEGORY(lcPtr) |
60 | Q_LOGGING_CATEGORY(lcDirty, "qt.quick.dirty" ) |
61 | Q_LOGGING_CATEGORY(lcTransient, "qt.quick.window.transient" ) |
62 | |
63 | bool QQuickWindowPrivate::defaultAlphaBuffer = false; |
64 | |
65 | #if defined(QT_QUICK_DEFAULT_TEXT_RENDER_TYPE) |
66 | QQuickWindow::TextRenderType QQuickWindowPrivate::textRenderType = QQuickWindow::QT_QUICK_DEFAULT_TEXT_RENDER_TYPE; |
67 | #else |
68 | QQuickWindow::TextRenderType QQuickWindowPrivate::textRenderType = QQuickWindow::QtTextRendering; |
69 | #endif |
70 | |
71 | class QQuickWindowIncubationController : public QObject, public QQmlIncubationController |
72 | { |
73 | Q_OBJECT |
74 | |
75 | public: |
76 | QQuickWindowIncubationController(QSGRenderLoop *loop) |
77 | : m_renderLoop(loop), m_timer(0) |
78 | { |
79 | // Allow incubation for 1/3 of a frame. |
80 | m_incubation_time = qMax(a: 1, b: int(1000 / QGuiApplication::primaryScreen()->refreshRate()) / 3); |
81 | |
82 | QAnimationDriver *animationDriver = m_renderLoop->animationDriver(); |
83 | if (animationDriver) { |
84 | connect(sender: animationDriver, SIGNAL(stopped()), receiver: this, SLOT(animationStopped())); |
85 | connect(sender: m_renderLoop, SIGNAL(timeToIncubate()), receiver: this, SLOT(incubate())); |
86 | } |
87 | } |
88 | |
89 | protected: |
90 | void timerEvent(QTimerEvent *) override |
91 | { |
92 | killTimer(id: m_timer); |
93 | m_timer = 0; |
94 | incubate(); |
95 | } |
96 | |
97 | void incubateAgain() { |
98 | if (m_timer == 0) { |
99 | // Wait for a while before processing the next batch. Using a |
100 | // timer to avoid starvation of system events. |
101 | m_timer = startTimer(interval: m_incubation_time); |
102 | } |
103 | } |
104 | |
105 | public slots: |
106 | void incubate() { |
107 | if (m_renderLoop && incubatingObjectCount()) { |
108 | if (m_renderLoop->interleaveIncubation()) { |
109 | incubateFor(msecs: m_incubation_time); |
110 | } else { |
111 | incubateFor(msecs: m_incubation_time * 2); |
112 | if (incubatingObjectCount()) |
113 | incubateAgain(); |
114 | } |
115 | } |
116 | } |
117 | |
118 | void animationStopped() { incubate(); } |
119 | |
120 | protected: |
121 | void incubatingObjectCountChanged(int count) override |
122 | { |
123 | if (count && m_renderLoop && !m_renderLoop->interleaveIncubation()) |
124 | incubateAgain(); |
125 | } |
126 | |
127 | private: |
128 | QPointer<QSGRenderLoop> m_renderLoop; |
129 | int m_incubation_time; |
130 | int m_timer; |
131 | }; |
132 | |
133 | #if QT_CONFIG(accessibility) |
134 | /*! |
135 | Returns an accessibility interface for this window, or 0 if such an |
136 | interface cannot be created. |
137 | */ |
138 | QAccessibleInterface *QQuickWindow::accessibleRoot() const |
139 | { |
140 | return QAccessible::queryAccessibleInterface(const_cast<QQuickWindow*>(this)); |
141 | } |
142 | #endif |
143 | |
144 | |
145 | /* |
146 | Focus behavior |
147 | ============== |
148 | |
149 | Prior to being added to a valid window items can set and clear focus with no |
150 | effect. Only once items are added to a window (by way of having a parent set that |
151 | already belongs to a window) do the focus rules apply. Focus goes back to |
152 | having no effect if an item is removed from a window. |
153 | |
154 | When an item is moved into a new focus scope (either being added to a window |
155 | for the first time, or having its parent changed), if the focus scope already has |
156 | a scope focused item that takes precedence over the item being added. Otherwise, |
157 | the focus of the added tree is used. In the case of a tree of items being |
158 | added to a window for the first time, which may have a conflicted focus state (two |
159 | or more items in one scope having focus set), the same rule is applied item by item - |
160 | thus the first item that has focus will get it (assuming the scope doesn't already |
161 | have a scope focused item), and the other items will have their focus cleared. |
162 | */ |
163 | |
164 | QQuickRootItem::QQuickRootItem() |
165 | { |
166 | // child items with ItemObservesViewport can treat the window's content item |
167 | // as the ultimate viewport: avoid populating SG nodes that fall outside |
168 | setFlag(flag: ItemIsViewport); |
169 | } |
170 | |
171 | /*! \reimp */ |
172 | void QQuickWindow::exposeEvent(QExposeEvent *) |
173 | { |
174 | Q_D(QQuickWindow); |
175 | if (d->windowManager) |
176 | d->windowManager->exposureChanged(window: this); |
177 | } |
178 | |
179 | /*! \reimp */ |
180 | void QQuickWindow::resizeEvent(QResizeEvent *ev) |
181 | { |
182 | Q_D(QQuickWindow); |
183 | if (d->contentItem) |
184 | d->contentItem->setSize(ev->size()); |
185 | if (d->windowManager) |
186 | d->windowManager->resize(this); |
187 | } |
188 | |
189 | /*! \reimp */ |
190 | void QQuickWindow::showEvent(QShowEvent *) |
191 | { |
192 | Q_D(QQuickWindow); |
193 | if (d->windowManager) |
194 | d->windowManager->show(window: this); |
195 | } |
196 | |
197 | /*! \reimp */ |
198 | void QQuickWindow::hideEvent(QHideEvent *) |
199 | { |
200 | Q_D(QQuickWindow); |
201 | if (auto da = d->deliveryAgentPrivate()) |
202 | da->handleWindowHidden(win: this); |
203 | if (d->windowManager) |
204 | d->windowManager->hide(window: this); |
205 | } |
206 | |
207 | /*! \reimp */ |
208 | void QQuickWindow::closeEvent(QCloseEvent *e) |
209 | { |
210 | QQuickCloseEvent qev; |
211 | qev.setAccepted(e->isAccepted()); |
212 | emit closing(close: &qev); |
213 | e->setAccepted(qev.isAccepted()); |
214 | } |
215 | |
216 | /*! \reimp */ |
217 | void QQuickWindow::focusOutEvent(QFocusEvent *ev) |
218 | { |
219 | Q_D(QQuickWindow); |
220 | if (d->contentItem) |
221 | d->contentItem->setFocus(focus: false, reason: ev->reason()); |
222 | } |
223 | |
224 | /*! \reimp */ |
225 | void QQuickWindow::focusInEvent(QFocusEvent *ev) |
226 | { |
227 | Q_D(QQuickWindow); |
228 | if (d->inDestructor) |
229 | return; |
230 | if (d->contentItem) |
231 | d->contentItem->setFocus(focus: true, reason: ev->reason()); |
232 | if (auto da = d->deliveryAgentPrivate()) |
233 | da->updateFocusItemTransform(); |
234 | } |
235 | |
236 | #if QT_CONFIG(im) |
237 | static bool transformDirtyOnItemOrAncestor(const QQuickItem *item) |
238 | { |
239 | while (item) { |
240 | if (QQuickItemPrivate::get(item)->dirtyAttributes & ( |
241 | QQuickItemPrivate::TransformOrigin | |
242 | QQuickItemPrivate::Transform | |
243 | QQuickItemPrivate::BasicTransform | |
244 | QQuickItemPrivate::Position | |
245 | QQuickItemPrivate::Size | |
246 | QQuickItemPrivate::ParentChanged | |
247 | QQuickItemPrivate::Clip)) { |
248 | return true; |
249 | } |
250 | item = item->parentItem(); |
251 | } |
252 | return false; |
253 | } |
254 | #endif |
255 | |
256 | /*! |
257 | * \internal |
258 | |
259 | A "polish loop" can occur inside QQuickWindowPrivate::polishItems(). It is when an item calls |
260 | polish() on an(other?) item from updatePolish(). If this anomaly happens repeatedly and without |
261 | interruption (of a well-behaved updatePolish() that doesn't call polish()), it is a strong |
262 | indication that we are heading towards an infinite polish loop. A polish loop is not a bug in |
263 | Qt Quick - it is a bug caused by ill-behaved items put in the scene. |
264 | |
265 | We can detect this sequence of polish loops easily, since the |
266 | QQuickWindowPrivate::itemsToPolish is basically a stack: polish() will push to it, and |
267 | polishItems() will pop from it. |
268 | Therefore if updatePolish() calls polish(), the immediate next item polishItems() processes is |
269 | the item that was polished by the previous call to updatePolish(). |
270 | We therefore just need to count the number of polish loops we detected in _sequence_. |
271 | */ |
272 | struct PolishLoopDetector |
273 | { |
274 | PolishLoopDetector(const QVector<QQuickItem*> &itemsToPolish) |
275 | : itemsToPolish(itemsToPolish) |
276 | { |
277 | } |
278 | |
279 | /* |
280 | * returns true when it detected a likely infinite loop |
281 | * (suggests it should abort the polish loop) |
282 | **/ |
283 | bool check(QQuickItem *item, int itemsRemainingBeforeUpdatePolish) |
284 | { |
285 | if (itemsToPolish.size() > itemsRemainingBeforeUpdatePolish) { |
286 | // Detected potential polish loop. |
287 | ++numPolishLoopsInSequence; |
288 | if (numPolishLoopsInSequence >= 1000) { |
289 | // Start to warn about polish loop after 1000 consecutive polish loops |
290 | if (numPolishLoopsInSequence == 100000) { |
291 | // We have looped 100,000 times without actually reducing the list of items to |
292 | // polish, give up for now. |
293 | // This is not a fix, just a remedy so that the application can be somewhat |
294 | // responsive. |
295 | numPolishLoopsInSequence = 0; |
296 | return true; |
297 | } else if (numPolishLoopsInSequence < 1005) { |
298 | // Show the 5 next items involved in the polish loop. |
299 | // (most likely they will be the same 5 items...) |
300 | QQuickItem *guiltyItem = itemsToPolish.last(); |
301 | qmlWarning(me: item) << "possible QQuickItem::polish() loop" ; |
302 | |
303 | auto typeAndObjectName = [](QQuickItem *item) { |
304 | QString typeName = QQmlMetaType::prettyTypeName(object: item); |
305 | QString objName = item->objectName(); |
306 | if (!objName.isNull()) |
307 | return QLatin1String("%1(%2)" ).arg(args&: typeName, args&: objName); |
308 | return typeName; |
309 | }; |
310 | |
311 | qmlWarning(me: guiltyItem) << typeAndObjectName(guiltyItem) |
312 | << " called polish() inside updatePolish() of " << typeAndObjectName(item); |
313 | |
314 | if (numPolishLoopsInSequence == 1004) |
315 | // Enough warnings. Reset counter in order to speed things up and re-detect |
316 | // more loops |
317 | numPolishLoopsInSequence = 0; |
318 | } |
319 | } |
320 | } else { |
321 | numPolishLoopsInSequence = 0; |
322 | } |
323 | return false; |
324 | } |
325 | const QVector<QQuickItem*> &itemsToPolish; // Just a ref to the one in polishItems() |
326 | int numPolishLoopsInSequence = 0; |
327 | }; |
328 | |
329 | void QQuickWindowPrivate::polishItems() |
330 | { |
331 | // An item can trigger polish on another item, or itself for that matter, |
332 | // during its updatePolish() call. Because of this, we cannot simply |
333 | // iterate through the set, we must continue pulling items out until it |
334 | // is empty. |
335 | // In the case where polish is called from updatePolish() either directly |
336 | // or indirectly, we use a PolishLoopDetector to determine if a warning should |
337 | // be printed to the user. |
338 | |
339 | PolishLoopDetector polishLoopDetector(itemsToPolish); |
340 | while (!itemsToPolish.isEmpty()) { |
341 | QQuickItem *item = itemsToPolish.takeLast(); |
342 | QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item); |
343 | itemPrivate->polishScheduled = false; |
344 | const int itemsRemaining = itemsToPolish.size(); |
345 | itemPrivate->updatePolish(); |
346 | item->updatePolish(); |
347 | if (polishLoopDetector.check(item, itemsRemainingBeforeUpdatePolish: itemsRemaining) == true) |
348 | break; |
349 | } |
350 | |
351 | #if QT_CONFIG(im) |
352 | if (QQuickItem *focusItem = q_func()->activeFocusItem()) { |
353 | // If the current focus item, or any of its anchestors, has changed location |
354 | // inside the window, we need inform IM about it. This to ensure that overlays |
355 | // such as selection handles will be updated. |
356 | const bool isActiveFocusItem = (focusItem == QGuiApplication::focusObject()); |
357 | const bool hasImEnabled = focusItem->inputMethodQuery(query: Qt::ImEnabled).toBool(); |
358 | if (isActiveFocusItem && hasImEnabled && transformDirtyOnItemOrAncestor(item: focusItem)) |
359 | deliveryAgentPrivate()->updateFocusItemTransform(); |
360 | } |
361 | #endif |
362 | } |
363 | |
364 | /*! |
365 | * Schedules the window to render another frame. |
366 | * |
367 | * Calling QQuickWindow::update() differs from QQuickItem::update() in that |
368 | * it always triggers a repaint, regardless of changes in the underlying |
369 | * scene graph or not. |
370 | */ |
371 | void QQuickWindow::update() |
372 | { |
373 | Q_D(QQuickWindow); |
374 | if (d->windowManager) |
375 | d->windowManager->update(window: this); |
376 | else if (d->renderControl) |
377 | QQuickRenderControlPrivate::get(renderControl: d->renderControl)->update(); |
378 | } |
379 | |
380 | static void updatePixelRatioHelper(QQuickItem *item, float pixelRatio) |
381 | { |
382 | if (item->flags() & QQuickItem::ItemHasContents) { |
383 | QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item); |
384 | itemPrivate->itemChange(QQuickItem::ItemDevicePixelRatioHasChanged, pixelRatio); |
385 | } |
386 | |
387 | QList <QQuickItem *> items = item->childItems(); |
388 | for (int i = 0; i < items.size(); ++i) |
389 | updatePixelRatioHelper(item: items.at(i), pixelRatio); |
390 | } |
391 | |
392 | void QQuickWindow::physicalDpiChanged() |
393 | { |
394 | Q_D(QQuickWindow); |
395 | const qreal newPixelRatio = effectiveDevicePixelRatio(); |
396 | if (qFuzzyCompare(p1: newPixelRatio, p2: d->lastReportedItemDevicePixelRatio)) |
397 | return; |
398 | d->lastReportedItemDevicePixelRatio = newPixelRatio; |
399 | if (d->contentItem) |
400 | updatePixelRatioHelper(item: d->contentItem, pixelRatio: newPixelRatio); |
401 | d->forcePolish(); |
402 | } |
403 | |
404 | void QQuickWindow::handleFontDatabaseChanged() |
405 | { |
406 | Q_D(QQuickWindow); |
407 | d->pendingFontUpdate = true; |
408 | } |
409 | |
410 | void forcePolishHelper(QQuickItem *item) |
411 | { |
412 | if (item->flags() & QQuickItem::ItemHasContents) { |
413 | item->polish(); |
414 | } |
415 | |
416 | QList <QQuickItem *> items = item->childItems(); |
417 | for (int i=0; i<items.size(); ++i) |
418 | forcePolishHelper(item: items.at(i)); |
419 | } |
420 | |
421 | void QQuickWindow::handleScreenChanged(QScreen *screen) |
422 | { |
423 | Q_D(QQuickWindow); |
424 | Q_UNUSED(screen); |
425 | d->forcePolish(); |
426 | } |
427 | |
428 | /*! |
429 | Schedules polish events on all items in the scene. |
430 | */ |
431 | void QQuickWindowPrivate::forcePolish() |
432 | { |
433 | Q_Q(QQuickWindow); |
434 | if (!q->screen()) |
435 | return; |
436 | forcePolishHelper(item: contentItem); |
437 | } |
438 | |
439 | void forceUpdate(QQuickItem *item) |
440 | { |
441 | if (item->flags() & QQuickItem::ItemHasContents) |
442 | item->update(); |
443 | QQuickItemPrivate::get(item)->dirty(QQuickItemPrivate::ChildrenUpdateMask); |
444 | |
445 | QList <QQuickItem *> items = item->childItems(); |
446 | for (int i=0; i<items.size(); ++i) |
447 | forceUpdate(item: items.at(i)); |
448 | } |
449 | |
450 | void QQuickWindowRenderTarget::reset(QRhi *rhi) |
451 | { |
452 | if (owns) { |
453 | if (rhi) { |
454 | delete renderTarget; |
455 | delete rpDesc; |
456 | delete texture; |
457 | delete renderBuffer; |
458 | delete depthStencil; |
459 | } |
460 | |
461 | delete paintDevice; |
462 | } |
463 | |
464 | renderTarget = nullptr; |
465 | rpDesc = nullptr; |
466 | texture = nullptr; |
467 | renderBuffer = nullptr; |
468 | depthStencil = nullptr; |
469 | paintDevice = nullptr; |
470 | owns = false; |
471 | } |
472 | |
473 | void QQuickWindowPrivate::invalidateFontData(QQuickItem *item) |
474 | { |
475 | QQuickTextInterface *textItem = qobject_cast<QQuickTextInterface *>(object: item); |
476 | if (textItem != nullptr) |
477 | textItem->invalidate(); |
478 | |
479 | QList<QQuickItem *> children = item->childItems(); |
480 | for (QQuickItem *child : children) |
481 | invalidateFontData(item: child); |
482 | } |
483 | |
484 | void QQuickWindowPrivate::ensureCustomRenderTarget() |
485 | { |
486 | // resolve() can be expensive when importing an existing native texture, so |
487 | // it is important to only do it when the QQuickRenderTarget* was really changed |
488 | if (!redirect.renderTargetDirty) |
489 | return; |
490 | |
491 | redirect.renderTargetDirty = false; |
492 | |
493 | redirect.rt.reset(rhi); |
494 | |
495 | // a default constructed QQuickRenderTarget means no redirection |
496 | if (customRenderTarget.isNull()) |
497 | return; |
498 | |
499 | QQuickRenderTargetPrivate::get(rt: &customRenderTarget)->resolve(rhi, dst: &redirect.rt); |
500 | } |
501 | |
502 | void QQuickWindowPrivate::setCustomCommandBuffer(QRhiCommandBuffer *cb) |
503 | { |
504 | // ownership not transferred |
505 | redirect.commandBuffer = cb; |
506 | } |
507 | |
508 | void QQuickWindowPrivate::syncSceneGraph() |
509 | { |
510 | Q_Q(QQuickWindow); |
511 | |
512 | ensureCustomRenderTarget(); |
513 | |
514 | QRhiCommandBuffer *cb = nullptr; |
515 | if (rhi) { |
516 | if (redirect.commandBuffer) |
517 | cb = redirect.commandBuffer; |
518 | else |
519 | cb = swapchain->currentFrameCommandBuffer(); |
520 | } |
521 | context->prepareSync(devicePixelRatio: q->effectiveDevicePixelRatio(), cb, config: graphicsConfig); |
522 | |
523 | animationController->beforeNodeSync(); |
524 | |
525 | emit q->beforeSynchronizing(); |
526 | runAndClearJobs(jobs: &beforeSynchronizingJobs); |
527 | |
528 | if (pendingFontUpdate) { |
529 | QFont::cleanup(); |
530 | invalidateFontData(item: contentItem); |
531 | } |
532 | |
533 | if (!renderer) { |
534 | forceUpdate(item: contentItem); |
535 | |
536 | QSGRootNode *rootNode = new QSGRootNode; |
537 | rootNode->appendChildNode(node: QQuickItemPrivate::get(item: contentItem)->itemNode()); |
538 | const bool useDepth = graphicsConfig.isDepthBufferEnabledFor2D(); |
539 | const QSGRendererInterface::RenderMode renderMode = useDepth ? QSGRendererInterface::RenderMode2D |
540 | : QSGRendererInterface::RenderMode2DNoDepthBuffer; |
541 | renderer = context->createRenderer(renderMode); |
542 | renderer->setRootNode(rootNode); |
543 | } |
544 | |
545 | updateDirtyNodes(); |
546 | |
547 | animationController->afterNodeSync(); |
548 | |
549 | // Copy the current state of clearing from window into renderer. |
550 | renderer->setClearColor(clearColor); |
551 | // Cannot skip clearing the color buffer in Qt 6 anymore. |
552 | const QSGAbstractRenderer::ClearMode mode = QSGAbstractRenderer::ClearColorBuffer |
553 | | QSGAbstractRenderer::ClearStencilBuffer |
554 | | QSGAbstractRenderer::ClearDepthBuffer; |
555 | renderer->setClearMode(mode); |
556 | |
557 | renderer->setVisualizationMode(visualizationMode); |
558 | |
559 | if (pendingFontUpdate) { |
560 | context->invalidateGlyphCaches(); |
561 | pendingFontUpdate = false; |
562 | } |
563 | |
564 | emit q->afterSynchronizing(); |
565 | runAndClearJobs(jobs: &afterSynchronizingJobs); |
566 | } |
567 | |
568 | void QQuickWindowPrivate::emitBeforeRenderPassRecording(void *ud) |
569 | { |
570 | QQuickWindow *w = reinterpret_cast<QQuickWindow *>(ud); |
571 | emit w->beforeRenderPassRecording(); |
572 | } |
573 | |
574 | void QQuickWindowPrivate::emitAfterRenderPassRecording(void *ud) |
575 | { |
576 | QQuickWindow *w = reinterpret_cast<QQuickWindow *>(ud); |
577 | emit w->afterRenderPassRecording(); |
578 | } |
579 | |
580 | void QQuickWindowPrivate::renderSceneGraph() |
581 | { |
582 | Q_Q(QQuickWindow); |
583 | if (!renderer) |
584 | return; |
585 | |
586 | ensureCustomRenderTarget(); |
587 | |
588 | QSGRenderTarget sgRenderTarget; |
589 | if (rhi) { |
590 | QRhiRenderTarget *rt; |
591 | QRhiRenderPassDescriptor *rp; |
592 | QRhiCommandBuffer *cb; |
593 | if (redirect.rt.renderTarget) { |
594 | rt = redirect.rt.renderTarget; |
595 | rp = rt->renderPassDescriptor(); |
596 | if (!rp) { |
597 | qWarning(msg: "Custom render target is set but no renderpass descriptor has been provided." ); |
598 | return; |
599 | } |
600 | cb = redirect.commandBuffer; |
601 | if (!cb) { |
602 | qWarning(msg: "Custom render target is set but no command buffer has been provided." ); |
603 | return; |
604 | } |
605 | } else { |
606 | if (!swapchain) { |
607 | qWarning(msg: "QQuickWindow: No render target (neither swapchain nor custom target was provided)" ); |
608 | return; |
609 | } |
610 | rt = swapchain->currentFrameRenderTarget(); |
611 | rp = rpDescForSwapchain; |
612 | cb = swapchain->currentFrameCommandBuffer(); |
613 | } |
614 | sgRenderTarget = QSGRenderTarget(rt, rp, cb); |
615 | } else { |
616 | sgRenderTarget = QSGRenderTarget(redirect.rt.paintDevice); |
617 | } |
618 | |
619 | context->beginNextFrame(renderer, |
620 | renderTarget: sgRenderTarget, |
621 | mainPassRecordingStart: emitBeforeRenderPassRecording, |
622 | mainPassRecordingEnd: emitAfterRenderPassRecording, |
623 | callbackUserData: q); |
624 | |
625 | animationController->advance(); |
626 | emit q->beforeRendering(); |
627 | runAndClearJobs(jobs: &beforeRenderingJobs); |
628 | |
629 | const qreal devicePixelRatio = q->effectiveDevicePixelRatio(); |
630 | QSize pixelSize; |
631 | if (redirect.rt.renderTarget) |
632 | pixelSize = redirect.rt.renderTarget->pixelSize(); |
633 | else if (redirect.rt.paintDevice) |
634 | pixelSize = QSize(redirect.rt.paintDevice->width(), redirect.rt.paintDevice->height()); |
635 | else if (rhi) |
636 | pixelSize = swapchain->currentPixelSize(); |
637 | else // software or other backend |
638 | pixelSize = q->size() * devicePixelRatio; |
639 | |
640 | renderer->setDevicePixelRatio(devicePixelRatio); |
641 | renderer->setDeviceRect(QRect(QPoint(0, 0), pixelSize)); |
642 | renderer->setViewportRect(QRect(QPoint(0, 0), pixelSize)); |
643 | |
644 | QSGAbstractRenderer::MatrixTransformFlags matrixFlags; |
645 | bool flipY = rhi ? !rhi->isYUpInNDC() : false; |
646 | if (!customRenderTarget.isNull() && customRenderTarget.mirrorVertically()) |
647 | flipY = !flipY; |
648 | if (flipY) |
649 | matrixFlags |= QSGAbstractRenderer::MatrixTransformFlipY; |
650 | |
651 | const QRectF rect(QPointF(0, 0), pixelSize / devicePixelRatio); |
652 | renderer->setProjectionMatrixToRect(rect, flags: matrixFlags, nativeNDCFlipY: rhi && !rhi->isYUpInNDC()); |
653 | |
654 | context->renderNextFrame(renderer); |
655 | |
656 | emit q->afterRendering(); |
657 | runAndClearJobs(jobs: &afterRenderingJobs); |
658 | |
659 | context->endNextFrame(renderer); |
660 | |
661 | if (renderer && renderer->hasVisualizationModeWithContinuousUpdate()) { |
662 | // For the overdraw visualizer. This update is not urgent so avoid a |
663 | // direct update() call, this is only here to keep the overdraw |
664 | // visualization box rotating even when the scene is static. |
665 | QCoreApplication::postEvent(receiver: q, event: new QEvent(QEvent::Type(FullUpdateRequest))); |
666 | } |
667 | } |
668 | |
669 | QQuickWindowPrivate::QQuickWindowPrivate() |
670 | : contentItem(nullptr) |
671 | , dirtyItemList(nullptr) |
672 | , lastReportedItemDevicePixelRatio(0) |
673 | , context(nullptr) |
674 | , renderer(nullptr) |
675 | , windowManager(nullptr) |
676 | , renderControl(nullptr) |
677 | , clearColor(Qt::white) |
678 | , persistentGraphics(true) |
679 | , persistentSceneGraph(true) |
680 | , componentComplete(true) |
681 | , inDestructor(false) |
682 | , incubationController(nullptr) |
683 | , hasActiveSwapchain(false) |
684 | , hasRenderableSwapchain(false) |
685 | , swapchainJustBecameRenderable(false) |
686 | , updatesEnabled(true) |
687 | { |
688 | } |
689 | |
690 | QQuickWindowPrivate::~QQuickWindowPrivate() |
691 | { |
692 | inDestructor = true; |
693 | redirect.rt.reset(rhi); |
694 | if (QQmlInspectorService *service = QQmlDebugConnector::service<QQmlInspectorService>()) |
695 | service->removeWindow(q_func()); |
696 | deliveryAgent = nullptr; |
697 | } |
698 | |
699 | void QQuickWindowPrivate::updateChildrenPalettes(const QPalette &parentPalette) |
700 | { |
701 | Q_Q(QQuickWindow); |
702 | if (auto root = q->contentItem()) { |
703 | for (auto &&child: root->childItems()) { |
704 | QQuickItemPrivate::get(item: child)->inheritPalette(parentPalette); |
705 | } |
706 | } |
707 | } |
708 | |
709 | void QQuickWindowPrivate::init(QQuickWindow *c, QQuickRenderControl *control) |
710 | { |
711 | q_ptr = c; |
712 | |
713 | |
714 | Q_Q(QQuickWindow); |
715 | |
716 | contentItem = new QQuickRootItem; |
717 | contentItem->setObjectName(q->objectName()); |
718 | QQml_setParent_noEvent(object: contentItem, parent: c); |
719 | QQmlEngine::setObjectOwnership(contentItem, QQmlEngine::CppOwnership); |
720 | QQuickItemPrivate *contentItemPrivate = QQuickItemPrivate::get(item: contentItem); |
721 | contentItemPrivate->window = q; |
722 | contentItemPrivate->windowRefCount = 1; |
723 | contentItemPrivate->flags |= QQuickItem::ItemIsFocusScope; |
724 | contentItem->setSize(q->size()); |
725 | deliveryAgent = new QQuickDeliveryAgent(contentItem); |
726 | |
727 | visualizationMode = qgetenv(varName: "QSG_VISUALIZE" ); |
728 | renderControl = control; |
729 | if (renderControl) |
730 | QQuickRenderControlPrivate::get(renderControl)->window = q; |
731 | |
732 | if (!renderControl) |
733 | windowManager = QSGRenderLoop::instance(); |
734 | |
735 | Q_ASSERT(windowManager || renderControl); |
736 | |
737 | QObject::connect(sender: static_cast<QGuiApplication *>(QGuiApplication::instance()), |
738 | signal: &QGuiApplication::fontDatabaseChanged, |
739 | context: q, |
740 | slot: &QQuickWindow::handleFontDatabaseChanged); |
741 | |
742 | if (q->screen()) { |
743 | lastReportedItemDevicePixelRatio = q->effectiveDevicePixelRatio(); |
744 | } |
745 | |
746 | QSGContext *sg; |
747 | if (renderControl) { |
748 | QQuickRenderControlPrivate *renderControlPriv = QQuickRenderControlPrivate::get(renderControl); |
749 | sg = renderControlPriv->sg; |
750 | context = renderControlPriv->rc; |
751 | } else { |
752 | windowManager->addWindow(win: q); |
753 | sg = windowManager->sceneGraphContext(); |
754 | context = windowManager->createRenderContext(sg); |
755 | } |
756 | |
757 | q->setSurfaceType(windowManager ? windowManager->windowSurfaceType() : QSurface::OpenGLSurface); |
758 | q->setFormat(sg->defaultSurfaceFormat()); |
759 | // When using Vulkan, associating a scenegraph-managed QVulkanInstance with |
760 | // the window (but only when not using renderControl) is deferred to |
761 | // QSGRhiSupport::createRhi(). This allows applications to set up their own |
762 | // QVulkanInstance and set that on the window, if they wish to. |
763 | |
764 | animationController.reset(other: new QQuickAnimatorController(q)); |
765 | |
766 | QObject::connect(sender: context, SIGNAL(initialized()), receiver: q, SIGNAL(sceneGraphInitialized()), Qt::DirectConnection); |
767 | QObject::connect(sender: context, SIGNAL(invalidated()), receiver: q, SIGNAL(sceneGraphInvalidated()), Qt::DirectConnection); |
768 | QObject::connect(sender: context, SIGNAL(invalidated()), receiver: q, SLOT(cleanupSceneGraph()), Qt::DirectConnection); |
769 | |
770 | QObject::connect(sender: q, SIGNAL(focusObjectChanged(QObject*)), receiver: q, SIGNAL(activeFocusItemChanged())); |
771 | QObject::connect(sender: q, SIGNAL(screenChanged(QScreen*)), receiver: q, SLOT(handleScreenChanged(QScreen*))); |
772 | QObject::connect(qApp, SIGNAL(applicationStateChanged(Qt::ApplicationState)), |
773 | receiver: q, SLOT(handleApplicationStateChanged(Qt::ApplicationState))); |
774 | QObject::connect(sender: q, SIGNAL(frameSwapped()), receiver: q, SLOT(runJobsAfterSwap()), Qt::DirectConnection); |
775 | |
776 | if (QQmlInspectorService *service = QQmlDebugConnector::service<QQmlInspectorService>()) |
777 | service->addWindow(q); |
778 | } |
779 | |
780 | void QQuickWindow::handleApplicationStateChanged(Qt::ApplicationState state) |
781 | { |
782 | Q_D(QQuickWindow); |
783 | if (state != Qt::ApplicationActive && d->contentItem) { |
784 | auto da = d->deliveryAgentPrivate(); |
785 | Q_ASSERT(da); |
786 | da->handleWindowDeactivate(win: this); |
787 | } |
788 | } |
789 | |
790 | /*! |
791 | \property QQuickWindow::data |
792 | \internal |
793 | */ |
794 | |
795 | QQmlListProperty<QObject> QQuickWindowPrivate::data() |
796 | { |
797 | return QQmlListProperty<QObject>(q_func(), nullptr, |
798 | QQuickWindowPrivate::data_append, |
799 | QQuickWindowPrivate::data_count, |
800 | QQuickWindowPrivate::data_at, |
801 | QQuickWindowPrivate::data_clear, |
802 | QQuickWindowPrivate::data_replace, |
803 | QQuickWindowPrivate::data_removeLast); |
804 | } |
805 | |
806 | void QQuickWindowPrivate::dirtyItem(QQuickItem *) |
807 | { |
808 | Q_Q(QQuickWindow); |
809 | q->maybeUpdate(); |
810 | } |
811 | |
812 | /*! |
813 | \deprecated Use QPointerEvent::exclusiveGrabber(). |
814 | Returns the item which currently has the mouse grab. |
815 | */ |
816 | QQuickItem *QQuickWindow::mouseGrabberItem() const |
817 | { |
818 | Q_D(const QQuickWindow); |
819 | auto da = const_cast<QQuickWindowPrivate *>(d)->deliveryAgentPrivate(); |
820 | Q_ASSERT(da); |
821 | // The normal use case is to call this function while an event is being delivered; |
822 | // but if the caller knows about the event, it should call QPointerEvent::exclusiveGrabber() instead. |
823 | if (auto epd = da->mousePointData()) |
824 | return qmlobject_cast<QQuickItem *>(object: epd->exclusiveGrabber); |
825 | |
826 | if (Q_LIKELY(d->deliveryAgentPrivate()->eventsInDelivery.isEmpty())) |
827 | // mousePointData() checked that already: it's one reason epd can be null |
828 | qCDebug(lcMouse, "mouse grabber ambiguous: no event is currently being delivered" ); |
829 | // If no event is being delivered, we can return "the mouse" grabber, |
830 | // but in general there could be more than one mouse, could be only a touchscreen etc. |
831 | // That's why this function is obsolete. |
832 | return qmlobject_cast<QQuickItem *>(object: QPointingDevicePrivate::get(q: QPointingDevice::primaryPointingDevice())-> |
833 | firstPointExclusiveGrabber()); |
834 | } |
835 | |
836 | void QQuickWindowPrivate::cleanup(QSGNode *n) |
837 | { |
838 | Q_Q(QQuickWindow); |
839 | |
840 | Q_ASSERT(!cleanupNodeList.contains(n)); |
841 | cleanupNodeList.append(t: n); |
842 | q->maybeUpdate(); |
843 | } |
844 | |
845 | /*! |
846 | \qmltype Window |
847 | \instantiates QQuickWindow |
848 | \inqmlmodule QtQuick |
849 | \ingroup qtquick-visual |
850 | \brief Creates a new top-level window. |
851 | |
852 | The Window object creates a new top-level window for a Qt Quick scene. It automatically sets up the |
853 | window for use with \c {QtQuick} graphical types. |
854 | |
855 | A Window can be declared inside an Item or inside another Window; in that |
856 | case the inner Window will automatically become "transient for" the outer |
857 | Window: that is, most platforms will show it centered upon the outer window |
858 | by default, and there may be other platform-dependent behaviors, depending |
859 | also on the \l flags. If the nested window is intended to be a dialog in |
860 | your application, you should also set \l flags to Qt.Dialog, because some |
861 | window managers will not provide the centering behavior without that flag. |
862 | You can also declare multiple windows inside a top-level \l QtObject, in which |
863 | case the windows will have no transient relationship. |
864 | |
865 | Alternatively you can set or bind \l x and \l y to position the Window |
866 | explicitly on the screen. |
867 | |
868 | When the user attempts to close a window, the \l closing signal will be |
869 | emitted. You can force the window to stay open (for example to prompt the |
870 | user to save changes) by writing an \c onClosing handler that sets |
871 | \c {close.accepted = false} unless it's safe to close the window (for example, |
872 | because there are no more unsaved changes). |
873 | |
874 | \code |
875 | onClosing: (close) => { |
876 | if (document.changed) { |
877 | close.accepted = false |
878 | confirmExitPopup.open() |
879 | } |
880 | } |
881 | |
882 | // The confirmExitPopup allows user to save or discard the document, |
883 | // or to cancel the closing. |
884 | \endcode |
885 | */ |
886 | |
887 | /*! |
888 | \class QQuickWindow |
889 | \since 5.0 |
890 | |
891 | \inmodule QtQuick |
892 | |
893 | \brief The QQuickWindow class provides the window for displaying a graphical QML scene. |
894 | |
895 | QQuickWindow provides the graphical scene management needed to interact with and display |
896 | a scene of QQuickItems. |
897 | |
898 | A QQuickWindow always has a single invisible root item. To add items to this window, |
899 | reparent the items to the root item or to an existing item in the scene. |
900 | |
901 | For easily displaying a scene from a QML file, see \l{QQuickView}. |
902 | |
903 | \section1 Rendering |
904 | |
905 | QQuickWindow uses a scene graph to represent what needs to be rendered. |
906 | This scene graph is disconnected from the QML scene and potentially lives in |
907 | another thread, depending on the platform implementation. Since the |
908 | rendering scene graph lives independently from the QML scene, it can also be |
909 | completely released without affecting the state of the QML scene. |
910 | |
911 | The sceneGraphInitialized() signal is emitted on the rendering thread before |
912 | the QML scene is rendered to the screen for the first time. If the rendering |
913 | scene graph has been released, the signal will be emitted again before the |
914 | next frame is rendered. A visible, on-screen QQuickWindow is driven |
915 | internally by a \c{render loop}, of which there are multiple implementations |
916 | provided in the scene graph. For details on the scene graph rendering |
917 | process, see \l{Qt Quick Scene Graph}. |
918 | |
919 | By default, a QQuickWindow renders using an accelerated 3D graphics API, |
920 | such as OpenGL or Vulkan. See \l{Scene Graph Adaptations} for a detailed |
921 | overview of scene graph backends and the supported graphics APIs. |
922 | |
923 | \warning It is crucial that graphics operations and interaction with the |
924 | scene graph happens exclusively on the rendering thread, primarily during |
925 | the updatePaintNode() phase. |
926 | |
927 | \warning As many of the signals related to rendering are emitted from the |
928 | rendering thread, connections should be made using Qt::DirectConnection. |
929 | |
930 | \section2 Integration with Accelerated 3D Graphics APIs |
931 | |
932 | It is possible to integrate OpenGL, Vulkan, Metal, or Direct3D 11 calls |
933 | directly into the QQuickWindow, as long as the QQuickWindow and the |
934 | underlying scene graph is rendering using the same API. To access native |
935 | graphics objects, such as device or context object handles, use |
936 | QSGRendererInterface. An instance of QSGRendererInterface is queriable from |
937 | QQuickWindow by calling rendererInterface(). The enablers for this |
938 | integration are the beforeRendering(), beforeRenderPassRecording(), |
939 | afterRenderPassRecording(), and related signals. These allow rendering |
940 | underlays or overlays. Alternatively, QNativeInterface::QSGOpenGLTexture, |
941 | QNativeInterface::QSGVulkanTexture, and other similar classes allow |
942 | wrapping an existing native texture or image object in a QSGTexture that |
943 | can then be used with the scene graph. |
944 | |
945 | \section2 Rendering without Acceleration |
946 | |
947 | A limited, pure software based rendering path is available as well. With the |
948 | \c software backend, a number of Qt Quick features are not available, QML |
949 | items relying on these will not be rendered at all. At the same time, this |
950 | allows QQuickWindow to be functional even on systems where there is no 3D |
951 | graphics API available at all. See \l{Qt Quick Software Adaptation} for more |
952 | details. |
953 | |
954 | \section2 Redirected Rendering |
955 | |
956 | A QQuickWindow is not necessarily backed by a native window on screen. The |
957 | rendering can be redirected to target a custom render target, such as a |
958 | given native texture. This is achieved in combination with the |
959 | QQuickRenderControl class, and functions such as setRenderTarget(), |
960 | setGraphicsDevice(), and setGraphicsConfiguration(). |
961 | |
962 | In this case, the QQuickWindow represents the scene, and provides the |
963 | intrastructure for rendering a frame. It will not be backed by a render |
964 | loop and a native window. Instead, in this case the application drives |
965 | rendering, effectively substituting for the render loops. This allows |
966 | generating image sequences, rendering into textures for use in external 3D |
967 | engines, or rendering Qt Quick content within a VR environment. |
968 | |
969 | \section2 Resource Management |
970 | |
971 | QML will try to cache images and scene graph nodes to improve performance, |
972 | but in some low-memory scenarios it might be required to aggressively |
973 | release these resources. The releaseResources() function can be used to |
974 | force the clean up of certain resources, especially resource that are cached |
975 | and can be recreated later when needed again. |
976 | |
977 | Additionally, calling releaseResources() may result in releasing the entire |
978 | scene graph and the associated graphics resources. The |
979 | sceneGraphInvalidated() signal will be emitted when this happens. This |
980 | behavior is controlled by the setPersistentGraphics() and |
981 | setPersistentSceneGraph() functions. |
982 | |
983 | \note All classes with QSG prefix should be used solely on the scene graph's |
984 | rendering thread. See \l {Scene Graph and Rendering} for more information. |
985 | |
986 | \section2 Exposure and Visibility |
987 | |
988 | When a QQuickWindow instance is deliberately hidden with hide() or |
989 | setVisible(false), it will stop rendering and its scene graph and graphics |
990 | context might be released as well. This depends on the settings configured |
991 | by setPersistentGraphics() and setPersistentSceneGraph(). The behavior in |
992 | this respect is identical to explicitly calling the releaseResources() |
993 | function. A window can become not exposed, in other words non-renderable, by |
994 | other means as well. This depends on the platform and windowing system. For |
995 | example, on Windows minimizing a window makes it stop rendering. On \macos |
996 | fully obscuring a window by other windows on top triggers the same. On |
997 | Linux/X11, the behavior is dependent on the window manager. |
998 | |
999 | \section2 OpenGL Context and Surface Formats |
1000 | |
1001 | While it is possible to specify a QSurfaceFormat for every QQuickWindow by |
1002 | calling the member function setFormat(), windows may also be created from |
1003 | QML by using the Window and ApplicationWindow elements. In this case there |
1004 | is no C++ code involved in the creation of the window instance, yet |
1005 | applications may still wish to set certain surface format values, for |
1006 | example to request a given OpenGL version or profile. Such applications can |
1007 | call the static function QSurfaceFormat::setDefaultFormat() at startup. The |
1008 | specified format will be used for all Quick windows created afterwards. |
1009 | |
1010 | \section2 Vulkan Instance |
1011 | |
1012 | When using Vulkan, a QQuickWindow is automatically associated with a |
1013 | QVulkanInstance that is created and managed internally by the scene graph. |
1014 | This way most applications do not need to worry about having a \c |
1015 | VkInstance available since it all happens automatically. In advanced cases |
1016 | an application may wish to create its own QVulkanInstance, in order to |
1017 | configure it in a specific way. That is possible as well. Calling |
1018 | \l{QWindow::setVulkanInstance()}{setVulkanInstance()} on the QQuickWindow |
1019 | right after construction, before making it visible, leads to using the |
1020 | application-supplied QVulkanInstance (and the underlying \c VkInstance). |
1021 | When redirecting via QQuickRenderControl, there is no QVulkanInstance |
1022 | provided automatically, but rather the application is expected to provide |
1023 | its own and associate it with the QQuickWindow. |
1024 | |
1025 | \section2 Graphics Contexts and Devices |
1026 | |
1027 | When the scene graph is initialized, which typically happens when the |
1028 | window becomes exposed or, in case of redirected rendering, initialization |
1029 | is performed \l{QQuickRenderControl::initialize()}{via |
1030 | QQuickRenderControl}, the context or device objects necessary for rendering |
1031 | are created automatically. This includes OpenGL contexts, Direct3D devices |
1032 | and device contexts, Vulkan and Metal devices. These are also queriable by |
1033 | application code afterwards via |
1034 | \l{QSGRendererInterface::getResource()}{QSGRendererInterface}. When using |
1035 | the \c basic render loop, which performs all rendering on the GUI thread, |
1036 | the same context or device is used with all visible QQuickWindows. The \c |
1037 | threaded render loop uses a dedicated context or device object for each |
1038 | rendering thread, and so for each QQuickWindow. With some graphics APIs, |
1039 | there is a degree of customizability provided via |
1040 | setGraphicsConfiguration(). This makes it possible, for example, to specify |
1041 | the list of Vulkan extensions to enable on the \c VkDevice. Alternatively, |
1042 | it is also possible to provide a set of existing context or device objects |
1043 | for use by the QQuickWindow, instead of letting it construct its own. This |
1044 | is achieved through setGraphicsDevice(). |
1045 | |
1046 | \sa QQuickView, QQuickRenderControl, QQuickRenderTarget, |
1047 | QQuickGraphicsDevice, QQuickGraphicsConfiguration, QSGRendererInterface |
1048 | */ |
1049 | |
1050 | /*! |
1051 | Constructs a window for displaying a QML scene with parent window \a parent. |
1052 | */ |
1053 | QQuickWindow::QQuickWindow(QWindow *parent) |
1054 | : QQuickWindow(*new QQuickWindowPrivate, parent) |
1055 | { |
1056 | } |
1057 | |
1058 | |
1059 | |
1060 | /*! |
1061 | \internal |
1062 | */ |
1063 | QQuickWindow::QQuickWindow(QQuickWindowPrivate &dd, QWindow *parent) |
1064 | : QWindow(dd, parent) |
1065 | { |
1066 | Q_D(QQuickWindow); |
1067 | d->init(c: this); |
1068 | } |
1069 | |
1070 | /*! |
1071 | Constructs a window for displaying a QML scene, whose rendering will |
1072 | be controlled by the \a control object. |
1073 | Please refer to QQuickRenderControl's documentation for more information. |
1074 | |
1075 | \since 5.4 |
1076 | */ |
1077 | QQuickWindow::QQuickWindow(QQuickRenderControl *control) |
1078 | : QWindow(*(new QQuickWindowPrivate), nullptr) |
1079 | { |
1080 | Q_D(QQuickWindow); |
1081 | d->init(c: this, control); |
1082 | } |
1083 | |
1084 | /*! |
1085 | \internal |
1086 | */ |
1087 | QQuickWindow::QQuickWindow(QQuickWindowPrivate &dd, QQuickRenderControl *control) |
1088 | : QWindow(dd, nullptr) |
1089 | { |
1090 | Q_D(QQuickWindow); |
1091 | d->init(c: this, control); |
1092 | } |
1093 | |
1094 | /*! |
1095 | Destroys the window. |
1096 | */ |
1097 | QQuickWindow::~QQuickWindow() |
1098 | { |
1099 | Q_D(QQuickWindow); |
1100 | d->inDestructor = true; |
1101 | if (d->renderControl) { |
1102 | QQuickRenderControlPrivate::get(renderControl: d->renderControl)->windowDestroyed(); |
1103 | } else if (d->windowManager) { |
1104 | d->windowManager->removeWindow(win: this); |
1105 | d->windowManager->windowDestroyed(window: this); |
1106 | } |
1107 | |
1108 | delete d->incubationController; d->incubationController = nullptr; |
1109 | QQuickRootItem *root = d->contentItem; |
1110 | d->contentItem = nullptr; |
1111 | root->setParent(nullptr); // avoid QChildEvent delivery during deletion |
1112 | delete root; |
1113 | d->deliveryAgent = nullptr; // avoid forwarding events there during destruction |
1114 | |
1115 | d->renderJobMutex.lock(); |
1116 | qDeleteAll(c: d->beforeSynchronizingJobs); |
1117 | d->beforeSynchronizingJobs.clear(); |
1118 | qDeleteAll(c: d->afterSynchronizingJobs); |
1119 | d->afterSynchronizingJobs.clear(); |
1120 | qDeleteAll(c: d->beforeRenderingJobs); |
1121 | d->beforeRenderingJobs.clear(); |
1122 | qDeleteAll(c: d->afterRenderingJobs); |
1123 | d->afterRenderingJobs.clear(); |
1124 | qDeleteAll(c: d->afterSwapJobs); |
1125 | d->afterSwapJobs.clear(); |
1126 | d->renderJobMutex.unlock(); |
1127 | |
1128 | // It is important that the pixmap cache is cleaned up during shutdown. |
1129 | // Besides playing nice, this also solves a practical problem that |
1130 | // QQuickTextureFactory implementations in other libraries need |
1131 | // have their destructors loaded while they the library is still |
1132 | // loaded into memory. |
1133 | QQuickPixmap::purgeCache(); |
1134 | } |
1135 | |
1136 | #if QT_CONFIG(quick_shadereffect) |
1137 | void qtquick_shadereffect_purge_gui_thread_shader_cache(); |
1138 | #endif |
1139 | |
1140 | /*! |
1141 | This function tries to release redundant resources currently held by the QML scene. |
1142 | |
1143 | Calling this function requests the scene graph to release cached graphics |
1144 | resources, such as graphics pipeline objects, shader programs, or image |
1145 | data. |
1146 | |
1147 | Additionally, depending on the render loop in use, this function may also |
1148 | result in the scene graph and all window-related rendering resources to be |
1149 | released. If this happens, the sceneGraphInvalidated() signal will be |
1150 | emitted, allowing users to clean up their own graphics resources. The |
1151 | setPersistentGraphics() and setPersistentSceneGraph() functions can be used |
1152 | to prevent this from happening, if handling the cleanup is not feasible in |
1153 | the application, at the cost of higher memory usage. |
1154 | |
1155 | \note The releasing of cached graphics resources, such as graphics |
1156 | pipelines or shader programs is not dependent on the persistency hints. The |
1157 | releasing of those will happen regardless of the values of the persistent |
1158 | graphics and scenegraph hints. |
1159 | |
1160 | \note This function is not related to the QQuickItem::releaseResources() |
1161 | virtual function. |
1162 | |
1163 | \sa sceneGraphInvalidated(), setPersistentGraphics(), setPersistentSceneGraph() |
1164 | */ |
1165 | |
1166 | void QQuickWindow::releaseResources() |
1167 | { |
1168 | Q_D(QQuickWindow); |
1169 | if (d->windowManager) |
1170 | d->windowManager->releaseResources(window: this); |
1171 | QQuickPixmap::purgeCache(); |
1172 | #if QT_CONFIG(quick_shadereffect) |
1173 | qtquick_shadereffect_purge_gui_thread_shader_cache(); |
1174 | #endif |
1175 | } |
1176 | |
1177 | |
1178 | |
1179 | /*! |
1180 | Sets whether the graphics resources (graphics device or context, |
1181 | swapchain, buffers, textures) should be preserved, and cannot be |
1182 | released until the last window is deleted, to \a persistent. The |
1183 | default value is true. |
1184 | |
1185 | When calling releaseResources(), or when the window gets hidden (more |
1186 | specifically, not renderable), some render loops have the possibility |
1187 | to release all, not just the cached, graphics resources. This can free |
1188 | up memory temporarily, but it also means the rendering engine will have |
1189 | to do a full, potentially costly reinitialization of the resources when |
1190 | the window needs to render again. |
1191 | |
1192 | \note The rules for when a window is not renderable are platform and |
1193 | window manager specific. |
1194 | |
1195 | \note All graphics resources are released when the last QQuickWindow is |
1196 | deleted, regardless of this setting. |
1197 | |
1198 | \note This is a hint, and is not guaranteed that it is taken into account. |
1199 | |
1200 | \note This hint does not apply to cached resources, that are relatively |
1201 | cheap to drop and then recreate later. Therefore, calling releaseResources() |
1202 | will typically lead to releasing those regardless of the value of this hint. |
1203 | |
1204 | \sa setPersistentSceneGraph(), sceneGraphInitialized(), sceneGraphInvalidated(), releaseResources() |
1205 | */ |
1206 | |
1207 | void QQuickWindow::setPersistentGraphics(bool persistent) |
1208 | { |
1209 | Q_D(QQuickWindow); |
1210 | d->persistentGraphics = persistent; |
1211 | } |
1212 | |
1213 | |
1214 | |
1215 | /*! |
1216 | Returns whether essential graphics resources can be released during the |
1217 | lifetime of the QQuickWindow. |
1218 | |
1219 | \note This is a hint, and is not guaranteed that it is taken into account. |
1220 | |
1221 | \sa setPersistentGraphics() |
1222 | */ |
1223 | |
1224 | bool QQuickWindow::isPersistentGraphics() const |
1225 | { |
1226 | Q_D(const QQuickWindow); |
1227 | return d->persistentGraphics; |
1228 | } |
1229 | |
1230 | |
1231 | |
1232 | /*! |
1233 | Sets whether the scene graph nodes and resources are \a persistent. |
1234 | Persistent means the nodes and resources cannot be released. |
1235 | The default value is \c true. |
1236 | |
1237 | When calling releaseResources(), when the window gets hidden (more |
1238 | specifically, not renderable), some render loops have the possibility |
1239 | to release the scene graph nodes and related graphics resources. This |
1240 | frees up memory temporarily, but will also mean the scene graph has to |
1241 | be rebuilt when the window renders next time. |
1242 | |
1243 | \note The rules for when a window is not renderable are platform and |
1244 | window manager specific. |
1245 | |
1246 | \note The scene graph nodes and resources are always released when the |
1247 | last QQuickWindow is deleted, regardless of this setting. |
1248 | |
1249 | \note This is a hint, and is not guaranteed that it is taken into account. |
1250 | |
1251 | \sa setPersistentGraphics(), sceneGraphInvalidated(), sceneGraphInitialized(), releaseResources() |
1252 | */ |
1253 | |
1254 | void QQuickWindow::setPersistentSceneGraph(bool persistent) |
1255 | { |
1256 | Q_D(QQuickWindow); |
1257 | d->persistentSceneGraph = persistent; |
1258 | } |
1259 | |
1260 | |
1261 | |
1262 | /*! |
1263 | Returns whether the scene graph nodes and resources can be |
1264 | released during the lifetime of this QQuickWindow. |
1265 | |
1266 | \note This is a hint. When and how this happens is implementation |
1267 | specific. |
1268 | */ |
1269 | |
1270 | bool QQuickWindow::isPersistentSceneGraph() const |
1271 | { |
1272 | Q_D(const QQuickWindow); |
1273 | return d->persistentSceneGraph; |
1274 | } |
1275 | |
1276 | /*! |
1277 | \qmlattachedproperty Item Window::contentItem |
1278 | \since 5.4 |
1279 | |
1280 | This attached property holds the invisible root item of the scene or |
1281 | \c null if the item is not in a window. The Window attached property |
1282 | can be attached to any Item. |
1283 | */ |
1284 | |
1285 | /*! |
1286 | \property QQuickWindow::contentItem |
1287 | \brief The invisible root item of the scene. |
1288 | |
1289 | A QQuickWindow always has a single invisible root item containing all of its content. |
1290 | To add items to this window, reparent the items to the contentItem or to an existing |
1291 | item in the scene. |
1292 | */ |
1293 | QQuickItem *QQuickWindow::contentItem() const |
1294 | { |
1295 | Q_D(const QQuickWindow); |
1296 | |
1297 | return d->contentItem; |
1298 | } |
1299 | |
1300 | /*! |
1301 | \property QQuickWindow::activeFocusItem |
1302 | |
1303 | \brief The item which currently has active focus or \c null if there is |
1304 | no item with active focus. |
1305 | |
1306 | \sa QQuickItem::forceActiveFocus(), {Keyboard Focus in Qt Quick} |
1307 | */ |
1308 | QQuickItem *QQuickWindow::activeFocusItem() const |
1309 | { |
1310 | Q_D(const QQuickWindow); |
1311 | auto da = d->deliveryAgentPrivate(); |
1312 | Q_ASSERT(da); |
1313 | return da->activeFocusItem; |
1314 | } |
1315 | |
1316 | /*! |
1317 | \internal |
1318 | \reimp |
1319 | */ |
1320 | QObject *QQuickWindow::focusObject() const |
1321 | { |
1322 | Q_D(const QQuickWindow); |
1323 | auto da = d->deliveryAgentPrivate(); |
1324 | Q_ASSERT(da); |
1325 | if (!d->inDestructor && da->activeFocusItem) |
1326 | return da->activeFocusItem; |
1327 | return const_cast<QQuickWindow*>(this); |
1328 | } |
1329 | |
1330 | /*! |
1331 | \internal |
1332 | |
1333 | Clears all exclusive and passive grabs for the points in \a pointerEvent. |
1334 | |
1335 | We never allow any kind of grab to persist after release, unless we're waiting |
1336 | for a synth event from QtGui (as with most tablet events), so for points that |
1337 | are fully released, the grab is cleared. |
1338 | |
1339 | Called when QQuickWindow::event dispatches events, or when the QQuickOverlay |
1340 | has filtered an event so that it bypasses normal delivery. |
1341 | */ |
1342 | void QQuickWindowPrivate::clearGrabbers(QPointerEvent *pointerEvent) |
1343 | { |
1344 | if (pointerEvent->isEndEvent() |
1345 | && !(QQuickDeliveryAgentPrivate::isTabletEvent(ev: pointerEvent) |
1346 | && (qApp->testAttribute(attribute: Qt::AA_SynthesizeMouseForUnhandledTabletEvents) |
1347 | || QWindowSystemInterfacePrivate::TabletEvent::platformSynthesizesMouse))) { |
1348 | if (pointerEvent->isSinglePointEvent()) { |
1349 | if (static_cast<QSinglePointEvent *>(pointerEvent)->buttons() == Qt::NoButton) { |
1350 | auto &firstPt = pointerEvent->point(i: 0); |
1351 | pointerEvent->setExclusiveGrabber(point: firstPt, exclusiveGrabber: nullptr); |
1352 | pointerEvent->clearPassiveGrabbers(point: firstPt); |
1353 | } |
1354 | } else { |
1355 | for (auto &point : pointerEvent->points()) { |
1356 | if (point.state() == QEventPoint::State::Released) { |
1357 | pointerEvent->setExclusiveGrabber(point, exclusiveGrabber: nullptr); |
1358 | pointerEvent->clearPassiveGrabbers(point); |
1359 | } |
1360 | } |
1361 | } |
1362 | } |
1363 | } |
1364 | |
1365 | /*! \reimp */ |
1366 | bool QQuickWindow::event(QEvent *event) |
1367 | { |
1368 | Q_D(QQuickWindow); |
1369 | |
1370 | // bypass QWindow::event dispatching of input events: deliveryAgent takes care of it |
1371 | QQuickDeliveryAgent *da = d->deliveryAgent; |
1372 | if (event->isPointerEvent()) { |
1373 | /* |
1374 | We can't bypass the virtual functions like mousePressEvent() tabletEvent() etc., |
1375 | for the sake of code that subclasses QQuickWindow and overrides them, even though |
1376 | we no longer need them as entry points for Qt Quick event delivery. |
1377 | So dispatch to them now, ahead of normal delivery, and stop them from calling |
1378 | back into this function if they were called from here (avoid recursion). |
1379 | It could also be that user code expects them to work as entry points, too; |
1380 | in that case, windowEventDispatch _won't_ be set, so the event comes here and |
1381 | we'll dispatch it further below. |
1382 | */ |
1383 | if (d->windowEventDispatch) |
1384 | return false; |
1385 | { |
1386 | const bool wasAccepted = event->isAccepted(); |
1387 | QBoolBlocker windowEventDispatchGuard(d->windowEventDispatch, true); |
1388 | qCDebug(lcPtr) << "dispatching to window functions in case of override" << event; |
1389 | QWindow::event(event); |
1390 | if (event->isAccepted() && !wasAccepted) |
1391 | return true; |
1392 | } |
1393 | /* |
1394 | QQuickWindow does not override touchEvent(). If the application has a subclass |
1395 | of QQuickWindow which allows the event to remain accepted, it means they want |
1396 | to stop propagation here, so return early (below). But otherwise we will call |
1397 | QWindow::touchEvent(), which will ignore(); in that case, we need to continue |
1398 | with the usual delivery below, so we need to undo the ignore(). |
1399 | */ |
1400 | auto pe = static_cast<QPointerEvent *>(event); |
1401 | if (QQuickDeliveryAgentPrivate::isTouchEvent(ev: pe)) |
1402 | event->accept(); |
1403 | // end of dispatch to user-overridden virtual window functions |
1404 | |
1405 | /* |
1406 | When delivering update and release events to existing grabbers, |
1407 | use the subscene delivery agent, if any. A possible scenario: |
1408 | 1) Two touchpoints pressed on the main window: QQuickWindowPrivate::deliveryAgent delivers to QQuick3DViewport, |
1409 | which does picking and finds two subscenes ("root" Items mapped onto two different 3D objects) to deliver it to. |
1410 | 2) The QTouchEvent is split up so that each subscene sees points relevant to it. |
1411 | 3) During delivery to either subscene, an item in the subscene grabs. |
1412 | 4) The user moves finger(s) generating a move event: the correct grabber item needs to get the update |
1413 | via the same subscene delivery agent from which it got the press, so that the coord transform will be done properly. |
1414 | 5) Likewise with the touchpoint releases. |
1415 | With single-point events (mouse, or only one finger) it's simplified: there can only be one subscene of interest; |
1416 | for (pt : pe->points()) would only iterate once, so we might as well skip that logic. |
1417 | */ |
1418 | if (pe->pointCount()) { |
1419 | const bool synthMouse = QQuickDeliveryAgentPrivate::isSynthMouse(ev: pe); |
1420 | if (QQuickDeliveryAgentPrivate::subsceneAgentsExist) { |
1421 | bool ret = false; |
1422 | // Split up the multi-point event according to the relevant QQuickDeliveryAgent that should deliver to each existing grabber |
1423 | // but send ungrabbed points to d->deliveryAgent() |
1424 | QFlatMap<QQuickDeliveryAgent*, QList<QEventPoint>> deliveryAgentsNeedingPoints; |
1425 | QEventPoint::States eventStates; |
1426 | |
1427 | auto insert = [&](QQuickDeliveryAgent *ptda, const QEventPoint &pt) { |
1428 | if (pt.state() == QEventPoint::Pressed && !synthMouse) |
1429 | pe->clearPassiveGrabbers(point: pt); |
1430 | auto &ptList = deliveryAgentsNeedingPoints[ptda]; |
1431 | auto idEquals = [](auto id) { return [id] (const auto &e) { return e.id() == id; }; }; |
1432 | if (std::none_of(first: ptList.cbegin(), last: ptList.cend(), pred: idEquals(pt.id()))) |
1433 | ptList.append(t: pt); |
1434 | }; |
1435 | |
1436 | for (const auto &pt : pe->points()) { |
1437 | eventStates |= pt.state(); |
1438 | auto epd = QPointingDevicePrivate::get(q: const_cast<QPointingDevice*>(pe->pointingDevice()))->queryPointById(id: pt.id()); |
1439 | Q_ASSERT(epd); |
1440 | bool foundAgent = false; |
1441 | if (!epd->exclusiveGrabber.isNull() && !epd->exclusiveGrabberContext.isNull()) { |
1442 | if (auto ptda = qobject_cast<QQuickDeliveryAgent *>(object: epd->exclusiveGrabberContext.data())) { |
1443 | insert(ptda, pt); |
1444 | qCDebug(lcPtr) << pe->type() << "point" << pt.id() << pt.state() |
1445 | << "@" << pt.scenePosition() << "will be re-delivered via known grabbing agent" << ptda << "to" << epd->exclusiveGrabber.data(); |
1446 | foundAgent = true; |
1447 | } |
1448 | } |
1449 | for (auto pgda : epd->passiveGrabbersContext) { |
1450 | if (auto ptda = qobject_cast<QQuickDeliveryAgent *>(object: pgda.data())) { |
1451 | insert(ptda, pt); |
1452 | qCDebug(lcPtr) << pe->type() << "point" << pt.id() << pt.state() |
1453 | << "@" << pt.scenePosition() << "will be re-delivered via known passive-grabbing agent" << ptda; |
1454 | foundAgent = true; |
1455 | } |
1456 | } |
1457 | // fallback: if we didn't find remembered/known grabber agent(s), expect the root DA to handle it |
1458 | if (!foundAgent) |
1459 | insert(da, pt); |
1460 | } |
1461 | for (auto daAndPoints : deliveryAgentsNeedingPoints) { |
1462 | if (pe->pointCount() > 1) { |
1463 | Q_ASSERT(QQuickDeliveryAgentPrivate::isTouchEvent(pe)); |
1464 | // if all points have the same state, set the event type accordingly |
1465 | QEvent::Type eventType = pe->type(); |
1466 | switch (eventStates) { |
1467 | case QEventPoint::State::Pressed: |
1468 | eventType = QEvent::TouchBegin; |
1469 | break; |
1470 | case QEventPoint::State::Released: |
1471 | eventType = QEvent::TouchEnd; |
1472 | break; |
1473 | default: |
1474 | eventType = QEvent::TouchUpdate; |
1475 | break; |
1476 | } |
1477 | // Make a new touch event for the subscene, the same way QQuickItemPrivate::localizedTouchEvent() does it |
1478 | QMutableTouchEvent te(eventType, pe->pointingDevice(), pe->modifiers(), daAndPoints.second); |
1479 | te.setTimestamp(pe->timestamp()); |
1480 | te.accept(); |
1481 | qCDebug(lcTouch) << daAndPoints.first << "shall now receive" << &te; |
1482 | ret = daAndPoints.first->event(ev: &te) || ret; |
1483 | } else { |
1484 | qCDebug(lcPtr) << daAndPoints.first << "shall now receive" << pe; |
1485 | ret = daAndPoints.first->event(ev: pe) || ret; |
1486 | } |
1487 | } |
1488 | |
1489 | if (ret) |
1490 | return true; |
1491 | } else if (!synthMouse) { |
1492 | // clear passive grabbers unless it's a system synth-mouse event |
1493 | // QTBUG-104890: Windows sends synth mouse events (which should be ignored) after touch events |
1494 | for (const auto &pt : pe->points()) { |
1495 | if (pt.state() == QEventPoint::Pressed) |
1496 | pe->clearPassiveGrabbers(point: pt); |
1497 | } |
1498 | } |
1499 | } |
1500 | |
1501 | // If it has no points, it's probably a TouchCancel, and DeliveryAgent needs to handle it. |
1502 | // If we didn't handle it in the block above, handle it now. |
1503 | // TODO should we deliver to all DAs at once then, since we don't know which one should get it? |
1504 | // or fix QTBUG-90851 so that the event always has points? |
1505 | bool ret = (da && da->event(ev: event)); |
1506 | |
1507 | d->clearGrabbers(pointerEvent: pe); |
1508 | |
1509 | if (ret) |
1510 | return true; |
1511 | } else if (event->isInputEvent()) { |
1512 | if (da && da->event(ev: event)) |
1513 | return true; |
1514 | } |
1515 | |
1516 | switch (event->type()) { |
1517 | // a few more types that are not QInputEvents, but QQuickDeliveryAgent needs to handle them anyway |
1518 | case QEvent::FocusAboutToChange: |
1519 | case QEvent::Enter: |
1520 | case QEvent::Leave: |
1521 | case QEvent::InputMethod: |
1522 | case QEvent::InputMethodQuery: |
1523 | #if QT_CONFIG(quick_draganddrop) |
1524 | case QEvent::DragEnter: |
1525 | case QEvent::DragLeave: |
1526 | case QEvent::DragMove: |
1527 | case QEvent::Drop: |
1528 | #endif |
1529 | if (d->inDestructor) |
1530 | return false; |
1531 | if (da && da->event(ev: event)) |
1532 | return true; |
1533 | break; |
1534 | case QEvent::LanguageChange: |
1535 | case QEvent::LocaleChange: |
1536 | if (d->contentItem) |
1537 | QCoreApplication::sendEvent(receiver: d->contentItem, event); |
1538 | break; |
1539 | case QEvent::UpdateRequest: |
1540 | if (d->windowManager) |
1541 | d->windowManager->handleUpdateRequest(this); |
1542 | break; |
1543 | case QEvent::PlatformSurface: |
1544 | if ((static_cast<QPlatformSurfaceEvent *>(event))->surfaceEventType() == QPlatformSurfaceEvent::SurfaceAboutToBeDestroyed) { |
1545 | // Ensure that the rendering thread is notified before |
1546 | // the QPlatformWindow is destroyed. |
1547 | if (d->windowManager) |
1548 | d->windowManager->hide(window: this); |
1549 | } |
1550 | break; |
1551 | case QEvent::WindowDeactivate: |
1552 | if (auto da = d->deliveryAgentPrivate()) |
1553 | da->handleWindowDeactivate(win: this); |
1554 | Q_FALLTHROUGH(); |
1555 | case QEvent::WindowActivate: |
1556 | if (d->contentItem) |
1557 | QCoreApplication::sendEvent(receiver: d->contentItem, event); |
1558 | break; |
1559 | case QEvent::ApplicationPaletteChange: |
1560 | d->inheritPalette(parentPalette: QGuiApplication::palette()); |
1561 | if (d->contentItem) |
1562 | QCoreApplication::sendEvent(receiver: d->contentItem, event); |
1563 | break; |
1564 | case QEvent::DevicePixelRatioChange: |
1565 | physicalDpiChanged(); |
1566 | break; |
1567 | default: |
1568 | break; |
1569 | } |
1570 | |
1571 | if (event->type() == QEvent::Type(QQuickWindowPrivate::FullUpdateRequest)) |
1572 | update(); |
1573 | else if (event->type() == QEvent::Type(QQuickWindowPrivate::TriggerContextCreationFailure)) |
1574 | d->windowManager->handleContextCreationFailure(window: this); |
1575 | |
1576 | if (event->isPointerEvent()) |
1577 | return true; |
1578 | else |
1579 | return QWindow::event(event); |
1580 | } |
1581 | |
1582 | /*! \reimp */ |
1583 | void QQuickWindow::keyPressEvent(QKeyEvent *e) |
1584 | { |
1585 | Q_D(QQuickWindow); |
1586 | if (d->windowEventDispatch) |
1587 | return; |
1588 | auto da = d->deliveryAgentPrivate(); |
1589 | Q_ASSERT(da); |
1590 | da->deliverKeyEvent(e); |
1591 | } |
1592 | |
1593 | /*! \reimp */ |
1594 | void QQuickWindow::keyReleaseEvent(QKeyEvent *e) |
1595 | { |
1596 | Q_D(QQuickWindow); |
1597 | if (d->windowEventDispatch) |
1598 | return; |
1599 | auto da = d->deliveryAgentPrivate(); |
1600 | Q_ASSERT(da); |
1601 | da->deliverKeyEvent(e); |
1602 | } |
1603 | |
1604 | #if QT_CONFIG(wheelevent) |
1605 | /*! \reimp */ |
1606 | void QQuickWindow::wheelEvent(QWheelEvent *event) |
1607 | { |
1608 | Q_D(QQuickWindow); |
1609 | if (d->windowEventDispatch) |
1610 | return; |
1611 | auto da = d->deliveryAgentPrivate(); |
1612 | Q_ASSERT(da); |
1613 | da->deliverSinglePointEventUntilAccepted(event); |
1614 | } |
1615 | #endif // wheelevent |
1616 | |
1617 | #if QT_CONFIG(tabletevent) |
1618 | /*! \reimp */ |
1619 | void QQuickWindow::tabletEvent(QTabletEvent *event) |
1620 | { |
1621 | Q_D(QQuickWindow); |
1622 | if (d->windowEventDispatch) |
1623 | return; |
1624 | auto da = d->deliveryAgentPrivate(); |
1625 | Q_ASSERT(da); |
1626 | da->deliverPointerEvent(event); |
1627 | } |
1628 | #endif // tabletevent |
1629 | |
1630 | /*! \reimp */ |
1631 | void QQuickWindow::mousePressEvent(QMouseEvent *event) |
1632 | { |
1633 | Q_D(QQuickWindow); |
1634 | if (d->windowEventDispatch) |
1635 | return; |
1636 | auto da = d->deliveryAgentPrivate(); |
1637 | Q_ASSERT(da); |
1638 | da->handleMouseEvent(event); |
1639 | } |
1640 | /*! \reimp */ |
1641 | void QQuickWindow::mouseMoveEvent(QMouseEvent *event) |
1642 | { |
1643 | Q_D(QQuickWindow); |
1644 | if (d->windowEventDispatch) |
1645 | return; |
1646 | auto da = d->deliveryAgentPrivate(); |
1647 | Q_ASSERT(da); |
1648 | da->handleMouseEvent(event); |
1649 | } |
1650 | /*! \reimp */ |
1651 | void QQuickWindow::mouseDoubleClickEvent(QMouseEvent *event) |
1652 | { |
1653 | Q_D(QQuickWindow); |
1654 | if (d->windowEventDispatch) |
1655 | return; |
1656 | auto da = d->deliveryAgentPrivate(); |
1657 | Q_ASSERT(da); |
1658 | da->handleMouseEvent(event); |
1659 | } |
1660 | /*! \reimp */ |
1661 | void QQuickWindow::mouseReleaseEvent(QMouseEvent *event) |
1662 | { |
1663 | Q_D(QQuickWindow); |
1664 | if (d->windowEventDispatch) |
1665 | return; |
1666 | auto da = d->deliveryAgentPrivate(); |
1667 | Q_ASSERT(da); |
1668 | da->handleMouseEvent(event); |
1669 | } |
1670 | |
1671 | #if QT_CONFIG(cursor) |
1672 | void QQuickWindowPrivate::updateCursor(const QPointF &scenePos, QQuickItem *rootItem) |
1673 | { |
1674 | Q_Q(QQuickWindow); |
1675 | if (!rootItem) |
1676 | rootItem = contentItem; |
1677 | auto cursorItemAndHandler = findCursorItemAndHandler(item: rootItem, scenePos); |
1678 | if (cursorItem != cursorItemAndHandler.first || cursorHandler != cursorItemAndHandler.second) { |
1679 | QWindow *renderWindow = QQuickRenderControl::renderWindowFor(win: q); |
1680 | QWindow *window = renderWindow ? renderWindow : q; |
1681 | cursorItem = cursorItemAndHandler.first; |
1682 | cursorHandler = cursorItemAndHandler.second; |
1683 | if (cursorItem) { |
1684 | const auto cursor = QQuickItemPrivate::get(item: cursorItem)->effectiveCursor(handler: cursorHandler); |
1685 | qCDebug(lcHoverTrace) << "setting cursor" << cursor << "from" << cursorHandler << "or" << cursorItem; |
1686 | window->setCursor(cursor); |
1687 | } else { |
1688 | qCDebug(lcHoverTrace) << "unsetting cursor" ; |
1689 | window->unsetCursor(); |
1690 | } |
1691 | } |
1692 | } |
1693 | |
1694 | QPair<QQuickItem*, QQuickPointerHandler*> QQuickWindowPrivate::findCursorItemAndHandler(QQuickItem *item, const QPointF &scenePos) const |
1695 | { |
1696 | QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item); |
1697 | if (itemPrivate->flags & QQuickItem::ItemClipsChildrenToShape) { |
1698 | QPointF p = item->mapFromScene(point: scenePos); |
1699 | if (!item->contains(point: p)) |
1700 | return {nullptr, nullptr}; |
1701 | } |
1702 | |
1703 | if (itemPrivate->subtreeCursorEnabled) { |
1704 | QList<QQuickItem *> children = itemPrivate->paintOrderChildItems(); |
1705 | for (int ii = children.size() - 1; ii >= 0; --ii) { |
1706 | QQuickItem *child = children.at(i: ii); |
1707 | if (!child->isVisible() || !child->isEnabled() || QQuickItemPrivate::get(item: child)->culled) |
1708 | continue; |
1709 | auto ret = findCursorItemAndHandler(item: child, scenePos); |
1710 | if (ret.first) |
1711 | return ret; |
1712 | } |
1713 | if (itemPrivate->hasCursorHandler) { |
1714 | if (auto handler = itemPrivate->effectiveCursorHandler()) { |
1715 | if (handler->parentContains(scenePosition: scenePos)) |
1716 | return {item, handler}; |
1717 | } |
1718 | } |
1719 | if (itemPrivate->hasCursor) { |
1720 | QPointF p = item->mapFromScene(point: scenePos); |
1721 | if (item->contains(point: p)) |
1722 | return {item, nullptr}; |
1723 | } |
1724 | } |
1725 | |
1726 | return {nullptr, nullptr}; |
1727 | } |
1728 | #endif |
1729 | |
1730 | void QQuickWindowPrivate::clearFocusObject() |
1731 | { |
1732 | if (auto da = deliveryAgentPrivate()) |
1733 | da->clearFocusObject(); |
1734 | } |
1735 | |
1736 | /*! |
1737 | \qmlproperty list<QtObject> Window::data |
1738 | \qmldefault |
1739 | |
1740 | The data property allows you to freely mix visual children, resources |
1741 | and other Windows in a Window. |
1742 | |
1743 | If you assign another Window to the data list, the nested window will |
1744 | become "transient for" the outer Window. |
1745 | |
1746 | If you assign an \l Item to the data list, it becomes a child of the |
1747 | Window's \l contentItem, so that it appears inside the window. The item's |
1748 | parent will be the window's contentItem, which is the root of the Item |
1749 | ownership tree within that Window. |
1750 | |
1751 | If you assign any other object type, it is added as a resource. |
1752 | |
1753 | It should not generally be necessary to refer to the \c data property, |
1754 | as it is the default property for Window and thus all child items are |
1755 | automatically assigned to this property. |
1756 | |
1757 | \sa QWindow::transientParent() |
1758 | */ |
1759 | |
1760 | void QQuickWindowPrivate::data_append(QQmlListProperty<QObject> *property, QObject *o) |
1761 | { |
1762 | if (!o) |
1763 | return; |
1764 | QQuickWindow *that = static_cast<QQuickWindow *>(property->object); |
1765 | if (QQuickWindow *window = qmlobject_cast<QQuickWindow *>(object: o)) { |
1766 | qCDebug(lcTransient) << window << "is transient for" << that; |
1767 | window->setTransientParent(that); |
1768 | } |
1769 | QQmlListProperty<QObject> itemProperty = QQuickItemPrivate::get(item: that->contentItem())->data(); |
1770 | itemProperty.append(&itemProperty, o); |
1771 | } |
1772 | |
1773 | qsizetype QQuickWindowPrivate::data_count(QQmlListProperty<QObject> *property) |
1774 | { |
1775 | QQuickWindow *win = static_cast<QQuickWindow*>(property->object); |
1776 | if (!win || !win->contentItem() || !QQuickItemPrivate::get(item: win->contentItem())->data().count) |
1777 | return 0; |
1778 | QQmlListProperty<QObject> itemProperty = QQuickItemPrivate::get(item: win->contentItem())->data(); |
1779 | return itemProperty.count(&itemProperty); |
1780 | } |
1781 | |
1782 | QObject *QQuickWindowPrivate::data_at(QQmlListProperty<QObject> *property, qsizetype i) |
1783 | { |
1784 | QQuickWindow *win = static_cast<QQuickWindow*>(property->object); |
1785 | QQmlListProperty<QObject> itemProperty = QQuickItemPrivate::get(item: win->contentItem())->data(); |
1786 | return itemProperty.at(&itemProperty, i); |
1787 | } |
1788 | |
1789 | void QQuickWindowPrivate::data_clear(QQmlListProperty<QObject> *property) |
1790 | { |
1791 | QQuickWindow *win = static_cast<QQuickWindow*>(property->object); |
1792 | QQmlListProperty<QObject> itemProperty = QQuickItemPrivate::get(item: win->contentItem())->data(); |
1793 | itemProperty.clear(&itemProperty); |
1794 | } |
1795 | |
1796 | void QQuickWindowPrivate::data_replace(QQmlListProperty<QObject> *property, qsizetype i, QObject *o) |
1797 | { |
1798 | QQuickWindow *win = static_cast<QQuickWindow*>(property->object); |
1799 | QQmlListProperty<QObject> itemProperty = QQuickItemPrivate::get(item: win->contentItem())->data(); |
1800 | itemProperty.replace(&itemProperty, i, o); |
1801 | } |
1802 | |
1803 | void QQuickWindowPrivate::data_removeLast(QQmlListProperty<QObject> *property) |
1804 | { |
1805 | QQuickWindow *win = static_cast<QQuickWindow*>(property->object); |
1806 | QQmlListProperty<QObject> itemProperty = QQuickItemPrivate::get(item: win->contentItem())->data(); |
1807 | itemProperty.removeLast(&itemProperty); |
1808 | } |
1809 | |
1810 | bool QQuickWindowPrivate::isRenderable() const |
1811 | { |
1812 | Q_Q(const QQuickWindow); |
1813 | return ((q->isExposed() && q->isVisible())) && q->geometry().isValid(); |
1814 | } |
1815 | |
1816 | void QQuickWindowPrivate::rhiCreationFailureMessage(const QString &backendName, |
1817 | QString *translatedMessage, |
1818 | QString *untranslatedMessage) |
1819 | { |
1820 | const char msg[] = QT_TRANSLATE_NOOP("QQuickWindow" , |
1821 | "Failed to initialize graphics backend for %1." ); |
1822 | *translatedMessage = QQuickWindow::tr(s: msg).arg(a: backendName); |
1823 | *untranslatedMessage = QString::fromLatin1(ba: msg).arg(a: backendName); |
1824 | } |
1825 | |
1826 | void QQuickWindowPrivate::cleanupNodes() |
1827 | { |
1828 | for (int ii = 0; ii < cleanupNodeList.size(); ++ii) |
1829 | delete cleanupNodeList.at(i: ii); |
1830 | cleanupNodeList.clear(); |
1831 | } |
1832 | |
1833 | void QQuickWindowPrivate::cleanupNodesOnShutdown(QQuickItem *item) |
1834 | { |
1835 | QQuickItemPrivate *p = QQuickItemPrivate::get(item); |
1836 | if (p->itemNodeInstance) { |
1837 | delete p->itemNodeInstance; |
1838 | p->itemNodeInstance = nullptr; |
1839 | |
1840 | if (p->extra.isAllocated()) { |
1841 | p->extra->opacityNode = nullptr; |
1842 | p->extra->clipNode = nullptr; |
1843 | p->extra->rootNode = nullptr; |
1844 | } |
1845 | |
1846 | p->paintNode = nullptr; |
1847 | |
1848 | p->dirty(QQuickItemPrivate::Window); |
1849 | } |
1850 | |
1851 | // Qt 7: Make invalidateSceneGraph a virtual member of QQuickItem |
1852 | if (p->flags & QQuickItem::ItemHasContents) { |
1853 | const QMetaObject *mo = item->metaObject(); |
1854 | int index = mo->indexOfSlot(slot: "invalidateSceneGraph()" ); |
1855 | if (index >= 0) { |
1856 | const QMetaMethod &method = mo->method(index); |
1857 | // Skip functions named invalidateSceneGraph() in QML items. |
1858 | if (strstr(haystack: method.enclosingMetaObject()->className(), needle: "_QML_" ) == nullptr) |
1859 | method.invoke(obj: item, c: Qt::DirectConnection); |
1860 | } |
1861 | } |
1862 | |
1863 | for (int ii = 0; ii < p->childItems.size(); ++ii) |
1864 | cleanupNodesOnShutdown(item: p->childItems.at(i: ii)); |
1865 | } |
1866 | |
1867 | // This must be called from the render thread, with the main thread frozen |
1868 | void QQuickWindowPrivate::cleanupNodesOnShutdown() |
1869 | { |
1870 | Q_Q(QQuickWindow); |
1871 | cleanupNodes(); |
1872 | cleanupNodesOnShutdown(item: contentItem); |
1873 | for (QSet<QQuickItem *>::const_iterator it = parentlessItems.begin(), cend = parentlessItems.end(); it != cend; ++it) |
1874 | cleanupNodesOnShutdown(item: *it); |
1875 | animationController->windowNodesDestroyed(); |
1876 | q->cleanupSceneGraph(); |
1877 | } |
1878 | |
1879 | void QQuickWindowPrivate::updateDirtyNodes() |
1880 | { |
1881 | qCDebug(lcDirty) << "QQuickWindowPrivate::updateDirtyNodes():" ; |
1882 | |
1883 | cleanupNodes(); |
1884 | |
1885 | QQuickItem *updateList = dirtyItemList; |
1886 | dirtyItemList = nullptr; |
1887 | if (updateList) QQuickItemPrivate::get(item: updateList)->prevDirtyItem = &updateList; |
1888 | |
1889 | while (updateList) { |
1890 | QQuickItem *item = updateList; |
1891 | QQuickItemPrivate *itemPriv = QQuickItemPrivate::get(item); |
1892 | itemPriv->removeFromDirtyList(); |
1893 | |
1894 | qCDebug(lcDirty) << " QSGNode:" << item << qPrintable(itemPriv->dirtyToString()); |
1895 | updateDirtyNode(item); |
1896 | } |
1897 | } |
1898 | |
1899 | static inline QSGNode *qquickitem_before_paintNode(QQuickItemPrivate *d) |
1900 | { |
1901 | const QList<QQuickItem *> childItems = d->paintOrderChildItems(); |
1902 | QQuickItem *before = nullptr; |
1903 | for (int i=0; i<childItems.size(); ++i) { |
1904 | QQuickItemPrivate *dd = QQuickItemPrivate::get(item: childItems.at(i)); |
1905 | // Perform the same check as the in fetchNextNode below. |
1906 | if (dd->z() < 0 && (dd->explicitVisible || (dd->extra.isAllocated() && dd->extra->effectRefCount))) |
1907 | before = childItems.at(i); |
1908 | else |
1909 | break; |
1910 | } |
1911 | return Q_UNLIKELY(before) ? QQuickItemPrivate::get(item: before)->itemNode() : nullptr; |
1912 | } |
1913 | |
1914 | static QSGNode *fetchNextNode(QQuickItemPrivate *itemPriv, int &ii, bool &returnedPaintNode) |
1915 | { |
1916 | QList<QQuickItem *> orderedChildren = itemPriv->paintOrderChildItems(); |
1917 | |
1918 | for (; ii < orderedChildren.size() && orderedChildren.at(i: ii)->z() < 0; ++ii) { |
1919 | QQuickItemPrivate *childPrivate = QQuickItemPrivate::get(item: orderedChildren.at(i: ii)); |
1920 | if (!childPrivate->explicitVisible && |
1921 | (!childPrivate->extra.isAllocated() || !childPrivate->extra->effectRefCount)) |
1922 | continue; |
1923 | |
1924 | ii++; |
1925 | return childPrivate->itemNode(); |
1926 | } |
1927 | |
1928 | if (itemPriv->paintNode && !returnedPaintNode) { |
1929 | returnedPaintNode = true; |
1930 | return itemPriv->paintNode; |
1931 | } |
1932 | |
1933 | for (; ii < orderedChildren.size(); ++ii) { |
1934 | QQuickItemPrivate *childPrivate = QQuickItemPrivate::get(item: orderedChildren.at(i: ii)); |
1935 | if (!childPrivate->explicitVisible && |
1936 | (!childPrivate->extra.isAllocated() || !childPrivate->extra->effectRefCount)) |
1937 | continue; |
1938 | |
1939 | ii++; |
1940 | return childPrivate->itemNode(); |
1941 | } |
1942 | |
1943 | return nullptr; |
1944 | } |
1945 | |
1946 | void QQuickWindowPrivate::updateDirtyNode(QQuickItem *item) |
1947 | { |
1948 | QQuickItemPrivate *itemPriv = QQuickItemPrivate::get(item); |
1949 | quint32 dirty = itemPriv->dirtyAttributes; |
1950 | itemPriv->dirtyAttributes = 0; |
1951 | |
1952 | if ((dirty & QQuickItemPrivate::TransformUpdateMask) || |
1953 | (dirty & QQuickItemPrivate::Size && itemPriv->origin() != QQuickItem::TopLeft && |
1954 | (itemPriv->scale() != 1. || itemPriv->rotation() != 0.))) { |
1955 | |
1956 | QMatrix4x4 matrix; |
1957 | |
1958 | if (itemPriv->x != 0. || itemPriv->y != 0.) |
1959 | matrix.translate(x: itemPriv->x, y: itemPriv->y); |
1960 | |
1961 | for (int ii = itemPriv->transforms.size() - 1; ii >= 0; --ii) |
1962 | itemPriv->transforms.at(i: ii)->applyTo(matrix: &matrix); |
1963 | |
1964 | if (itemPriv->scale() != 1. || itemPriv->rotation() != 0.) { |
1965 | QPointF origin = item->transformOriginPoint(); |
1966 | matrix.translate(x: origin.x(), y: origin.y()); |
1967 | if (itemPriv->scale() != 1.) |
1968 | matrix.scale(x: itemPriv->scale(), y: itemPriv->scale()); |
1969 | if (itemPriv->rotation() != 0.) |
1970 | matrix.rotate(angle: itemPriv->rotation(), x: 0, y: 0, z: 1); |
1971 | matrix.translate(x: -origin.x(), y: -origin.y()); |
1972 | } |
1973 | |
1974 | itemPriv->itemNode()->setMatrix(matrix); |
1975 | } |
1976 | |
1977 | const bool clipEffectivelyChanged = dirty & (QQuickItemPrivate::Clip | QQuickItemPrivate::Window); |
1978 | if (clipEffectivelyChanged) { |
1979 | QSGNode *parent = itemPriv->opacityNode() ? (QSGNode *)itemPriv->opacityNode() |
1980 | : (QSGNode *)itemPriv->itemNode(); |
1981 | QSGNode *child = itemPriv->rootNode(); |
1982 | |
1983 | if (bool initializeClipNode = item->clip() && itemPriv->clipNode() == nullptr; |
1984 | initializeClipNode) { |
1985 | QQuickDefaultClipNode *clip = new QQuickDefaultClipNode(item->clipRect()); |
1986 | itemPriv->extra.value().clipNode = clip; |
1987 | clip->update(); |
1988 | |
1989 | if (!child) { |
1990 | parent->reparentChildNodesTo(newParent: clip); |
1991 | parent->appendChildNode(node: clip); |
1992 | } else { |
1993 | parent->removeChildNode(node: child); |
1994 | clip->appendChildNode(node: child); |
1995 | parent->appendChildNode(node: clip); |
1996 | } |
1997 | |
1998 | } else if (bool updateClipNode = item->clip() && itemPriv->clipNode() != nullptr; |
1999 | updateClipNode) { |
2000 | QQuickDefaultClipNode *clip = itemPriv->clipNode(); |
2001 | clip->setClipRect(item->clipRect()); |
2002 | clip->update(); |
2003 | } else if (bool removeClipNode = !item->clip() && itemPriv->clipNode() != nullptr; |
2004 | removeClipNode) { |
2005 | QQuickDefaultClipNode *clip = itemPriv->clipNode(); |
2006 | parent->removeChildNode(node: clip); |
2007 | if (child) { |
2008 | clip->removeChildNode(node: child); |
2009 | parent->appendChildNode(node: child); |
2010 | } else { |
2011 | clip->reparentChildNodesTo(newParent: parent); |
2012 | } |
2013 | |
2014 | delete itemPriv->clipNode(); |
2015 | itemPriv->extra->clipNode = nullptr; |
2016 | } |
2017 | } |
2018 | |
2019 | const int effectRefCount = itemPriv->extra.isAllocated() ? itemPriv->extra->effectRefCount : 0; |
2020 | const bool effectRefEffectivelyChanged = |
2021 | (dirty & (QQuickItemPrivate::EffectReference | QQuickItemPrivate::Window)) |
2022 | && ((effectRefCount == 0) != (itemPriv->rootNode() == nullptr)); |
2023 | if (effectRefEffectivelyChanged) { |
2024 | if (dirty & QQuickItemPrivate::ChildrenUpdateMask) |
2025 | itemPriv->childContainerNode()->removeAllChildNodes(); |
2026 | |
2027 | QSGNode *parent = itemPriv->clipNode(); |
2028 | if (!parent) |
2029 | parent = itemPriv->opacityNode(); |
2030 | if (!parent) |
2031 | parent = itemPriv->itemNode(); |
2032 | |
2033 | if (itemPriv->extra.isAllocated() && itemPriv->extra->effectRefCount) { |
2034 | Q_ASSERT(itemPriv->rootNode() == nullptr); |
2035 | QSGRootNode *root = new QSGRootNode(); |
2036 | itemPriv->extra->rootNode = root; |
2037 | parent->reparentChildNodesTo(newParent: root); |
2038 | parent->appendChildNode(node: root); |
2039 | } else { |
2040 | Q_ASSERT(itemPriv->rootNode() != nullptr); |
2041 | QSGRootNode *root = itemPriv->rootNode(); |
2042 | parent->removeChildNode(node: root); |
2043 | root->reparentChildNodesTo(newParent: parent); |
2044 | delete itemPriv->rootNode(); |
2045 | itemPriv->extra->rootNode = nullptr; |
2046 | } |
2047 | } |
2048 | |
2049 | if (dirty & QQuickItemPrivate::ChildrenUpdateMask) { |
2050 | int ii = 0; |
2051 | bool fetchedPaintNode = false; |
2052 | QList<QQuickItem *> orderedChildren = itemPriv->paintOrderChildItems(); |
2053 | int desiredNodesSize = orderedChildren.size() + (itemPriv->paintNode ? 1 : 0); |
2054 | |
2055 | // now start making current state match the promised land of |
2056 | // desiredNodes. in the case of our current state matching desiredNodes |
2057 | // (though why would we get ChildrenUpdateMask with no changes?) then we |
2058 | // should make no changes at all. |
2059 | |
2060 | // how many nodes did we process, when examining changes |
2061 | int desiredNodesProcessed = 0; |
2062 | |
2063 | // currentNode is how far, in our present tree, we have processed. we |
2064 | // make use of this later on to trim the current child list if the |
2065 | // desired list is shorter. |
2066 | QSGNode *groupNode = itemPriv->childContainerNode(); |
2067 | QSGNode *currentNode = groupNode->firstChild(); |
2068 | QSGNode *desiredNode = nullptr; |
2069 | |
2070 | while (currentNode && (desiredNode = fetchNextNode(itemPriv, ii, returnedPaintNode&: fetchedPaintNode))) { |
2071 | if (currentNode != desiredNode) { |
2072 | // uh oh... reality and our utopic paradise are diverging! |
2073 | // we need to reconcile this... |
2074 | if (currentNode->nextSibling() == desiredNode) { |
2075 | // nice and simple: a node was removed, and the next in line is correct. |
2076 | groupNode->removeChildNode(node: currentNode); |
2077 | } else { |
2078 | // a node needs to be added.. |
2079 | // remove it from any pre-existing parent, and push it before currentNode, |
2080 | // so it's in the correct place... |
2081 | if (desiredNode->parent()) { |
2082 | desiredNode->parent()->removeChildNode(node: desiredNode); |
2083 | } |
2084 | groupNode->insertChildNodeBefore(node: desiredNode, before: currentNode); |
2085 | } |
2086 | |
2087 | // continue iteration at the correct point, now desiredNode is in place... |
2088 | currentNode = desiredNode; |
2089 | } |
2090 | |
2091 | currentNode = currentNode->nextSibling(); |
2092 | desiredNodesProcessed++; |
2093 | } |
2094 | |
2095 | // if we didn't process as many nodes as in the new list, then we have |
2096 | // more nodes at the end of desiredNodes to append to our list. |
2097 | // this will be the case when adding new nodes, for instance. |
2098 | if (desiredNodesProcessed < desiredNodesSize) { |
2099 | while ((desiredNode = fetchNextNode(itemPriv, ii, returnedPaintNode&: fetchedPaintNode))) { |
2100 | if (desiredNode->parent()) |
2101 | desiredNode->parent()->removeChildNode(node: desiredNode); |
2102 | groupNode->appendChildNode(node: desiredNode); |
2103 | } |
2104 | } else if (currentNode) { |
2105 | // on the other hand, if we processed less than our current node |
2106 | // tree, then nodes have been _removed_ from the scene, and we need |
2107 | // to take care of that here. |
2108 | while (currentNode) { |
2109 | QSGNode *node = currentNode->nextSibling(); |
2110 | groupNode->removeChildNode(node: currentNode); |
2111 | currentNode = node; |
2112 | } |
2113 | } |
2114 | } |
2115 | |
2116 | if ((dirty & QQuickItemPrivate::Size) && itemPriv->clipNode()) { |
2117 | itemPriv->clipNode()->setRect(item->clipRect()); |
2118 | itemPriv->clipNode()->update(); |
2119 | } |
2120 | |
2121 | if (dirty & (QQuickItemPrivate::OpacityValue | QQuickItemPrivate::Visible |
2122 | | QQuickItemPrivate::HideReference | QQuickItemPrivate::Window)) |
2123 | { |
2124 | qreal opacity = itemPriv->explicitVisible && (!itemPriv->extra.isAllocated() || itemPriv->extra->hideRefCount == 0) |
2125 | ? itemPriv->opacity() : qreal(0); |
2126 | |
2127 | if (opacity != 1 && !itemPriv->opacityNode()) { |
2128 | QSGOpacityNode *node = new QSGOpacityNode; |
2129 | itemPriv->extra.value().opacityNode = node; |
2130 | |
2131 | QSGNode *parent = itemPriv->itemNode(); |
2132 | QSGNode *child = itemPriv->clipNode(); |
2133 | if (!child) |
2134 | child = itemPriv->rootNode(); |
2135 | |
2136 | if (child) { |
2137 | parent->removeChildNode(node: child); |
2138 | node->appendChildNode(node: child); |
2139 | parent->appendChildNode(node); |
2140 | } else { |
2141 | parent->reparentChildNodesTo(newParent: node); |
2142 | parent->appendChildNode(node); |
2143 | } |
2144 | } |
2145 | if (itemPriv->opacityNode()) |
2146 | itemPriv->opacityNode()->setOpacity(opacity); |
2147 | } |
2148 | |
2149 | if (dirty & QQuickItemPrivate::ContentUpdateMask) { |
2150 | |
2151 | if (itemPriv->flags & QQuickItem::ItemHasContents) { |
2152 | updatePaintNodeData.transformNode = itemPriv->itemNode(); |
2153 | itemPriv->paintNode = item->updatePaintNode(itemPriv->paintNode, &updatePaintNodeData); |
2154 | |
2155 | Q_ASSERT(itemPriv->paintNode == nullptr || |
2156 | itemPriv->paintNode->parent() == nullptr || |
2157 | itemPriv->paintNode->parent() == itemPriv->childContainerNode()); |
2158 | |
2159 | if (itemPriv->paintNode && itemPriv->paintNode->parent() == nullptr) { |
2160 | QSGNode *before = qquickitem_before_paintNode(d: itemPriv); |
2161 | if (before && before->parent()) { |
2162 | Q_ASSERT(before->parent() == itemPriv->childContainerNode()); |
2163 | itemPriv->childContainerNode()->insertChildNodeAfter(node: itemPriv->paintNode, after: before); |
2164 | } else { |
2165 | itemPriv->childContainerNode()->prependChildNode(node: itemPriv->paintNode); |
2166 | } |
2167 | } |
2168 | } else if (itemPriv->paintNode) { |
2169 | delete itemPriv->paintNode; |
2170 | itemPriv->paintNode = nullptr; |
2171 | } |
2172 | } |
2173 | |
2174 | #ifndef QT_NO_DEBUG |
2175 | // Check consistency. |
2176 | |
2177 | QList<QSGNode *> nodes; |
2178 | nodes << itemPriv->itemNodeInstance |
2179 | << itemPriv->opacityNode() |
2180 | << itemPriv->clipNode() |
2181 | << itemPriv->rootNode() |
2182 | << itemPriv->paintNode; |
2183 | nodes.removeAll(t: nullptr); |
2184 | |
2185 | Q_ASSERT(nodes.constFirst() == itemPriv->itemNodeInstance); |
2186 | for (int i=1; i<nodes.size(); ++i) { |
2187 | QSGNode *n = nodes.at(i); |
2188 | // Failing this means we messed up reparenting |
2189 | Q_ASSERT(n->parent() == nodes.at(i-1)); |
2190 | // Only the paintNode and the one who is childContainer may have more than one child. |
2191 | Q_ASSERT(n == itemPriv->paintNode || n == itemPriv->childContainerNode() || n->childCount() == 1); |
2192 | } |
2193 | #endif |
2194 | |
2195 | } |
2196 | |
2197 | bool QQuickWindowPrivate::emitError(QQuickWindow::SceneGraphError error, const QString &msg) |
2198 | { |
2199 | Q_Q(QQuickWindow); |
2200 | static const QMetaMethod errorSignal = QMetaMethod::fromSignal(signal: &QQuickWindow::sceneGraphError); |
2201 | if (q->isSignalConnected(signal: errorSignal)) { |
2202 | emit q->sceneGraphError(error, message: msg); |
2203 | return true; |
2204 | } |
2205 | return false; |
2206 | } |
2207 | |
2208 | void QQuickWindow::maybeUpdate() |
2209 | { |
2210 | Q_D(QQuickWindow); |
2211 | if (d->renderControl) |
2212 | QQuickRenderControlPrivate::get(renderControl: d->renderControl)->maybeUpdate(); |
2213 | else if (d->windowManager) |
2214 | d->windowManager->maybeUpdate(window: this); |
2215 | } |
2216 | |
2217 | void QQuickWindow::cleanupSceneGraph() |
2218 | { |
2219 | Q_D(QQuickWindow); |
2220 | if (!d->renderer) |
2221 | return; |
2222 | |
2223 | delete d->renderer->rootNode(); |
2224 | delete d->renderer; |
2225 | d->renderer = nullptr; |
2226 | |
2227 | d->runAndClearJobs(jobs: &d->beforeSynchronizingJobs); |
2228 | d->runAndClearJobs(jobs: &d->afterSynchronizingJobs); |
2229 | d->runAndClearJobs(jobs: &d->beforeRenderingJobs); |
2230 | d->runAndClearJobs(jobs: &d->afterRenderingJobs); |
2231 | d->runAndClearJobs(jobs: &d->afterSwapJobs); |
2232 | } |
2233 | |
2234 | void QQuickWindow::setTransientParent_helper(QQuickWindow *window) |
2235 | { |
2236 | qCDebug(lcTransient) << this << "is transient for" << window; |
2237 | setTransientParent(window); |
2238 | disconnect(sender: sender(), SIGNAL(windowChanged(QQuickWindow*)), |
2239 | receiver: this, SLOT(setTransientParent_helper(QQuickWindow*))); |
2240 | } |
2241 | |
2242 | QOpenGLContext *QQuickWindowPrivate::openglContext() |
2243 | { |
2244 | #if QT_CONFIG(opengl) |
2245 | if (context && context->isValid()) { |
2246 | QSGRendererInterface *rif = context->sceneGraphContext()->rendererInterface(renderContext: context); |
2247 | if (rif) { |
2248 | Q_Q(QQuickWindow); |
2249 | return reinterpret_cast<QOpenGLContext *>(rif->getResource(window: q, resource: QSGRendererInterface::OpenGLContextResource)); |
2250 | } |
2251 | } |
2252 | #endif |
2253 | return nullptr; |
2254 | } |
2255 | |
2256 | /*! |
2257 | Returns true if the scene graph has been initialized; otherwise returns false. |
2258 | */ |
2259 | bool QQuickWindow::isSceneGraphInitialized() const |
2260 | { |
2261 | Q_D(const QQuickWindow); |
2262 | return d->context != nullptr && d->context->isValid(); |
2263 | } |
2264 | |
2265 | /*! |
2266 | \fn void QQuickWindow::frameSwapped() |
2267 | |
2268 | This signal is emitted when a frame has been queued for presenting. With |
2269 | vertical synchronization enabled the signal is emitted at most once per |
2270 | vsync interval in a continuously animating scene. |
2271 | |
2272 | This signal will be emitted from the scene graph rendering thread. |
2273 | */ |
2274 | |
2275 | /*! |
2276 | \qmlsignal QtQuick::Window::frameSwapped() |
2277 | |
2278 | This signal is emitted when a frame has been queued for presenting. With |
2279 | vertical synchronization enabled the signal is emitted at most once per |
2280 | vsync interval in a continuously animating scene. |
2281 | */ |
2282 | |
2283 | /*! |
2284 | \fn void QQuickWindow::sceneGraphInitialized() |
2285 | |
2286 | This signal is emitted when the scene graph has been initialized. |
2287 | |
2288 | This signal will be emitted from the scene graph rendering thread. |
2289 | */ |
2290 | |
2291 | /*! |
2292 | \qmlsignal QtQuick::Window::sceneGraphInitialized() |
2293 | \internal |
2294 | */ |
2295 | |
2296 | /*! |
2297 | \fn void QQuickWindow::sceneGraphInvalidated() |
2298 | |
2299 | This signal is emitted when the scene graph has been invalidated. |
2300 | |
2301 | This signal implies that the graphics rendering context used |
2302 | has been invalidated and all user resources tied to that context |
2303 | should be released. |
2304 | |
2305 | When rendering with OpenGL, the QOpenGLContext of this window will |
2306 | be bound when this function is called. The only exception is if |
2307 | the native OpenGL has been destroyed outside Qt's control, for |
2308 | instance through EGL_CONTEXT_LOST. |
2309 | |
2310 | This signal will be emitted from the scene graph rendering thread. |
2311 | */ |
2312 | |
2313 | /*! |
2314 | \qmlsignal QtQuick::Window::sceneGraphInvalidated() |
2315 | \internal |
2316 | */ |
2317 | |
2318 | /*! |
2319 | \fn void QQuickWindow::sceneGraphError(SceneGraphError error, const QString &message) |
2320 | |
2321 | This signal is emitted when an \a error occurred during scene graph initialization. |
2322 | |
2323 | Applications should connect to this signal if they wish to handle errors, |
2324 | like graphics context creation failures, in a custom way. When no slot is |
2325 | connected to the signal, the behavior will be different: Quick will print |
2326 | the \a message, or show a message box, and terminate the application. |
2327 | |
2328 | This signal will be emitted from the GUI thread. |
2329 | |
2330 | \since 5.3 |
2331 | */ |
2332 | |
2333 | /*! |
2334 | \qmlsignal QtQuick::Window::sceneGraphError(SceneGraphError error, QString message) |
2335 | |
2336 | This signal is emitted when an \a error occurred during scene graph initialization. |
2337 | |
2338 | You can implement onSceneGraphError(error, message) to handle errors, |
2339 | such as graphics context creation failures, in a custom way. |
2340 | If no handler is connected to this signal, Quick will print the \a message, |
2341 | or show a message box, and terminate the application. |
2342 | |
2343 | \since 5.3 |
2344 | */ |
2345 | |
2346 | /*! |
2347 | \class QQuickCloseEvent |
2348 | \internal |
2349 | \since 5.1 |
2350 | |
2351 | \inmodule QtQuick |
2352 | |
2353 | \brief Notification that a \l QQuickWindow is about to be closed |
2354 | */ |
2355 | /*! |
2356 | \qmltype CloseEvent |
2357 | \instantiates QQuickCloseEvent |
2358 | \inqmlmodule QtQuick |
2359 | \ingroup qtquick-visual |
2360 | \brief Notification that a \l Window is about to be closed. |
2361 | \since 5.1 |
2362 | |
2363 | Notification that a window is about to be closed by the windowing system |
2364 | (e.g. the user clicked the title bar close button). The CloseEvent contains |
2365 | an accepted property which can be set to false to abort closing the window. |
2366 | */ |
2367 | |
2368 | /*! |
2369 | \qmlproperty bool CloseEvent::accepted |
2370 | |
2371 | This property indicates whether the application will allow the user to |
2372 | close the window. It is true by default. |
2373 | */ |
2374 | |
2375 | /*! |
2376 | \internal |
2377 | \fn void QQuickWindow::closing(QQuickCloseEvent *close) |
2378 | \since 5.1 |
2379 | |
2380 | This signal is emitted when the window receives the event \a close from |
2381 | the windowing system. |
2382 | |
2383 | On \macOs, Qt will create a menu item \c Quit if there is no menu item |
2384 | whose text is "quit" or "exit". This menu item calls the \c QCoreApplication::quit |
2385 | signal, not the \c QQuickWindow::closing() signal. |
2386 | |
2387 | \sa {QMenuBar as a Global Menu Bar} |
2388 | */ |
2389 | |
2390 | /*! |
2391 | \qmlsignal QtQuick::Window::closing(CloseEvent close) |
2392 | \since 5.1 |
2393 | |
2394 | This signal is emitted when the user tries to close the window. |
2395 | |
2396 | This signal includes a \a close parameter. The \c {close.accepted} |
2397 | property is true by default so that the window is allowed to close; but you |
2398 | can implement an \c onClosing handler and set \c {close.accepted = false} if |
2399 | you need to do something else before the window can be closed. |
2400 | */ |
2401 | |
2402 | /*! |
2403 | Sets the render target for this window to be \a target. |
2404 | |
2405 | A QQuickRenderTarget serves as an opaque handle for a renderable native |
2406 | object, most commonly a 2D texture, and associated metadata, such as the |
2407 | size in pixels. |
2408 | |
2409 | A default constructed QQuickRenderTarget means no redirection. A valid |
2410 | \a target, created via one of the static QQuickRenderTarget factory functions, |
2411 | on the other hand, enables redirection of the rendering of the Qt Quick |
2412 | scene: it will no longer target the color buffers for the surface |
2413 | associated with the window, but rather the textures or other graphics |
2414 | objects specified in \a target. |
2415 | |
2416 | For example, assuming the scenegraph is using Vulkan to render, one can |
2417 | redirect its output into a \c VkImage. For graphics APIs like Vulkan, the |
2418 | image layout must be provided as well. QQuickRenderTarget instances are |
2419 | implicitly shared and are copyable and can be passed by value. They do not |
2420 | own the associated native objects (such as, the VkImage in the example), |
2421 | however. |
2422 | |
2423 | \badcode |
2424 | QQuickRenderTarget rt = QQuickRenderTarget::fromVulkanImage(vulkanImage, VK_IMAGE_LAYOUT_PREINITIALIZED, pixelSize); |
2425 | quickWindow->setRenderTarget(rt); |
2426 | \endcode |
2427 | |
2428 | This function is very often used in combination with QQuickRenderControl |
2429 | and an invisible QQuickWindow, in order to render Qt Quick content into a |
2430 | texture, without creating an on-screen native window for this QQuickWindow. |
2431 | |
2432 | When the desired target, or associated data, such as the size, changes, |
2433 | call this function with a new QQuickRenderTarget. Constructing |
2434 | QQuickRenderTarget instances and calling this function is cheap, but be |
2435 | aware that setting a new \a target with a different native object or other |
2436 | data may lead to potentially expensive initialization steps when the |
2437 | scenegraph is about to render the next frame. Therefore change the target |
2438 | only when necessary. |
2439 | |
2440 | \note The window does not take ownership of any native objects referenced |
2441 | in \a target. |
2442 | |
2443 | \note It is the caller's responsibility to ensure the native objects |
2444 | referred to in \a target are valid for the scenegraph renderer too. For |
2445 | instance, with Vulkan, Metal, and Direct3D this implies that the texture or |
2446 | image is created on the same graphics device that is used by the scenegraph |
2447 | internally. Therefore, when texture objects created on an already existing |
2448 | device or context are involved, this function is often used in combination |
2449 | with setGraphicsDevice(). |
2450 | |
2451 | \note With graphics APIs where relevant, the application must pay attention |
2452 | to image layout transitions performed by the scenegraph. For example, once |
2453 | a VkImage is associated with the scenegraph by calling this function, its |
2454 | layout will transition to \c VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL when |
2455 | rendering a frame. |
2456 | |
2457 | \warning This function can only be called from the thread doing the |
2458 | rendering. |
2459 | |
2460 | \since 6.0 |
2461 | |
2462 | \sa QQuickRenderControl, setGraphicsDevice(), setGraphicsApi() |
2463 | */ |
2464 | void QQuickWindow::setRenderTarget(const QQuickRenderTarget &target) |
2465 | { |
2466 | Q_D(QQuickWindow); |
2467 | if (target != d->customRenderTarget) { |
2468 | d->customRenderTarget = target; |
2469 | d->redirect.renderTargetDirty = true; |
2470 | } |
2471 | } |
2472 | |
2473 | /*! |
2474 | \return the QQuickRenderTarget passed to setRenderTarget(), or a default |
2475 | constructed one otherwise |
2476 | |
2477 | \since 6.0 |
2478 | |
2479 | \sa setRenderTarget() |
2480 | */ |
2481 | QQuickRenderTarget QQuickWindow::renderTarget() const |
2482 | { |
2483 | Q_D(const QQuickWindow); |
2484 | return d->customRenderTarget; |
2485 | } |
2486 | |
2487 | #ifdef Q_OS_WEBOS |
2488 | class GrabWindowForProtectedContent : public QRunnable |
2489 | { |
2490 | public: |
2491 | GrabWindowForProtectedContent(QQuickWindow *window, QImage *image, QWaitCondition *condition) |
2492 | : m_window(window) |
2493 | , m_image(image) |
2494 | , m_condition(condition) |
2495 | { |
2496 | } |
2497 | |
2498 | bool checkGrabbable() |
2499 | { |
2500 | if (!m_window) |
2501 | return false; |
2502 | if (!m_image) |
2503 | return false; |
2504 | if (!QQuickWindowPrivate::get(m_window)) |
2505 | return false; |
2506 | |
2507 | return true; |
2508 | } |
2509 | |
2510 | void run() override |
2511 | { |
2512 | if (!checkGrabbable()) |
2513 | return; |
2514 | |
2515 | *m_image = QSGRhiSupport::instance()->grabOffscreenForProtectedContent(m_window); |
2516 | if (m_condition) |
2517 | m_condition->wakeOne(); |
2518 | return; |
2519 | } |
2520 | |
2521 | private: |
2522 | QQuickWindow *m_window; |
2523 | QImage *m_image; |
2524 | QWaitCondition *m_condition; |
2525 | |
2526 | }; |
2527 | #endif |
2528 | |
2529 | /*! |
2530 | Grabs the contents of the window and returns it as an image. |
2531 | |
2532 | It is possible to call the grabWindow() function when the window is not |
2533 | visible. This requires that the window is \l{QWindow::create()} {created} |
2534 | and has a valid size and that no other QQuickWindow instances are rendering |
2535 | in the same process. |
2536 | |
2537 | \note When using this window in combination with QQuickRenderControl, the |
2538 | result of this function is an empty image, unless the \c software backend |
2539 | is in use. This is because when redirecting the output to an |
2540 | application-managed graphics resource (such as, a texture) by using |
2541 | QQuickRenderControl and setRenderTarget(), the application is better suited |
2542 | for managing and executing an eventual read back operation, since it is in |
2543 | full control of the resource to begin with. |
2544 | |
2545 | \warning Calling this function will cause performance problems. |
2546 | |
2547 | \warning This function can only be called from the GUI thread. |
2548 | */ |
2549 | QImage QQuickWindow::grabWindow() |
2550 | { |
2551 | Q_D(QQuickWindow); |
2552 | |
2553 | if (!d->isRenderable() && !d->renderControl) { |
2554 | // backends like software can grab regardless of the window state |
2555 | if (d->windowManager && (d->windowManager->flags() & QSGRenderLoop::SupportsGrabWithoutExpose)) |
2556 | return d->windowManager->grab(window: this); |
2557 | |
2558 | if (!isSceneGraphInitialized()) { |
2559 | // We do not have rendering up and running. Forget the render loop, |
2560 | // do a frame completely offscreen and synchronously into a |
2561 | // texture. This can be *very* slow due to all the device/context |
2562 | // and resource initialization but the documentation warns for it, |
2563 | // and is still important for some use cases. |
2564 | Q_ASSERT(!d->rhi); |
2565 | return QSGRhiSupport::instance()->grabOffscreen(window: this); |
2566 | } |
2567 | } |
2568 | |
2569 | #ifdef Q_OS_WEBOS |
2570 | if (requestedFormat().testOption(QSurfaceFormat::ProtectedContent)) { |
2571 | QImage image; |
2572 | QMutex mutex; |
2573 | QWaitCondition condition; |
2574 | mutex.lock(); |
2575 | GrabWindowForProtectedContent *job = new GrabWindowForProtectedContent(this, &image, &condition); |
2576 | if (!job) { |
2577 | qWarning("QQuickWindow::grabWindow: Failed to create a job for capturing protected content" ); |
2578 | mutex.unlock(); |
2579 | return QImage(); |
2580 | } |
2581 | scheduleRenderJob(job, QQuickWindow::NoStage); |
2582 | condition.wait(&mutex); |
2583 | mutex.unlock(); |
2584 | return image; |
2585 | } |
2586 | #endif |
2587 | // The common case: we have an exposed window with an initialized |
2588 | // scenegraph, meaning we can request grabbing via the render loop, or we |
2589 | // are not targeting the window, in which case the request is to be |
2590 | // forwarded to the rendercontrol. |
2591 | if (d->renderControl) |
2592 | return QQuickRenderControlPrivate::get(renderControl: d->renderControl)->grab(); |
2593 | else if (d->windowManager) |
2594 | return d->windowManager->grab(window: this); |
2595 | |
2596 | return QImage(); |
2597 | } |
2598 | |
2599 | /*! |
2600 | Returns an incubation controller that splices incubation between frames |
2601 | for this window. QQuickView automatically installs this controller for you, |
2602 | otherwise you will need to install it yourself using \l{QQmlEngine::setIncubationController()}. |
2603 | |
2604 | The controller is owned by the window and will be destroyed when the window |
2605 | is deleted. |
2606 | */ |
2607 | QQmlIncubationController *QQuickWindow::incubationController() const |
2608 | { |
2609 | Q_D(const QQuickWindow); |
2610 | |
2611 | if (!d->windowManager) |
2612 | return nullptr; // TODO: make sure that this is safe |
2613 | |
2614 | if (!d->incubationController) |
2615 | d->incubationController = new QQuickWindowIncubationController(d->windowManager); |
2616 | return d->incubationController; |
2617 | } |
2618 | |
2619 | |
2620 | |
2621 | /*! |
2622 | \enum QQuickWindow::CreateTextureOption |
2623 | |
2624 | The CreateTextureOption enums are used to customize a texture is wrapped. |
2625 | |
2626 | \value TextureHasAlphaChannel The texture has an alpha channel and should |
2627 | be drawn using blending. |
2628 | |
2629 | \value TextureHasMipmaps The texture has mipmaps and can be drawn with |
2630 | mipmapping enabled. |
2631 | |
2632 | \value TextureOwnsGLTexture As of Qt 6.0, this flag is not used in practice |
2633 | and is ignored. Native graphics resource ownership is not transferable to |
2634 | the wrapping QSGTexture, because Qt Quick may not have the necessary details |
2635 | on how such an object and the associated memory should be freed. |
2636 | |
2637 | \value TextureCanUseAtlas The image can be uploaded into a texture atlas. |
2638 | |
2639 | \value TextureIsOpaque The texture will return false for |
2640 | QSGTexture::hasAlphaChannel() and will not be blended. This flag was added |
2641 | in Qt 5.6. |
2642 | |
2643 | */ |
2644 | |
2645 | /*! |
2646 | \enum QQuickWindow::SceneGraphError |
2647 | |
2648 | This enum describes the error in a sceneGraphError() signal. |
2649 | |
2650 | \value ContextNotAvailable graphics context creation failed. This typically means that |
2651 | no suitable OpenGL implementation was found, for example because no graphics drivers |
2652 | are installed and so no OpenGL 2 support is present. On mobile and embedded boards |
2653 | that use OpenGL ES such an error is likely to indicate issues in the windowing system |
2654 | integration and possibly an incorrect configuration of Qt. |
2655 | |
2656 | \since 5.3 |
2657 | */ |
2658 | |
2659 | /*! |
2660 | \enum QQuickWindow::TextRenderType |
2661 | \since 5.10 |
2662 | |
2663 | This enum describes the default render type of text-like elements in Qt |
2664 | Quick (\l Text, \l TextInput, etc.). |
2665 | |
2666 | Select NativeTextRendering if you prefer text to look native on the target |
2667 | platform and do not require advanced features such as transformation of the |
2668 | text. Using such features in combination with the NativeTextRendering |
2669 | render type will lend poor and sometimes pixelated results. |
2670 | |
2671 | \value QtTextRendering Use Qt's own rasterization algorithm. |
2672 | |
2673 | \value NativeTextRendering Use the operating system's native rasterizer for text. |
2674 | */ |
2675 | |
2676 | /*! |
2677 | \fn void QQuickWindow::beforeSynchronizing() |
2678 | |
2679 | This signal is emitted before the scene graph is synchronized with the QML state. |
2680 | |
2681 | Even though the signal is emitted from the scene graph rendering thread, |
2682 | the GUI thread is guaranteed to be blocked, like it is in |
2683 | QQuickItem::updatePaintNode(). Therefore, it is safe to access GUI thread |
2684 | thread data in a slot or lambda that is connected with |
2685 | Qt::DirectConnection. |
2686 | |
2687 | This signal can be used to do any preparation required before calls to |
2688 | QQuickItem::updatePaintNode(). |
2689 | |
2690 | When using OpenGL, the QOpenGLContext used for rendering by the scene graph |
2691 | will be bound at this point. |
2692 | |
2693 | \warning This signal is emitted from the scene graph rendering thread. If your |
2694 | slot function needs to finish before execution continues, you must make sure that |
2695 | the connection is direct (see Qt::ConnectionType). |
2696 | |
2697 | \warning When using OpenGL, be aware that setting OpenGL 3.x or 4.x specific |
2698 | states and leaving these enabled or set to non-default values when returning |
2699 | from the connected slot can interfere with the scene graph's rendering. |
2700 | */ |
2701 | |
2702 | /*! |
2703 | \qmlsignal QtQuick::Window::beforeSynchronizing() |
2704 | \internal |
2705 | */ |
2706 | |
2707 | /*! |
2708 | \fn void QQuickWindow::afterSynchronizing() |
2709 | |
2710 | This signal is emitted after the scene graph is synchronized with the QML state. |
2711 | |
2712 | This signal can be used to do preparation required after calls to |
2713 | QQuickItem::updatePaintNode(), while the GUI thread is still locked. |
2714 | |
2715 | When using OpenGL, the QOpenGLContext used for rendering by the scene graph |
2716 | will be bound at this point. |
2717 | |
2718 | \warning This signal is emitted from the scene graph rendering thread. If your |
2719 | slot function needs to finish before execution continues, you must make sure that |
2720 | the connection is direct (see Qt::ConnectionType). |
2721 | |
2722 | \warning When using OpenGL, be aware that setting OpenGL 3.x or 4.x specific |
2723 | states and leaving these enabled or set to non-default values when returning |
2724 | from the connected slot can interfere with the scene graph's rendering. |
2725 | |
2726 | \since 5.3 |
2727 | */ |
2728 | |
2729 | /*! |
2730 | \qmlsignal QtQuick::Window::afterSynchronizing() |
2731 | \internal |
2732 | \since 5.3 |
2733 | */ |
2734 | |
2735 | /*! |
2736 | \fn void QQuickWindow::beforeRendering() |
2737 | |
2738 | This signal is emitted after the preparations for the frame have been done, |
2739 | meaning there is a command buffer in recording mode, where applicable. If |
2740 | desired, the slot function connected to this signal can query native |
2741 | resources like the command before via QSGRendererInterface. Note however |
2742 | that the recording of the main render pass is not yet started at this point |
2743 | and it is not possible to add commands within that pass. Starting a pass |
2744 | means clearing the color, depth, and stencil buffers so it is not possible |
2745 | to achieve an underlay type of rendering by just connecting to this |
2746 | signal. Rather, connect to beforeRenderPassRecording(). However, connecting |
2747 | to this signal is still important if the recording of copy type of commands |
2748 | is desired since those cannot be enqueued within a render pass. |
2749 | |
2750 | \warning This signal is emitted from the scene graph rendering thread. If your |
2751 | slot function needs to finish before execution continues, you must make sure that |
2752 | the connection is direct (see Qt::ConnectionType). |
2753 | |
2754 | \note When using OpenGL, be aware that setting OpenGL 3.x or 4.x specific |
2755 | states and leaving these enabled or set to non-default values when |
2756 | returning from the connected slot can interfere with the scene graph's |
2757 | rendering. The QOpenGLContext used for rendering by the scene graph will be |
2758 | bound when the signal is emitted. |
2759 | |
2760 | \sa rendererInterface(), {Scene Graph - RHI Under QML}, {Scene Graph - |
2761 | OpenGL Under QML}, {Scene Graph - Metal Under QML}, {Scene Graph - Vulkan |
2762 | Under QML}, {Scene Graph - Direct3D 11 Under QML} |
2763 | */ |
2764 | |
2765 | /*! |
2766 | \qmlsignal QtQuick::Window::beforeRendering() |
2767 | \internal |
2768 | */ |
2769 | |
2770 | /*! |
2771 | \fn void QQuickWindow::afterRendering() |
2772 | |
2773 | The signal is emitted after scene graph has added its commands to the |
2774 | command buffer, which is not yet submitted to the graphics queue. If |
2775 | desired, the slot function connected to this signal can query native |
2776 | resources, like the command buffer, before via QSGRendererInterface. Note |
2777 | however that the render pass (or passes) are already recorded at this point |
2778 | and it is not possible to add more commands within the scenegraph's |
2779 | pass. Instead, use afterRenderPassRecording() for that. This signal has |
2780 | therefore limited use in Qt 6, unlike in Qt 5. Rather, it is the combination |
2781 | of beforeRendering() and beforeRenderPassRecording(), or beforeRendering() |
2782 | and afterRenderPassRecording(), that is typically used to achieve under- or |
2783 | overlaying of the custom rendering. |
2784 | |
2785 | \warning This signal is emitted from the scene graph rendering thread. If your |
2786 | slot function needs to finish before execution continues, you must make sure that |
2787 | the connection is direct (see Qt::ConnectionType). |
2788 | |
2789 | \note When using OpenGL, be aware that setting OpenGL 3.x or 4.x specific |
2790 | states and leaving these enabled or set to non-default values when |
2791 | returning from the connected slot can interfere with the scene graph's |
2792 | rendering. The QOpenGLContext used for rendering by the scene graph will be |
2793 | bound when the signal is emitted. |
2794 | |
2795 | \sa rendererInterface(), {Scene Graph - RHI Under QML}, {Scene Graph - |
2796 | OpenGL Under QML}, {Scene Graph - Metal Under QML}, {Scene Graph - Vulkan |
2797 | Under QML}, {Scene Graph - Direct3D 11 Under QML} |
2798 | */ |
2799 | |
2800 | /*! |
2801 | \qmlsignal QtQuick::Window::afterRendering() |
2802 | \internal |
2803 | */ |
2804 | |
2805 | /*! |
2806 | \fn void QQuickWindow::beforeRenderPassRecording() |
2807 | |
2808 | This signal is emitted before the scenegraph starts recording commands for |
2809 | the main render pass. (Layers have their own passes and are fully recorded |
2810 | by the time this signal is emitted.) The render pass is already active on |
2811 | the command buffer when the signal is emitted. |
2812 | |
2813 | This signal is emitted later than beforeRendering() and it guarantees that |
2814 | not just the frame, but also the recording of the scenegraph's main render |
2815 | pass is active. This allows inserting commands without having to generate an |
2816 | entire, separate render pass (which would typically clear the attached |
2817 | images). The native graphics objects can be queried via |
2818 | QSGRendererInterface. |
2819 | |
2820 | \note Resource updates (uploads, copies) typically cannot be enqueued from |
2821 | within a render pass. Therefore, more complex user rendering will need to |
2822 | connect to both beforeRendering() and this signal. |
2823 | |
2824 | \warning This signal is emitted from the scene graph rendering thread. If your |
2825 | slot function needs to finish before execution continues, you must make sure that |
2826 | the connection is direct (see Qt::ConnectionType). |
2827 | |
2828 | \sa rendererInterface() |
2829 | |
2830 | \since 5.14 |
2831 | |
2832 | \sa {Scene Graph - RHI Under QML} |
2833 | */ |
2834 | |
2835 | /*! |
2836 | \qmlsignal QtQuick::Window::beforeRenderPassRecording() |
2837 | \internal |
2838 | \since 5.14 |
2839 | */ |
2840 | |
2841 | /*! |
2842 | \fn void QQuickWindow::afterRenderPassRecording() |
2843 | |
2844 | This signal is emitted after the scenegraph has recorded the commands for |
2845 | its main render pass, but the pass is not yet finalized on the command |
2846 | buffer. |
2847 | |
2848 | This signal is emitted earlier than afterRendering(), and it guarantees that |
2849 | not just the frame but also the recording of the scenegraph's main render |
2850 | pass is still active. This allows inserting commands without having to |
2851 | generate an entire, separate render pass (which would typically clear the |
2852 | attached images). The native graphics objects can be queried via |
2853 | QSGRendererInterface. |
2854 | |
2855 | \note Resource updates (uploads, copies) typically cannot be enqueued from |
2856 | within a render pass. Therefore, more complex user rendering will need to |
2857 | connect to both beforeRendering() and this signal. |
2858 | |
2859 | \warning This signal is emitted from the scene graph rendering thread. If your |
2860 | slot function needs to finish before execution continues, you must make sure that |
2861 | the connection is direct (see Qt::ConnectionType). |
2862 | |
2863 | \sa rendererInterface() |
2864 | |
2865 | \since 5.14 |
2866 | |
2867 | \sa {Scene Graph - RHI Under QML} |
2868 | */ |
2869 | |
2870 | /*! |
2871 | \fn void QQuickWindow::beforeFrameBegin() |
2872 | |
2873 | This signal is emitted before the scene graph starts preparing the frame. |
2874 | This precedes signals like beforeSynchronizing() or beforeRendering(). It is |
2875 | the earliest signal that is emitted by the scene graph rendering thread |
2876 | when starting to prepare a new frame. |
2877 | |
2878 | This signal is relevant for lower level graphics frameworks that need to |
2879 | execute certain operations, such as resource cleanup, at a stage where Qt |
2880 | Quick has not initiated the recording of a new frame via the underlying |
2881 | rendering hardware interface APIs. |
2882 | |
2883 | \warning This signal is emitted from the scene graph rendering thread. If your |
2884 | slot function needs to finish before execution continues, you must make sure that |
2885 | the connection is direct (see Qt::ConnectionType). |
2886 | |
2887 | \since 6.0 |
2888 | |
2889 | \sa afterFrameEnd(), rendererInterface() |
2890 | */ |
2891 | |
2892 | /*! |
2893 | \qmlsignal QtQuick::Window::beforeFrameBegin() |
2894 | \internal |
2895 | */ |
2896 | |
2897 | /*! |
2898 | \fn void QQuickWindow::afterFrameEnd() |
2899 | |
2900 | This signal is emitted when the scene graph has submitted a frame. This is |
2901 | emitted after all other related signals, such as afterRendering(). It is |
2902 | the last signal that is emitted by the scene graph rendering thread when |
2903 | rendering a frame. |
2904 | |
2905 | \note Unlike frameSwapped(), this signal is guaranteed to be emitted also |
2906 | when the Qt Quick output is redirected via QQuickRenderControl. |
2907 | |
2908 | \warning This signal is emitted from the scene graph rendering thread. If your |
2909 | slot function needs to finish before execution continues, you must make sure that |
2910 | the connection is direct (see Qt::ConnectionType). |
2911 | |
2912 | \since 6.0 |
2913 | |
2914 | \sa beforeFrameBegin(), rendererInterface() |
2915 | */ |
2916 | |
2917 | /*! |
2918 | \qmlsignal QtQuick::Window::afterFrameEnd() |
2919 | \internal |
2920 | */ |
2921 | |
2922 | /*! |
2923 | \qmlsignal QtQuick::Window::afterRenderPassRecording() |
2924 | \internal |
2925 | \since 5.14 |
2926 | */ |
2927 | |
2928 | /*! |
2929 | \fn void QQuickWindow::afterAnimating() |
2930 | |
2931 | This signal is emitted on the GUI thread before requesting the render thread to |
2932 | perform the synchronization of the scene graph. |
2933 | |
2934 | Unlike the other similar signals, this one is emitted on the GUI thread |
2935 | instead of the render thread. It can be used to synchronize external |
2936 | animation systems with the QML content. At the same time this means that |
2937 | this signal is not suitable for triggering graphics operations. |
2938 | |
2939 | \since 5.3 |
2940 | */ |
2941 | |
2942 | /*! |
2943 | \qmlsignal QtQuick::Window::afterAnimating() |
2944 | |
2945 | This signal is emitted on the GUI thread before requesting the render thread to |
2946 | perform the synchronization of the scene graph. |
2947 | |
2948 | You can implement onAfterAnimating to do additional processing after each animation step. |
2949 | |
2950 | \since 5.3 |
2951 | */ |
2952 | |
2953 | /*! |
2954 | \fn void QQuickWindow::sceneGraphAboutToStop() |
2955 | |
2956 | This signal is emitted on the render thread when the scene graph is |
2957 | about to stop rendering. This happens usually because the window |
2958 | has been hidden. |
2959 | |
2960 | Applications may use this signal to release resources, but should be |
2961 | prepared to reinstantiated them again fast. The scene graph and the |
2962 | graphics context are not released at this time. |
2963 | |
2964 | \warning This signal is emitted from the scene graph rendering thread. If your |
2965 | slot function needs to finish before execution continues, you must make sure that |
2966 | the connection is direct (see Qt::ConnectionType). |
2967 | |
2968 | \warning Make very sure that a signal handler for sceneGraphAboutToStop() leaves the |
2969 | graphics context in the same state as it was when the signal handler was entered. |
2970 | Failing to do so can result in the scene not rendering properly. |
2971 | |
2972 | \sa sceneGraphInvalidated() |
2973 | \since 5.3 |
2974 | */ |
2975 | |
2976 | /*! |
2977 | \qmlsignal QtQuick::Window::sceneGraphAboutToStop() |
2978 | \internal |
2979 | \since 5.3 |
2980 | */ |
2981 | |
2982 | /*! |
2983 | \overload |
2984 | */ |
2985 | |
2986 | QSGTexture *QQuickWindow::createTextureFromImage(const QImage &image) const |
2987 | { |
2988 | return createTextureFromImage(image, options: {}); |
2989 | } |
2990 | |
2991 | |
2992 | /*! |
2993 | Creates a new QSGTexture from the supplied \a image. If the image has an |
2994 | alpha channel, the corresponding texture will have an alpha channel. |
2995 | |
2996 | The caller of the function is responsible for deleting the returned texture. |
2997 | The underlying native texture object is then destroyed together with the |
2998 | QSGTexture. |
2999 | |
3000 | When \a options contains TextureCanUseAtlas, the engine may put the image |
3001 | into a texture atlas. Textures in an atlas need to rely on |
3002 | QSGTexture::normalizedTextureSubRect() for their geometry and will not |
3003 | support QSGTexture::Repeat. Other values from CreateTextureOption are |
3004 | ignored. |
3005 | |
3006 | When \a options contains TextureIsOpaque, the engine will create an RGB |
3007 | texture which returns false for QSGTexture::hasAlphaChannel(). Opaque |
3008 | textures will in most cases be faster to render. When this flag is not set, |
3009 | the texture will have an alpha channel based on the image's format. |
3010 | |
3011 | When \a options contains TextureHasMipmaps, the engine will create a texture |
3012 | which can use mipmap filtering. Mipmapped textures can not be in an atlas. |
3013 | |
3014 | Setting TextureHasAlphaChannel in \a options serves no purpose for this |
3015 | function since assuming an alpha channel and blending is the default. To opt |
3016 | out, set TextureIsOpaque. |
3017 | |
3018 | When the scene graph uses OpenGL, the returned texture will be using \c |
3019 | GL_TEXTURE_2D as texture target and \c GL_RGBA as internal format. With |
3020 | other graphics APIs, the texture format is typically \c RGBA8. Reimplement |
3021 | QSGTexture to create textures with different parameters. |
3022 | |
3023 | \warning This function will return 0 if the scene graph has not yet been |
3024 | initialized. |
3025 | |
3026 | \warning The returned texture is not memory managed by the scene graph and |
3027 | must be explicitly deleted by the caller on the rendering thread. This is |
3028 | achieved by deleting the texture from a QSGNode destructor or by using |
3029 | deleteLater() in the case where the texture already has affinity to the |
3030 | rendering thread. |
3031 | |
3032 | This function can be called from both the main and the render thread. |
3033 | |
3034 | \sa sceneGraphInitialized(), QSGTexture |
3035 | */ |
3036 | |
3037 | QSGTexture *QQuickWindow::createTextureFromImage(const QImage &image, CreateTextureOptions options) const |
3038 | { |
3039 | Q_D(const QQuickWindow); |
3040 | if (!isSceneGraphInitialized()) // check both for d->context and d->context->isValid() |
3041 | return nullptr; |
3042 | uint flags = 0; |
3043 | if (options & TextureCanUseAtlas) flags |= QSGRenderContext::CreateTexture_Atlas; |
3044 | if (options & TextureHasMipmaps) flags |= QSGRenderContext::CreateTexture_Mipmap; |
3045 | if (!(options & TextureIsOpaque)) flags |= QSGRenderContext::CreateTexture_Alpha; |
3046 | return d->context->createTexture(image, flags); |
3047 | } |
3048 | |
3049 | /*! |
3050 | Creates a new QSGTexture from the supplied \a texture. |
3051 | |
3052 | Use \a options to customize the texture attributes. Only the |
3053 | TextureHasAlphaChannel flag is taken into account by this function. When |
3054 | set, the resulting QSGTexture is always treated by the scene graph renderer |
3055 | as needing blending. For textures that are fully opaque, not setting the |
3056 | flag can save the cost of performing alpha blending during rendering. The |
3057 | flag has no direct correspondence to the \l{QRhiTexture::format()}{format} |
3058 | of the QRhiTexture, i.e. not setting the flag while having a texture format |
3059 | such as the commonly used \l QRhiTexture::RGBA8 is perfectly normal. |
3060 | |
3061 | Mipmapping is not controlled by \a options since \a texture is already |
3062 | created and has the presence or lack of mipmaps baked in. |
3063 | |
3064 | The returned QSGTexture owns the QRhiTexture, meaning \a texture is |
3065 | destroyed together with the returned QSGTexture. |
3066 | |
3067 | If \a texture owns its underlying native graphics resources (OpenGL texture |
3068 | object, Vulkan image, etc.), that depends on how the QRhiTexture was created |
3069 | (\l{QRhiTexture::create()} or \l{QRhiTexture::createFrom()}), and that is |
3070 | not controlled or changed by this function. |
3071 | |
3072 | \note This is only functional when the scene graph has already initialized |
3073 | and is using the default, \l{QRhi}-based \l{Scene Graph |
3074 | Adaptations}{adaptation}. The return value is \nullptr otherwise. |
3075 | |
3076 | \note This function can only be called on the scene graph render thread. |
3077 | |
3078 | \since 6.6 |
3079 | |
3080 | \sa createTextureFromImage(), sceneGraphInitialized(), QSGTexture |
3081 | */ |
3082 | QSGTexture *QQuickWindow::createTextureFromRhiTexture(QRhiTexture *texture, CreateTextureOptions options) const |
3083 | { |
3084 | Q_D(const QQuickWindow); |
3085 | if (!d->rhi) |
3086 | return nullptr; |
3087 | |
3088 | QSGPlainTexture *t = new QSGPlainTexture; |
3089 | t->setOwnsTexture(true); |
3090 | t->setTexture(texture); |
3091 | t->setHasAlphaChannel(options & QQuickWindow::TextureHasAlphaChannel); |
3092 | t->setTextureSize(texture->pixelSize()); |
3093 | return t; |
3094 | } |
3095 | |
3096 | // Legacy, private alternative to createTextureFromRhiTexture() that internally |
3097 | // creates a QRhiTexture wrapping the existing native graphics resource. |
3098 | // New code should prefer using the public API. |
3099 | QSGTexture *QQuickWindowPrivate::createTextureFromNativeTexture(quint64 nativeObjectHandle, |
3100 | int nativeLayoutOrState, |
3101 | uint nativeFormat, |
3102 | const QSize &size, |
3103 | QQuickWindow::CreateTextureOptions options, |
3104 | TextureFromNativeTextureFlags flags) const |
3105 | { |
3106 | if (!rhi) |
3107 | return nullptr; |
3108 | |
3109 | QSGPlainTexture *texture = new QSGPlainTexture; |
3110 | texture->setTextureFromNativeTexture(rhi, nativeObjectHandle, nativeLayoutOrState, nativeFormat, |
3111 | size, options, flags); |
3112 | texture->setHasAlphaChannel(options & QQuickWindow::TextureHasAlphaChannel); |
3113 | // note that the QRhiTexture does not (and cannot) own the native object |
3114 | texture->setOwnsTexture(true); // texture meaning the QRhiTexture here, not the native object |
3115 | texture->setTextureSize(size); |
3116 | return texture; |
3117 | } |
3118 | |
3119 | /*! |
3120 | \qmlproperty color Window::color |
3121 | |
3122 | The background color for the window. |
3123 | |
3124 | Setting this property is more efficient than using a separate Rectangle. |
3125 | |
3126 | \note If you set the color to \c "transparent" or to a color with alpha translucency, |
3127 | you should also set suitable \l flags such as \c {flags: Qt.FramelessWindowHint}. |
3128 | Otherwise, window translucency may not be enabled consistently on all platforms. |
3129 | */ |
3130 | |
3131 | /*! |
3132 | \property QQuickWindow::color |
3133 | \brief The color used to clear the color buffer at the beginning of each frame. |
3134 | |
3135 | By default, the clear color is white. |
3136 | |
3137 | \sa setDefaultAlphaBuffer() |
3138 | */ |
3139 | |
3140 | void QQuickWindow::setColor(const QColor &color) |
3141 | { |
3142 | Q_D(QQuickWindow); |
3143 | if (color == d->clearColor) |
3144 | return; |
3145 | |
3146 | if (color.alpha() != d->clearColor.alpha()) { |
3147 | QSurfaceFormat fmt = requestedFormat(); |
3148 | if (color.alpha() < 255) |
3149 | fmt.setAlphaBufferSize(8); |
3150 | else |
3151 | fmt.setAlphaBufferSize(-1); |
3152 | setFormat(fmt); |
3153 | } |
3154 | d->clearColor = color; |
3155 | emit colorChanged(color); |
3156 | update(); |
3157 | } |
3158 | |
3159 | QColor QQuickWindow::color() const |
3160 | { |
3161 | return d_func()->clearColor; |
3162 | } |
3163 | |
3164 | /*! |
3165 | \brief Returns whether to use alpha transparency on newly created windows. |
3166 | |
3167 | \since 5.1 |
3168 | \sa setDefaultAlphaBuffer() |
3169 | */ |
3170 | bool QQuickWindow::hasDefaultAlphaBuffer() |
3171 | { |
3172 | return QQuickWindowPrivate::defaultAlphaBuffer; |
3173 | } |
3174 | |
3175 | /*! |
3176 | \brief \a useAlpha specifies whether to use alpha transparency on newly created windows. |
3177 | \since 5.1 |
3178 | |
3179 | In any application which expects to create translucent windows, it's necessary to set |
3180 | this to true before creating the first QQuickWindow. The default value is false. |
3181 | |
3182 | \sa hasDefaultAlphaBuffer() |
3183 | */ |
3184 | void QQuickWindow::setDefaultAlphaBuffer(bool useAlpha) |
3185 | { |
3186 | QQuickWindowPrivate::defaultAlphaBuffer = useAlpha; |
3187 | } |
3188 | |
3189 | /*! |
3190 | \struct QQuickWindow::GraphicsStateInfo |
3191 | \inmodule QtQuick |
3192 | \since 5.14 |
3193 | |
3194 | \brief Describes some of the RHI's graphics state at the point of a |
3195 | \l{QQuickWindow::beginExternalCommands()}{beginExternalCommands()} call. |
3196 | */ |
3197 | |
3198 | /*! |
3199 | \variable QQuickWindow::GraphicsStateInfo::currentFrameSlot |
3200 | \since 5.14 |
3201 | \brief the current frame slot index while recording a frame. |
3202 | |
3203 | When the scenegraph renders with lower level 3D APIs such as Vulkan or |
3204 | Metal, it is the Qt's responsibility to ensure blocking whenever starting a |
3205 | new frame and finding the CPU is already a certain number of frames ahead |
3206 | of the GPU (because the command buffer submitted in frame no. \c{current} - |
3207 | \c{FramesInFlight} has not yet completed). With other graphics APIs, such |
3208 | as OpenGL or Direct 3D 11 this level of control is not exposed to the API |
3209 | client but rather handled by the implementation of the graphics API. |
3210 | |
3211 | By extension, this also means that the appropriate double (or triple) |
3212 | buffering of resources, such as buffers, is up to the graphics API client |
3213 | to manage. Most commonly, a uniform buffer where the data changes between |
3214 | frames cannot simply change its contents when submitting a frame, given |
3215 | that the frame may still be active ("in flight") when starting to record |
3216 | the next frame. To avoid stalling the pipeline, one way is to have multiple |
3217 | buffers (and memory allocations) under the hood, thus realizing at least a |
3218 | double buffered scheme for such resources. |
3219 | |
3220 | Applications that integrate rendering done directly with a graphics API |
3221 | such as Vulkan may want to perform a similar double or triple buffering of |
3222 | their own graphics resources, in a way that is compatible with the Qt |
3223 | rendering engine's frame submission process. That then involves knowing the |
3224 | values for the maximum number of in-flight frames (which is typically 2 or |
3225 | 3) and the current frame slot index, which is a number running 0, 1, .., |
3226 | FramesInFlight-1, and then wrapping around. The former is exposed in the |
3227 | \l{QQuickWindow::GraphicsStateInfo::framesInFlight}{framesInFlight} |
3228 | variable. The latter, current index, is this value. |
3229 | |
3230 | For an example of using these values in practice, refer to the {Scene Graph |
3231 | - Vulkan Under QML} and {Scene Graph - Vulkan Texture Import} examples. |
3232 | */ |
3233 | |
3234 | /*! |
3235 | \variable QQuickWindow::GraphicsStateInfo::framesInFlight |
3236 | \since 5.14 |
3237 | \brief the maximum number of frames kept in flight. |
3238 | |
3239 | See \l{QQuickWindow::GraphicsStateInfo::currentFrameSlot}{currentFrameSlot} |
3240 | for a detailed description. |
3241 | */ |
3242 | |
3243 | /*! |
3244 | \return a reference to a GraphicsStateInfo struct describing some of the |
3245 | RHI's internal state, in particular, the double or tripple buffering status |
3246 | of the backend (such as, the Vulkan or Metal integrations). This is |
3247 | relevant when the underlying graphics APIs is Vulkan or Metal, and the |
3248 | external rendering code wishes to perform double or tripple buffering of |
3249 | its own often-changing resources, such as, uniform buffers, in order to |
3250 | avoid stalling the pipeline. |
3251 | */ |
3252 | const QQuickWindow::GraphicsStateInfo &QQuickWindow::graphicsStateInfo() |
3253 | { |
3254 | Q_D(QQuickWindow); |
3255 | if (d->rhi) { |
3256 | d->rhiStateInfo.currentFrameSlot = d->rhi->currentFrameSlot(); |
3257 | d->rhiStateInfo.framesInFlight = d->rhi->resourceLimit(limit: QRhi::FramesInFlight); |
3258 | } |
3259 | return d->rhiStateInfo; |
3260 | } |
3261 | |
3262 | /*! |
3263 | When mixing raw graphics (OpenGL, Vulkan, Metal, etc.) commands with scene |
3264 | graph rendering, it is necessary to call this function before recording |
3265 | commands to the command buffer used by the scene graph to render its main |
3266 | render pass. This is to avoid clobbering state. |
3267 | |
3268 | In practice this function is often called from a slot connected to the |
3269 | beforeRenderPassRecording() or afterRenderPassRecording() signals. |
3270 | |
3271 | The function does not need to be called when recording commands to the |
3272 | application's own command buffer (such as, a VkCommandBuffer or |
3273 | MTLCommandBuffer + MTLRenderCommandEncoder created and managed by the |
3274 | application, not retrieved from the scene graph). With graphics APIs where |
3275 | no native command buffer concept is exposed (OpenGL, Direct 3D 11), |
3276 | beginExternalCommands() and endExternalCommands() together provide a |
3277 | replacement for the Qt 5 resetOpenGLState() function. |
3278 | |
3279 | Calling this function and endExternalCommands() is not necessary within the |
3280 | \l{QSGRenderNode::render()}{render()} implementation of a QSGRenderNode |
3281 | because the scene graph performs the necessary steps implicitly for render |
3282 | nodes. |
3283 | |
3284 | Native graphics objects (such as, graphics device, command buffer or |
3285 | encoder) are accessible via QSGRendererInterface::getResource(). |
3286 | |
3287 | \warning Watch out for the fact that |
3288 | QSGRendererInterface::CommandListResource may return a different object |
3289 | between beginExternalCommands() - endExternalCommands(). This can happen |
3290 | when the underlying implementation provides a dedicated secondary command |
3291 | buffer for recording external graphics commands within a render pass. |
3292 | Therefore, always query CommandListResource after calling this function. Do |
3293 | not attempt to reuse an object from an earlier query. |
3294 | |
3295 | \note When the scenegraph is using OpenGL, pay attention to the fact that |
3296 | the OpenGL state in the context can have arbitrary settings, and this |
3297 | function does not perform any resetting of the state back to defaults. |
3298 | |
3299 | \sa endExternalCommands(), QQuickOpenGLUtils::resetOpenGLState() |
3300 | |
3301 | \since 5.14 |
3302 | */ |
3303 | void QQuickWindow::beginExternalCommands() |
3304 | { |
3305 | Q_D(QQuickWindow); |
3306 | if (d->rhi && d->context && d->context->isValid()) { |
3307 | QSGDefaultRenderContext *rc = static_cast<QSGDefaultRenderContext *>(d->context); |
3308 | QRhiCommandBuffer *cb = rc->currentFrameCommandBuffer(); |
3309 | if (cb) |
3310 | cb->beginExternal(); |
3311 | } |
3312 | } |
3313 | |
3314 | /*! |
3315 | When mixing raw graphics (OpenGL, Vulkan, Metal, etc.) commands with scene |
3316 | graph rendering, it is necessary to call this function after recording |
3317 | commands to the command buffer used by the scene graph to render its main |
3318 | render pass. This is to avoid clobbering state. |
3319 | |
3320 | In practice this function is often called from a slot connected to the |
3321 | beforeRenderPassRecording() or afterRenderPassRecording() signals. |
3322 | |
3323 | The function does not need to be called when recording commands to the |
3324 | application's own command buffer (such as, a VkCommandBuffer or |
3325 | MTLCommandBuffer + MTLRenderCommandEncoder created and managed by the |
3326 | application, not retrieved from the scene graph). With graphics APIs where |
3327 | no native command buffer concept is exposed (OpenGL, Direct 3D 11), |
3328 | beginExternalCommands() and endExternalCommands() together provide a |
3329 | replacement for the Qt 5 resetOpenGLState() function. |
3330 | |
3331 | Calling this function and beginExternalCommands() is not necessary within the |
3332 | \l{QSGRenderNode::render()}{render()} implementation of a QSGRenderNode |
3333 | because the scene graph performs the necessary steps implicitly for render |
3334 | nodes. |
3335 | |
3336 | \sa beginExternalCommands(), QQuickOpenGLUtils::resetOpenGLState() |
3337 | |
3338 | \since 5.14 |
3339 | */ |
3340 | void QQuickWindow::endExternalCommands() |
3341 | { |
3342 | Q_D(QQuickWindow); |
3343 | if (d->rhi && d->context && d->context->isValid()) { |
3344 | QSGDefaultRenderContext *rc = static_cast<QSGDefaultRenderContext *>(d->context); |
3345 | QRhiCommandBuffer *cb = rc->currentFrameCommandBuffer(); |
3346 | if (cb) |
3347 | cb->endExternal(); |
3348 | } |
3349 | } |
3350 | |
3351 | /*! |
3352 | \qmlproperty string Window::title |
3353 | |
3354 | The window's title in the windowing system. |
3355 | |
3356 | The window title might appear in the title area of the window decorations, |
3357 | depending on the windowing system and the window flags. It might also |
3358 | be used by the windowing system to identify the window in other contexts, |
3359 | such as in the task switcher. |
3360 | */ |
3361 | |
3362 | /*! |
3363 | \qmlproperty Qt::WindowModality Window::modality |
3364 | |
3365 | The modality of the window. |
3366 | |
3367 | A modal window prevents other windows from receiving input events. |
3368 | Possible values are Qt.NonModal (the default), Qt.WindowModal, |
3369 | and Qt.ApplicationModal. |
3370 | */ |
3371 | |
3372 | /*! |
3373 | \qmlproperty Qt::WindowFlags Window::flags |
3374 | |
3375 | The window flags of the window. |
3376 | |
3377 | The window flags control the window's appearance in the windowing system, |
3378 | whether it's a dialog, popup, or a regular window, and whether it should |
3379 | have a title bar, etc. |
3380 | |
3381 | The flags that you read from this property might differ from the ones |
3382 | that you set if the requested flags could not be fulfilled. |
3383 | |
3384 | \snippet qml/splashWindow.qml entire |
3385 | |
3386 | \sa Qt::WindowFlags, {Qt Quick Examples - Window and Screen} |
3387 | */ |
3388 | |
3389 | /*! |
3390 | \qmlattachedproperty Window Window::window |
3391 | \since 5.7 |
3392 | |
3393 | This attached property holds the item's window. |
3394 | The Window attached property can be attached to any Item. |
3395 | */ |
3396 | |
3397 | /*! |
3398 | \qmlattachedproperty int Window::width |
3399 | \qmlattachedproperty int Window::height |
3400 | \since 5.5 |
3401 | |
3402 | These attached properties hold the size of the item's window. |
3403 | The Window attached property can be attached to any Item. |
3404 | */ |
3405 | |
3406 | /*! |
3407 | \qmlproperty int Window::x |
3408 | \qmlproperty int Window::y |
3409 | \qmlproperty int Window::width |
3410 | \qmlproperty int Window::height |
3411 | |
3412 | Defines the window's position and size. |
3413 | |
3414 | The (x,y) position is relative to the \l Screen if there is only one, |
3415 | or to the virtual desktop (arrangement of multiple screens). |
3416 | |
3417 | \qml |
3418 | Window { x: 100; y: 100; width: 100; height: 100 } |
3419 | \endqml |
3420 | |
3421 | \image screen-and-window-dimensions.jpg |
3422 | */ |
3423 | |
3424 | /*! |
3425 | \qmlproperty int Window::minimumWidth |
3426 | \qmlproperty int Window::minimumHeight |
3427 | \since 5.1 |
3428 | |
3429 | Defines the window's minimum size. |
3430 | |
3431 | This is a hint to the window manager to prevent resizing below the specified |
3432 | width and height. |
3433 | */ |
3434 | |
3435 | /*! |
3436 | \qmlproperty int Window::maximumWidth |
3437 | \qmlproperty int Window::maximumHeight |
3438 | \since 5.1 |
3439 | |
3440 | Defines the window's maximum size. |
3441 | |
3442 | This is a hint to the window manager to prevent resizing above the specified |
3443 | width and height. |
3444 | */ |
3445 | |
3446 | /*! |
3447 | \qmlproperty bool Window::visible |
3448 | |
3449 | Whether the window is visible on the screen. |
3450 | |
3451 | Setting visible to false is the same as setting \l visibility to \l {QWindow::}{Hidden}. |
3452 | |
3453 | \sa visibility |
3454 | */ |
3455 | |
3456 | /*! |
3457 | \keyword qml-window-visibility-prop |
3458 | \qmlproperty QWindow::Visibility Window::visibility |
3459 | |
3460 | The screen-occupation state of the window. |
3461 | |
3462 | Visibility is whether the window should appear in the windowing system as |
3463 | normal, minimized, maximized, fullscreen or hidden. |
3464 | |
3465 | To set the visibility to \l {QWindow::}{AutomaticVisibility} means to give the |
3466 | window a default visible state, which might be \l {QWindow::}{FullScreen} or |
3467 | \l {QWindow::}{Windowed} depending on the platform. However when reading the |
3468 | visibility property you will always get the actual state, never |
3469 | \c AutomaticVisibility. |
3470 | |
3471 | When a window is not visible, its visibility is \c Hidden, and setting |
3472 | visibility to \l {QWindow::}{Hidden} is the same as setting \l visible to \c false. |
3473 | |
3474 | \snippet qml/windowVisibility.qml entire |
3475 | |
3476 | \sa visible, {Qt Quick Examples - Window and Screen} |
3477 | \since 5.1 |
3478 | */ |
3479 | |
3480 | /*! |
3481 | \qmlattachedproperty QWindow::Visibility Window::visibility |
3482 | \readonly |
3483 | \since 5.4 |
3484 | |
3485 | This attached property holds whether the window is currently shown |
3486 | in the windowing system as normal, minimized, maximized, fullscreen or |
3487 | hidden. The \c Window attached property can be attached to any Item. If the |
3488 | item is not shown in any window, the value will be \l {QWindow::}{Hidden}. |
3489 | |
3490 | \sa visible, {qml-window-visibility-prop}{visibility} |
3491 | */ |
3492 | |
3493 | /*! |
3494 | \qmlproperty Item Window::contentItem |
3495 | \readonly |
3496 | \brief The invisible root item of the scene. |
3497 | */ |
3498 | |
3499 | /*! |
3500 | \qmlproperty Qt::ScreenOrientation Window::contentOrientation |
3501 | |
3502 | This is a hint to the window manager in case it needs to display |
3503 | additional content like popups, dialogs, status bars, or similar |
3504 | in relation to the window. |
3505 | |
3506 | The recommended orientation is \l {Screen::orientation}{Screen.orientation}, but |
3507 | an application doesn't have to support all possible orientations, |
3508 | and thus can opt to ignore the current screen orientation. |
3509 | |
3510 | The difference between the window and the content orientation |
3511 | determines how much to rotate the content by. |
3512 | |
3513 | The default value is Qt::PrimaryOrientation. |
3514 | |
3515 | \sa Screen |
3516 | |
3517 | \since 5.1 |
3518 | */ |
3519 | |
3520 | /*! |
3521 | \qmlproperty real Window::opacity |
3522 | |
3523 | The opacity of the window. |
3524 | |
3525 | If the windowing system supports window opacity, this can be used to fade the |
3526 | window in and out, or to make it semitransparent. |
3527 | |
3528 | A value of 1.0 or above is treated as fully opaque, whereas a value of 0.0 or below |
3529 | is treated as fully transparent. Values inbetween represent varying levels of |
3530 | translucency between the two extremes. |
3531 | |
3532 | The default value is 1.0. |
3533 | |
3534 | \since 5.1 |
3535 | */ |
3536 | |
3537 | /*! |
3538 | \qmlproperty variant Window::screen |
3539 | |
3540 | The screen with which the window is associated. |
3541 | |
3542 | If specified before showing a window, will result in the window being shown |
3543 | on that screen, unless an explicit window position has been set. The value |
3544 | must be an element from the Qt.application.screens array. |
3545 | |
3546 | \note To ensure that the window is associated with the desired screen when |
3547 | the underlying native window is created, make sure this property is set as |
3548 | early as possible and that the setting of its value is not deferred. This |
3549 | can be particularly important on embedded platforms without a windowing system, |
3550 | where only one window per screen is allowed at a time. Setting the screen after |
3551 | a window has been created does not move the window if the new screen is part of |
3552 | the same virtual desktop as the old screen. |
3553 | |
3554 | \since 5.9 |
3555 | |
3556 | \sa QWindow::setScreen(), QWindow::screen(), QScreen, {QtQml::Qt::application}{Qt.application} |
3557 | */ |
3558 | |
3559 | /*! |
3560 | \qmlproperty QWindow Window::transientParent |
3561 | \since 5.13 |
3562 | |
3563 | The window for which this window is a transient pop-up. |
3564 | |
3565 | This is a hint to the window manager that this window is a dialog or pop-up |
3566 | on behalf of the transient parent. It usually means that the transient |
3567 | window will be centered over its transient parent when it is initially |
3568 | shown, that minimizing the parent window will also minimize the transient |
3569 | window, and so on; however results vary somewhat from platform to platform. |
3570 | |
3571 | Normally if you declare a Window inside an Item or inside another Window, |
3572 | this relationship is deduced automatically. In that case, if you declare |
3573 | this window's \l visible property \c true, it will not actually be shown |
3574 | until the \c transientParent window is shown. |
3575 | |
3576 | However if you set this property, then Qt Quick will no longer wait until |
3577 | the \c transientParent window is shown before showing this window. If you |
3578 | want to be able to show a transient window independently of the "parent" |
3579 | Item or Window within which it was declared, you can remove that |
3580 | relationship by setting \c transientParent to \c null: |
3581 | |
3582 | \snippet qml/nestedWindowTransientParent.qml 0 |
3583 | \snippet qml/nestedWindowTransientParent.qml 1 |
3584 | |
3585 | In order to cause the window to be centered above its transient parent by |
3586 | default, depending on the window manager, it may also be necessary to set |
3587 | the \l Window::flags property with a suitable \l Qt::WindowType (such as |
3588 | \c Qt::Dialog). |
3589 | */ |
3590 | |
3591 | /*! |
3592 | \property QQuickWindow::transientParent |
3593 | \brief The window for which this window is a transient pop-up. |
3594 | \since 5.13 |
3595 | |
3596 | This is a hint to the window manager that this window is a dialog or pop-up |
3597 | on behalf of the transient parent, which may be any kind of \l QWindow. |
3598 | |
3599 | In order to cause the window to be centered above its transient parent by |
3600 | default, depending on the window manager, it may also be necessary to set |
3601 | the \l flags property with a suitable \l Qt::WindowType (such as \c Qt::Dialog). |
3602 | |
3603 | \sa parent() |
3604 | */ |
3605 | |
3606 | /*! |
3607 | \qmlproperty Item Window::activeFocusItem |
3608 | \since 5.1 |
3609 | |
3610 | The item which currently has active focus or \c null if there is |
3611 | no item with active focus. |
3612 | */ |
3613 | |
3614 | /*! |
3615 | \qmlattachedproperty Item Window::activeFocusItem |
3616 | \since 5.4 |
3617 | |
3618 | This attached property holds the item which currently has active focus or |
3619 | \c null if there is no item with active focus. The Window attached property |
3620 | can be attached to any Item. |
3621 | */ |
3622 | |
3623 | /*! |
3624 | \qmlproperty bool Window::active |
3625 | \since 5.1 |
3626 | |
3627 | The active status of the window. |
3628 | |
3629 | \snippet qml/windowPalette.qml declaration-and-color |
3630 | \snippet qml/windowPalette.qml closing-brace |
3631 | |
3632 | \sa requestActivate() |
3633 | */ |
3634 | |
3635 | /*! |
3636 | \qmlattachedproperty bool Window::active |
3637 | \since 5.4 |
3638 | |
3639 | This attached property tells whether the window is active. The Window |
3640 | attached property can be attached to any Item. |
3641 | |
3642 | Here is an example which changes a label to show the active state of the |
3643 | window in which it is shown: |
3644 | |
3645 | \snippet qml/windowActiveAttached.qml entire |
3646 | */ |
3647 | |
3648 | /*! |
3649 | \qmlmethod QtQuick::Window::requestActivate() |
3650 | \since 5.1 |
3651 | |
3652 | Requests the window to be activated, i.e. receive keyboard focus. |
3653 | */ |
3654 | |
3655 | /*! |
3656 | \qmlmethod QtQuick::Window::alert(int msec) |
3657 | \since 5.1 |
3658 | |
3659 | Causes an alert to be shown for \a msec milliseconds. If \a msec is \c 0 |
3660 | (the default), then the alert is shown indefinitely until the window |
3661 | becomes active again. |
3662 | |
3663 | In alert state, the window indicates that it demands attention, for example |
3664 | by flashing or bouncing the taskbar entry. |
3665 | */ |
3666 | |
3667 | /*! |
3668 | \qmlmethod QtQuick::Window::close() |
3669 | |
3670 | Closes the window. |
3671 | |
3672 | When this method is called, or when the user tries to close the window by |
3673 | its title bar button, the \l closing signal will be emitted. If there is no |
3674 | handler, or the handler does not revoke permission to close, the window |
3675 | will subsequently close. If the QGuiApplication::quitOnLastWindowClosed |
3676 | property is \c true, and there are no other windows open, the application |
3677 | will quit. |
3678 | */ |
3679 | |
3680 | /*! |
3681 | \qmlmethod QtQuick::Window::raise() |
3682 | |
3683 | Raises the window in the windowing system. |
3684 | |
3685 | Requests that the window be raised to appear above other windows. |
3686 | */ |
3687 | |
3688 | /*! |
3689 | \qmlmethod QtQuick::Window::lower() |
3690 | |
3691 | Lowers the window in the windowing system. |
3692 | |
3693 | Requests that the window be lowered to appear below other windows. |
3694 | */ |
3695 | |
3696 | /*! |
3697 | \qmlmethod QtQuick::Window::show() |
3698 | |
3699 | Shows the window. |
3700 | |
3701 | This is equivalent to calling showFullScreen(), showMaximized(), or showNormal(), |
3702 | depending on the platform's default behavior for the window type and flags. |
3703 | |
3704 | \sa showFullScreen(), showMaximized(), showNormal(), hide(), QQuickItem::flags() |
3705 | */ |
3706 | |
3707 | /*! |
3708 | \qmlmethod QtQuick::Window::hide() |
3709 | |
3710 | Hides the window. |
3711 | |
3712 | Equivalent to setting \l visible to \c false or \l visibility to \l {QWindow::}{Hidden}. |
3713 | |
3714 | \sa show() |
3715 | */ |
3716 | |
3717 | /*! |
3718 | \qmlmethod QtQuick::Window::showMinimized() |
3719 | |
3720 | Shows the window as minimized. |
3721 | |
3722 | Equivalent to setting \l visibility to \l {QWindow::}{Minimized}. |
3723 | */ |
3724 | |
3725 | /*! |
3726 | \qmlmethod QtQuick::Window::showMaximized() |
3727 | |
3728 | Shows the window as maximized. |
3729 | |
3730 | Equivalent to setting \l visibility to \l {QWindow::}{Maximized}. |
3731 | */ |
3732 | |
3733 | /*! |
3734 | \qmlmethod QtQuick::Window::showFullScreen() |
3735 | |
3736 | Shows the window as fullscreen. |
3737 | |
3738 | Equivalent to setting \l visibility to \l {QWindow::}{FullScreen}. |
3739 | */ |
3740 | |
3741 | /*! |
3742 | \qmlmethod QtQuick::Window::showNormal() |
3743 | |
3744 | Shows the window as normal, i.e. neither maximized, minimized, nor fullscreen. |
3745 | |
3746 | Equivalent to setting \l visibility to \l {QWindow::}{Windowed}. |
3747 | */ |
3748 | |
3749 | /*! |
3750 | \enum QQuickWindow::RenderStage |
3751 | \since 5.4 |
3752 | |
3753 | \value BeforeSynchronizingStage Before synchronization. |
3754 | \value AfterSynchronizingStage After synchronization. |
3755 | \value BeforeRenderingStage Before rendering. |
3756 | \value AfterRenderingStage After rendering. |
3757 | \value AfterSwapStage After the frame is swapped. |
3758 | \value NoStage As soon as possible. This value was added in Qt 5.6. |
3759 | |
3760 | \sa {Scene Graph and Rendering} |
3761 | */ |
3762 | |
3763 | /*! |
3764 | \since 5.4 |
3765 | |
3766 | Schedules \a job to run when the rendering of this window reaches |
3767 | the given \a stage. |
3768 | |
3769 | This is a convenience to the equivalent signals in QQuickWindow for |
3770 | "one shot" tasks. |
3771 | |
3772 | The window takes ownership over \a job and will delete it when the |
3773 | job is completed. |
3774 | |
3775 | If rendering is shut down before \a job has a chance to run, the |
3776 | job will be run and then deleted as part of the scene graph cleanup. |
3777 | If the window is never shown and no rendering happens before the QQuickWindow |
3778 | is destroyed, all pending jobs will be destroyed without their run() |
3779 | method being called. |
3780 | |
3781 | If the rendering is happening on a different thread, then the job |
3782 | will happen on the rendering thread. |
3783 | |
3784 | If \a stage is \l NoStage, \a job will be run at the earliest opportunity |
3785 | whenever the render thread is not busy rendering a frame. If the window is |
3786 | not exposed, and is not renderable, at the time the job is either posted or |
3787 | handled, the job is deleted without executing the run() method. If a |
3788 | non-threaded renderer is in use, the run() method of the job is executed |
3789 | synchronously. When rendering with OpenGL, the OpenGL context is changed to |
3790 | the renderer's context before executing any job, including \l NoStage jobs. |
3791 | |
3792 | \note This function does not trigger rendering; the jobs targeting any other |
3793 | stage than NoStage will be stored run until rendering is triggered elsewhere. |
3794 | To force the job to run earlier, call QQuickWindow::update(); |
3795 | |
3796 | \sa beforeRendering(), afterRendering(), beforeSynchronizing(), |
3797 | afterSynchronizing(), frameSwapped(), sceneGraphInvalidated() |
3798 | */ |
3799 | |
3800 | void QQuickWindow::scheduleRenderJob(QRunnable *job, RenderStage stage) |
3801 | { |
3802 | Q_D(QQuickWindow); |
3803 | |
3804 | d->renderJobMutex.lock(); |
3805 | if (stage == BeforeSynchronizingStage) { |
3806 | d->beforeSynchronizingJobs << job; |
3807 | } else if (stage == AfterSynchronizingStage) { |
3808 | d->afterSynchronizingJobs << job; |
3809 | } else if (stage == BeforeRenderingStage) { |
3810 | d->beforeRenderingJobs << job; |
3811 | } else if (stage == AfterRenderingStage) { |
3812 | d->afterRenderingJobs << job; |
3813 | } else if (stage == AfterSwapStage) { |
3814 | d->afterSwapJobs << job; |
3815 | } else if (stage == NoStage) { |
3816 | if (d->renderControl && d->rhi && d->rhi->thread() == QThread::currentThread()) { |
3817 | job->run(); |
3818 | delete job; |
3819 | } else if (isExposed()) { |
3820 | d->windowManager->postJob(window: this, job); |
3821 | } else { |
3822 | delete job; |
3823 | } |
3824 | } |
3825 | d->renderJobMutex.unlock(); |
3826 | } |
3827 | |
3828 | void QQuickWindowPrivate::runAndClearJobs(QList<QRunnable *> *jobs) |
3829 | { |
3830 | renderJobMutex.lock(); |
3831 | QList<QRunnable *> jobList = *jobs; |
3832 | jobs->clear(); |
3833 | renderJobMutex.unlock(); |
3834 | |
3835 | for (QRunnable *r : std::as_const(t&: jobList)) { |
3836 | r->run(); |
3837 | delete r; |
3838 | } |
3839 | } |
3840 | |
3841 | void QQuickWindow::runJobsAfterSwap() |
3842 | { |
3843 | Q_D(QQuickWindow); |
3844 | d->runAndClearJobs(jobs: &d->afterSwapJobs); |
3845 | } |
3846 | |
3847 | /*! |
3848 | Returns the device pixel ratio for this window. |
3849 | |
3850 | This is different from QWindow::devicePixelRatio() in that it supports |
3851 | redirected rendering via QQuickRenderControl and QQuickRenderTarget. When |
3852 | using a QQuickRenderControl, the QQuickWindow is often not fully created, |
3853 | meaning it is never shown and there is no underlying native window created |
3854 | in the windowing system. As a result, querying properties like the device |
3855 | pixel ratio cannot give correct results. This function takes into account |
3856 | both QQuickRenderControl::renderWindowFor() and |
3857 | QQuickRenderTarget::devicePixelRatio(). When no redirection is in effect, |
3858 | the result is same as QWindow::devicePixelRatio(). |
3859 | |
3860 | \sa QQuickRenderControl, QQuickRenderTarget, setRenderTarget(), QWindow::devicePixelRatio() |
3861 | */ |
3862 | qreal QQuickWindow::effectiveDevicePixelRatio() const |
3863 | { |
3864 | Q_D(const QQuickWindow); |
3865 | QWindow *w = QQuickRenderControl::renderWindowFor(win: const_cast<QQuickWindow *>(this)); |
3866 | if (w) |
3867 | return w->devicePixelRatio(); |
3868 | |
3869 | if (!d->customRenderTarget.isNull()) |
3870 | return d->customRenderTarget.devicePixelRatio(); |
3871 | |
3872 | return devicePixelRatio(); |
3873 | } |
3874 | |
3875 | /*! |
3876 | \return the current renderer interface. The value is always valid and is never null. |
3877 | |
3878 | \note This function can be called at any time after constructing the |
3879 | QQuickWindow, even while isSceneGraphInitialized() is still false. However, |
3880 | some renderer interface functions, in particular |
3881 | QSGRendererInterface::getResource() will not be functional until the |
3882 | scenegraph is up and running. Backend queries, like |
3883 | QSGRendererInterface::graphicsApi() or QSGRendererInterface::shaderType(), |
3884 | will always be functional on the other hand. |
3885 | |
3886 | \note The ownership of the returned pointer stays with Qt. The returned |
3887 | instance may or may not be shared between different QQuickWindow instances, |
3888 | depending on the scenegraph backend in use. Therefore applications are |
3889 | expected to query the interface object for each QQuickWindow instead of |
3890 | reusing the already queried pointer. |
3891 | |
3892 | \sa QSGRenderNode, QSGRendererInterface |
3893 | |
3894 | \since 5.8 |
3895 | */ |
3896 | QSGRendererInterface *QQuickWindow::rendererInterface() const |
3897 | { |
3898 | Q_D(const QQuickWindow); |
3899 | |
3900 | // no context validity check - it is essential to be able to return a |
3901 | // renderer interface instance before scenegraphInitialized() is emitted |
3902 | // (depending on the backend, that can happen way too late for some of the |
3903 | // rif use cases, like examining the graphics api or shading language in |
3904 | // use) |
3905 | |
3906 | return d->context->sceneGraphContext()->rendererInterface(renderContext: d->context); |
3907 | } |
3908 | |
3909 | /*! |
3910 | \return the QRhi object used by this window for rendering. |
3911 | |
3912 | Available only when the window is using Qt's 3D API and shading language |
3913 | abstractions, meaning the result is always null when using the \c software |
3914 | adaptation. |
3915 | |
3916 | The result is valid only when rendering has been initialized, which is |
3917 | indicated by the emission of the sceneGraphInitialized() signal. Before |
3918 | that point, the returned value is null. With a regular, on-screen |
3919 | QQuickWindow scenegraph initialization typically happens when the native |
3920 | window gets exposed (shown) the first time. When using QQuickRenderControl, |
3921 | initialization is done in the explicit |
3922 | \l{QQuickRenderControl::initialize()}{initialize()} call. |
3923 | |
3924 | In practice this function is a shortcut to querying the QRhi via the |
3925 | QSGRendererInterface. |
3926 | |
3927 | \since 6.6 |
3928 | */ |
3929 | QRhi *QQuickWindow::rhi() const |
3930 | { |
3931 | Q_D(const QQuickWindow); |
3932 | return d->rhi; |
3933 | } |
3934 | |
3935 | /*! |
3936 | \return the QRhiSwapChain used by this window, if there is one. |
3937 | |
3938 | \note Only on-screen windows backed by one of the standard render loops |
3939 | (such as, \c basic or \c threaded) will have a swapchain. Otherwise the |
3940 | returned value is null. For example, the result is always null when the |
3941 | window is used with QQuickRenderControl. |
3942 | |
3943 | \since 6.6 |
3944 | */ |
3945 | QRhiSwapChain *QQuickWindow::swapChain() const |
3946 | { |
3947 | Q_D(const QQuickWindow); |
3948 | return d->swapchain; |
3949 | } |
3950 | |
3951 | /*! |
3952 | Requests the specified graphics \a api. |
3953 | |
3954 | When the built-in, default graphics adaptation is used, \a api specifies |
3955 | which graphics API (OpenGL, Vulkan, Metal, or Direct3D) the scene graph |
3956 | should use to render. In addition, the \c software backend is built-in as |
3957 | well, and can be requested by setting \a api to |
3958 | QSGRendererInterface::Software. |
3959 | |
3960 | Unlike setSceneGraphBackend(), which can only be used to request a given |
3961 | backend (shipped either built-in or installed as dynamically loaded |
3962 | plugins), this function works with the higher level concept of graphics |
3963 | APIs. It covers the backends that ship with Qt Quick, and thus have |
3964 | corresponding values in the QSGRendererInterface::GraphicsApi enum. |
3965 | |
3966 | When this function is not called at all, and the equivalent environment |
3967 | variable \c{QSG_RHI_BACKEND} is not set either, the scene graph will choose |
3968 | the graphics API to use based on the platform. |
3969 | |
3970 | This function becomes important in applications that are only prepared for |
3971 | rendering with a given API. For example, if there is native OpenGL or |
3972 | Vulkan rendering done by the application, it will want to ensure Qt Quick |
3973 | is rendering using OpenGL or Vulkan too. Such applications are expected to |
3974 | call this function early in their main() function. |
3975 | |
3976 | \note The call to the function must happen before constructing the first |
3977 | QQuickWindow in the application. The graphics API cannot be changed |
3978 | afterwards. |
3979 | |
3980 | \note When used in combination with QQuickRenderControl, this rule is |
3981 | relaxed: it is possible to change the graphics API, but only when all |
3982 | existing QQuickRenderControl and QQuickWindow instances have been |
3983 | destroyed. |
3984 | |
3985 | To query what graphics API the scene graph is using to render, |
3986 | QSGRendererInterface::graphicsApi() after the scene graph |
3987 | \l{QQuickWindow::isSceneGraphInitialized()}{has initialized}, which |
3988 | typically happens either when the window becomes visible for the first time, or |
3989 | when QQuickRenderControl::initialize() is called. |
3990 | |
3991 | To switch back to the default behavior, where the scene graph chooses a |
3992 | graphics API based on the platform and other conditions, set \a api to |
3993 | QSGRendererInterface::Unknown. |
3994 | |
3995 | \since 6.0 |
3996 | */ |
3997 | void QQuickWindow::setGraphicsApi(QSGRendererInterface::GraphicsApi api) |
3998 | { |
3999 | // Special cases: these are different scenegraph backends. |
4000 | switch (api) { |
4001 | case QSGRendererInterface::Software: |
4002 | setSceneGraphBackend(QStringLiteral("software" )); |
4003 | break; |
4004 | case QSGRendererInterface::OpenVG: |
4005 | setSceneGraphBackend(QStringLiteral("openvg" )); |
4006 | break; |
4007 | default: |
4008 | break; |
4009 | } |
4010 | |
4011 | // Standard case: tell the QRhi-based default adaptation what graphics api |
4012 | // (QRhi backend) to use. |
4013 | if (QSGRendererInterface::isApiRhiBased(api) || api == QSGRendererInterface::Unknown) |
4014 | QSGRhiSupport::instance_internal()->configure(api); |
4015 | } |
4016 | |
4017 | /*! |
4018 | \return the graphics API that would be used by the scene graph if it was |
4019 | initialized at this point in time. |
4020 | |
4021 | The standard way to query the API used by the scene graph is to use |
4022 | QSGRendererInterface::graphicsApi() once the scene graph has initialized, |
4023 | for example when or after the sceneGraphInitialized() signal is emitted. In |
4024 | that case one gets the true, real result, because then it is known that |
4025 | everything was initialized correctly using that graphics API. |
4026 | |
4027 | This is not always convenient. If the application needs to set up external |
4028 | frameworks, or needs to work with setGraphicsDevice() in a manner that |
4029 | depends on the scene graph's built in API selection logic, it is not always |
4030 | feasiable to defer such operations until after the QQuickWindow has been |
4031 | made visible or QQuickRenderControl::initialize() has been called. |
4032 | |
4033 | Therefore, this static function is provided as a counterpart to |
4034 | setGraphicsApi(): it can be called at any time, and the result reflects |
4035 | what API the scene graph would choose if it was initialized at the point of |
4036 | the call. |
4037 | |
4038 | \note This static function is intended to be called on the main (GUI) |
4039 | thread only. For querying the API when rendering, use QSGRendererInterface |
4040 | since that object lives on the render thread. |
4041 | |
4042 | \note This function does not take scene graph backends into account. |
4043 | |
4044 | \since 6.0 |
4045 | */ |
4046 | QSGRendererInterface::GraphicsApi QQuickWindow::graphicsApi() |
4047 | { |
4048 | // Note that this applies the settings e.g. from the env vars |
4049 | // (QSG_RHI_BACKEND) if it was not done at least once already. Whereas if |
4050 | // setGraphicsApi() was called before, or the scene graph is already |
4051 | // initialized, then this is just a simple query. |
4052 | return QSGRhiSupport::instance()->graphicsApi(); |
4053 | } |
4054 | |
4055 | /*! |
4056 | Requests a Qt Quick scenegraph \a backend. Backends can either be built-in |
4057 | or be installed in form of dynamically loaded plugins. |
4058 | |
4059 | \overload |
4060 | |
4061 | \note The call to the function must happen before constructing the first |
4062 | QQuickWindow in the application. It cannot be changed afterwards. |
4063 | |
4064 | See \l{Switch Between Adaptations in Your Application} for more information |
4065 | about the list of backends. If \a backend is invalid or an error occurs, the |
4066 | request is ignored. |
4067 | |
4068 | \note Calling this function is equivalent to setting the |
4069 | \c QT_QUICK_BACKEND or \c QMLSCENE_DEVICE environment variables. However, this |
4070 | API is safer to use in applications that spawn other processes as there is |
4071 | no need to worry about environment inheritance. |
4072 | |
4073 | \since 5.8 |
4074 | */ |
4075 | void QQuickWindow::setSceneGraphBackend(const QString &backend) |
4076 | { |
4077 | QSGContext::setBackend(backend); |
4078 | } |
4079 | |
4080 | /*! |
4081 | Returns the requested Qt Quick scenegraph backend. |
4082 | |
4083 | \note The return value of this function may still be outdated by |
4084 | subsequent calls to setSceneGraphBackend() until the first QQuickWindow in the |
4085 | application has been constructed. |
4086 | |
4087 | \note The value only reflects the request in the \c{QT_QUICK_BACKEND} |
4088 | environment variable after a QQuickWindow has been constructed. |
4089 | |
4090 | \since 5.9 |
4091 | */ |
4092 | QString QQuickWindow::sceneGraphBackend() |
4093 | { |
4094 | return QSGContext::backend(); |
4095 | } |
4096 | |
4097 | /*! |
4098 | Sets the graphics device objects for this window. The scenegraph will use |
4099 | existing device, physical device, and other objects specified by \a device |
4100 | instead of creating new ones. |
4101 | |
4102 | This function is very often used in combination with QQuickRenderControl |
4103 | and setRenderTarget(), in order to redirect Qt Quick rendering into a |
4104 | texture. |
4105 | |
4106 | A default constructed QQuickGraphicsDevice does not change the default |
4107 | behavior in any way. Once a \a device created via one of the |
4108 | QQuickGraphicsDevice factory functions, such as, |
4109 | QQuickGraphicsDevice::fromDeviceObjects(), is passed in, and the scenegraph |
4110 | uses a matching graphics API (with the example of fromDeviceObjects(), that |
4111 | would be Vulkan), the scenegraph will use the existing device objects (such |
4112 | as, the \c VkPhysicalDevice, \c VkDevice, and graphics queue family index, |
4113 | in case of Vulkan) encapsulated by the QQuickGraphicsDevice. This allows |
4114 | using the same device, and so sharing resources, such as buffers and |
4115 | textures, between Qt Quick and native rendering engines. |
4116 | |
4117 | \warning This function can only be called before initializing the |
4118 | scenegraph and will have no effect if called afterwards. In practice this |
4119 | typically means calling it right before QQuickRenderControl::initialize(). |
4120 | |
4121 | As an example, this time with Direct3D, the typical usage is expected to be |
4122 | the following: |
4123 | |
4124 | \badcode |
4125 | // native graphics resources set up by a custom D3D rendering engine |
4126 | ID3D11Device *device; |
4127 | ID3D11DeviceContext *context; |
4128 | ID3D11Texture2D *texture; |
4129 | ... |
4130 | // now to redirect Qt Quick content into 'texture' we could do the following: |
4131 | QQuickRenderControl *renderControl = new QQuickRenderControl; |
4132 | QQuickWindow *window = new QQuickWindow(renderControl); // this window will never be shown on-screen |
4133 | ... |
4134 | window->setGraphicsDevice(QQuickGraphicsDevice::fromDeviceAndContext(device, context)); |
4135 | renderControl->initialize(); |
4136 | window->setRenderTarget(QQuickRenderTarget::fromD3D11Texture(texture, textureSize); |
4137 | ... |
4138 | \endcode |
4139 | |
4140 | The key aspect of using this function is to ensure that resources or |
4141 | handles to resources, such as \c texture in the above example, are visible |
4142 | to and usable by both the external rendering engine and the scenegraph |
4143 | renderer. This requires using the same graphics device (or with OpenGL, |
4144 | OpenGL context). |
4145 | |
4146 | QQuickGraphicsDevice instances are implicitly shared, copyable, and |
4147 | can be passed by value. They do not own the associated native objects (such |
4148 | as, the ID3D11Device in the example). |
4149 | |
4150 | \note Using QQuickRenderControl does not always imply having to call this |
4151 | function. When adopting an existing device or context is not needed, this |
4152 | function should not be called, and the scene graph will then initialize its |
4153 | own devices and contexts normally, just as it would with an on-screen |
4154 | QQuickWindow. |
4155 | |
4156 | \since 6.0 |
4157 | |
4158 | \sa QQuickRenderControl, setRenderTarget(), setGraphicsApi() |
4159 | */ |
4160 | void QQuickWindow::setGraphicsDevice(const QQuickGraphicsDevice &device) |
4161 | { |
4162 | Q_D(QQuickWindow); |
4163 | d->customDeviceObjects = device; |
4164 | } |
4165 | |
4166 | /*! |
4167 | \return the QQuickGraphicsDevice passed to setGraphicsDevice(), or a |
4168 | default constructed one otherwise |
4169 | |
4170 | \since 6.0 |
4171 | |
4172 | \sa setGraphicsDevice() |
4173 | */ |
4174 | QQuickGraphicsDevice QQuickWindow::graphicsDevice() const |
4175 | { |
4176 | Q_D(const QQuickWindow); |
4177 | return d->customDeviceObjects; |
4178 | } |
4179 | |
4180 | /*! |
4181 | Sets the graphics configuration for this window. \a config contains various |
4182 | settings that may be taken into account by the scene graph when |
4183 | initializing the underlying graphics devices and contexts. |
4184 | |
4185 | Such additional configuration, specifying for example what device |
4186 | extensions to enable for Vulkan, becomes relevant and essential when |
4187 | integrating native graphics rendering code that relies on certain |
4188 | extensions. The same is true when integrating with an external 3D or VR |
4189 | engines, such as OpenXR. |
4190 | |
4191 | \note The configuration is ignored when adopting existing graphics devices |
4192 | via setGraphicsDevice() since the scene graph is then not in control of the |
4193 | actual construction of those objects. |
4194 | |
4195 | QQuickGraphicsConfiguration instances are implicitly shared, copyable, and |
4196 | can be passed by value. |
4197 | |
4198 | \warning Setting a QQuickGraphicsConfiguration on a QQuickWindow must |
4199 | happen early enough, before the scene graph is initialized for the first |
4200 | time for that window. With on-screen windows this means the call must be |
4201 | done before invoking show() on the QQuickWindow or QQuickView. With |
4202 | QQuickRenderControl the configuration must be finalized before calling |
4203 | \l{QQuickRenderControl::initialize()}{initialize()}. |
4204 | |
4205 | \since 6.0 |
4206 | */ |
4207 | void QQuickWindow::setGraphicsConfiguration(const QQuickGraphicsConfiguration &config) |
4208 | { |
4209 | Q_D(QQuickWindow); |
4210 | d->graphicsConfig = config; |
4211 | } |
4212 | |
4213 | /*! |
4214 | \return the QQuickGraphicsDevice passed to setGraphicsDevice(), or a |
4215 | default constructed one otherwise |
4216 | |
4217 | \since 6.0 |
4218 | |
4219 | \sa setGraphicsConfiguration() |
4220 | */ |
4221 | QQuickGraphicsConfiguration QQuickWindow::graphicsConfiguration() const |
4222 | { |
4223 | Q_D(const QQuickWindow); |
4224 | return d->graphicsConfig; |
4225 | } |
4226 | |
4227 | /*! |
4228 | Creates a simple rectangle node. When the scenegraph is not initialized, the return value is null. |
4229 | |
4230 | This is cross-backend alternative to constructing a QSGSimpleRectNode directly. |
4231 | |
4232 | \since 5.8 |
4233 | \sa QSGRectangleNode |
4234 | */ |
4235 | QSGRectangleNode *QQuickWindow::createRectangleNode() const |
4236 | { |
4237 | Q_D(const QQuickWindow); |
4238 | return isSceneGraphInitialized() ? d->context->sceneGraphContext()->createRectangleNode() : nullptr; |
4239 | } |
4240 | |
4241 | /*! |
4242 | Creates a simple image node. When the scenegraph is not initialized, the return value is null. |
4243 | |
4244 | This is cross-backend alternative to constructing a QSGSimpleTextureNode directly. |
4245 | |
4246 | \since 5.8 |
4247 | \sa QSGImageNode |
4248 | */ |
4249 | QSGImageNode *QQuickWindow::createImageNode() const |
4250 | { |
4251 | Q_D(const QQuickWindow); |
4252 | return isSceneGraphInitialized() ? d->context->sceneGraphContext()->createImageNode() : nullptr; |
4253 | } |
4254 | |
4255 | /*! |
4256 | Creates a nine patch node. When the scenegraph is not initialized, the return value is null. |
4257 | |
4258 | \since 5.8 |
4259 | */ |
4260 | QSGNinePatchNode *QQuickWindow::createNinePatchNode() const |
4261 | { |
4262 | Q_D(const QQuickWindow); |
4263 | return isSceneGraphInitialized() ? d->context->sceneGraphContext()->createNinePatchNode() : nullptr; |
4264 | } |
4265 | |
4266 | /*! |
4267 | \since 5.10 |
4268 | |
4269 | Returns the render type of text-like elements in Qt Quick. |
4270 | The default is QQuickWindow::QtTextRendering. |
4271 | |
4272 | \sa setTextRenderType() |
4273 | */ |
4274 | QQuickWindow::TextRenderType QQuickWindow::textRenderType() |
4275 | { |
4276 | return QQuickWindowPrivate::textRenderType; |
4277 | } |
4278 | |
4279 | /*! |
4280 | \since 5.10 |
4281 | |
4282 | Sets the default render type of text-like elements in Qt Quick to \a renderType. |
4283 | |
4284 | \note setting the render type will only affect elements created afterwards; |
4285 | the render type of existing elements will not be modified. |
4286 | |
4287 | \sa textRenderType() |
4288 | */ |
4289 | void QQuickWindow::setTextRenderType(QQuickWindow::TextRenderType renderType) |
4290 | { |
4291 | QQuickWindowPrivate::textRenderType = renderType; |
4292 | } |
4293 | |
4294 | |
4295 | /*! |
4296 | \since 6.0 |
4297 | \qmlproperty Palette Window::palette |
4298 | |
4299 | This property holds the palette currently set for the window. |
4300 | |
4301 | The default palette depends on the system environment. QGuiApplication maintains a system/theme |
4302 | palette which serves as a default for all application windows. You can also set the default palette |
4303 | for windows by passing a custom palette to QGuiApplication::setPalette(), before loading any QML. |
4304 | |
4305 | Window propagates explicit palette properties to child items and controls, |
4306 | overriding any system defaults for that property. |
4307 | |
4308 | \snippet qml/windowPalette.qml entire |
4309 | |
4310 | \sa Item::palette, Popup::palette, ColorGroup, SystemPalette |
4311 | //! internal \sa QQuickAbstractPaletteProvider, QQuickPalette |
4312 | */ |
4313 | |
4314 | #ifndef QT_NO_DEBUG_STREAM |
4315 | QDebug operator<<(QDebug debug, const QQuickWindow *win) |
4316 | { |
4317 | QDebugStateSaver saver(debug); |
4318 | debug.nospace(); |
4319 | if (!win) { |
4320 | debug << "QQuickWindow(nullptr)" ; |
4321 | return debug; |
4322 | } |
4323 | |
4324 | debug << win->metaObject()->className() << '(' << static_cast<const void *>(win); |
4325 | if (win->isActive()) |
4326 | debug << " active" ; |
4327 | if (win->isExposed()) |
4328 | debug << " exposed" ; |
4329 | debug << ", visibility=" << win->visibility() << ", flags=" << win->flags(); |
4330 | if (!win->title().isEmpty()) |
4331 | debug << ", title=" << win->title(); |
4332 | if (!win->objectName().isEmpty()) |
4333 | debug << ", name=" << win->objectName(); |
4334 | if (win->parent()) |
4335 | debug << ", parent=" << static_cast<const void *>(win->parent()); |
4336 | if (win->transientParent()) |
4337 | debug << ", transientParent=" << static_cast<const void *>(win->transientParent()); |
4338 | debug << ", geometry=" ; |
4339 | QtDebugUtils::formatQRect(debug, rect: win->geometry()); |
4340 | debug << ')'; |
4341 | return debug; |
4342 | } |
4343 | #endif |
4344 | |
4345 | QT_END_NAMESPACE |
4346 | |
4347 | #include "qquickwindow.moc" |
4348 | #include "moc_qquickwindow_p.cpp" |
4349 | #include "moc_qquickwindow.cpp" |
4350 | |