1 /****************************************************************************
3 ** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
4 ** All rights reserved.
5 ** Contact: Nokia Corporation (qt-info@nokia.com)
7 ** This file is part of the QtDeclarative module of the Qt Toolkit.
9 ** $QT_BEGIN_LICENSE:LGPL$
10 ** GNU Lesser General Public License Usage
11 ** This file may be used under the terms of the GNU Lesser General Public
12 ** License version 2.1 as published by the Free Software Foundation and
13 ** appearing in the file LICENSE.LGPL included in the packaging of this
14 ** file. Please review the following information to ensure the GNU Lesser
15 ** General Public License version 2.1 requirements will be met:
16 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
18 ** In addition, as a special exception, Nokia gives you certain additional
19 ** rights. These rights are described in the Nokia Qt LGPL Exception
20 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
22 ** GNU General Public License Usage
23 ** Alternatively, this file may be used under the terms of the GNU General
24 ** Public License version 3.0 as published by the Free Software Foundation
25 ** and appearing in the file LICENSE.GPL included in the packaging of this
26 ** file. Please review the following information to ensure the GNU General
27 ** Public License version 3.0 requirements will be met:
28 ** http://www.gnu.org/copyleft/gpl.html.
31 ** Alternatively, this file may be used in accordance with the terms and
32 ** conditions contained in a signed written agreement between you and Nokia.
40 ****************************************************************************/
42 #include "qquickcanvas.h"
43 #include "qquickcanvas_p.h"
45 #include "qquickitem.h"
46 #include "qquickitem_p.h"
48 #include <private/qsgrenderer_p.h>
49 #include <private/qsgtexture_p.h>
50 #include <private/qsgflashnode_p.h>
51 #include <qsgengine.h>
53 #include <private/qguiapplication_p.h>
54 #include <QtGui/QInputPanel>
56 #include <private/qabstractanimation_p.h>
58 #include <QtGui/qpainter.h>
59 #include <QtGui/qevent.h>
60 #include <QtGui/qmatrix4x4.h>
61 #include <QtCore/qvarlengtharray.h>
62 #include <QtCore/qabstractanimation.h>
63 #include <QtDeclarative/qdeclarativeincubator.h>
65 #include <private/qdeclarativedebugtrace_p.h>
69 #define QQUICK_CANVAS_TIMING
70 #ifdef QQUICK_CANVAS_TIMING
71 static bool qquick_canvas_timing = !qgetenv("QML_CANVAS_TIMING").isEmpty();
72 static QTime threadTimer;
74 static int renderTime;
78 DEFINE_BOOL_CONFIG_OPTION(qmlFixedAnimationStep, QML_FIXED_ANIMATION_STEP)
79 DEFINE_BOOL_CONFIG_OPTION(qmlNoThreadedRenderer, QML_BAD_GUI_RENDER_LOOP)
81 extern Q_GUI_EXPORT QImage qt_gl_read_framebuffer(const QSize &size, bool alpha_format, bool include_alpha);
83 void QQuickCanvasPrivate::updateFocusItemTransform()
86 QQuickItem *focus = q->activeFocusItem();
87 if (focus && qApp->inputPanel()->inputItem() == focus)
88 qApp->inputPanel()->setInputItemTransform(QQuickItemPrivate::get(focus)->itemToCanvasTransform());
91 class QQuickCanvasIncubationController : public QObject, public QDeclarativeIncubationController
94 QQuickCanvasIncubationController(QQuickCanvasPrivate *canvas)
95 : m_canvas(canvas), m_eventSent(false) {}
98 virtual bool event(QEvent *e)
100 if (e->type() == QEvent::User) {
101 Q_ASSERT(m_eventSent);
103 bool *amtp = m_canvas->thread->allowMainThreadProcessing();
104 while (incubatingObjectCount()) {
109 QCoreApplication::processEvents();
114 return QObject::event(e);
117 virtual void incubatingObjectCountChanged(int count)
119 if (count && !m_eventSent) {
121 QCoreApplication::postEvent(this, new QEvent(QEvent::User));
126 QQuickCanvasPrivate *m_canvas;
130 class QQuickCanvasPlainRenderLoop : public QObject, public QQuickCanvasRenderLoop
133 QQuickCanvasPlainRenderLoop()
134 : updatePending(false)
135 , animationRunning(false)
139 virtual void paint() {
140 updatePending = false;
141 if (animationRunning && animationDriver())
142 animationDriver()->advance();
146 glViewport(0, 0, size.width(), size.height());
147 renderSceneGraph(size);
150 if (animationRunning)
154 virtual QImage grab() {
155 return qt_gl_read_framebuffer(size, false, false);
158 virtual void startRendering() {
162 initializeSceneGraph();
169 virtual void stopRendering() { }
171 virtual void maybeUpdate() {
172 if (!updatePending) {
173 QCoreApplication::postEvent(this, new QEvent(QEvent::User));
174 updatePending = true;
178 virtual void animationStarted() {
179 animationRunning = true;
183 virtual void animationStopped() {
184 animationRunning = false;
187 virtual bool isRunning() const { return glContext(); } // Event loop is always running...
188 virtual void resize(const QSize &s) { size = s; }
189 virtual void setWindowSize(const QSize &s) { size = s; }
191 bool event(QEvent *e) {
192 if (e->type() == QEvent::User) {
196 return QObject::event(e);
201 uint updatePending : 1;
202 uint animationRunning : 1;
211 Prior to being added to a valid canvas items can set and clear focus with no
212 effect. Only once items are added to a canvas (by way of having a parent set that
213 already belongs to a canvas) do the focus rules apply. Focus goes back to
214 having no effect if an item is removed from a canvas.
216 When an item is moved into a new focus scope (either being added to a canvas
217 for the first time, or having its parent changed), if the focus scope already has
218 a scope focused item that takes precedence over the item being added. Otherwise,
219 the focus of the added tree is used. In the case of of a tree of items being
220 added to a canvas for the first time, which may have a conflicted focus state (two
221 or more items in one scope having focus set), the same rule is applied item by item -
222 thus the first item that has focus will get it (assuming the scope doesn't already
223 have a scope focused item), and the other items will have their focus cleared.
230 The threaded rendering uses a number of different variables to track potential
231 states used to handle resizing, initial paint, grabbing and driving animations
232 while ALWAYS keeping the GL context in the rendering thread and keeping the
233 overhead of normal one-shot paints and vblank driven animations at a minimum.
235 Resize, initial show and grab suffer slightly in this model as they are locked
236 to the rendering in the rendering thread, but this is a necessary evil for
239 Variables that are used:
241 Private::animationRunning: This is true while the animations are running, and only
242 written to inside locks.
244 RenderThread::isGuiBlocked: This is used to indicate that the GUI thread owns the
245 lock. This variable is an integer to allow for recursive calls to lockInGui()
246 without using a recursive mutex. See isGuiBlockPending.
248 RenderThread::isPaintComplete: This variable is cleared when rendering starts and
249 set once rendering is complete. It is monitored in the paintEvent(),
250 resizeEvent() and grab() functions to force them to wait for rendering to
253 RenderThread::isGuiBlockPending: This variable is set in the render thread just
254 before the sync event is sent to the GUI thread. It is used to avoid deadlocks
255 in the case where render thread waits while waiting for GUI to pick up the sync
256 event and GUI thread gets a resizeEvent, the initial paintEvent or a grab.
257 When this happens, we use the
258 exhaustSyncEvent() function to do the sync right there and mark the coming
259 sync event to be discarded. There can only ever be one sync incoming.
261 RenderThread::isRenderBlock: This variable is true when animations are not
262 running and the render thread has gone to sleep, waiting for more to do.
264 RenderThread::isExternalUpdatePending: This variable is set to false when
265 a new render pass is started and to true in maybeUpdate(). It is an
266 indication to the render thread that another render pass needs to take
267 place, rather than the render thread going to sleep after completing its swap.
269 RenderThread::doGrab: This variable is set by the grab() function and
270 tells the renderer to do a grab after rendering is complete and before
273 RenderThread::shouldExit: This variable is used to determine if the render
274 thread should do a nother pass. It is typically set as a result of show()
275 and unset as a result of hide() or during shutdown()
277 RenderThread::hasExited: Used by the GUI thread to synchronize the shutdown
278 after shouldExit has been set to true.
281 // #define FOCUS_DEBUG
282 // #define MOUSE_DEBUG
283 // #define TOUCH_DEBUG
284 // #define DIRTY_DEBUG
285 // #define THREAD_DEBUG
287 // #define FRAME_TIMING
290 static QTime frameTimer;
291 int sceneGraphRenderTime;
295 QQuickItem::UpdatePaintNodeData::UpdatePaintNodeData()
300 QQuickRootItem::QQuickRootItem()
304 void QQuickCanvas::exposeEvent(QExposeEvent *)
310 void QQuickCanvas::resizeEvent(QResizeEvent *)
313 d->thread->resize(size());
316 void QQuickCanvas::animationStarted()
318 d_func()->thread->animationStarted();
321 void QQuickCanvas::animationStopped()
323 d_func()->thread->animationStopped();
326 void QQuickCanvas::showEvent(QShowEvent *)
329 if (d->vsyncAnimations) {
330 if (!d->animationDriver) {
331 d->animationDriver = d->context->createAnimationDriver(this);
332 connect(d->animationDriver, SIGNAL(started()), this, SLOT(animationStarted()), Qt::DirectConnection);
333 connect(d->animationDriver, SIGNAL(stopped()), this, SLOT(animationStopped()), Qt::DirectConnection);
335 d->animationDriver->install();
338 if (!d->thread->isRunning()) {
339 d->thread->setWindowSize(size());
340 d->thread->startRendering();
344 void QQuickCanvas::hideEvent(QHideEvent *)
347 d->thread->stopRendering();
350 void QQuickCanvas::focusOutEvent(QFocusEvent *)
353 d->rootItem->setFocus(false);
356 void QQuickCanvas::focusInEvent(QFocusEvent *)
359 d->rootItem->setFocus(true);
364 Sets weither this canvas should use vsync driven animations.
366 This option can only be set on one single QQuickCanvas, and that it's
367 vsync signal will then be used to drive all animations in the
370 This feature is primarily useful for single QQuickCanvas, QML-only
373 \warning Enabling vsync on multiple QQuickCanvas instances has
376 void QQuickCanvas::setVSyncAnimations(bool enabled)
380 qWarning("QQuickCanvas::setVSyncAnimations: Cannot be changed when widget is shown");
383 d->vsyncAnimations = enabled;
389 Returns true if this canvas should use vsync driven animations;
390 otherwise returns false.
392 bool QQuickCanvas::vsyncAnimations() const
394 Q_D(const QQuickCanvas);
395 return d->vsyncAnimations;
398 void QQuickCanvasPrivate::initializeSceneGraph()
401 context = QSGContext::createDefaultContext();
403 if (context->isReady())
406 QOpenGLContext *glctx = const_cast<QOpenGLContext *>(QOpenGLContext::currentContext());
407 context->initialize(glctx);
410 QObject::connect(context->renderer(), SIGNAL(sceneGraphChanged()), q, SLOT(maybeUpdate()),
411 Qt::DirectConnection);
413 if (!QQuickItemPrivate::get(rootItem)->itemNode()->parent()) {
414 context->rootNode()->appendChildNode(QQuickItemPrivate::get(rootItem)->itemNode());
417 engine = new QSGEngine();
418 engine->setCanvas(q);
420 emit q_func()->sceneGraphInitialized();
423 void QQuickCanvasPrivate::polishItems()
425 while (!itemsToPolish.isEmpty()) {
426 QSet<QQuickItem *>::Iterator iter = itemsToPolish.begin();
427 QQuickItem *item = *iter;
428 itemsToPolish.erase(iter);
429 QQuickItemPrivate::get(item)->polishScheduled = false;
430 item->updatePolish();
432 updateFocusItemTransform();
436 void QQuickCanvasPrivate::syncSceneGraph()
440 // Copy the current state of clearing from canvas into renderer.
441 context->renderer()->setClearColor(clearColor);
442 QSGRenderer::ClearMode mode = QSGRenderer::ClearStencilBuffer | QSGRenderer::ClearDepthBuffer;
443 if (clearBeforeRendering)
444 mode |= QSGRenderer::ClearColorBuffer;
445 context->renderer()->setClearMode(mode);
449 void QQuickCanvasPrivate::renderSceneGraph(const QSize &size)
452 context->renderer()->setDeviceRect(QRect(QPoint(0, 0), size));
453 context->renderer()->setViewportRect(QRect(QPoint(0, 0), renderTarget ? renderTarget->size() : size));
454 context->renderer()->setProjectionMatrixToDeviceRect();
456 emit q->beforeRendering();
457 context->renderNextFrame(renderTarget);
458 emit q->afterRendering();
461 sceneGraphRenderTime = frameTimer.elapsed();
466 // glReadPixels(0, 0, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, &pixel);
467 readbackTime = frameTimer.elapsed();
472 // ### Do we need this?
473 void QQuickCanvas::sceneGraphChanged()
475 // Q_D(QQuickCanvas);
476 // d->needsRepaint = true;
479 QQuickCanvasPrivate::QQuickCanvasPrivate()
482 , mouseGrabberItem(0)
485 , clearColor(Qt::white)
486 , vsyncAnimations(false)
487 , clearBeforeRendering(true)
491 , incubationController(0)
495 QQuickCanvasPrivate::~QQuickCanvasPrivate()
499 void QQuickCanvasPrivate::init(QQuickCanvas *c)
501 QUnifiedTimer* ut = QUnifiedTimer::instance(true);
502 if (qmlFixedAnimationStep())
503 ut->setConsistentTiming(true);
509 rootItem = new QQuickRootItem;
510 QQuickItemPrivate *rootItemPrivate = QQuickItemPrivate::get(rootItem);
511 rootItemPrivate->canvas = q;
512 rootItemPrivate->flags |= QQuickItem::ItemIsFocusScope;
514 // In the absence of a focus in event on some platforms assume the window will
515 // be activated immediately and set focus on the rootItem
516 // ### Remove when QTBUG-22415 is resolved.
517 //It is important that this call happens after the rootItem has a canvas..
518 rootItem->setFocus(true);
520 bool threaded = !qmlNoThreadedRenderer();
522 if (!QGuiApplicationPrivate::platformIntegration()->hasCapability(QPlatformIntegration::ThreadedOpenGL)) {
523 qWarning("QQuickCanvas: platform does not support threaded rendering!");
528 thread = new QQuickCanvasRenderThread();
530 thread = new QQuickCanvasPlainRenderLoop();
532 thread->renderer = q;
535 context = QSGContext::createDefaultContext();
536 thread->moveContextToThread(context);
538 q->setSurfaceType(QWindow::OpenGLSurface);
539 q->setFormat(context->defaultSurfaceFormat());
542 void QQuickCanvasPrivate::transformTouchPoints(QList<QTouchEvent::TouchPoint> &touchPoints, const QTransform &transform)
544 for (int i=0; i<touchPoints.count(); i++) {
545 QTouchEvent::TouchPoint &touchPoint = touchPoints[i];
546 touchPoint.setRect(transform.mapRect(touchPoint.sceneRect()));
547 touchPoint.setStartPos(transform.map(touchPoint.startScenePos()));
548 touchPoint.setLastPos(transform.map(touchPoint.lastScenePos()));
554 Translates the data in \a touchEvent to this canvas. This method leaves the item local positions in
555 \a touchEvent untouched (these are filled in later).
557 void QQuickCanvasPrivate::translateTouchEvent(QTouchEvent *touchEvent)
559 // Q_Q(QQuickCanvas);
561 // touchEvent->setWidget(q); // ### refactor...
563 QList<QTouchEvent::TouchPoint> touchPoints = touchEvent->touchPoints();
564 for (int i = 0; i < touchPoints.count(); ++i) {
565 QTouchEvent::TouchPoint &touchPoint = touchPoints[i];
567 touchPoint.setScreenRect(touchPoint.sceneRect());
568 touchPoint.setStartScreenPos(touchPoint.startScenePos());
569 touchPoint.setLastScreenPos(touchPoint.lastScenePos());
571 touchPoint.setSceneRect(touchPoint.rect());
572 touchPoint.setStartScenePos(touchPoint.startPos());
573 touchPoint.setLastScenePos(touchPoint.lastPos());
575 if (touchPoint.isPrimary())
576 lastMousePosition = touchPoint.pos().toPoint();
578 touchEvent->setTouchPoints(touchPoints);
581 void QQuickCanvasPrivate::setFocusInScope(QQuickItem *scope, QQuickItem *item, FocusOptions options)
586 Q_ASSERT(scope || item == rootItem);
589 qWarning() << "QQuickCanvasPrivate::setFocusInScope():";
590 qWarning() << " scope:" << (QObject *)scope;
592 qWarning() << " scopeSubFocusItem:" << (QObject *)QQuickItemPrivate::get(scope)->subFocusItem;
593 qWarning() << " item:" << (QObject *)item;
594 qWarning() << " activeFocusItem:" << (QObject *)activeFocusItem;
597 QQuickItemPrivate *scopePrivate = scope ? QQuickItemPrivate::get(scope) : 0;
598 QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item);
600 QQuickItem *oldActiveFocusItem = 0;
601 QQuickItem *newActiveFocusItem = 0;
603 QVarLengthArray<QQuickItem *, 20> changed;
605 // Does this change the active focus?
606 if (item == rootItem || scopePrivate->activeFocus) {
607 oldActiveFocusItem = activeFocusItem;
608 newActiveFocusItem = item;
609 while (newActiveFocusItem->isFocusScope() && newActiveFocusItem->scopedFocusItem())
610 newActiveFocusItem = newActiveFocusItem->scopedFocusItem();
612 if (oldActiveFocusItem) {
614 qApp->inputPanel()->commit();
618 QFocusEvent event(QEvent::FocusOut, Qt::OtherFocusReason);
619 q->sendEvent(oldActiveFocusItem, &event);
621 QQuickItem *afi = oldActiveFocusItem;
622 while (afi != scope) {
623 if (QQuickItemPrivate::get(afi)->activeFocus) {
624 QQuickItemPrivate::get(afi)->activeFocus = false;
627 afi = afi->parentItem();
632 if (item != rootItem) {
633 QQuickItem *oldSubFocusItem = scopePrivate->subFocusItem;
634 // Correct focus chain in scope
635 if (oldSubFocusItem) {
636 QQuickItem *sfi = scopePrivate->subFocusItem->parentItem();
637 while (sfi != scope) {
638 QQuickItemPrivate::get(sfi)->subFocusItem = 0;
639 sfi = sfi->parentItem();
643 scopePrivate->subFocusItem = item;
644 QQuickItem *sfi = scopePrivate->subFocusItem->parentItem();
645 while (sfi != scope) {
646 QQuickItemPrivate::get(sfi)->subFocusItem = item;
647 sfi = sfi->parentItem();
651 if (oldSubFocusItem) {
652 QQuickItemPrivate::get(oldSubFocusItem)->focus = false;
653 changed << oldSubFocusItem;
657 if (!(options & DontChangeFocusProperty)) {
658 // if (item != rootItem || QGuiApplication::focusWindow() == q) { // QTBUG-22415
659 itemPrivate->focus = true;
664 if (newActiveFocusItem && rootItem->hasFocus()) {
665 activeFocusItem = newActiveFocusItem;
667 QQuickItemPrivate::get(newActiveFocusItem)->activeFocus = true;
668 changed << newActiveFocusItem;
670 QQuickItem *afi = newActiveFocusItem->parentItem();
671 while (afi && afi != scope) {
672 if (afi->isFocusScope()) {
673 QQuickItemPrivate::get(afi)->activeFocus = true;
676 afi = afi->parentItem();
679 updateInputMethodData();
681 QFocusEvent event(QEvent::FocusIn, Qt::OtherFocusReason);
682 q->sendEvent(newActiveFocusItem, &event);
684 updateInputMethodData();
687 if (!changed.isEmpty())
688 notifyFocusChangesRecur(changed.data(), changed.count() - 1);
691 void QQuickCanvasPrivate::clearFocusInScope(QQuickItem *scope, QQuickItem *item, FocusOptions options)
697 Q_ASSERT(scope || item == rootItem);
700 qWarning() << "QQuickCanvasPrivate::clearFocusInScope():";
701 qWarning() << " scope:" << (QObject *)scope;
702 qWarning() << " item:" << (QObject *)item;
703 qWarning() << " activeFocusItem:" << (QObject *)activeFocusItem;
706 QQuickItemPrivate *scopePrivate = scope ? QQuickItemPrivate::get(scope) : 0;
708 QQuickItem *oldActiveFocusItem = 0;
709 QQuickItem *newActiveFocusItem = 0;
711 QVarLengthArray<QQuickItem *, 20> changed;
713 Q_ASSERT(item == rootItem || item == scopePrivate->subFocusItem);
715 // Does this change the active focus?
716 if (item == rootItem || scopePrivate->activeFocus) {
717 oldActiveFocusItem = activeFocusItem;
718 newActiveFocusItem = scope;
720 Q_ASSERT(oldActiveFocusItem);
723 qApp->inputPanel()->commit();
727 QFocusEvent event(QEvent::FocusOut, Qt::OtherFocusReason);
728 q->sendEvent(oldActiveFocusItem, &event);
730 QQuickItem *afi = oldActiveFocusItem;
731 while (afi != scope) {
732 if (QQuickItemPrivate::get(afi)->activeFocus) {
733 QQuickItemPrivate::get(afi)->activeFocus = false;
736 afi = afi->parentItem();
740 if (item != rootItem) {
741 QQuickItem *oldSubFocusItem = scopePrivate->subFocusItem;
742 // Correct focus chain in scope
743 if (oldSubFocusItem) {
744 QQuickItem *sfi = scopePrivate->subFocusItem->parentItem();
745 while (sfi != scope) {
746 QQuickItemPrivate::get(sfi)->subFocusItem = 0;
747 sfi = sfi->parentItem();
750 scopePrivate->subFocusItem = 0;
752 if (oldSubFocusItem && !(options & DontChangeFocusProperty)) {
753 QQuickItemPrivate::get(oldSubFocusItem)->focus = false;
754 changed << oldSubFocusItem;
756 } else if (!(options & DontChangeFocusProperty)) {
757 QQuickItemPrivate::get(item)->focus = false;
761 if (newActiveFocusItem) {
762 Q_ASSERT(newActiveFocusItem == scope);
763 activeFocusItem = scope;
765 updateInputMethodData();
767 QFocusEvent event(QEvent::FocusIn, Qt::OtherFocusReason);
768 q->sendEvent(newActiveFocusItem, &event);
770 updateInputMethodData();
773 if (!changed.isEmpty())
774 notifyFocusChangesRecur(changed.data(), changed.count() - 1);
777 void QQuickCanvasPrivate::notifyFocusChangesRecur(QQuickItem **items, int remaining)
779 QDeclarativeGuard<QQuickItem> item(*items);
782 notifyFocusChangesRecur(items + 1, remaining - 1);
785 QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item);
787 if (itemPrivate->notifiedFocus != itemPrivate->focus) {
788 itemPrivate->notifiedFocus = itemPrivate->focus;
789 emit item->focusChanged(itemPrivate->focus);
792 if (item && itemPrivate->notifiedActiveFocus != itemPrivate->activeFocus) {
793 itemPrivate->notifiedActiveFocus = itemPrivate->activeFocus;
794 itemPrivate->itemChange(QQuickItem::ItemActiveFocusHasChanged, itemPrivate->activeFocus);
795 emit item->activeFocusChanged(itemPrivate->activeFocus);
800 void QQuickCanvasPrivate::updateInputMethodData()
802 QQuickItem *inputItem = 0;
803 if (activeFocusItem && activeFocusItem->flags() & QQuickItem::ItemAcceptsInputMethod)
804 inputItem = activeFocusItem;
805 qApp->inputPanel()->setInputItem(inputItem);
809 Queries the Input Method.
811 QVariant QQuickCanvas::inputMethodQuery(Qt::InputMethodQuery query) const
813 Q_D(const QQuickCanvas);
814 if (!d->activeFocusItem || !(QQuickItemPrivate::get(d->activeFocusItem)->flags & QQuickItem::ItemAcceptsInputMethod))
816 QVariant value = d->activeFocusItem->inputMethodQuery(query);
819 QVariant::Type type = value.type();
820 if (type == QVariant::RectF || type == QVariant::Rect) {
821 const QTransform transform = QQuickItemPrivate::get(d->activeFocusItem)->itemToCanvasTransform();
822 value = transform.mapRect(value.toRectF());
823 } else if (type == QVariant::PointF || type == QVariant::Point) {
824 const QTransform transform = QQuickItemPrivate::get(d->activeFocusItem)->itemToCanvasTransform();
825 value = transform.map(value.toPointF());
830 void QQuickCanvasPrivate::dirtyItem(QQuickItem *)
836 void QQuickCanvasPrivate::cleanup(QSGNode *n)
840 Q_ASSERT(!cleanupNodeList.contains(n));
841 cleanupNodeList.append(n);
849 \brief The QQuickCanvas class provides the canvas for displaying a graphical QML scene
851 QQuickCanvas provides the graphical scene management needed to interact with and display
852 a scene of QQuickItems.
854 A QQuickCanvas always has a single invisible root item. To add items to this canvas,
855 reparent the items to the root item or to an existing item in the scene.
857 For easily displaying a scene from a QML file, see \l{QQuickView}.
859 QQuickCanvas::QQuickCanvas(QWindow *parent)
860 : QWindow(*(new QQuickCanvasPrivate), parent)
866 QQuickCanvas::QQuickCanvas(QQuickCanvasPrivate &dd, QWindow *parent)
867 : QWindow(dd, parent)
873 QQuickCanvas::~QQuickCanvas()
877 /* The threaded renderer will clean up the nodes which will fire
878 sceneGraphChanged events through back to the canvas. This signal
879 is connected to maybeUpdate which should only be called from GUI or during
880 updatePaintNode(), so disconnect them before starting the shutdown
882 disconnect(d->context->renderer(), SIGNAL(sceneGraphChanged()), this, SLOT(maybeUpdate()));
883 if (d->thread->isRunning())
884 d->thread->stopRendering();
886 // ### should we change ~QQuickItem to handle this better?
887 // manually cleanup for the root item (item destructor only handles these when an item is parented)
888 QQuickItemPrivate *rootItemPrivate = QQuickItemPrivate::get(d->rootItem);
889 rootItemPrivate->removeFromDirtyList();
891 delete d->incubationController; d->incubationController = 0;
893 delete d->rootItem; d->rootItem = 0;
896 delete d->thread; d->thread = 0;
900 Returns the invisible root item of the scene.
902 A QQuickCanvas always has a single invisible root item. To add items to this canvas,
903 reparent the items to the root item or to an existing item in the scene.
905 QQuickItem *QQuickCanvas::rootItem() const
907 Q_D(const QQuickCanvas);
913 Returns the item which currently has active focus.
915 QQuickItem *QQuickCanvas::activeFocusItem() const
917 Q_D(const QQuickCanvas);
919 return d->activeFocusItem;
923 Returns the item which currently has the mouse grab.
925 QQuickItem *QQuickCanvas::mouseGrabberItem() const
927 Q_D(const QQuickCanvas);
929 return d->mouseGrabberItem;
933 bool QQuickCanvasPrivate::clearHover()
935 if (hoverItems.isEmpty())
938 QPointF pos = QCursor::pos(); // ### refactor: q->mapFromGlobal(QCursor::pos());
940 bool accepted = false;
941 foreach (QQuickItem* item, hoverItems)
942 accepted = sendHoverEvent(QEvent::HoverLeave, item, pos, pos, QGuiApplication::keyboardModifiers(), true) || accepted;
948 bool QQuickCanvas::event(QEvent *e)
954 case QEvent::TouchBegin:
955 case QEvent::TouchUpdate:
956 case QEvent::TouchEnd:
958 QTouchEvent *touch = static_cast<QTouchEvent *>(e);
959 d->translateTouchEvent(touch);
960 d->deliverTouchEvent(touch);
961 if (!touch->isAccepted())
967 d->lastMousePosition = QPoint();
969 case QEvent::DragEnter:
970 case QEvent::DragLeave:
971 case QEvent::DragMove:
973 d->deliverDragEvent(&d->dragGrabber, e);
975 case QEvent::WindowDeactivate:
976 rootItem()->windowDeactivateEvent();
982 return QWindow::event(e);
985 void QQuickCanvas::keyPressEvent(QKeyEvent *e)
989 if (d->activeFocusItem)
990 sendEvent(d->activeFocusItem, e);
993 void QQuickCanvas::keyReleaseEvent(QKeyEvent *e)
997 if (d->activeFocusItem)
998 sendEvent(d->activeFocusItem, e);
1001 void QQuickCanvas::inputMethodEvent(QInputMethodEvent *e)
1005 if (d->activeFocusItem)
1006 sendEvent(d->activeFocusItem, e);
1009 bool QQuickCanvasPrivate::deliverInitialMousePressEvent(QQuickItem *item, QMouseEvent *event)
1013 QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item);
1014 if (itemPrivate->opacity == 0.0)
1017 if (itemPrivate->flags & QQuickItem::ItemClipsChildrenToShape) {
1018 QPointF p = item->mapFromScene(event->windowPos());
1019 if (!QRectF(0, 0, item->width(), item->height()).contains(p))
1023 QList<QQuickItem *> children = itemPrivate->paintOrderChildItems();
1024 for (int ii = children.count() - 1; ii >= 0; --ii) {
1025 QQuickItem *child = children.at(ii);
1026 if (!child->isVisible() || !child->isEnabled())
1028 if (deliverInitialMousePressEvent(child, event))
1032 if (itemPrivate->acceptedMouseButtons & event->button()) {
1033 QPointF p = item->mapFromScene(event->windowPos());
1034 if (QRectF(0, 0, item->width(), item->height()).contains(p)) {
1035 QMouseEvent me(event->type(), p, event->windowPos(), event->screenPos(),
1036 event->button(), event->buttons(), event->modifiers());
1038 mouseGrabberItem = item;
1039 q->sendEvent(item, &me);
1040 event->setAccepted(me.isAccepted());
1041 if (me.isAccepted())
1043 mouseGrabberItem->ungrabMouse();
1044 mouseGrabberItem = 0;
1051 bool QQuickCanvasPrivate::deliverMouseEvent(QMouseEvent *event)
1055 lastMousePosition = event->windowPos();
1057 if (!mouseGrabberItem &&
1058 event->type() == QEvent::MouseButtonPress &&
1059 (event->button() & event->buttons()) == event->buttons()) {
1060 return deliverInitialMousePressEvent(rootItem, event);
1063 if (mouseGrabberItem) {
1064 QQuickItemPrivate *mgPrivate = QQuickItemPrivate::get(mouseGrabberItem);
1065 const QTransform &transform = mgPrivate->canvasToItemTransform();
1066 QMouseEvent me(event->type(), transform.map(event->windowPos()), event->windowPos(), event->screenPos(),
1067 event->button(), event->buttons(), event->modifiers());
1069 q->sendEvent(mouseGrabberItem, &me);
1070 event->setAccepted(me.isAccepted());
1071 if (me.isAccepted())
1078 void QQuickCanvas::mousePressEvent(QMouseEvent *event)
1083 qWarning() << "QQuickCanvas::mousePressEvent()" << event->pos() << event->button() << event->buttons();
1086 d->deliverMouseEvent(event);
1089 void QQuickCanvas::mouseReleaseEvent(QMouseEvent *event)
1094 qWarning() << "QQuickCanvas::mouseReleaseEvent()" << event->pos() << event->button() << event->buttons();
1097 if (!d->mouseGrabberItem) {
1098 QWindow::mouseReleaseEvent(event);
1102 d->deliverMouseEvent(event);
1103 d->mouseGrabberItem = 0;
1106 void QQuickCanvas::mouseDoubleClickEvent(QMouseEvent *event)
1111 qWarning() << "QQuickCanvas::mouseDoubleClickEvent()" << event->pos() << event->button() << event->buttons();
1114 if (!d->mouseGrabberItem && (event->button() & event->buttons()) == event->buttons()) {
1115 if (d->deliverInitialMousePressEvent(d->rootItem, event))
1122 d->deliverMouseEvent(event);
1125 bool QQuickCanvasPrivate::sendHoverEvent(QEvent::Type type, QQuickItem *item,
1126 const QPointF &scenePos, const QPointF &lastScenePos,
1127 Qt::KeyboardModifiers modifiers, bool accepted)
1130 const QTransform transform = QQuickItemPrivate::get(item)->canvasToItemTransform();
1132 //create copy of event
1133 QHoverEvent hoverEvent(type, transform.map(scenePos), transform.map(lastScenePos), modifiers);
1134 hoverEvent.setAccepted(accepted);
1136 q->sendEvent(item, &hoverEvent);
1138 return hoverEvent.isAccepted();
1141 void QQuickCanvas::mouseMoveEvent(QMouseEvent *event)
1146 qWarning() << "QQuickCanvas::mouseMoveEvent()" << event->pos() << event->button() << event->buttons();
1149 if (!d->mouseGrabberItem) {
1150 if (d->lastMousePosition.isNull())
1151 d->lastMousePosition = event->windowPos();
1152 QPointF last = d->lastMousePosition;
1153 d->lastMousePosition = event->windowPos();
1155 bool accepted = event->isAccepted();
1156 bool delivered = d->deliverHoverEvent(d->rootItem, event->windowPos(), last, event->modifiers(), accepted);
1158 //take care of any exits
1159 accepted = d->clearHover();
1161 event->setAccepted(accepted);
1165 d->deliverMouseEvent(event);
1168 bool QQuickCanvasPrivate::deliverHoverEvent(QQuickItem *item, const QPointF &scenePos, const QPointF &lastScenePos,
1169 Qt::KeyboardModifiers modifiers, bool &accepted)
1171 QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item);
1172 if (itemPrivate->opacity == 0.0)
1175 if (itemPrivate->flags & QQuickItem::ItemClipsChildrenToShape) {
1176 QPointF p = item->mapFromScene(scenePos);
1177 if (!QRectF(0, 0, item->width(), item->height()).contains(p))
1181 QList<QQuickItem *> children = itemPrivate->paintOrderChildItems();
1182 for (int ii = children.count() - 1; ii >= 0; --ii) {
1183 QQuickItem *child = children.at(ii);
1184 if (!child->isVisible() || !child->isEnabled())
1186 if (deliverHoverEvent(child, scenePos, lastScenePos, modifiers, accepted))
1190 if (itemPrivate->hoverEnabled) {
1191 QPointF p = item->mapFromScene(scenePos);
1192 if (QRectF(0, 0, item->width(), item->height()).contains(p)) {
1193 if (!hoverItems.isEmpty() && hoverItems[0] == item) {
1195 accepted = sendHoverEvent(QEvent::HoverMove, item, scenePos, lastScenePos, modifiers, accepted);
1197 QList<QQuickItem *> itemsToHover;
1198 QQuickItem* parent = item;
1199 itemsToHover << item;
1200 while ((parent = parent->parentItem()))
1201 itemsToHover << parent;
1203 // Leaving from previous hovered items until we reach the item or one of its ancestors.
1204 while (!hoverItems.isEmpty() && !itemsToHover.contains(hoverItems[0])) {
1205 sendHoverEvent(QEvent::HoverLeave, hoverItems[0], scenePos, lastScenePos, modifiers, accepted);
1206 hoverItems.removeFirst();
1209 if (!hoverItems.isEmpty() && hoverItems[0] == item){//Not entering a new Item
1210 // ### Shouldn't we send moves for the parent items as well?
1211 accepted = sendHoverEvent(QEvent::HoverMove, item, scenePos, lastScenePos, modifiers, accepted);
1213 // Enter items that are not entered yet.
1215 if (!hoverItems.isEmpty())
1216 startIdx = itemsToHover.indexOf(hoverItems[0]) - 1;
1218 startIdx = itemsToHover.count() - 1;
1220 for (int i = startIdx; i >= 0; i--) {
1221 QQuickItem *itemToHover = itemsToHover[i];
1222 if (QQuickItemPrivate::get(itemToHover)->hoverEnabled) {
1223 hoverItems.prepend(itemToHover);
1224 sendHoverEvent(QEvent::HoverEnter, itemToHover, scenePos, lastScenePos, modifiers, accepted);
1236 bool QQuickCanvasPrivate::deliverWheelEvent(QQuickItem *item, QWheelEvent *event)
1239 QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item);
1240 if (itemPrivate->opacity == 0.0)
1243 if (itemPrivate->flags & QQuickItem::ItemClipsChildrenToShape) {
1244 QPointF p = item->mapFromScene(event->posF());
1245 if (!QRectF(0, 0, item->width(), item->height()).contains(p))
1249 QList<QQuickItem *> children = itemPrivate->paintOrderChildItems();
1250 for (int ii = children.count() - 1; ii >= 0; --ii) {
1251 QQuickItem *child = children.at(ii);
1252 if (!child->isVisible() || !child->isEnabled())
1254 if (deliverWheelEvent(child, event))
1258 QPointF p = item->mapFromScene(event->posF());
1259 if (QRectF(0, 0, item->width(), item->height()).contains(p)) {
1260 QWheelEvent wheel(p, event->delta(), event->buttons(), event->modifiers(), event->orientation());
1262 q->sendEvent(item, &wheel);
1263 if (wheel.isAccepted()) {
1272 #ifndef QT_NO_WHEELEVENT
1273 void QQuickCanvas::wheelEvent(QWheelEvent *event)
1277 qWarning() << "QQuickCanvas::wheelEvent()" << event->pos() << event->delta() << event->orientation();
1280 d->deliverWheelEvent(d->rootItem, event);
1282 #endif // QT_NO_WHEELEVENT
1284 bool QQuickCanvasPrivate::deliverTouchEvent(QTouchEvent *event)
1287 if (event->type() == QEvent::TouchBegin)
1288 qWarning("touchBeginEvent");
1289 else if (event->type() == QEvent::TouchUpdate)
1290 qWarning("touchUpdateEvent");
1291 else if (event->type() == QEvent::TouchEnd)
1292 qWarning("touchEndEvent");
1295 QHash<QQuickItem *, QList<QTouchEvent::TouchPoint> > updatedPoints;
1297 if (event->type() == QTouchEvent::TouchBegin) { // all points are new touch points
1298 QSet<int> acceptedNewPoints;
1299 deliverTouchPoints(rootItem, event, event->touchPoints(), &acceptedNewPoints, &updatedPoints);
1300 if (acceptedNewPoints.count() > 0)
1302 return event->isAccepted();
1305 const QList<QTouchEvent::TouchPoint> &touchPoints = event->touchPoints();
1306 QList<QTouchEvent::TouchPoint> newPoints;
1307 QQuickItem *item = 0;
1308 for (int i=0; i<touchPoints.count(); i++) {
1309 const QTouchEvent::TouchPoint &touchPoint = touchPoints[i];
1310 switch (touchPoint.state()) {
1311 case Qt::TouchPointPressed:
1312 newPoints << touchPoint;
1314 case Qt::TouchPointMoved:
1315 case Qt::TouchPointStationary:
1316 case Qt::TouchPointReleased:
1317 if (itemForTouchPointId.contains(touchPoint.id())) {
1318 item = itemForTouchPointId[touchPoint.id()];
1320 updatedPoints[item].append(touchPoint);
1328 if (newPoints.count() > 0 || updatedPoints.count() > 0) {
1329 QSet<int> acceptedNewPoints;
1330 int prevCount = updatedPoints.count();
1331 deliverTouchPoints(rootItem, event, newPoints, &acceptedNewPoints, &updatedPoints);
1332 if (acceptedNewPoints.count() > 0 || updatedPoints.count() != prevCount)
1336 if (event->touchPointStates() & Qt::TouchPointReleased) {
1337 for (int i=0; i<touchPoints.count(); i++) {
1338 if (touchPoints[i].state() == Qt::TouchPointReleased)
1339 itemForTouchPointId.remove(touchPoints[i].id());
1343 return event->isAccepted();
1346 bool QQuickCanvasPrivate::deliverTouchPoints(QQuickItem *item, QTouchEvent *event, const QList<QTouchEvent::TouchPoint> &newPoints, QSet<int> *acceptedNewPoints, QHash<QQuickItem *, QList<QTouchEvent::TouchPoint> > *updatedPoints)
1349 QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item);
1351 if (itemPrivate->opacity == 0.0)
1354 if (itemPrivate->flags & QQuickItem::ItemClipsChildrenToShape) {
1355 QRectF bounds(0, 0, item->width(), item->height());
1356 for (int i=0; i<newPoints.count(); i++) {
1357 QPointF p = item->mapFromScene(newPoints[i].scenePos());
1358 if (!bounds.contains(p))
1363 QList<QQuickItem *> children = itemPrivate->paintOrderChildItems();
1364 for (int ii = children.count() - 1; ii >= 0; --ii) {
1365 QQuickItem *child = children.at(ii);
1366 if (!child->isEnabled())
1368 if (deliverTouchPoints(child, event, newPoints, acceptedNewPoints, updatedPoints))
1372 QList<QTouchEvent::TouchPoint> matchingPoints;
1373 if (newPoints.count() > 0 && acceptedNewPoints->count() < newPoints.count()) {
1374 QRectF bounds(0, 0, item->width(), item->height());
1375 for (int i=0; i<newPoints.count(); i++) {
1376 if (acceptedNewPoints->contains(newPoints[i].id()))
1378 QPointF p = item->mapFromScene(newPoints[i].scenePos());
1379 if (bounds.contains(p))
1380 matchingPoints << newPoints[i];
1384 if (matchingPoints.count() > 0 || (*updatedPoints)[item].count() > 0) {
1385 QList<QTouchEvent::TouchPoint> &eventPoints = (*updatedPoints)[item];
1386 eventPoints.append(matchingPoints);
1387 transformTouchPoints(eventPoints, itemPrivate->canvasToItemTransform());
1389 Qt::TouchPointStates eventStates;
1390 for (int i=0; i<eventPoints.count(); i++)
1391 eventStates |= eventPoints[i].state();
1392 // if all points have the same state, set the event type accordingly
1393 QEvent::Type eventType;
1394 switch (eventStates) {
1395 case Qt::TouchPointPressed:
1396 eventType = QEvent::TouchBegin;
1398 case Qt::TouchPointReleased:
1399 eventType = QEvent::TouchEnd;
1402 eventType = QEvent::TouchUpdate;
1406 if (eventStates != Qt::TouchPointStationary) {
1407 QTouchEvent touchEvent(eventType);
1408 // touchEvent.setWidget(q); // ### refactor: what is the consequence of not setting the widget?
1409 touchEvent.setDeviceType(event->deviceType());
1410 touchEvent.setModifiers(event->modifiers());
1411 touchEvent.setTouchPointStates(eventStates);
1412 touchEvent.setTouchPoints(eventPoints);
1413 touchEvent.setTimestamp(event->timestamp());
1415 touchEvent.accept();
1416 q->sendEvent(item, &touchEvent);
1418 if (touchEvent.isAccepted()) {
1419 for (int i=0; i<matchingPoints.count(); i++) {
1420 itemForTouchPointId[matchingPoints[i].id()] = item;
1421 acceptedNewPoints->insert(matchingPoints[i].id());
1427 updatedPoints->remove(item);
1428 if (acceptedNewPoints->count() == newPoints.count() && updatedPoints->isEmpty())
1434 void QQuickCanvasPrivate::deliverDragEvent(QQuickDragGrabber *grabber, QEvent *event)
1437 grabber->resetTarget();
1438 QQuickDragGrabber::iterator grabItem = grabber->begin();
1439 if (grabItem != grabber->end()) {
1440 Q_ASSERT(event->type() != QEvent::DragEnter);
1441 if (event->type() == QEvent::Drop) {
1442 QDropEvent *e = static_cast<QDropEvent *>(event);
1443 for (e->setAccepted(false); !e->isAccepted() && grabItem != grabber->end(); grabItem = grabber->release(grabItem)) {
1444 QPointF p = (**grabItem)->mapFromScene(e->pos());
1445 QDropEvent translatedEvent(
1447 e->possibleActions(),
1450 e->keyboardModifiers());
1451 QQuickDropEventEx::copyActions(&translatedEvent, *e);
1452 q->sendEvent(**grabItem, &translatedEvent);
1453 e->setAccepted(translatedEvent.isAccepted());
1454 e->setDropAction(translatedEvent.dropAction());
1455 grabber->setTarget(**grabItem);
1458 if (event->type() != QEvent::DragMove) { // Either an accepted drop or a leave.
1459 QDragLeaveEvent leaveEvent;
1460 for (; grabItem != grabber->end(); grabItem = grabber->release(grabItem))
1461 q->sendEvent(**grabItem, &leaveEvent);
1463 } else for (; grabItem != grabber->end(); grabItem = grabber->release(grabItem)) {
1464 QDragMoveEvent *moveEvent = static_cast<QDragMoveEvent *>(event);
1465 if (deliverDragEvent(grabber, **grabItem, moveEvent)) {
1466 moveEvent->setAccepted(true);
1467 for (++grabItem; grabItem != grabber->end();) {
1468 QPointF p = (**grabItem)->mapFromScene(moveEvent->pos());
1469 if (QRectF(0, 0, (**grabItem)->width(), (**grabItem)->height()).contains(p)) {
1470 QDragMoveEvent translatedEvent(
1472 moveEvent->possibleActions(),
1473 moveEvent->mimeData(),
1474 moveEvent->mouseButtons(),
1475 moveEvent->keyboardModifiers());
1476 QQuickDropEventEx::copyActions(&translatedEvent, *moveEvent);
1477 q->sendEvent(**grabItem, &translatedEvent);
1480 QDragLeaveEvent leaveEvent;
1481 q->sendEvent(**grabItem, &leaveEvent);
1482 grabItem = grabber->release(grabItem);
1487 QDragLeaveEvent leaveEvent;
1488 q->sendEvent(**grabItem, &leaveEvent);
1492 if (event->type() == QEvent::DragEnter || event->type() == QEvent::DragMove) {
1493 QDragMoveEvent *e = static_cast<QDragMoveEvent *>(event);
1494 QDragEnterEvent enterEvent(
1496 e->possibleActions(),
1499 e->keyboardModifiers());
1500 QQuickDropEventEx::copyActions(&enterEvent, *e);
1501 event->setAccepted(deliverDragEvent(grabber, rootItem, &enterEvent));
1505 bool QQuickCanvasPrivate::deliverDragEvent(QQuickDragGrabber *grabber, QQuickItem *item, QDragMoveEvent *event)
1508 bool accepted = false;
1509 QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item);
1510 if (itemPrivate->opacity == 0.0 || !item->isVisible() || !item->isEnabled())
1513 QPointF p = item->mapFromScene(event->pos());
1514 if (QRectF(0, 0, item->width(), item->height()).contains(p)) {
1515 if (event->type() == QEvent::DragMove || itemPrivate->flags & QQuickItem::ItemAcceptsDrops) {
1516 QDragMoveEvent translatedEvent(
1518 event->possibleActions(),
1520 event->mouseButtons(),
1521 event->keyboardModifiers(),
1523 QQuickDropEventEx::copyActions(&translatedEvent, *event);
1524 q->sendEvent(item, &translatedEvent);
1525 if (event->type() == QEvent::DragEnter) {
1526 if (translatedEvent.isAccepted()) {
1527 grabber->grab(item);
1534 } else if (itemPrivate->flags & QQuickItem::ItemClipsChildrenToShape) {
1538 QDragEnterEvent enterEvent(
1540 event->possibleActions(),
1542 event->mouseButtons(),
1543 event->keyboardModifiers());
1544 QQuickDropEventEx::copyActions(&enterEvent, *event);
1545 QList<QQuickItem *> children = itemPrivate->paintOrderChildItems();
1546 for (int ii = children.count() - 1; ii >= 0; --ii) {
1547 if (deliverDragEvent(grabber, children.at(ii), &enterEvent))
1554 bool QQuickCanvasPrivate::sendFilteredMouseEvent(QQuickItem *target, QQuickItem *item, QEvent *event)
1559 QQuickItemPrivate *targetPrivate = QQuickItemPrivate::get(target);
1560 if (targetPrivate->filtersChildMouseEvents)
1561 if (target->childMouseEventFilter(item, event))
1564 if (sendFilteredMouseEvent(target->parentItem(), item, event))
1571 Propagates an event to a QQuickItem on the canvas
1573 bool QQuickCanvas::sendEvent(QQuickItem *item, QEvent *e)
1578 qWarning("QQuickCanvas::sendEvent: Cannot send event to a null item");
1584 switch (e->type()) {
1585 case QEvent::KeyPress:
1586 case QEvent::KeyRelease:
1588 QQuickItemPrivate::get(item)->deliverKeyEvent(static_cast<QKeyEvent *>(e));
1589 while (!e->isAccepted() && (item = item->parentItem())) {
1591 QQuickItemPrivate::get(item)->deliverKeyEvent(static_cast<QKeyEvent *>(e));
1594 case QEvent::InputMethod:
1596 QQuickItemPrivate::get(item)->deliverInputMethodEvent(static_cast<QInputMethodEvent *>(e));
1597 while (!e->isAccepted() && (item = item->parentItem())) {
1599 QQuickItemPrivate::get(item)->deliverInputMethodEvent(static_cast<QInputMethodEvent *>(e));
1602 case QEvent::FocusIn:
1603 case QEvent::FocusOut:
1604 QQuickItemPrivate::get(item)->deliverFocusEvent(static_cast<QFocusEvent *>(e));
1606 case QEvent::MouseButtonPress:
1607 case QEvent::MouseButtonRelease:
1608 case QEvent::MouseButtonDblClick:
1609 case QEvent::MouseMove:
1610 // XXX todo - should sendEvent be doing this? how does it relate to forwarded events?
1611 if (!d->sendFilteredMouseEvent(item->parentItem(), item, e)) {
1613 QQuickItemPrivate::get(item)->deliverMouseEvent(static_cast<QMouseEvent *>(e));
1617 QQuickItemPrivate::get(item)->deliverWheelEvent(static_cast<QWheelEvent *>(e));
1619 case QEvent::HoverEnter:
1620 case QEvent::HoverLeave:
1621 case QEvent::HoverMove:
1622 QQuickItemPrivate::get(item)->deliverHoverEvent(static_cast<QHoverEvent *>(e));
1624 case QEvent::TouchBegin:
1625 case QEvent::TouchUpdate:
1626 case QEvent::TouchEnd:
1627 // XXX todo - should sendEvent be doing this? how does it relate to forwarded events?
1628 if (!d->sendFilteredMouseEvent(item->parentItem(), item, e)) {
1630 QQuickItemPrivate::get(item)->deliverTouchEvent(static_cast<QTouchEvent *>(e));
1633 case QEvent::DragEnter:
1634 case QEvent::DragMove:
1635 case QEvent::DragLeave:
1637 QQuickItemPrivate::get(item)->deliverDragEvent(e);
1646 void QQuickCanvasPrivate::cleanupNodes()
1648 for (int ii = 0; ii < cleanupNodeList.count(); ++ii)
1649 delete cleanupNodeList.at(ii);
1650 cleanupNodeList.clear();
1653 void QQuickCanvasPrivate::cleanupNodesOnShutdown(QQuickItem *item)
1655 QQuickItemPrivate *p = QQuickItemPrivate::get(item);
1656 if (p->itemNodeInstance) {
1657 delete p->itemNodeInstance;
1658 p->itemNodeInstance = 0;
1665 for (int ii = 0; ii < p->childItems.count(); ++ii)
1666 cleanupNodesOnShutdown(p->childItems.at(ii));
1669 // This must be called from the render thread, with the main thread frozen
1670 void QQuickCanvasPrivate::cleanupNodesOnShutdown()
1674 cleanupNodesOnShutdown(rootItem);
1677 void QQuickCanvasPrivate::updateDirtyNodes()
1680 qWarning() << "QQuickCanvasPrivate::updateDirtyNodes():";
1685 QQuickItem *updateList = dirtyItemList;
1687 if (updateList) QQuickItemPrivate::get(updateList)->prevDirtyItem = &updateList;
1689 while (updateList) {
1690 QQuickItem *item = updateList;
1691 QQuickItemPrivate *itemPriv = QQuickItemPrivate::get(item);
1692 itemPriv->removeFromDirtyList();
1695 qWarning() << " QSGNode:" << item << qPrintable(itemPriv->dirtyToString());
1697 updateDirtyNode(item);
1701 void QQuickCanvasPrivate::updateDirtyNode(QQuickItem *item)
1703 #ifdef QML_RUNTIME_TESTING
1704 bool didFlash = false;
1707 QQuickItemPrivate *itemPriv = QQuickItemPrivate::get(item);
1708 quint32 dirty = itemPriv->dirtyAttributes;
1709 itemPriv->dirtyAttributes = 0;
1711 if ((dirty & QQuickItemPrivate::TransformUpdateMask) ||
1712 (dirty & QQuickItemPrivate::Size && itemPriv->origin != QQuickItem::TopLeft &&
1713 (itemPriv->scale != 1. || itemPriv->rotation != 0.))) {
1717 if (itemPriv->x != 0. || itemPriv->y != 0.)
1718 matrix.translate(itemPriv->x, itemPriv->y);
1720 for (int ii = itemPriv->transforms.count() - 1; ii >= 0; --ii)
1721 itemPriv->transforms.at(ii)->applyTo(&matrix);
1723 if (itemPriv->scale != 1. || itemPriv->rotation != 0.) {
1724 QPointF origin = item->transformOriginPoint();
1725 matrix.translate(origin.x(), origin.y());
1726 if (itemPriv->scale != 1.)
1727 matrix.scale(itemPriv->scale, itemPriv->scale);
1728 if (itemPriv->rotation != 0.)
1729 matrix.rotate(itemPriv->rotation, 0, 0, 1);
1730 matrix.translate(-origin.x(), -origin.y());
1733 itemPriv->itemNode()->setMatrix(matrix);
1736 bool clipEffectivelyChanged = dirty & QQuickItemPrivate::Clip &&
1737 ((item->clip() == false) != (itemPriv->clipNode == 0));
1738 bool effectRefEffectivelyChanged = dirty & QQuickItemPrivate::EffectReference &&
1739 ((itemPriv->effectRefCount == 0) != (itemPriv->rootNode == 0));
1741 if (clipEffectivelyChanged) {
1742 QSGNode *parent = itemPriv->opacityNode ? (QSGNode *) itemPriv->opacityNode : (QSGNode *)itemPriv->itemNode();
1743 QSGNode *child = itemPriv->rootNode ? (QSGNode *)itemPriv->rootNode : (QSGNode *)itemPriv->groupNode;
1746 Q_ASSERT(itemPriv->clipNode == 0);
1747 itemPriv->clipNode = new QQuickDefaultClipNode(item->boundingRect());
1748 itemPriv->clipNode->update();
1751 parent->removeChildNode(child);
1752 parent->appendChildNode(itemPriv->clipNode);
1754 itemPriv->clipNode->appendChildNode(child);
1757 Q_ASSERT(itemPriv->clipNode != 0);
1758 parent->removeChildNode(itemPriv->clipNode);
1760 itemPriv->clipNode->removeChildNode(child);
1761 delete itemPriv->clipNode;
1762 itemPriv->clipNode = 0;
1764 parent->appendChildNode(child);
1768 if (dirty & QQuickItemPrivate::ChildrenUpdateMask)
1769 itemPriv->childContainerNode()->removeAllChildNodes();
1771 if (effectRefEffectivelyChanged) {
1772 QSGNode *parent = itemPriv->clipNode;
1774 parent = itemPriv->opacityNode;
1776 parent = itemPriv->itemNode();
1777 QSGNode *child = itemPriv->groupNode;
1779 if (itemPriv->effectRefCount) {
1780 Q_ASSERT(itemPriv->rootNode == 0);
1781 itemPriv->rootNode = new QSGRootNode;
1784 parent->removeChildNode(child);
1785 parent->appendChildNode(itemPriv->rootNode);
1787 itemPriv->rootNode->appendChildNode(child);
1789 Q_ASSERT(itemPriv->rootNode != 0);
1790 parent->removeChildNode(itemPriv->rootNode);
1792 itemPriv->rootNode->removeChildNode(child);
1793 delete itemPriv->rootNode;
1794 itemPriv->rootNode = 0;
1796 parent->appendChildNode(child);
1800 if (dirty & QQuickItemPrivate::ChildrenUpdateMask) {
1801 QSGNode *groupNode = itemPriv->groupNode;
1803 groupNode->removeAllChildNodes();
1805 QList<QQuickItem *> orderedChildren = itemPriv->paintOrderChildItems();
1808 for (; ii < orderedChildren.count() && orderedChildren.at(ii)->z() < 0; ++ii) {
1809 QQuickItemPrivate *childPrivate = QQuickItemPrivate::get(orderedChildren.at(ii));
1810 if (!childPrivate->explicitVisible && !childPrivate->effectRefCount)
1812 if (childPrivate->itemNode()->parent())
1813 childPrivate->itemNode()->parent()->removeChildNode(childPrivate->itemNode());
1815 itemPriv->childContainerNode()->appendChildNode(childPrivate->itemNode());
1817 itemPriv->beforePaintNode = itemPriv->groupNode ? itemPriv->groupNode->lastChild() : 0;
1819 if (itemPriv->paintNode)
1820 itemPriv->childContainerNode()->appendChildNode(itemPriv->paintNode);
1822 for (; ii < orderedChildren.count(); ++ii) {
1823 QQuickItemPrivate *childPrivate = QQuickItemPrivate::get(orderedChildren.at(ii));
1824 if (!childPrivate->explicitVisible && !childPrivate->effectRefCount)
1826 if (childPrivate->itemNode()->parent())
1827 childPrivate->itemNode()->parent()->removeChildNode(childPrivate->itemNode());
1829 itemPriv->childContainerNode()->appendChildNode(childPrivate->itemNode());
1833 if ((dirty & QQuickItemPrivate::Size) && itemPriv->clipNode) {
1834 itemPriv->clipNode->setRect(item->boundingRect());
1835 itemPriv->clipNode->update();
1838 if (dirty & (QQuickItemPrivate::OpacityValue | QQuickItemPrivate::Visible | QQuickItemPrivate::HideReference)) {
1839 qreal opacity = itemPriv->explicitVisible && itemPriv->hideRefCount == 0
1840 ? itemPriv->opacity : qreal(0);
1842 if (opacity != 1 && !itemPriv->opacityNode) {
1843 itemPriv->opacityNode = new QSGOpacityNode;
1845 QSGNode *parent = itemPriv->itemNode();
1846 QSGNode *child = itemPriv->clipNode;
1848 child = itemPriv->rootNode;
1850 child = itemPriv->groupNode;
1853 parent->removeChildNode(child);
1854 parent->appendChildNode(itemPriv->opacityNode);
1856 itemPriv->opacityNode->appendChildNode(child);
1858 if (itemPriv->opacityNode)
1859 itemPriv->opacityNode->setOpacity(opacity);
1862 if (dirty & QQuickItemPrivate::ContentUpdateMask) {
1864 if (itemPriv->flags & QQuickItem::ItemHasContents) {
1865 updatePaintNodeData.transformNode = itemPriv->itemNode();
1866 itemPriv->paintNode = item->updatePaintNode(itemPriv->paintNode, &updatePaintNodeData);
1868 Q_ASSERT(itemPriv->paintNode == 0 ||
1869 itemPriv->paintNode->parent() == 0 ||
1870 itemPriv->paintNode->parent() == itemPriv->childContainerNode());
1872 if (itemPriv->paintNode && itemPriv->paintNode->parent() == 0) {
1873 if (itemPriv->beforePaintNode)
1874 itemPriv->childContainerNode()->insertChildNodeAfter(itemPriv->paintNode, itemPriv->beforePaintNode);
1876 itemPriv->childContainerNode()->prependChildNode(itemPriv->paintNode);
1878 } else if (itemPriv->paintNode) {
1879 delete itemPriv->paintNode;
1880 itemPriv->paintNode = 0;
1885 // Check consistency.
1886 const QSGNode *nodeChain[] = {
1887 itemPriv->itemNodeInstance,
1888 itemPriv->opacityNode,
1891 itemPriv->groupNode,
1892 itemPriv->paintNode,
1897 while (ip < 5 && nodeChain[ip] == 0)
1902 while (ic < 5 && nodeChain[ic] == 0)
1904 const QSGNode *parent = nodeChain[ip];
1905 const QSGNode *child = nodeChain[ic];
1907 Q_ASSERT(parent == itemPriv->groupNode || parent->childCount() == 0);
1909 Q_ASSERT(parent == itemPriv->groupNode || parent->childCount() == 1);
1910 Q_ASSERT(child->parent() == parent);
1911 bool containsChild = false;
1912 for (QSGNode *n = parent->firstChild(); n; n = n->nextSibling())
1913 containsChild |= (n == child);
1914 Q_ASSERT(containsChild);
1920 #ifdef QML_RUNTIME_TESTING
1921 if (itemPriv->sceneGraphContext()->isFlashModeEnabled()) {
1922 QSGFlashNode *flash = new QSGFlashNode();
1923 flash->setRect(item->boundingRect());
1924 itemPriv->childContainerNode()->appendChildNode(flash);
1935 void QQuickCanvas::maybeUpdate()
1939 if (d->thread && d->thread->isRunning())
1940 d->thread->maybeUpdate();
1944 \fn void QSGEngine::sceneGraphInitialized();
1946 This signal is emitted when the scene graph has been initialized.
1948 This signal will be emitted from the scene graph rendering thread.
1952 Returns the QSGEngine used for this scene.
1954 The engine will only be available once the scene graph has been
1955 initialized. Register for the sceneGraphEngine() signal to get
1956 notification about this.
1961 QSGEngine *QQuickCanvas::sceneGraphEngine() const
1963 Q_D(const QQuickCanvas);
1964 qWarning("QQuickCanvas::sceneGraphEngine() is deprecated, use members of QQuickCanvas instead");
1965 if (d->context && d->context->isReady())
1973 Sets the render target for this canvas to be \a fbo.
1975 The specified fbo must be created in the context of the canvas
1976 or one that shares with it.
1979 This function can only be called from the thread doing
1983 void QQuickCanvas::setRenderTarget(QOpenGLFramebufferObject *fbo)
1986 if (d->context && d->context && QThread::currentThread() != d->context->thread()) {
1987 qWarning("QQuickCanvas::setRenderThread: Cannot set render target from outside the rendering thread");
1991 d->renderTarget = fbo;
1997 Returns the render target for this canvas.
1999 The default is to render to the surface of the canvas, in which
2000 case the render target is 0.
2002 QOpenGLFramebufferObject *QQuickCanvas::renderTarget() const
2004 Q_D(const QQuickCanvas);
2005 return d->renderTarget;
2010 Grabs the contents of the framebuffer and returns it as an image.
2012 This function might not work if the view is not visible.
2014 \warning Calling this function will cause performance problems.
2016 \warning This function can only be called from the GUI thread.
2018 QImage QQuickCanvas::grabFrameBuffer()
2021 return d->thread ? d->thread->grab() : QImage();
2025 Returns an incubation controller that splices incubation between frames
2026 for this canvas. QQuickView automatically installs this controller for you,
2027 otherwise you will need to install it yourself using \l{QDeclarativeEngine::setIncubationController}
2029 The controller is owned by the canvas and will be destroyed when the canvas
2032 QDeclarativeIncubationController *QQuickCanvas::incubationController() const
2034 Q_D(const QQuickCanvas);
2036 if (!d->incubationController)
2037 d->incubationController = new QQuickCanvasIncubationController(const_cast<QQuickCanvasPrivate *>(d));
2038 return d->incubationController;
2044 \enum QQuickCanvas::CreateTextureOption
2046 The CreateTextureOption enums are used to customize a texture is wrapped.
2048 \value TextureHasAlphaChannel The texture has an alpha channel and should
2049 be drawn using blending.
2051 \value TextureHasMipmaps The texture has mipmaps and can be drawn with
2054 \value TextureOwnsGLTexture The texture object owns the texture id and
2055 will delete the GL texture when the texture object is deleted.
2059 \fn void QQuickCanvas::beforeRendering()
2061 This signal is emitted before the scene starts rendering.
2063 Combined with the modes for clearing the background, this option
2064 can be used to paint using raw GL under QML content.
2066 The GL context used for rendering the scene graph will be bound
2069 Since this signal is emitted from the scene graph rendering thread, the receiver should
2070 be on the scene graph thread or the connection should be Qt::DirectConnection.
2075 \fn void QQuickCanvas::afterRendering()
2077 This signal is emitted after the scene has completed rendering, before swapbuffers is called.
2079 This signal can be used to paint using raw GL on top of QML content,
2080 or to do screen scraping of the current frame buffer.
2082 The GL context used for rendering the scene graph will be bound at this point.
2084 Since this signal is emitted from the scene graph rendering thread, the receiver should
2085 be on the scene graph thread or the connection should be Qt::DirectConnection.
2091 Sets weither the scene graph rendering of QML should clear the color buffer
2092 before it starts rendering to \a enbled.
2094 By disabling clearing of the color buffer, it is possible to do GL painting
2095 under the scene graph.
2097 The color buffer is cleared by default.
2099 \sa beforeRendering()
2102 void QQuickCanvas::setClearBeforeRendering(bool enabled)
2105 d->clearBeforeRendering = enabled;
2111 Returns weither clearing of the color buffer is done before rendering or not.
2114 bool QQuickCanvas::clearBeforeRendering() const
2116 Q_D(const QQuickCanvas);
2117 return d->clearBeforeRendering;
2123 Creates a new QSGTexture from the supplied \a image. If the image has an
2124 alpha channel, the corresponding texture will have an alpha channel.
2126 The caller of the function is responsible for deleting the returned texture.
2127 The actual GL texture will be deleted when the texture object is deleted.
2129 \warning This function will return 0 if the scene graph has not yet been
2132 This function can be called both from the GUI thread and the rendering thread.
2134 \sa sceneGraphInitialized()
2137 QSGTexture *QQuickCanvas::createTextureFromImage(const QImage &image) const
2139 Q_D(const QQuickCanvas);
2141 return d->context->createTexture(image);
2149 Creates a new QSGTexture object from an existing GL texture \a id.
2151 The caller of the function is responsible for deleting the returned texture.
2153 Use \a options to customize the texture attributes.
2155 \warning This function will return 0 if the scenegraph has not yet been
2158 \sa sceneGraphInitialized()
2160 QSGTexture *QQuickCanvas::createTextureFromId(uint id, const QSize &size, CreateTextureOptions options) const
2162 Q_D(const QQuickCanvas);
2164 QSGPlainTexture *texture = new QSGPlainTexture();
2165 texture->setTextureId(id);
2166 texture->setHasAlphaChannel(options & TextureHasAlphaChannel);
2167 texture->setHasMipmaps(options & TextureHasMipmaps);
2168 texture->setOwnsTexture(options & TextureOwnsGLTexture);
2169 texture->setTextureSize(size);
2177 Sets the color used to clear the opengl context to \a color.
2179 Setting the clear color has no effect when clearing is disabled.
2181 \sa setClearBeforeRendering()
2184 void QQuickCanvas::setClearColor(const QColor &color)
2186 d_func()->clearColor = color;
2192 Returns the color used to clear the opengl context.
2195 QColor QQuickCanvas::clearColor() const
2197 return d_func()->clearColor;
2202 void QQuickCanvasRenderLoop::createGLContext()
2204 gl = new QOpenGLContext();
2205 gl->setFormat(renderer->requestedFormat());
2209 void QQuickCanvasRenderThread::run()
2212 qDebug("QML Rendering Thread Started");
2218 initializeSceneGraph();
2223 while (!shouldExit) {
2226 bool sizeChanged = false;
2227 isExternalUpdatePending = false;
2229 if (renderedSize != windowSize) {
2231 printf(" RenderThread: window has changed size...\n");
2233 glViewport(0, 0, windowSize.width(), windowSize.height());
2238 printf(" RenderThread: preparing to sync...\n");
2241 if (!isGuiBlocked) {
2242 isGuiBlockPending = true;
2245 printf(" RenderThread: aquired sync lock...\n");
2247 allowMainThreadProcessingFlag = false;
2248 QCoreApplication::postEvent(this, new QEvent(QEvent::User));
2250 printf(" RenderThread: going to sleep...\n");
2254 isGuiBlockPending = false;
2258 printf(" RenderThread: Doing locked sync\n");
2260 #ifdef QQUICK_CANVAS_TIMING
2261 if (qquick_canvas_timing)
2262 threadTimer.start();
2268 // Wake GUI after sync to let it continue animating and event processing.
2269 allowMainThreadProcessingFlag = true;
2273 printf(" RenderThread: sync done\n");
2275 #ifdef QQUICK_CANVAS_TIMING
2276 if (qquick_canvas_timing)
2277 syncTime = threadTimer.elapsed();
2281 printf(" RenderThread: rendering... %d x %d\n", windowSize.width(), windowSize.height());
2284 renderSceneGraph(windowSize);
2285 #ifdef QQUICK_CANVAS_TIMING
2286 if (qquick_canvas_timing)
2287 renderTime = threadTimer.elapsed() - syncTime;
2290 // The content of the target buffer is undefined after swap() so grab needs
2291 // to happen before swap();
2294 printf(" RenderThread: doing a grab...\n");
2296 grabContent = qt_gl_read_framebuffer(windowSize, false, false);
2301 printf(" RenderThread: wait for swap...\n");
2306 printf(" RenderThread: swap complete...\n");
2308 #ifdef QQUICK_CANVAS_TIMING
2309 if (qquick_canvas_timing) {
2310 swapTime = threadTimer.elapsed() - renderTime;
2311 qDebug() << "- Breakdown of frame time: sync:" << syncTime
2312 << "ms render:" << renderTime << "ms swap:" << swapTime
2313 << "ms total:" << swapTime + renderTime << "ms";
2318 isPaintCompleted = true;
2320 renderedSize = windowSize;
2322 // Wake the GUI thread now that rendering is complete, to signal that painting
2323 // is done, resizing is done or grabbing is completed. For grabbing, we're
2324 // signalling this much later than needed (we could have done it before swap)
2325 // but we don't want to lock an extra time.
2328 if (!animationRunning && !isExternalUpdatePending && !shouldExit && !doGrab) {
2330 printf(" RenderThread: nothing to do, going to sleep...\n");
2332 isRenderBlocked = true;
2334 isRenderBlocked = false;
2339 QCoreApplication::processEvents();
2341 // Process any "deleteLater" objects...
2342 QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
2346 printf(" RenderThread: deleting all outstanding nodes\n");
2348 cleanupNodesOnShutdown();
2351 printf(" RenderThread: render loop exited... Good Night!\n");
2359 printf(" RenderThread: waking GUI for final sleep..\n");
2365 printf(" RenderThread: All done...\n");
2371 bool QQuickCanvasRenderThread::event(QEvent *e)
2373 Q_ASSERT(QThread::currentThread() == qApp->thread());
2375 if (e->type() == QEvent::User) {
2376 if (!syncAlreadyHappened)
2379 syncAlreadyHappened = false;
2381 if (animationRunning && animationDriver()) {
2383 qDebug("GUI: Advancing animations...\n");
2386 animationDriver()->advance();
2389 qDebug("GUI: Animations advanced...\n");
2396 return QThread::event(e);
2401 void QQuickCanvasRenderThread::exhaustSyncEvent()
2403 if (isGuiBlockPending) {
2405 syncAlreadyHappened = true;
2411 void QQuickCanvasRenderThread::sync(bool guiAlreadyLocked)
2414 printf("GUI: sync - %s\n", guiAlreadyLocked ? "outside event" : "inside event");
2416 if (!guiAlreadyLocked)
2419 renderThreadAwakened = false;
2426 if (!guiAlreadyLocked)
2434 Acquires the mutex for the GUI thread. The function uses the isGuiBlocked
2435 variable to keep track of how many recursion levels the gui is locked with.
2436 We only actually acquire the mutex for the first level to avoid deadlocking
2440 void QQuickCanvasRenderThread::lockInGui()
2442 // We must avoid recursive locking in the GUI thread, hence we
2443 // only lock when we are the first one to try to block.
2450 printf("GUI: aquired lock... %d\n", isGuiBlocked);
2456 void QQuickCanvasRenderThread::unlockInGui()
2459 printf("GUI: releasing lock... %d\n", isGuiBlocked);
2469 void QQuickCanvasRenderThread::animationStarted()
2472 printf("GUI: animationStarted()\n");
2477 animationRunning = true;
2479 if (isRenderBlocked)
2487 void QQuickCanvasRenderThread::animationStopped()
2490 printf("GUI: animationStopped()...\n");
2494 animationRunning = false;
2499 void QQuickCanvasRenderThread::paint()
2502 printf("GUI: paint called..\n");
2508 isPaintCompleted = false;
2509 while (isRunning() && !isPaintCompleted) {
2510 if (isRenderBlocked)
2519 void QQuickCanvasRenderThread::resize(const QSize &size)
2522 printf("GUI: Resize Event: %dx%d\n", size.width(), size.height());
2535 while (isRunning() && renderedSize != windowSize) {
2536 if (isRenderBlocked)
2545 void QQuickCanvasRenderThread::startRendering()
2548 printf("GUI: Starting Render Thread\n");
2553 isGuiBlockPending = false;
2559 void QQuickCanvasRenderThread::stopRendering()
2562 printf("GUI: stopping render thread\n");
2569 if (isRenderBlocked) {
2571 printf("GUI: waking up render thread\n");
2576 while (!hasExited) {
2578 printf("GUI: waiting for render thread to have exited..\n");
2586 printf("GUI: waiting for render thread to terminate..\n");
2588 // Actually wait for the thread to terminate. Otherwise we can delete it
2589 // too early and crash.
2593 printf("GUI: thread has terminated and we're all good..\n");
2600 QImage QQuickCanvasRenderThread::grab()
2605 if (QThread::currentThread() != qApp->thread()) {
2606 qWarning("QQuickCanvas::grabFrameBuffer: can only be called from the GUI thread");
2611 printf("GUI: doing a pixelwise grab..\n");
2618 isPaintCompleted = false;
2619 while (isRunning() && !isPaintCompleted) {
2620 if (isRenderBlocked)
2625 QImage grabbed = grabContent;
2626 grabContent = QImage();
2635 void QQuickCanvasRenderThread::maybeUpdate()
2637 Q_ASSERT_X(QThread::currentThread() == QCoreApplication::instance()->thread() || inSync,
2638 "QQuickCanvas::update",
2639 "Function can only be called from GUI thread or during QQuickItem::updatePaintNode()");
2642 isExternalUpdatePending = true;
2644 } else if (!renderThreadAwakened) {
2646 printf("GUI: doing update...\n");
2648 renderThreadAwakened = true;
2650 isExternalUpdatePending = true;
2651 if (isRenderBlocked)
2658 #include "moc_qquickcanvas.cpp"