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 "qsgcanvas.h"
43 #include "qsgcanvas_p.h"
46 #include "qsgitem_p.h"
50 #include <private/qsgrenderer_p.h>
51 #include <private/qsgflashnode_p.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>
64 #include <private/qdeclarativedebugtrace_p.h>
68 DEFINE_BOOL_CONFIG_OPTION(qmlFixedAnimationStep, QML_FIXED_ANIMATION_STEP)
69 DEFINE_BOOL_CONFIG_OPTION(qmlNoThreadedRenderer, QML_BAD_GUI_RENDER_LOOP)
71 extern Q_GUI_EXPORT QImage qt_gl_read_framebuffer(const QSize &size, bool alpha_format, bool include_alpha);
73 void QSGCanvasPrivate::updateFocusItemTransform()
76 QSGItem *focus = q->activeFocusItem();
77 if (focus && qApp->inputPanel()->inputItem() == focus)
78 qApp->inputPanel()->setInputItemTransform(QSGItemPrivate::get(focus)->itemToCanvasTransform());
81 class QSGCanvasPlainRenderLoop : public QObject, public QSGCanvasRenderLoop
84 QSGCanvasPlainRenderLoop()
85 : updatePending(false)
86 , animationRunning(false)
88 qWarning("QSGCanvas: using non-threaded render loop. Be very sure to not access scene graph "
89 "objects outside the QSGItem::updatePaintNode() call. Failing to do so will cause "
90 "your code to crash on other platforms!");
93 virtual void paint() {
94 updatePending = false;
95 if (animationRunning && animationDriver())
96 animationDriver()->advance();
100 glViewport(0, 0, size.width(), size.height());
101 renderSceneGraph(size);
104 if (animationRunning)
108 virtual QImage grab() {
109 return qt_gl_read_framebuffer(size, false, false);
112 virtual void startRendering() {
116 initializeSceneGraph();
123 virtual void stopRendering() { }
125 virtual void maybeUpdate() {
126 if (!updatePending) {
127 QCoreApplication::postEvent(this, new QEvent(QEvent::User));
128 updatePending = true;
132 virtual void animationStarted() {
133 animationRunning = true;
137 virtual void animationStopped() {
138 animationRunning = false;
141 virtual bool isRunning() const { return glContext(); } // Event loop is always running...
142 virtual void resize(const QSize &s) { size = s; }
143 virtual void setWindowSize(const QSize &s) { size = s; }
145 bool event(QEvent *e) {
146 if (e->type() == QEvent::User) {
150 return QObject::event(e);
155 uint updatePending : 1;
156 uint animationRunning : 1;
165 Prior to being added to a valid canvas items can set and clear focus with no
166 effect. Only once items are added to a canvas (by way of having a parent set that
167 already belongs to a canvas) do the focus rules apply. Focus goes back to
168 having no effect if an item is removed from a canvas.
170 When an item is moved into a new focus scope (either being added to a canvas
171 for the first time, or having its parent changed), if the focus scope already has
172 a scope focused item that takes precedence over the item being added. Otherwise,
173 the focus of the added tree is used. In the case of of a tree of items being
174 added to a canvas for the first time, which may have a conflicted focus state (two
175 or more items in one scope having focus set), the same rule is applied item by item -
176 thus the first item that has focus will get it (assuming the scope doesn't already
177 have a scope focused item), and the other items will have their focus cleared.
184 The threaded rendering uses a number of different variables to track potential
185 states used to handle resizing, initial paint, grabbing and driving animations
186 while ALWAYS keeping the GL context in the rendering thread and keeping the
187 overhead of normal one-shot paints and vblank driven animations at a minimum.
189 Resize, initial show and grab suffer slightly in this model as they are locked
190 to the rendering in the rendering thread, but this is a necessary evil for
193 Variables that are used:
195 Private::animationRunning: This is true while the animations are running, and only
196 written to inside locks.
198 RenderThread::isGuiBlocked: This is used to indicate that the GUI thread owns the
199 lock. This variable is an integer to allow for recursive calls to lockInGui()
200 without using a recursive mutex. See isGuiBlockPending.
202 RenderThread::isPaintComplete: This variable is cleared when rendering starts and
203 set once rendering is complete. It is monitored in the paintEvent(),
204 resizeEvent() and grab() functions to force them to wait for rendering to
207 RenderThread::isGuiBlockPending: This variable is set in the render thread just
208 before the sync event is sent to the GUI thread. It is used to avoid deadlocks
209 in the case where render thread waits while waiting for GUI to pick up the sync
210 event and GUI thread gets a resizeEvent, the initial paintEvent or a grab.
211 When this happens, we use the
212 exhaustSyncEvent() function to do the sync right there and mark the coming
213 sync event to be discarded. There can only ever be one sync incoming.
215 RenderThread::isRenderBlock: This variable is true when animations are not
216 running and the render thread has gone to sleep, waiting for more to do.
218 RenderThread::isExternalUpdatePending: This variable is set to false during
219 the sync phase in the GUI thread and to true in maybeUpdate(). It is an
220 indication to the render thread that another render pass needs to take
221 place, rather than the render thread going to sleep after completing its swap.
223 RenderThread::doGrab: This variable is set by the grab() function and
224 tells the renderer to do a grab after rendering is complete and before
227 RenderThread::shouldExit: This variable is used to determine if the render
228 thread should do a nother pass. It is typically set as a result of show()
229 and unset as a result of hide() or during shutdown()
231 RenderThread::hasExited: Used by the GUI thread to synchronize the shutdown
232 after shouldExit has been set to true.
235 // #define FOCUS_DEBUG
236 // #define MOUSE_DEBUG
237 // #define TOUCH_DEBUG
238 // #define DIRTY_DEBUG
239 // #define THREAD_DEBUG
241 // #define FRAME_TIMING
244 static QTime frameTimer;
245 int sceneGraphRenderTime;
249 QSGItem::UpdatePaintNodeData::UpdatePaintNodeData()
254 QSGRootItem::QSGRootItem()
258 void QSGCanvas::exposeEvent(QExposeEvent *)
264 void QSGCanvas::resizeEvent(QResizeEvent *)
267 d->thread->resize(size());
270 void QSGCanvas::animationStarted()
272 d_func()->thread->animationStarted();
275 void QSGCanvas::animationStopped()
277 d_func()->thread->animationStopped();
280 void QSGCanvas::showEvent(QShowEvent *)
283 if (d->vsyncAnimations) {
284 if (!d->animationDriver) {
285 d->animationDriver = d->context->createAnimationDriver(this);
286 connect(d->animationDriver, SIGNAL(started()), this, SLOT(animationStarted()), Qt::DirectConnection);
287 connect(d->animationDriver, SIGNAL(stopped()), this, SLOT(animationStopped()), Qt::DirectConnection);
289 d->animationDriver->install();
292 if (!d->thread->isRunning()) {
293 d->thread->setWindowSize(size());
294 d->thread->startRendering();
298 void QSGCanvas::hideEvent(QHideEvent *)
301 d->thread->stopRendering();
307 Sets weither this canvas should use vsync driven animations.
309 This option can only be set on one single QSGCanvas, and that it's
310 vsync signal will then be used to drive all animations in the
313 This feature is primarily useful for single QSGCanvas, QML-only
316 \warning Enabling vsync on multiple QSGCanvas instances has
319 void QSGCanvas::setVSyncAnimations(bool enabled)
323 qWarning("QSGCanvas::setVSyncAnimations: Cannot be changed when widget is shown");
326 d->vsyncAnimations = enabled;
332 Returns true if this canvas should use vsync driven animations;
333 otherwise returns false.
335 bool QSGCanvas::vsyncAnimations() const
337 Q_D(const QSGCanvas);
338 return d->vsyncAnimations;
341 void QSGCanvasPrivate::initializeSceneGraph()
344 context = QSGContext::createDefaultContext();
346 if (context->isReady())
349 QOpenGLContext *glctx = const_cast<QOpenGLContext *>(QOpenGLContext::currentContext());
350 context->initialize(glctx);
353 QObject::connect(context->renderer(), SIGNAL(sceneGraphChanged()), q, SLOT(maybeUpdate()),
354 Qt::DirectConnection);
356 if (!QSGItemPrivate::get(rootItem)->itemNode()->parent()) {
357 context->rootNode()->appendChildNode(QSGItemPrivate::get(rootItem)->itemNode());
360 emit q_func()->sceneGraphInitialized();
363 void QSGCanvasPrivate::polishItems()
365 while (!itemsToPolish.isEmpty()) {
366 QSet<QSGItem *>::Iterator iter = itemsToPolish.begin();
367 QSGItem *item = *iter;
368 itemsToPolish.erase(iter);
369 QSGItemPrivate::get(item)->polishScheduled = false;
370 item->updatePolish();
372 updateFocusItemTransform();
376 void QSGCanvasPrivate::syncSceneGraph()
382 void QSGCanvasPrivate::renderSceneGraph(const QSize &size)
384 context->renderer()->setDeviceRect(QRect(QPoint(0, 0), size));
385 context->renderer()->setViewportRect(QRect(QPoint(0, 0), renderTarget ? renderTarget->size() : size));
386 context->renderer()->setProjectionMatrixToDeviceRect();
388 context->renderNextFrame(renderTarget);
391 sceneGraphRenderTime = frameTimer.elapsed();
396 // glReadPixels(0, 0, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, &pixel);
397 readbackTime = frameTimer.elapsed();
402 // ### Do we need this?
403 void QSGCanvas::sceneGraphChanged()
406 // d->needsRepaint = true;
409 QSGCanvasPrivate::QSGCanvasPrivate()
412 , mouseGrabberItem(0)
415 , vsyncAnimations(false)
422 QSGCanvasPrivate::~QSGCanvasPrivate()
426 void QSGCanvasPrivate::init(QSGCanvas *c)
428 QUnifiedTimer::instance(true)->setConsistentTiming(qmlFixedAnimationStep());
434 rootItem = new QSGRootItem;
435 QSGItemPrivate *rootItemPrivate = QSGItemPrivate::get(rootItem);
436 rootItemPrivate->canvas = q;
437 rootItemPrivate->flags |= QSGItem::ItemIsFocusScope;
439 // QML always has focus. It is important that this call happens after the rootItem
441 rootItem->setFocus(true);
443 bool threaded = !qmlNoThreadedRenderer();
445 if (!QGuiApplicationPrivate::platformIntegration()->hasCapability(QPlatformIntegration::ThreadedOpenGL)) {
446 qWarning("QSGCanvas: platform does not support threaded rendering!");
451 thread = new QSGCanvasRenderThread();
453 thread = new QSGCanvasPlainRenderLoop();
455 thread->renderer = q;
458 context = QSGContext::createDefaultContext();
459 thread->moveContextToThread(context);
461 q->setSurfaceType(QWindow::OpenGLSurface);
464 void QSGCanvasPrivate::transformTouchPoints(QList<QTouchEvent::TouchPoint> &touchPoints, const QTransform &transform)
466 for (int i=0; i<touchPoints.count(); i++) {
467 QTouchEvent::TouchPoint &touchPoint = touchPoints[i];
468 touchPoint.setRect(transform.mapRect(touchPoint.sceneRect()));
469 touchPoint.setStartPos(transform.map(touchPoint.startScenePos()));
470 touchPoint.setLastPos(transform.map(touchPoint.lastScenePos()));
476 Translates the data in \a touchEvent to this canvas. This method leaves the item local positions in
477 \a touchEvent untouched (these are filled in later).
479 void QSGCanvasPrivate::translateTouchEvent(QTouchEvent *touchEvent)
483 // touchEvent->setWidget(q); // ### refactor...
485 QList<QTouchEvent::TouchPoint> touchPoints = touchEvent->touchPoints();
486 for (int i = 0; i < touchPoints.count(); ++i) {
487 QTouchEvent::TouchPoint &touchPoint = touchPoints[i];
489 touchPoint.setScreenRect(touchPoint.sceneRect());
490 touchPoint.setStartScreenPos(touchPoint.startScenePos());
491 touchPoint.setLastScreenPos(touchPoint.lastScenePos());
493 touchPoint.setSceneRect(touchPoint.rect());
494 touchPoint.setStartScenePos(touchPoint.startPos());
495 touchPoint.setLastScenePos(touchPoint.lastPos());
497 if (touchPoint.isPrimary())
498 lastMousePosition = touchPoint.pos().toPoint();
500 touchEvent->setTouchPoints(touchPoints);
503 void QSGCanvasPrivate::setFocusInScope(QSGItem *scope, QSGItem *item, FocusOptions options)
508 Q_ASSERT(scope || item == rootItem);
511 qWarning() << "QSGCanvasPrivate::setFocusInScope():";
512 qWarning() << " scope:" << (QObject *)scope;
514 qWarning() << " scopeSubFocusItem:" << (QObject *)QSGItemPrivate::get(scope)->subFocusItem;
515 qWarning() << " item:" << (QObject *)item;
516 qWarning() << " activeFocusItem:" << (QObject *)activeFocusItem;
519 QSGItemPrivate *scopePrivate = scope ? QSGItemPrivate::get(scope) : 0;
520 QSGItemPrivate *itemPrivate = QSGItemPrivate::get(item);
522 QSGItem *oldActiveFocusItem = 0;
523 QSGItem *newActiveFocusItem = 0;
525 QVarLengthArray<QSGItem *, 20> changed;
527 // Does this change the active focus?
528 if (item == rootItem || scopePrivate->activeFocus) {
529 oldActiveFocusItem = activeFocusItem;
530 newActiveFocusItem = item;
531 while (newActiveFocusItem->isFocusScope() && newActiveFocusItem->scopedFocusItem())
532 newActiveFocusItem = newActiveFocusItem->scopedFocusItem();
534 if (oldActiveFocusItem) {
536 qApp->inputPanel()->commit();
540 QFocusEvent event(QEvent::FocusOut, Qt::OtherFocusReason);
541 q->sendEvent(oldActiveFocusItem, &event);
543 QSGItem *afi = oldActiveFocusItem;
544 while (afi != scope) {
545 if (QSGItemPrivate::get(afi)->activeFocus) {
546 QSGItemPrivate::get(afi)->activeFocus = false;
549 afi = afi->parentItem();
554 if (item != rootItem) {
555 QSGItem *oldSubFocusItem = scopePrivate->subFocusItem;
556 // Correct focus chain in scope
557 if (oldSubFocusItem) {
558 QSGItem *sfi = scopePrivate->subFocusItem->parentItem();
559 while (sfi != scope) {
560 QSGItemPrivate::get(sfi)->subFocusItem = 0;
561 sfi = sfi->parentItem();
565 scopePrivate->subFocusItem = item;
566 QSGItem *sfi = scopePrivate->subFocusItem->parentItem();
567 while (sfi != scope) {
568 QSGItemPrivate::get(sfi)->subFocusItem = item;
569 sfi = sfi->parentItem();
573 if (oldSubFocusItem) {
574 QSGItemPrivate::get(oldSubFocusItem)->focus = false;
575 changed << oldSubFocusItem;
579 if (!(options & DontChangeFocusProperty)) {
580 // if (item != rootItem || q->hasFocus()) { // ### refactor: focus handling...
581 itemPrivate->focus = true;
586 if (newActiveFocusItem) { // ### refactor: && q->hasFocus()) {
587 activeFocusItem = newActiveFocusItem;
589 QSGItemPrivate::get(newActiveFocusItem)->activeFocus = true;
590 changed << newActiveFocusItem;
592 QSGItem *afi = newActiveFocusItem->parentItem();
593 while (afi && afi != scope) {
594 if (afi->isFocusScope()) {
595 QSGItemPrivate::get(afi)->activeFocus = true;
598 afi = afi->parentItem();
601 updateInputMethodData();
603 QFocusEvent event(QEvent::FocusIn, Qt::OtherFocusReason);
604 q->sendEvent(newActiveFocusItem, &event);
606 updateInputMethodData();
609 if (!changed.isEmpty())
610 notifyFocusChangesRecur(changed.data(), changed.count() - 1);
613 void QSGCanvasPrivate::clearFocusInScope(QSGItem *scope, QSGItem *item, FocusOptions options)
619 Q_ASSERT(scope || item == rootItem);
622 qWarning() << "QSGCanvasPrivate::clearFocusInScope():";
623 qWarning() << " scope:" << (QObject *)scope;
624 qWarning() << " item:" << (QObject *)item;
625 qWarning() << " activeFocusItem:" << (QObject *)activeFocusItem;
628 QSGItemPrivate *scopePrivate = scope ? QSGItemPrivate::get(scope) : 0;
630 QSGItem *oldActiveFocusItem = 0;
631 QSGItem *newActiveFocusItem = 0;
633 QVarLengthArray<QSGItem *, 20> changed;
635 Q_ASSERT(item == rootItem || item == scopePrivate->subFocusItem);
637 // Does this change the active focus?
638 if (item == rootItem || scopePrivate->activeFocus) {
639 oldActiveFocusItem = activeFocusItem;
640 newActiveFocusItem = scope;
642 Q_ASSERT(oldActiveFocusItem);
645 qApp->inputPanel()->commit();
649 QFocusEvent event(QEvent::FocusOut, Qt::OtherFocusReason);
650 q->sendEvent(oldActiveFocusItem, &event);
652 QSGItem *afi = oldActiveFocusItem;
653 while (afi != scope) {
654 if (QSGItemPrivate::get(afi)->activeFocus) {
655 QSGItemPrivate::get(afi)->activeFocus = false;
658 afi = afi->parentItem();
662 if (item != rootItem) {
663 QSGItem *oldSubFocusItem = scopePrivate->subFocusItem;
664 // Correct focus chain in scope
665 if (oldSubFocusItem) {
666 QSGItem *sfi = scopePrivate->subFocusItem->parentItem();
667 while (sfi != scope) {
668 QSGItemPrivate::get(sfi)->subFocusItem = 0;
669 sfi = sfi->parentItem();
672 scopePrivate->subFocusItem = 0;
674 if (oldSubFocusItem && !(options & DontChangeFocusProperty)) {
675 QSGItemPrivate::get(oldSubFocusItem)->focus = false;
676 changed << oldSubFocusItem;
678 } else if (!(options & DontChangeFocusProperty)) {
679 QSGItemPrivate::get(item)->focus = false;
683 if (newActiveFocusItem) {
684 Q_ASSERT(newActiveFocusItem == scope);
685 activeFocusItem = scope;
687 updateInputMethodData();
689 QFocusEvent event(QEvent::FocusIn, Qt::OtherFocusReason);
690 q->sendEvent(newActiveFocusItem, &event);
692 updateInputMethodData();
695 if (!changed.isEmpty())
696 notifyFocusChangesRecur(changed.data(), changed.count() - 1);
699 void QSGCanvasPrivate::notifyFocusChangesRecur(QSGItem **items, int remaining)
701 QDeclarativeGuard<QSGItem> item(*items);
704 notifyFocusChangesRecur(items + 1, remaining - 1);
707 QSGItemPrivate *itemPrivate = QSGItemPrivate::get(item);
709 if (itemPrivate->notifiedFocus != itemPrivate->focus) {
710 itemPrivate->notifiedFocus = itemPrivate->focus;
711 emit item->focusChanged(itemPrivate->focus);
714 if (item && itemPrivate->notifiedActiveFocus != itemPrivate->activeFocus) {
715 itemPrivate->notifiedActiveFocus = itemPrivate->activeFocus;
716 itemPrivate->itemChange(QSGItem::ItemActiveFocusHasChanged, itemPrivate->activeFocus);
717 emit item->activeFocusChanged(itemPrivate->activeFocus);
722 void QSGCanvasPrivate::updateInputMethodData()
724 qApp->inputPanel()->setInputItem(activeFocusItem);
727 QVariant QSGCanvas::inputMethodQuery(Qt::InputMethodQuery query) const
729 Q_D(const QSGCanvas);
730 if (!d->activeFocusItem || !(QSGItemPrivate::get(d->activeFocusItem)->flags & QSGItem::ItemAcceptsInputMethod))
732 QVariant value = d->activeFocusItem->inputMethodQuery(query);
735 QVariant::Type type = value.type();
736 if (type == QVariant::RectF || type == QVariant::Rect) {
737 const QTransform transform = QSGItemPrivate::get(d->activeFocusItem)->itemToCanvasTransform();
738 value = transform.mapRect(value.toRectF());
739 } else if (type == QVariant::PointF || type == QVariant::Point) {
740 const QTransform transform = QSGItemPrivate::get(d->activeFocusItem)->itemToCanvasTransform();
741 value = transform.map(value.toPointF());
746 void QSGCanvasPrivate::dirtyItem(QSGItem *)
752 void QSGCanvasPrivate::cleanup(QSGNode *n)
756 Q_ASSERT(!cleanupNodeList.contains(n));
757 cleanupNodeList.append(n);
762 QSGCanvas::QSGCanvas(QWindow *parent)
763 : QWindow(*(new QSGCanvasPrivate), parent)
769 QSGCanvas::QSGCanvas(QSGCanvasPrivate &dd, QWindow *parent)
770 : QWindow(dd, parent)
776 QSGCanvas::~QSGCanvas()
780 if (d->thread->isRunning()) {
781 d->thread->stopRendering();
786 // ### should we change ~QSGItem to handle this better?
787 // manually cleanup for the root item (item destructor only handles these when an item is parented)
788 QSGItemPrivate *rootItemPrivate = QSGItemPrivate::get(d->rootItem);
789 rootItemPrivate->removeFromDirtyList();
791 delete d->rootItem; d->rootItem = 0;
795 QSGItem *QSGCanvas::rootItem() const
797 Q_D(const QSGCanvas);
802 QSGItem *QSGCanvas::activeFocusItem() const
804 Q_D(const QSGCanvas);
806 return d->activeFocusItem;
809 QSGItem *QSGCanvas::mouseGrabberItem() const
811 Q_D(const QSGCanvas);
813 return d->mouseGrabberItem;
817 bool QSGCanvasPrivate::clearHover()
819 if (hoverItems.isEmpty())
822 QPointF pos = QCursor::pos(); // ### refactor: q->mapFromGlobal(QCursor::pos());
824 bool accepted = false;
825 foreach (QSGItem* item, hoverItems)
826 accepted = sendHoverEvent(QEvent::HoverLeave, item, pos, pos, QGuiApplication::keyboardModifiers(), true) || accepted;
832 bool QSGCanvas::event(QEvent *e)
838 case QEvent::TouchBegin:
839 case QEvent::TouchUpdate:
840 case QEvent::TouchEnd:
842 QTouchEvent *touch = static_cast<QTouchEvent *>(e);
843 d->translateTouchEvent(touch);
844 d->deliverTouchEvent(touch);
845 if (!touch->isAccepted())
851 d->lastMousePosition = QPoint();
853 case QSGEvent::SGDragEnter:
854 case QSGEvent::SGDragExit:
855 case QSGEvent::SGDragMove:
856 case QSGEvent::SGDragDrop:
857 d->deliverDragEvent(static_cast<QSGDragEvent *>(e));
859 case QEvent::WindowDeactivate:
860 rootItem()->windowDeactivateEvent();
866 return QWindow::event(e);
869 void QSGCanvas::keyPressEvent(QKeyEvent *e)
873 if (d->activeFocusItem)
874 sendEvent(d->activeFocusItem, e);
877 void QSGCanvas::keyReleaseEvent(QKeyEvent *e)
881 if (d->activeFocusItem)
882 sendEvent(d->activeFocusItem, e);
885 void QSGCanvas::inputMethodEvent(QInputMethodEvent *e)
889 if (d->activeFocusItem)
890 sendEvent(d->activeFocusItem, e);
893 bool QSGCanvasPrivate::deliverInitialMousePressEvent(QSGItem *item, QMouseEvent *event)
897 QSGItemPrivate *itemPrivate = QSGItemPrivate::get(item);
898 if (itemPrivate->opacity == 0.0)
901 if (itemPrivate->flags & QSGItem::ItemClipsChildrenToShape) {
902 QPointF p = item->mapFromScene(event->windowPos());
903 if (!QRectF(0, 0, item->width(), item->height()).contains(p))
907 QList<QSGItem *> children = itemPrivate->paintOrderChildItems();
908 for (int ii = children.count() - 1; ii >= 0; --ii) {
909 QSGItem *child = children.at(ii);
910 if (!child->isVisible() || !child->isEnabled())
912 if (deliverInitialMousePressEvent(child, event))
916 if (itemPrivate->acceptedMouseButtons & event->button()) {
917 QPointF p = item->mapFromScene(event->windowPos());
918 if (QRectF(0, 0, item->width(), item->height()).contains(p)) {
919 QMouseEvent me(event->type(), p, event->windowPos(), event->screenPos(),
920 event->button(), event->buttons(), event->modifiers());
922 mouseGrabberItem = item;
923 q->sendEvent(item, &me);
924 event->setAccepted(me.isAccepted());
927 mouseGrabberItem->ungrabMouse();
928 mouseGrabberItem = 0;
935 bool QSGCanvasPrivate::deliverMouseEvent(QMouseEvent *event)
939 lastMousePosition = event->windowPos();
941 if (!mouseGrabberItem &&
942 event->type() == QEvent::MouseButtonPress &&
943 (event->button() & event->buttons()) == event->buttons()) {
945 return deliverInitialMousePressEvent(rootItem, event);
948 if (mouseGrabberItem) {
949 QSGItemPrivate *mgPrivate = QSGItemPrivate::get(mouseGrabberItem);
950 const QTransform &transform = mgPrivate->canvasToItemTransform();
951 QMouseEvent me(event->type(), transform.map(event->windowPos()), event->windowPos(), event->screenPos(),
952 event->button(), event->buttons(), event->modifiers());
954 q->sendEvent(mouseGrabberItem, &me);
955 event->setAccepted(me.isAccepted());
963 void QSGCanvas::mousePressEvent(QMouseEvent *event)
968 qWarning() << "QSGCanvas::mousePressEvent()" << event->pos() << event->button() << event->buttons();
971 d->deliverMouseEvent(event);
974 void QSGCanvas::mouseReleaseEvent(QMouseEvent *event)
979 qWarning() << "QSGCanvas::mouseReleaseEvent()" << event->pos() << event->button() << event->buttons();
982 if (!d->mouseGrabberItem) {
983 QWindow::mouseReleaseEvent(event);
987 d->deliverMouseEvent(event);
988 d->mouseGrabberItem = 0;
991 void QSGCanvas::mouseDoubleClickEvent(QMouseEvent *event)
996 qWarning() << "QSGCanvas::mouseDoubleClickEvent()" << event->pos() << event->button() << event->buttons();
999 if (!d->mouseGrabberItem && (event->button() & event->buttons()) == event->buttons()) {
1000 if (d->deliverInitialMousePressEvent(d->rootItem, event))
1007 d->deliverMouseEvent(event);
1010 bool QSGCanvasPrivate::sendHoverEvent(QEvent::Type type, QSGItem *item,
1011 const QPointF &scenePos, const QPointF &lastScenePos,
1012 Qt::KeyboardModifiers modifiers, bool accepted)
1015 const QTransform transform = QSGItemPrivate::get(item)->canvasToItemTransform();
1017 //create copy of event
1018 QHoverEvent hoverEvent(type, transform.map(scenePos), transform.map(lastScenePos), modifiers);
1019 hoverEvent.setAccepted(accepted);
1021 q->sendEvent(item, &hoverEvent);
1023 return hoverEvent.isAccepted();
1026 void QSGCanvas::mouseMoveEvent(QMouseEvent *event)
1031 qWarning() << "QSGCanvas::mouseMoveEvent()" << event->pos() << event->button() << event->buttons();
1034 if (!d->mouseGrabberItem) {
1035 if (d->lastMousePosition.isNull())
1036 d->lastMousePosition = event->windowPos();
1037 QPointF last = d->lastMousePosition;
1038 d->lastMousePosition = event->windowPos();
1040 bool accepted = event->isAccepted();
1041 bool delivered = d->deliverHoverEvent(d->rootItem, event->windowPos(), last, event->modifiers(), accepted);
1043 //take care of any exits
1044 accepted = d->clearHover();
1046 event->setAccepted(accepted);
1050 d->deliverMouseEvent(event);
1053 bool QSGCanvasPrivate::deliverHoverEvent(QSGItem *item, const QPointF &scenePos, const QPointF &lastScenePos,
1054 Qt::KeyboardModifiers modifiers, bool &accepted)
1056 QSGItemPrivate *itemPrivate = QSGItemPrivate::get(item);
1057 if (itemPrivate->opacity == 0.0)
1060 if (itemPrivate->flags & QSGItem::ItemClipsChildrenToShape) {
1061 QPointF p = item->mapFromScene(scenePos);
1062 if (!QRectF(0, 0, item->width(), item->height()).contains(p))
1066 QList<QSGItem *> children = itemPrivate->paintOrderChildItems();
1067 for (int ii = children.count() - 1; ii >= 0; --ii) {
1068 QSGItem *child = children.at(ii);
1069 if (!child->isVisible() || !child->isEnabled())
1071 if (deliverHoverEvent(child, scenePos, lastScenePos, modifiers, accepted))
1075 if (itemPrivate->hoverEnabled) {
1076 QPointF p = item->mapFromScene(scenePos);
1077 if (QRectF(0, 0, item->width(), item->height()).contains(p)) {
1078 if (!hoverItems.isEmpty() && hoverItems[0] == item) {
1080 accepted = sendHoverEvent(QEvent::HoverMove, item, scenePos, lastScenePos, modifiers, accepted);
1082 QList<QSGItem *> itemsToHover;
1083 QSGItem* parent = item;
1084 itemsToHover << item;
1085 while ((parent = parent->parentItem()))
1086 itemsToHover << parent;
1088 // Leaving from previous hovered items until we reach the item or one of its ancestors.
1089 while (!hoverItems.isEmpty() && !itemsToHover.contains(hoverItems[0])) {
1090 sendHoverEvent(QEvent::HoverLeave, hoverItems[0], scenePos, lastScenePos, modifiers, accepted);
1091 hoverItems.removeFirst();
1094 if (!hoverItems.isEmpty() && hoverItems[0] == item){//Not entering a new Item
1095 // ### Shouldn't we send moves for the parent items as well?
1096 accepted = sendHoverEvent(QEvent::HoverMove, item, scenePos, lastScenePos, modifiers, accepted);
1098 // Enter items that are not entered yet.
1100 if (!hoverItems.isEmpty())
1101 startIdx = itemsToHover.indexOf(hoverItems[0]) - 1;
1103 startIdx = itemsToHover.count() - 1;
1105 for (int i = startIdx; i >= 0; i--) {
1106 QSGItem *itemToHover = itemsToHover[i];
1107 if (QSGItemPrivate::get(itemToHover)->hoverEnabled) {
1108 hoverItems.prepend(itemToHover);
1109 sendHoverEvent(QEvent::HoverEnter, itemToHover, scenePos, lastScenePos, modifiers, accepted);
1121 bool QSGCanvasPrivate::deliverWheelEvent(QSGItem *item, QWheelEvent *event)
1124 QSGItemPrivate *itemPrivate = QSGItemPrivate::get(item);
1125 if (itemPrivate->opacity == 0.0)
1128 if (itemPrivate->flags & QSGItem::ItemClipsChildrenToShape) {
1129 QPointF p = item->mapFromScene(event->posF());
1130 if (!QRectF(0, 0, item->width(), item->height()).contains(p))
1134 QList<QSGItem *> children = itemPrivate->paintOrderChildItems();
1135 for (int ii = children.count() - 1; ii >= 0; --ii) {
1136 QSGItem *child = children.at(ii);
1137 if (!child->isVisible() || !child->isEnabled())
1139 if (deliverWheelEvent(child, event))
1143 QPointF p = item->mapFromScene(event->posF());
1144 if (QRectF(0, 0, item->width(), item->height()).contains(p)) {
1145 QWheelEvent wheel(p, event->delta(), event->buttons(), event->modifiers(), event->orientation());
1147 q->sendEvent(item, &wheel);
1148 if (wheel.isAccepted()) {
1157 #ifndef QT_NO_WHEELEVENT
1158 void QSGCanvas::wheelEvent(QWheelEvent *event)
1162 qWarning() << "QSGCanvas::wheelEvent()" << event->pos() << event->delta() << event->orientation();
1165 d->deliverWheelEvent(d->rootItem, event);
1167 #endif // QT_NO_WHEELEVENT
1169 bool QSGCanvasPrivate::deliverTouchEvent(QTouchEvent *event)
1172 if (event->type() == QEvent::TouchBegin)
1173 qWarning("touchBeginEvent");
1174 else if (event->type() == QEvent::TouchUpdate)
1175 qWarning("touchUpdateEvent");
1176 else if (event->type() == QEvent::TouchEnd)
1177 qWarning("touchEndEvent");
1180 QHash<QSGItem *, QList<QTouchEvent::TouchPoint> > updatedPoints;
1182 if (event->type() == QTouchEvent::TouchBegin) { // all points are new touch points
1183 QSet<int> acceptedNewPoints;
1184 deliverTouchPoints(rootItem, event, event->touchPoints(), &acceptedNewPoints, &updatedPoints);
1185 if (acceptedNewPoints.count() > 0)
1187 return event->isAccepted();
1190 const QList<QTouchEvent::TouchPoint> &touchPoints = event->touchPoints();
1191 QList<QTouchEvent::TouchPoint> newPoints;
1193 for (int i=0; i<touchPoints.count(); i++) {
1194 const QTouchEvent::TouchPoint &touchPoint = touchPoints[i];
1195 switch (touchPoint.state()) {
1196 case Qt::TouchPointPressed:
1197 newPoints << touchPoint;
1199 case Qt::TouchPointMoved:
1200 case Qt::TouchPointStationary:
1201 case Qt::TouchPointReleased:
1202 if (itemForTouchPointId.contains(touchPoint.id())) {
1203 item = itemForTouchPointId[touchPoint.id()];
1205 updatedPoints[item].append(touchPoint);
1213 if (newPoints.count() > 0 || updatedPoints.count() > 0) {
1214 QSet<int> acceptedNewPoints;
1215 int prevCount = updatedPoints.count();
1216 deliverTouchPoints(rootItem, event, newPoints, &acceptedNewPoints, &updatedPoints);
1217 if (acceptedNewPoints.count() > 0 || updatedPoints.count() != prevCount)
1221 if (event->touchPointStates() & Qt::TouchPointReleased) {
1222 for (int i=0; i<touchPoints.count(); i++) {
1223 if (touchPoints[i].state() == Qt::TouchPointReleased)
1224 itemForTouchPointId.remove(touchPoints[i].id());
1228 return event->isAccepted();
1231 bool QSGCanvasPrivate::deliverTouchPoints(QSGItem *item, QTouchEvent *event, const QList<QTouchEvent::TouchPoint> &newPoints, QSet<int> *acceptedNewPoints, QHash<QSGItem *, QList<QTouchEvent::TouchPoint> > *updatedPoints)
1234 QSGItemPrivate *itemPrivate = QSGItemPrivate::get(item);
1236 if (itemPrivate->opacity == 0.0)
1239 if (itemPrivate->flags & QSGItem::ItemClipsChildrenToShape) {
1240 QRectF bounds(0, 0, item->width(), item->height());
1241 for (int i=0; i<newPoints.count(); i++) {
1242 QPointF p = item->mapFromScene(newPoints[i].scenePos());
1243 if (!bounds.contains(p))
1248 QList<QSGItem *> children = itemPrivate->paintOrderChildItems();
1249 for (int ii = children.count() - 1; ii >= 0; --ii) {
1250 QSGItem *child = children.at(ii);
1251 if (!child->isEnabled())
1253 if (deliverTouchPoints(child, event, newPoints, acceptedNewPoints, updatedPoints))
1257 QList<QTouchEvent::TouchPoint> matchingPoints;
1258 if (newPoints.count() > 0 && acceptedNewPoints->count() < newPoints.count()) {
1259 QRectF bounds(0, 0, item->width(), item->height());
1260 for (int i=0; i<newPoints.count(); i++) {
1261 if (acceptedNewPoints->contains(newPoints[i].id()))
1263 QPointF p = item->mapFromScene(newPoints[i].scenePos());
1264 if (bounds.contains(p))
1265 matchingPoints << newPoints[i];
1269 if (matchingPoints.count() > 0 || (*updatedPoints)[item].count() > 0) {
1270 QList<QTouchEvent::TouchPoint> &eventPoints = (*updatedPoints)[item];
1271 eventPoints.append(matchingPoints);
1272 transformTouchPoints(eventPoints, itemPrivate->canvasToItemTransform());
1274 Qt::TouchPointStates eventStates;
1275 for (int i=0; i<eventPoints.count(); i++)
1276 eventStates |= eventPoints[i].state();
1277 // if all points have the same state, set the event type accordingly
1278 QEvent::Type eventType;
1279 switch (eventStates) {
1280 case Qt::TouchPointPressed:
1281 eventType = QEvent::TouchBegin;
1283 case Qt::TouchPointReleased:
1284 eventType = QEvent::TouchEnd;
1287 eventType = QEvent::TouchUpdate;
1291 if (eventStates != Qt::TouchPointStationary) {
1292 QTouchEvent touchEvent(eventType);
1293 // touchEvent.setWidget(q); // ### refactor: what is the consequence of not setting the widget?
1294 touchEvent.setDeviceType(event->deviceType());
1295 touchEvent.setModifiers(event->modifiers());
1296 touchEvent.setTouchPointStates(eventStates);
1297 touchEvent.setTouchPoints(eventPoints);
1299 touchEvent.accept();
1300 q->sendEvent(item, &touchEvent);
1302 if (touchEvent.isAccepted()) {
1303 for (int i=0; i<matchingPoints.count(); i++) {
1304 itemForTouchPointId[matchingPoints[i].id()] = item;
1305 acceptedNewPoints->insert(matchingPoints[i].id());
1311 updatedPoints->remove(item);
1312 if (acceptedNewPoints->count() == newPoints.count() && updatedPoints->isEmpty())
1318 void QSGCanvasPrivate::deliverDragEvent(QSGDragEvent *event)
1321 if (event->type() == QSGEvent::SGDragExit || event->type() == QSGEvent::SGDragDrop) {
1322 if (QSGItem *grabItem = event->grabItem()) {
1323 event->setPosition(grabItem->mapFromScene(event->scenePosition()));
1324 q->sendEvent(grabItem, event);
1326 } else if (!deliverDragEvent(rootItem, event)) {
1327 if (QSGItem *grabItem = event->grabItem()) {
1328 QSGDragEvent exitEvent(QSGEvent::SGDragExit, *event);
1329 exitEvent.setPosition(grabItem->mapFromScene(event->scenePosition()));
1330 q->sendEvent(grabItem, &exitEvent);
1331 event->setDropItem(0);
1332 event->setGrabItem(0);
1334 event->setAccepted(false);
1338 bool QSGCanvasPrivate::deliverDragEvent(QSGItem *item, QSGDragEvent *event)
1341 QSGItemPrivate *itemPrivate = QSGItemPrivate::get(item);
1342 if (itemPrivate->opacity == 0.0)
1345 if (itemPrivate->flags & QSGItem::ItemClipsChildrenToShape) {
1346 QPointF p = item->mapFromScene(event->scenePosition());
1347 if (!QRectF(0, 0, item->width(), item->height()).contains(p))
1351 QList<QSGItem *> children = itemPrivate->paintOrderChildItems();
1352 for (int ii = children.count() - 1; ii >= 0; --ii) {
1353 QSGItem *child = children.at(ii);
1354 if (!child->isVisible() || !child->isEnabled())
1356 if (deliverDragEvent(child, event))
1360 QPointF p = item->mapFromScene(event->scenePosition());
1361 if (QRectF(0, 0, item->width(), item->height()).contains(p)) {
1362 event->setPosition(p);
1364 if (event->type() == QSGEvent::SGDragMove && item != event->grabItem()) {
1365 QSGDragEvent enterEvent(QSGEvent::SGDragEnter, *event);
1366 q->sendEvent(item, &enterEvent);
1367 if (enterEvent.isAccepted()) {
1368 if (QSGItem *grabItem = event->grabItem()) {
1369 QSGDragEvent exitEvent(QSGEvent::SGDragExit, *event);
1370 q->sendEvent(grabItem, &exitEvent);
1372 event->setDropItem(enterEvent.dropItem());
1373 event->setGrabItem(item);
1379 q->sendEvent(item, event);
1380 if (event->isAccepted()) {
1381 event->setGrabItem(item);
1384 event->setAccepted(true);
1390 bool QSGCanvasPrivate::sendFilteredMouseEvent(QSGItem *target, QSGItem *item, QMouseEvent *event)
1395 if (sendFilteredMouseEvent(target->parentItem(), item, event))
1398 QSGItemPrivate *targetPrivate = QSGItemPrivate::get(target);
1399 if (targetPrivate->filtersChildMouseEvents)
1400 if (target->childMouseEventFilter(item, event))
1406 bool QSGCanvas::sendEvent(QSGItem *item, QEvent *e)
1411 qWarning("QSGCanvas::sendEvent: Cannot send event to a null item");
1417 switch (e->type()) {
1418 case QEvent::KeyPress:
1419 case QEvent::KeyRelease:
1421 QSGItemPrivate::get(item)->deliverKeyEvent(static_cast<QKeyEvent *>(e));
1422 while (!e->isAccepted() && (item = item->parentItem())) {
1424 QSGItemPrivate::get(item)->deliverKeyEvent(static_cast<QKeyEvent *>(e));
1427 case QEvent::InputMethod:
1429 QSGItemPrivate::get(item)->deliverInputMethodEvent(static_cast<QInputMethodEvent *>(e));
1430 while (!e->isAccepted() && (item = item->parentItem())) {
1432 QSGItemPrivate::get(item)->deliverInputMethodEvent(static_cast<QInputMethodEvent *>(e));
1435 case QEvent::FocusIn:
1436 case QEvent::FocusOut:
1437 QSGItemPrivate::get(item)->deliverFocusEvent(static_cast<QFocusEvent *>(e));
1439 case QEvent::MouseButtonPress:
1440 case QEvent::MouseButtonRelease:
1441 case QEvent::MouseButtonDblClick:
1442 case QEvent::MouseMove:
1443 // XXX todo - should sendEvent be doing this? how does it relate to forwarded events?
1445 QMouseEvent *se = static_cast<QMouseEvent *>(e);
1446 if (!d->sendFilteredMouseEvent(item->parentItem(), item, se)) {
1448 QSGItemPrivate::get(item)->deliverMouseEvent(se);
1453 QSGItemPrivate::get(item)->deliverWheelEvent(static_cast<QWheelEvent *>(e));
1455 case QEvent::HoverEnter:
1456 case QEvent::HoverLeave:
1457 case QEvent::HoverMove:
1458 QSGItemPrivate::get(item)->deliverHoverEvent(static_cast<QHoverEvent *>(e));
1460 case QEvent::TouchBegin:
1461 case QEvent::TouchUpdate:
1462 case QEvent::TouchEnd:
1463 QSGItemPrivate::get(item)->deliverTouchEvent(static_cast<QTouchEvent *>(e));
1465 case QSGEvent::SGDragEnter:
1466 case QSGEvent::SGDragExit:
1467 case QSGEvent::SGDragMove:
1468 case QSGEvent::SGDragDrop:
1469 QSGItemPrivate::get(item)->deliverDragEvent(static_cast<QSGDragEvent *>(e));
1478 void QSGCanvasPrivate::cleanupNodes()
1480 for (int ii = 0; ii < cleanupNodeList.count(); ++ii)
1481 delete cleanupNodeList.at(ii);
1482 cleanupNodeList.clear();
1485 void QSGCanvasPrivate::updateDirtyNodes()
1488 qWarning() << "QSGCanvasPrivate::updateDirtyNodes():";
1493 QSGItem *updateList = dirtyItemList;
1495 if (updateList) QSGItemPrivate::get(updateList)->prevDirtyItem = &updateList;
1497 while (updateList) {
1498 QSGItem *item = updateList;
1499 QSGItemPrivate *itemPriv = QSGItemPrivate::get(item);
1500 itemPriv->removeFromDirtyList();
1503 qWarning() << " QSGNode:" << item << qPrintable(itemPriv->dirtyToString());
1505 updateDirtyNode(item);
1509 void QSGCanvasPrivate::updateDirtyNode(QSGItem *item)
1511 #ifdef QML_RUNTIME_TESTING
1512 bool didFlash = false;
1515 QSGItemPrivate *itemPriv = QSGItemPrivate::get(item);
1516 quint32 dirty = itemPriv->dirtyAttributes;
1517 itemPriv->dirtyAttributes = 0;
1519 if ((dirty & QSGItemPrivate::TransformUpdateMask) ||
1520 (dirty & QSGItemPrivate::Size && itemPriv->origin != QSGItem::TopLeft &&
1521 (itemPriv->scale != 1. || itemPriv->rotation != 0.))) {
1525 if (itemPriv->x != 0. || itemPriv->y != 0.)
1526 matrix.translate(itemPriv->x, itemPriv->y);
1528 for (int ii = itemPriv->transforms.count() - 1; ii >= 0; --ii)
1529 itemPriv->transforms.at(ii)->applyTo(&matrix);
1531 if (itemPriv->scale != 1. || itemPriv->rotation != 0.) {
1532 QPointF origin = item->transformOriginPoint();
1533 matrix.translate(origin.x(), origin.y());
1534 if (itemPriv->scale != 1.)
1535 matrix.scale(itemPriv->scale, itemPriv->scale);
1536 if (itemPriv->rotation != 0.)
1537 matrix.rotate(itemPriv->rotation, 0, 0, 1);
1538 matrix.translate(-origin.x(), -origin.y());
1541 itemPriv->itemNode()->setMatrix(matrix);
1544 bool clipEffectivelyChanged = dirty & QSGItemPrivate::Clip &&
1545 ((item->clip() == false) != (itemPriv->clipNode == 0));
1546 bool effectRefEffectivelyChanged = dirty & QSGItemPrivate::EffectReference &&
1547 ((itemPriv->effectRefCount == 0) != (itemPriv->rootNode == 0));
1549 if (clipEffectivelyChanged) {
1550 QSGNode *parent = itemPriv->opacityNode ? (QSGNode *) itemPriv->opacityNode : (QSGNode *)itemPriv->itemNode();
1551 QSGNode *child = itemPriv->rootNode ? (QSGNode *)itemPriv->rootNode : (QSGNode *)itemPriv->groupNode;
1554 Q_ASSERT(itemPriv->clipNode == 0);
1555 itemPriv->clipNode = new QSGDefaultClipNode(item->boundingRect());
1556 itemPriv->clipNode->update();
1559 parent->removeChildNode(child);
1560 parent->appendChildNode(itemPriv->clipNode);
1562 itemPriv->clipNode->appendChildNode(child);
1565 Q_ASSERT(itemPriv->clipNode != 0);
1566 parent->removeChildNode(itemPriv->clipNode);
1568 itemPriv->clipNode->removeChildNode(child);
1569 delete itemPriv->clipNode;
1570 itemPriv->clipNode = 0;
1572 parent->appendChildNode(child);
1576 if (dirty & QSGItemPrivate::ChildrenUpdateMask)
1577 itemPriv->childContainerNode()->removeAllChildNodes();
1579 if (effectRefEffectivelyChanged) {
1580 QSGNode *parent = itemPriv->clipNode;
1582 parent = itemPriv->opacityNode;
1584 parent = itemPriv->itemNode();
1585 QSGNode *child = itemPriv->groupNode;
1587 if (itemPriv->effectRefCount) {
1588 Q_ASSERT(itemPriv->rootNode == 0);
1589 itemPriv->rootNode = new QSGRootNode;
1592 parent->removeChildNode(child);
1593 parent->appendChildNode(itemPriv->rootNode);
1595 itemPriv->rootNode->appendChildNode(child);
1597 Q_ASSERT(itemPriv->rootNode != 0);
1598 parent->removeChildNode(itemPriv->rootNode);
1600 itemPriv->rootNode->removeChildNode(child);
1601 delete itemPriv->rootNode;
1602 itemPriv->rootNode = 0;
1604 parent->appendChildNode(child);
1608 if (dirty & QSGItemPrivate::ChildrenUpdateMask) {
1609 QSGNode *groupNode = itemPriv->groupNode;
1611 groupNode->removeAllChildNodes();
1613 QList<QSGItem *> orderedChildren = itemPriv->paintOrderChildItems();
1616 for (; ii < orderedChildren.count() && orderedChildren.at(ii)->z() < 0; ++ii) {
1617 QSGItemPrivate *childPrivate = QSGItemPrivate::get(orderedChildren.at(ii));
1618 if (!childPrivate->explicitVisible && !childPrivate->effectRefCount)
1620 if (childPrivate->itemNode()->parent())
1621 childPrivate->itemNode()->parent()->removeChildNode(childPrivate->itemNode());
1623 itemPriv->childContainerNode()->appendChildNode(childPrivate->itemNode());
1625 itemPriv->beforePaintNode = itemPriv->groupNode ? itemPriv->groupNode->lastChild() : 0;
1627 if (itemPriv->paintNode)
1628 itemPriv->childContainerNode()->appendChildNode(itemPriv->paintNode);
1630 for (; ii < orderedChildren.count(); ++ii) {
1631 QSGItemPrivate *childPrivate = QSGItemPrivate::get(orderedChildren.at(ii));
1632 if (!childPrivate->explicitVisible && !childPrivate->effectRefCount)
1634 if (childPrivate->itemNode()->parent())
1635 childPrivate->itemNode()->parent()->removeChildNode(childPrivate->itemNode());
1637 itemPriv->childContainerNode()->appendChildNode(childPrivate->itemNode());
1641 if ((dirty & QSGItemPrivate::Size) && itemPriv->clipNode) {
1642 itemPriv->clipNode->setRect(item->boundingRect());
1643 itemPriv->clipNode->update();
1646 if (dirty & (QSGItemPrivate::OpacityValue | QSGItemPrivate::Visible | QSGItemPrivate::HideReference)) {
1647 qreal opacity = itemPriv->explicitVisible && itemPriv->hideRefCount == 0
1648 ? itemPriv->opacity : qreal(0);
1650 if (opacity != 1 && !itemPriv->opacityNode) {
1651 itemPriv->opacityNode = new QSGOpacityNode;
1653 QSGNode *parent = itemPriv->itemNode();
1654 QSGNode *child = itemPriv->clipNode;
1656 child = itemPriv->rootNode;
1658 child = itemPriv->groupNode;
1661 parent->removeChildNode(child);
1662 parent->appendChildNode(itemPriv->opacityNode);
1664 itemPriv->opacityNode->appendChildNode(child);
1666 if (itemPriv->opacityNode)
1667 itemPriv->opacityNode->setOpacity(opacity);
1670 if (dirty & QSGItemPrivate::ContentUpdateMask) {
1672 if (itemPriv->flags & QSGItem::ItemHasContents) {
1673 updatePaintNodeData.transformNode = itemPriv->itemNode();
1674 itemPriv->paintNode = item->updatePaintNode(itemPriv->paintNode, &updatePaintNodeData);
1676 Q_ASSERT(itemPriv->paintNode == 0 ||
1677 itemPriv->paintNode->parent() == 0 ||
1678 itemPriv->paintNode->parent() == itemPriv->childContainerNode());
1680 if (itemPriv->paintNode && itemPriv->paintNode->parent() == 0) {
1681 if (itemPriv->beforePaintNode)
1682 itemPriv->childContainerNode()->insertChildNodeAfter(itemPriv->paintNode, itemPriv->beforePaintNode);
1684 itemPriv->childContainerNode()->prependChildNode(itemPriv->paintNode);
1686 } else if (itemPriv->paintNode) {
1687 delete itemPriv->paintNode;
1688 itemPriv->paintNode = 0;
1693 // Check consistency.
1694 const QSGNode *nodeChain[] = {
1695 itemPriv->itemNodeInstance,
1696 itemPriv->opacityNode,
1699 itemPriv->groupNode,
1700 itemPriv->paintNode,
1705 while (ip < 5 && nodeChain[ip] == 0)
1710 while (ic < 5 && nodeChain[ic] == 0)
1712 const QSGNode *parent = nodeChain[ip];
1713 const QSGNode *child = nodeChain[ic];
1715 Q_ASSERT(parent == itemPriv->groupNode || parent->childCount() == 0);
1717 Q_ASSERT(parent == itemPriv->groupNode || parent->childCount() == 1);
1718 Q_ASSERT(child->parent() == parent);
1719 bool containsChild = false;
1720 for (QSGNode *n = parent->firstChild(); n; n = n->nextSibling())
1721 containsChild |= (n == child);
1722 Q_ASSERT(containsChild);
1728 #ifdef QML_RUNTIME_TESTING
1729 if (itemPriv->sceneGraphContext()->isFlashModeEnabled()) {
1730 QSGFlashNode *flash = new QSGFlashNode();
1731 flash->setRect(item->boundingRect());
1732 itemPriv->childContainerNode()->appendChildNode(flash);
1743 void QSGCanvas::maybeUpdate()
1747 if (d->thread && d->thread->isRunning())
1748 d->thread->maybeUpdate();
1752 \fn void QSGEngine::sceneGraphInitialized();
1754 This signal is emitted when the scene graph has been initialized.
1756 This signal will be emitted from the scene graph rendering thread.
1760 Returns the QSGEngine used for this scene.
1762 The engine will only be available once the scene graph has been
1763 initialized. Register for the sceneGraphEngine() signal to get
1764 notification about this.
1767 QSGEngine *QSGCanvas::sceneGraphEngine() const
1769 Q_D(const QSGCanvas);
1770 if (d->context && d->context->isReady())
1771 return d->context->engine();
1778 Sets the render target for this canvas to be \a fbo.
1780 The specified fbo must be created in the context of the canvas
1781 or one that shares with it.
1784 This function can only be called from the thread doing
1788 void QSGCanvas::setRenderTarget(QOpenGLFramebufferObject *fbo)
1791 if (d->context && d->context && QThread::currentThread() != d->context->thread()) {
1792 qWarning("QSGCanvas::setRenderThread: Cannot set render target from outside the rendering thread");
1796 d->renderTarget = fbo;
1802 Returns the render target for this canvas.
1804 The default is to render to the surface of the canvas, in which
1805 case the render target is 0.
1807 QOpenGLFramebufferObject *QSGCanvas::renderTarget() const
1809 Q_D(const QSGCanvas);
1810 return d->renderTarget;
1815 Grabs the contents of the framebuffer and returns it as an image.
1817 This function might not work if the view is not visible.
1819 \warning Calling this function will cause performance problems.
1821 \warning This function can only be called from the GUI thread.
1823 QImage QSGCanvas::grabFrameBuffer()
1826 return d->thread ? d->thread->grab() : QImage();
1831 void QSGCanvasRenderLoop::createGLContext()
1833 gl = new QOpenGLContext();
1834 gl->setFormat(renderer->format());
1839 void QSGCanvasRenderThread::run()
1842 qDebug("QML Rendering Thread Started");
1848 initializeSceneGraph();
1853 while (!shouldExit) {
1856 bool sizeChanged = false;
1857 isExternalUpdatePending = false;
1859 if (renderedSize != windowSize) {
1861 printf(" RenderThread: window has changed size...\n");
1863 glViewport(0, 0, windowSize.width(), windowSize.height());
1868 printf(" RenderThread: preparing to sync...\n");
1871 if (!isGuiBlocked) {
1872 isGuiBlockPending = true;
1875 printf(" RenderThread: aquired sync lock...\n");
1877 QCoreApplication::postEvent(this, new QEvent(QEvent::User));
1879 printf(" RenderThread: going to sleep...\n");
1883 isGuiBlockPending = false;
1887 printf(" RenderThread: Doing locked sync\n");
1893 // Wake GUI after sync to let it continue animating and event processing.
1897 printf(" RenderThread: sync done\n");
1903 printf(" RenderThread: rendering... %d x %d\n", windowSize.width(), windowSize.height());
1906 renderSceneGraph(windowSize);
1908 // The content of the target buffer is undefined after swap() so grab needs
1909 // to happen before swap();
1912 printf(" RenderThread: doing a grab...\n");
1914 grabContent = qt_gl_read_framebuffer(windowSize, false, false);
1919 printf(" RenderThread: wait for swap...\n");
1924 printf(" RenderThread: swap complete...\n");
1928 isPaintCompleted = true;
1930 renderedSize = windowSize;
1932 // Wake the GUI thread now that rendering is complete, to signal that painting
1933 // is done, resizing is done or grabbing is completed. For grabbing, we're
1934 // signalling this much later than needed (we could have done it before swap)
1935 // but we don't want to lock an extra time.
1938 if (!animationRunning && !isExternalUpdatePending && !shouldExit && !doGrab) {
1940 printf(" RenderThread: nothing to do, going to sleep...\n");
1942 isRenderBlocked = true;
1944 isRenderBlocked = false;
1949 // Process any "deleteLater" objects...
1950 QCoreApplication::processEvents();
1954 printf(" RenderThread: render loop exited... Good Night!\n");
1962 printf(" RenderThread: waking GUI for final sleep..\n");
1968 printf(" RenderThread: All done...\n");
1974 bool QSGCanvasRenderThread::event(QEvent *e)
1976 Q_ASSERT(QThread::currentThread() == qApp->thread());
1978 if (e->type() == QEvent::User) {
1979 if (!syncAlreadyHappened)
1982 syncAlreadyHappened = false;
1984 if (animationRunning && animationDriver()) {
1986 qDebug("GUI: Advancing animations...\n");
1989 animationDriver()->advance();
1992 qDebug("GUI: Animations advanced...\n");
1999 return QThread::event(e);
2004 void QSGCanvasRenderThread::exhaustSyncEvent()
2006 if (isGuiBlockPending) {
2008 syncAlreadyHappened = true;
2014 void QSGCanvasRenderThread::sync(bool guiAlreadyLocked)
2017 printf("GUI: sync - %s\n", guiAlreadyLocked ? "outside event" : "inside event");
2019 if (!guiAlreadyLocked)
2022 renderThreadAwakened = false;
2029 if (!guiAlreadyLocked)
2037 Acquires the mutex for the GUI thread. The function uses the isGuiBlocked
2038 variable to keep track of how many recursion levels the gui is locket with.
2039 We only actually acquire the mutex for the first level to avoid deadlocking
2043 void QSGCanvasRenderThread::lockInGui()
2045 // We must avoid recursive locking in the GUI thread, hence we
2046 // only lock when we are the first one to try to block.
2053 printf("GUI: aquired lock... %d\n", isGuiBlocked);
2059 void QSGCanvasRenderThread::unlockInGui()
2062 printf("GUI: releasing lock... %d\n", isGuiBlocked);
2072 void QSGCanvasRenderThread::animationStarted()
2075 printf("GUI: animationStarted()\n");
2080 animationRunning = true;
2082 if (isRenderBlocked)
2090 void QSGCanvasRenderThread::animationStopped()
2093 printf("GUI: animationStopped()...\n");
2097 animationRunning = false;
2102 void QSGCanvasRenderThread::paint()
2105 printf("GUI: paint called..\n");
2111 isPaintCompleted = false;
2112 while (isRunning() && !isPaintCompleted) {
2113 if (isRenderBlocked)
2122 void QSGCanvasRenderThread::resize(const QSize &size)
2125 printf("GUI: Resize Event: %dx%d\n", size.width(), size.height());
2138 while (isRunning() && renderedSize != windowSize) {
2139 if (isRenderBlocked)
2148 void QSGCanvasRenderThread::startRendering()
2151 printf("GUI: Starting Render Thread\n");
2156 isGuiBlockPending = false;
2162 void QSGCanvasRenderThread::stopRendering()
2165 printf("GUI: stopping render thread\n");
2172 if (isRenderBlocked) {
2174 printf("GUI: waking up render thread\n");
2179 while (!hasExited) {
2181 printf("GUI: waiting for render thread to have exited..\n");
2189 printf("GUI: waiting for render thread to terminate..\n");
2191 // Actually wait for the thread to terminate. Otherwise we can delete it
2192 // too early and crash.
2196 printf("GUI: thread has terminated and we're all good..\n");
2203 QImage QSGCanvasRenderThread::grab()
2208 if (QThread::currentThread() != qApp->thread()) {
2209 qWarning("QSGCanvas::grabFrameBuffer: can only be called from the GUI thread");
2214 printf("GUI: doing a pixelwise grab..\n");
2221 isPaintCompleted = false;
2222 while (isRunning() && !isPaintCompleted) {
2223 if (isRenderBlocked)
2228 QImage grabbed = grabContent;
2229 grabContent = QImage();
2238 void QSGCanvasRenderThread::maybeUpdate()
2240 Q_ASSERT_X(QThread::currentThread() == QCoreApplication::instance()->thread() || inSync,
2241 "QSGCanvas::update",
2242 "Function can only be called from GUI thread or during QSGItem::updatePaintNode()");
2245 isExternalUpdatePending = true;
2247 } else if (!renderThreadAwakened) {
2249 printf("GUI: doing update...\n");
2251 renderThreadAwakened = true;
2253 isExternalUpdatePending = true;
2254 if (isRenderBlocked)
2261 #include "moc_qsgcanvas.cpp"