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);
462 q->setFormat(context->defaultSurfaceFormat());
465 void QSGCanvasPrivate::transformTouchPoints(QList<QTouchEvent::TouchPoint> &touchPoints, const QTransform &transform)
467 for (int i=0; i<touchPoints.count(); i++) {
468 QTouchEvent::TouchPoint &touchPoint = touchPoints[i];
469 touchPoint.setRect(transform.mapRect(touchPoint.sceneRect()));
470 touchPoint.setStartPos(transform.map(touchPoint.startScenePos()));
471 touchPoint.setLastPos(transform.map(touchPoint.lastScenePos()));
477 Translates the data in \a touchEvent to this canvas. This method leaves the item local positions in
478 \a touchEvent untouched (these are filled in later).
480 void QSGCanvasPrivate::translateTouchEvent(QTouchEvent *touchEvent)
484 // touchEvent->setWidget(q); // ### refactor...
486 QList<QTouchEvent::TouchPoint> touchPoints = touchEvent->touchPoints();
487 for (int i = 0; i < touchPoints.count(); ++i) {
488 QTouchEvent::TouchPoint &touchPoint = touchPoints[i];
490 touchPoint.setScreenRect(touchPoint.sceneRect());
491 touchPoint.setStartScreenPos(touchPoint.startScenePos());
492 touchPoint.setLastScreenPos(touchPoint.lastScenePos());
494 touchPoint.setSceneRect(touchPoint.rect());
495 touchPoint.setStartScenePos(touchPoint.startPos());
496 touchPoint.setLastScenePos(touchPoint.lastPos());
498 if (touchPoint.isPrimary())
499 lastMousePosition = touchPoint.pos().toPoint();
501 touchEvent->setTouchPoints(touchPoints);
504 void QSGCanvasPrivate::setFocusInScope(QSGItem *scope, QSGItem *item, FocusOptions options)
509 Q_ASSERT(scope || item == rootItem);
512 qWarning() << "QSGCanvasPrivate::setFocusInScope():";
513 qWarning() << " scope:" << (QObject *)scope;
515 qWarning() << " scopeSubFocusItem:" << (QObject *)QSGItemPrivate::get(scope)->subFocusItem;
516 qWarning() << " item:" << (QObject *)item;
517 qWarning() << " activeFocusItem:" << (QObject *)activeFocusItem;
520 QSGItemPrivate *scopePrivate = scope ? QSGItemPrivate::get(scope) : 0;
521 QSGItemPrivate *itemPrivate = QSGItemPrivate::get(item);
523 QSGItem *oldActiveFocusItem = 0;
524 QSGItem *newActiveFocusItem = 0;
526 QVarLengthArray<QSGItem *, 20> changed;
528 // Does this change the active focus?
529 if (item == rootItem || scopePrivate->activeFocus) {
530 oldActiveFocusItem = activeFocusItem;
531 newActiveFocusItem = item;
532 while (newActiveFocusItem->isFocusScope() && newActiveFocusItem->scopedFocusItem())
533 newActiveFocusItem = newActiveFocusItem->scopedFocusItem();
535 if (oldActiveFocusItem) {
537 qApp->inputPanel()->commit();
541 QFocusEvent event(QEvent::FocusOut, Qt::OtherFocusReason);
542 q->sendEvent(oldActiveFocusItem, &event);
544 QSGItem *afi = oldActiveFocusItem;
545 while (afi != scope) {
546 if (QSGItemPrivate::get(afi)->activeFocus) {
547 QSGItemPrivate::get(afi)->activeFocus = false;
550 afi = afi->parentItem();
555 if (item != rootItem) {
556 QSGItem *oldSubFocusItem = scopePrivate->subFocusItem;
557 // Correct focus chain in scope
558 if (oldSubFocusItem) {
559 QSGItem *sfi = scopePrivate->subFocusItem->parentItem();
560 while (sfi != scope) {
561 QSGItemPrivate::get(sfi)->subFocusItem = 0;
562 sfi = sfi->parentItem();
566 scopePrivate->subFocusItem = item;
567 QSGItem *sfi = scopePrivate->subFocusItem->parentItem();
568 while (sfi != scope) {
569 QSGItemPrivate::get(sfi)->subFocusItem = item;
570 sfi = sfi->parentItem();
574 if (oldSubFocusItem) {
575 QSGItemPrivate::get(oldSubFocusItem)->focus = false;
576 changed << oldSubFocusItem;
580 if (!(options & DontChangeFocusProperty)) {
581 // if (item != rootItem || q->hasFocus()) { // ### refactor: focus handling...
582 itemPrivate->focus = true;
587 if (newActiveFocusItem) { // ### refactor: && q->hasFocus()) {
588 activeFocusItem = newActiveFocusItem;
590 QSGItemPrivate::get(newActiveFocusItem)->activeFocus = true;
591 changed << newActiveFocusItem;
593 QSGItem *afi = newActiveFocusItem->parentItem();
594 while (afi && afi != scope) {
595 if (afi->isFocusScope()) {
596 QSGItemPrivate::get(afi)->activeFocus = true;
599 afi = afi->parentItem();
602 updateInputMethodData();
604 QFocusEvent event(QEvent::FocusIn, Qt::OtherFocusReason);
605 q->sendEvent(newActiveFocusItem, &event);
607 updateInputMethodData();
610 if (!changed.isEmpty())
611 notifyFocusChangesRecur(changed.data(), changed.count() - 1);
614 void QSGCanvasPrivate::clearFocusInScope(QSGItem *scope, QSGItem *item, FocusOptions options)
620 Q_ASSERT(scope || item == rootItem);
623 qWarning() << "QSGCanvasPrivate::clearFocusInScope():";
624 qWarning() << " scope:" << (QObject *)scope;
625 qWarning() << " item:" << (QObject *)item;
626 qWarning() << " activeFocusItem:" << (QObject *)activeFocusItem;
629 QSGItemPrivate *scopePrivate = scope ? QSGItemPrivate::get(scope) : 0;
631 QSGItem *oldActiveFocusItem = 0;
632 QSGItem *newActiveFocusItem = 0;
634 QVarLengthArray<QSGItem *, 20> changed;
636 Q_ASSERT(item == rootItem || item == scopePrivate->subFocusItem);
638 // Does this change the active focus?
639 if (item == rootItem || scopePrivate->activeFocus) {
640 oldActiveFocusItem = activeFocusItem;
641 newActiveFocusItem = scope;
643 Q_ASSERT(oldActiveFocusItem);
646 qApp->inputPanel()->commit();
650 QFocusEvent event(QEvent::FocusOut, Qt::OtherFocusReason);
651 q->sendEvent(oldActiveFocusItem, &event);
653 QSGItem *afi = oldActiveFocusItem;
654 while (afi != scope) {
655 if (QSGItemPrivate::get(afi)->activeFocus) {
656 QSGItemPrivate::get(afi)->activeFocus = false;
659 afi = afi->parentItem();
663 if (item != rootItem) {
664 QSGItem *oldSubFocusItem = scopePrivate->subFocusItem;
665 // Correct focus chain in scope
666 if (oldSubFocusItem) {
667 QSGItem *sfi = scopePrivate->subFocusItem->parentItem();
668 while (sfi != scope) {
669 QSGItemPrivate::get(sfi)->subFocusItem = 0;
670 sfi = sfi->parentItem();
673 scopePrivate->subFocusItem = 0;
675 if (oldSubFocusItem && !(options & DontChangeFocusProperty)) {
676 QSGItemPrivate::get(oldSubFocusItem)->focus = false;
677 changed << oldSubFocusItem;
679 } else if (!(options & DontChangeFocusProperty)) {
680 QSGItemPrivate::get(item)->focus = false;
684 if (newActiveFocusItem) {
685 Q_ASSERT(newActiveFocusItem == scope);
686 activeFocusItem = scope;
688 updateInputMethodData();
690 QFocusEvent event(QEvent::FocusIn, Qt::OtherFocusReason);
691 q->sendEvent(newActiveFocusItem, &event);
693 updateInputMethodData();
696 if (!changed.isEmpty())
697 notifyFocusChangesRecur(changed.data(), changed.count() - 1);
700 void QSGCanvasPrivate::notifyFocusChangesRecur(QSGItem **items, int remaining)
702 QDeclarativeGuard<QSGItem> item(*items);
705 notifyFocusChangesRecur(items + 1, remaining - 1);
708 QSGItemPrivate *itemPrivate = QSGItemPrivate::get(item);
710 if (itemPrivate->notifiedFocus != itemPrivate->focus) {
711 itemPrivate->notifiedFocus = itemPrivate->focus;
712 emit item->focusChanged(itemPrivate->focus);
715 if (item && itemPrivate->notifiedActiveFocus != itemPrivate->activeFocus) {
716 itemPrivate->notifiedActiveFocus = itemPrivate->activeFocus;
717 itemPrivate->itemChange(QSGItem::ItemActiveFocusHasChanged, itemPrivate->activeFocus);
718 emit item->activeFocusChanged(itemPrivate->activeFocus);
723 void QSGCanvasPrivate::updateInputMethodData()
725 qApp->inputPanel()->setInputItem(activeFocusItem);
728 QVariant QSGCanvas::inputMethodQuery(Qt::InputMethodQuery query) const
730 Q_D(const QSGCanvas);
731 if (!d->activeFocusItem || !(QSGItemPrivate::get(d->activeFocusItem)->flags & QSGItem::ItemAcceptsInputMethod))
733 QVariant value = d->activeFocusItem->inputMethodQuery(query);
736 QVariant::Type type = value.type();
737 if (type == QVariant::RectF || type == QVariant::Rect) {
738 const QTransform transform = QSGItemPrivate::get(d->activeFocusItem)->itemToCanvasTransform();
739 value = transform.mapRect(value.toRectF());
740 } else if (type == QVariant::PointF || type == QVariant::Point) {
741 const QTransform transform = QSGItemPrivate::get(d->activeFocusItem)->itemToCanvasTransform();
742 value = transform.map(value.toPointF());
747 void QSGCanvasPrivate::dirtyItem(QSGItem *)
753 void QSGCanvasPrivate::cleanup(QSGNode *n)
757 Q_ASSERT(!cleanupNodeList.contains(n));
758 cleanupNodeList.append(n);
763 QSGCanvas::QSGCanvas(QWindow *parent)
764 : QWindow(*(new QSGCanvasPrivate), parent)
770 QSGCanvas::QSGCanvas(QSGCanvasPrivate &dd, QWindow *parent)
771 : QWindow(dd, parent)
777 QSGCanvas::~QSGCanvas()
781 if (d->thread->isRunning()) {
782 d->thread->stopRendering();
787 // ### should we change ~QSGItem to handle this better?
788 // manually cleanup for the root item (item destructor only handles these when an item is parented)
789 QSGItemPrivate *rootItemPrivate = QSGItemPrivate::get(d->rootItem);
790 rootItemPrivate->removeFromDirtyList();
792 delete d->rootItem; d->rootItem = 0;
796 QSGItem *QSGCanvas::rootItem() const
798 Q_D(const QSGCanvas);
803 QSGItem *QSGCanvas::activeFocusItem() const
805 Q_D(const QSGCanvas);
807 return d->activeFocusItem;
810 QSGItem *QSGCanvas::mouseGrabberItem() const
812 Q_D(const QSGCanvas);
814 return d->mouseGrabberItem;
818 bool QSGCanvasPrivate::clearHover()
820 if (hoverItems.isEmpty())
823 QPointF pos = QCursor::pos(); // ### refactor: q->mapFromGlobal(QCursor::pos());
825 bool accepted = false;
826 foreach (QSGItem* item, hoverItems)
827 accepted = sendHoverEvent(QEvent::HoverLeave, item, pos, pos, QGuiApplication::keyboardModifiers(), true) || accepted;
833 bool QSGCanvas::event(QEvent *e)
839 case QEvent::TouchBegin:
840 case QEvent::TouchUpdate:
841 case QEvent::TouchEnd:
843 QTouchEvent *touch = static_cast<QTouchEvent *>(e);
844 d->translateTouchEvent(touch);
845 d->deliverTouchEvent(touch);
846 if (!touch->isAccepted())
852 d->lastMousePosition = QPoint();
854 case QSGEvent::SGDragEnter:
855 case QSGEvent::SGDragExit:
856 case QSGEvent::SGDragMove:
857 case QSGEvent::SGDragDrop:
858 d->deliverDragEvent(static_cast<QSGDragEvent *>(e));
860 case QEvent::WindowDeactivate:
861 rootItem()->windowDeactivateEvent();
867 return QWindow::event(e);
870 void QSGCanvas::keyPressEvent(QKeyEvent *e)
874 if (d->activeFocusItem)
875 sendEvent(d->activeFocusItem, e);
878 void QSGCanvas::keyReleaseEvent(QKeyEvent *e)
882 if (d->activeFocusItem)
883 sendEvent(d->activeFocusItem, e);
886 void QSGCanvas::inputMethodEvent(QInputMethodEvent *e)
890 if (d->activeFocusItem)
891 sendEvent(d->activeFocusItem, e);
894 bool QSGCanvasPrivate::deliverInitialMousePressEvent(QSGItem *item, QMouseEvent *event)
898 QSGItemPrivate *itemPrivate = QSGItemPrivate::get(item);
899 if (itemPrivate->opacity == 0.0)
902 if (itemPrivate->flags & QSGItem::ItemClipsChildrenToShape) {
903 QPointF p = item->mapFromScene(event->windowPos());
904 if (!QRectF(0, 0, item->width(), item->height()).contains(p))
908 QList<QSGItem *> children = itemPrivate->paintOrderChildItems();
909 for (int ii = children.count() - 1; ii >= 0; --ii) {
910 QSGItem *child = children.at(ii);
911 if (!child->isVisible() || !child->isEnabled())
913 if (deliverInitialMousePressEvent(child, event))
917 if (itemPrivate->acceptedMouseButtons & event->button()) {
918 QPointF p = item->mapFromScene(event->windowPos());
919 if (QRectF(0, 0, item->width(), item->height()).contains(p)) {
920 QMouseEvent me(event->type(), p, event->windowPos(), event->screenPos(),
921 event->button(), event->buttons(), event->modifiers());
923 mouseGrabberItem = item;
924 q->sendEvent(item, &me);
925 event->setAccepted(me.isAccepted());
928 mouseGrabberItem->ungrabMouse();
929 mouseGrabberItem = 0;
936 bool QSGCanvasPrivate::deliverMouseEvent(QMouseEvent *event)
940 lastMousePosition = event->windowPos();
942 if (!mouseGrabberItem &&
943 event->type() == QEvent::MouseButtonPress &&
944 (event->button() & event->buttons()) == event->buttons()) {
946 return deliverInitialMousePressEvent(rootItem, event);
949 if (mouseGrabberItem) {
950 QSGItemPrivate *mgPrivate = QSGItemPrivate::get(mouseGrabberItem);
951 const QTransform &transform = mgPrivate->canvasToItemTransform();
952 QMouseEvent me(event->type(), transform.map(event->windowPos()), event->windowPos(), event->screenPos(),
953 event->button(), event->buttons(), event->modifiers());
955 q->sendEvent(mouseGrabberItem, &me);
956 event->setAccepted(me.isAccepted());
964 void QSGCanvas::mousePressEvent(QMouseEvent *event)
969 qWarning() << "QSGCanvas::mousePressEvent()" << event->pos() << event->button() << event->buttons();
972 d->deliverMouseEvent(event);
975 void QSGCanvas::mouseReleaseEvent(QMouseEvent *event)
980 qWarning() << "QSGCanvas::mouseReleaseEvent()" << event->pos() << event->button() << event->buttons();
983 if (!d->mouseGrabberItem) {
984 QWindow::mouseReleaseEvent(event);
988 d->deliverMouseEvent(event);
989 d->mouseGrabberItem = 0;
992 void QSGCanvas::mouseDoubleClickEvent(QMouseEvent *event)
997 qWarning() << "QSGCanvas::mouseDoubleClickEvent()" << event->pos() << event->button() << event->buttons();
1000 if (!d->mouseGrabberItem && (event->button() & event->buttons()) == event->buttons()) {
1001 if (d->deliverInitialMousePressEvent(d->rootItem, event))
1008 d->deliverMouseEvent(event);
1011 bool QSGCanvasPrivate::sendHoverEvent(QEvent::Type type, QSGItem *item,
1012 const QPointF &scenePos, const QPointF &lastScenePos,
1013 Qt::KeyboardModifiers modifiers, bool accepted)
1016 const QTransform transform = QSGItemPrivate::get(item)->canvasToItemTransform();
1018 //create copy of event
1019 QHoverEvent hoverEvent(type, transform.map(scenePos), transform.map(lastScenePos), modifiers);
1020 hoverEvent.setAccepted(accepted);
1022 q->sendEvent(item, &hoverEvent);
1024 return hoverEvent.isAccepted();
1027 void QSGCanvas::mouseMoveEvent(QMouseEvent *event)
1032 qWarning() << "QSGCanvas::mouseMoveEvent()" << event->pos() << event->button() << event->buttons();
1035 if (!d->mouseGrabberItem) {
1036 if (d->lastMousePosition.isNull())
1037 d->lastMousePosition = event->windowPos();
1038 QPointF last = d->lastMousePosition;
1039 d->lastMousePosition = event->windowPos();
1041 bool accepted = event->isAccepted();
1042 bool delivered = d->deliverHoverEvent(d->rootItem, event->windowPos(), last, event->modifiers(), accepted);
1044 //take care of any exits
1045 accepted = d->clearHover();
1047 event->setAccepted(accepted);
1051 d->deliverMouseEvent(event);
1054 bool QSGCanvasPrivate::deliverHoverEvent(QSGItem *item, const QPointF &scenePos, const QPointF &lastScenePos,
1055 Qt::KeyboardModifiers modifiers, bool &accepted)
1057 QSGItemPrivate *itemPrivate = QSGItemPrivate::get(item);
1058 if (itemPrivate->opacity == 0.0)
1061 if (itemPrivate->flags & QSGItem::ItemClipsChildrenToShape) {
1062 QPointF p = item->mapFromScene(scenePos);
1063 if (!QRectF(0, 0, item->width(), item->height()).contains(p))
1067 QList<QSGItem *> children = itemPrivate->paintOrderChildItems();
1068 for (int ii = children.count() - 1; ii >= 0; --ii) {
1069 QSGItem *child = children.at(ii);
1070 if (!child->isVisible() || !child->isEnabled())
1072 if (deliverHoverEvent(child, scenePos, lastScenePos, modifiers, accepted))
1076 if (itemPrivate->hoverEnabled) {
1077 QPointF p = item->mapFromScene(scenePos);
1078 if (QRectF(0, 0, item->width(), item->height()).contains(p)) {
1079 if (!hoverItems.isEmpty() && hoverItems[0] == item) {
1081 accepted = sendHoverEvent(QEvent::HoverMove, item, scenePos, lastScenePos, modifiers, accepted);
1083 QList<QSGItem *> itemsToHover;
1084 QSGItem* parent = item;
1085 itemsToHover << item;
1086 while ((parent = parent->parentItem()))
1087 itemsToHover << parent;
1089 // Leaving from previous hovered items until we reach the item or one of its ancestors.
1090 while (!hoverItems.isEmpty() && !itemsToHover.contains(hoverItems[0])) {
1091 sendHoverEvent(QEvent::HoverLeave, hoverItems[0], scenePos, lastScenePos, modifiers, accepted);
1092 hoverItems.removeFirst();
1095 if (!hoverItems.isEmpty() && hoverItems[0] == item){//Not entering a new Item
1096 // ### Shouldn't we send moves for the parent items as well?
1097 accepted = sendHoverEvent(QEvent::HoverMove, item, scenePos, lastScenePos, modifiers, accepted);
1099 // Enter items that are not entered yet.
1101 if (!hoverItems.isEmpty())
1102 startIdx = itemsToHover.indexOf(hoverItems[0]) - 1;
1104 startIdx = itemsToHover.count() - 1;
1106 for (int i = startIdx; i >= 0; i--) {
1107 QSGItem *itemToHover = itemsToHover[i];
1108 if (QSGItemPrivate::get(itemToHover)->hoverEnabled) {
1109 hoverItems.prepend(itemToHover);
1110 sendHoverEvent(QEvent::HoverEnter, itemToHover, scenePos, lastScenePos, modifiers, accepted);
1122 bool QSGCanvasPrivate::deliverWheelEvent(QSGItem *item, QWheelEvent *event)
1125 QSGItemPrivate *itemPrivate = QSGItemPrivate::get(item);
1126 if (itemPrivate->opacity == 0.0)
1129 if (itemPrivate->flags & QSGItem::ItemClipsChildrenToShape) {
1130 QPointF p = item->mapFromScene(event->posF());
1131 if (!QRectF(0, 0, item->width(), item->height()).contains(p))
1135 QList<QSGItem *> children = itemPrivate->paintOrderChildItems();
1136 for (int ii = children.count() - 1; ii >= 0; --ii) {
1137 QSGItem *child = children.at(ii);
1138 if (!child->isVisible() || !child->isEnabled())
1140 if (deliverWheelEvent(child, event))
1144 QPointF p = item->mapFromScene(event->posF());
1145 if (QRectF(0, 0, item->width(), item->height()).contains(p)) {
1146 QWheelEvent wheel(p, event->delta(), event->buttons(), event->modifiers(), event->orientation());
1148 q->sendEvent(item, &wheel);
1149 if (wheel.isAccepted()) {
1158 #ifndef QT_NO_WHEELEVENT
1159 void QSGCanvas::wheelEvent(QWheelEvent *event)
1163 qWarning() << "QSGCanvas::wheelEvent()" << event->pos() << event->delta() << event->orientation();
1166 d->deliverWheelEvent(d->rootItem, event);
1168 #endif // QT_NO_WHEELEVENT
1170 bool QSGCanvasPrivate::deliverTouchEvent(QTouchEvent *event)
1173 if (event->type() == QEvent::TouchBegin)
1174 qWarning("touchBeginEvent");
1175 else if (event->type() == QEvent::TouchUpdate)
1176 qWarning("touchUpdateEvent");
1177 else if (event->type() == QEvent::TouchEnd)
1178 qWarning("touchEndEvent");
1181 QHash<QSGItem *, QList<QTouchEvent::TouchPoint> > updatedPoints;
1183 if (event->type() == QTouchEvent::TouchBegin) { // all points are new touch points
1184 QSet<int> acceptedNewPoints;
1185 deliverTouchPoints(rootItem, event, event->touchPoints(), &acceptedNewPoints, &updatedPoints);
1186 if (acceptedNewPoints.count() > 0)
1188 return event->isAccepted();
1191 const QList<QTouchEvent::TouchPoint> &touchPoints = event->touchPoints();
1192 QList<QTouchEvent::TouchPoint> newPoints;
1194 for (int i=0; i<touchPoints.count(); i++) {
1195 const QTouchEvent::TouchPoint &touchPoint = touchPoints[i];
1196 switch (touchPoint.state()) {
1197 case Qt::TouchPointPressed:
1198 newPoints << touchPoint;
1200 case Qt::TouchPointMoved:
1201 case Qt::TouchPointStationary:
1202 case Qt::TouchPointReleased:
1203 if (itemForTouchPointId.contains(touchPoint.id())) {
1204 item = itemForTouchPointId[touchPoint.id()];
1206 updatedPoints[item].append(touchPoint);
1214 if (newPoints.count() > 0 || updatedPoints.count() > 0) {
1215 QSet<int> acceptedNewPoints;
1216 int prevCount = updatedPoints.count();
1217 deliverTouchPoints(rootItem, event, newPoints, &acceptedNewPoints, &updatedPoints);
1218 if (acceptedNewPoints.count() > 0 || updatedPoints.count() != prevCount)
1222 if (event->touchPointStates() & Qt::TouchPointReleased) {
1223 for (int i=0; i<touchPoints.count(); i++) {
1224 if (touchPoints[i].state() == Qt::TouchPointReleased)
1225 itemForTouchPointId.remove(touchPoints[i].id());
1229 return event->isAccepted();
1232 bool QSGCanvasPrivate::deliverTouchPoints(QSGItem *item, QTouchEvent *event, const QList<QTouchEvent::TouchPoint> &newPoints, QSet<int> *acceptedNewPoints, QHash<QSGItem *, QList<QTouchEvent::TouchPoint> > *updatedPoints)
1235 QSGItemPrivate *itemPrivate = QSGItemPrivate::get(item);
1237 if (itemPrivate->opacity == 0.0)
1240 if (itemPrivate->flags & QSGItem::ItemClipsChildrenToShape) {
1241 QRectF bounds(0, 0, item->width(), item->height());
1242 for (int i=0; i<newPoints.count(); i++) {
1243 QPointF p = item->mapFromScene(newPoints[i].scenePos());
1244 if (!bounds.contains(p))
1249 QList<QSGItem *> children = itemPrivate->paintOrderChildItems();
1250 for (int ii = children.count() - 1; ii >= 0; --ii) {
1251 QSGItem *child = children.at(ii);
1252 if (!child->isEnabled())
1254 if (deliverTouchPoints(child, event, newPoints, acceptedNewPoints, updatedPoints))
1258 QList<QTouchEvent::TouchPoint> matchingPoints;
1259 if (newPoints.count() > 0 && acceptedNewPoints->count() < newPoints.count()) {
1260 QRectF bounds(0, 0, item->width(), item->height());
1261 for (int i=0; i<newPoints.count(); i++) {
1262 if (acceptedNewPoints->contains(newPoints[i].id()))
1264 QPointF p = item->mapFromScene(newPoints[i].scenePos());
1265 if (bounds.contains(p))
1266 matchingPoints << newPoints[i];
1270 if (matchingPoints.count() > 0 || (*updatedPoints)[item].count() > 0) {
1271 QList<QTouchEvent::TouchPoint> &eventPoints = (*updatedPoints)[item];
1272 eventPoints.append(matchingPoints);
1273 transformTouchPoints(eventPoints, itemPrivate->canvasToItemTransform());
1275 Qt::TouchPointStates eventStates;
1276 for (int i=0; i<eventPoints.count(); i++)
1277 eventStates |= eventPoints[i].state();
1278 // if all points have the same state, set the event type accordingly
1279 QEvent::Type eventType;
1280 switch (eventStates) {
1281 case Qt::TouchPointPressed:
1282 eventType = QEvent::TouchBegin;
1284 case Qt::TouchPointReleased:
1285 eventType = QEvent::TouchEnd;
1288 eventType = QEvent::TouchUpdate;
1292 if (eventStates != Qt::TouchPointStationary) {
1293 QTouchEvent touchEvent(eventType);
1294 // touchEvent.setWidget(q); // ### refactor: what is the consequence of not setting the widget?
1295 touchEvent.setDeviceType(event->deviceType());
1296 touchEvent.setModifiers(event->modifiers());
1297 touchEvent.setTouchPointStates(eventStates);
1298 touchEvent.setTouchPoints(eventPoints);
1300 touchEvent.accept();
1301 q->sendEvent(item, &touchEvent);
1303 if (touchEvent.isAccepted()) {
1304 for (int i=0; i<matchingPoints.count(); i++) {
1305 itemForTouchPointId[matchingPoints[i].id()] = item;
1306 acceptedNewPoints->insert(matchingPoints[i].id());
1312 updatedPoints->remove(item);
1313 if (acceptedNewPoints->count() == newPoints.count() && updatedPoints->isEmpty())
1319 void QSGCanvasPrivate::deliverDragEvent(QSGDragEvent *event)
1322 if (event->type() == QSGEvent::SGDragExit || event->type() == QSGEvent::SGDragDrop) {
1323 if (QSGItem *grabItem = event->grabItem()) {
1324 event->setPosition(grabItem->mapFromScene(event->scenePosition()));
1325 q->sendEvent(grabItem, event);
1327 } else if (!deliverDragEvent(rootItem, event)) {
1328 if (QSGItem *grabItem = event->grabItem()) {
1329 QSGDragEvent exitEvent(QSGEvent::SGDragExit, *event);
1330 exitEvent.setPosition(grabItem->mapFromScene(event->scenePosition()));
1331 q->sendEvent(grabItem, &exitEvent);
1332 event->setDropItem(0);
1333 event->setGrabItem(0);
1335 event->setAccepted(false);
1339 bool QSGCanvasPrivate::deliverDragEvent(QSGItem *item, QSGDragEvent *event)
1342 QSGItemPrivate *itemPrivate = QSGItemPrivate::get(item);
1343 if (itemPrivate->opacity == 0.0)
1346 if (itemPrivate->flags & QSGItem::ItemClipsChildrenToShape) {
1347 QPointF p = item->mapFromScene(event->scenePosition());
1348 if (!QRectF(0, 0, item->width(), item->height()).contains(p))
1352 QList<QSGItem *> children = itemPrivate->paintOrderChildItems();
1353 for (int ii = children.count() - 1; ii >= 0; --ii) {
1354 QSGItem *child = children.at(ii);
1355 if (!child->isVisible() || !child->isEnabled())
1357 if (deliverDragEvent(child, event))
1361 QPointF p = item->mapFromScene(event->scenePosition());
1362 if (QRectF(0, 0, item->width(), item->height()).contains(p)) {
1363 event->setPosition(p);
1365 if (event->type() == QSGEvent::SGDragMove && item != event->grabItem()) {
1366 QSGDragEvent enterEvent(QSGEvent::SGDragEnter, *event);
1367 q->sendEvent(item, &enterEvent);
1368 if (enterEvent.isAccepted()) {
1369 if (QSGItem *grabItem = event->grabItem()) {
1370 QSGDragEvent exitEvent(QSGEvent::SGDragExit, *event);
1371 q->sendEvent(grabItem, &exitEvent);
1373 event->setDropItem(enterEvent.dropItem());
1374 event->setGrabItem(item);
1380 q->sendEvent(item, event);
1381 if (event->isAccepted()) {
1382 event->setGrabItem(item);
1385 event->setAccepted(true);
1391 bool QSGCanvasPrivate::sendFilteredMouseEvent(QSGItem *target, QSGItem *item, QMouseEvent *event)
1396 if (sendFilteredMouseEvent(target->parentItem(), item, event))
1399 QSGItemPrivate *targetPrivate = QSGItemPrivate::get(target);
1400 if (targetPrivate->filtersChildMouseEvents)
1401 if (target->childMouseEventFilter(item, event))
1407 bool QSGCanvas::sendEvent(QSGItem *item, QEvent *e)
1412 qWarning("QSGCanvas::sendEvent: Cannot send event to a null item");
1418 switch (e->type()) {
1419 case QEvent::KeyPress:
1420 case QEvent::KeyRelease:
1422 QSGItemPrivate::get(item)->deliverKeyEvent(static_cast<QKeyEvent *>(e));
1423 while (!e->isAccepted() && (item = item->parentItem())) {
1425 QSGItemPrivate::get(item)->deliverKeyEvent(static_cast<QKeyEvent *>(e));
1428 case QEvent::InputMethod:
1430 QSGItemPrivate::get(item)->deliverInputMethodEvent(static_cast<QInputMethodEvent *>(e));
1431 while (!e->isAccepted() && (item = item->parentItem())) {
1433 QSGItemPrivate::get(item)->deliverInputMethodEvent(static_cast<QInputMethodEvent *>(e));
1436 case QEvent::FocusIn:
1437 case QEvent::FocusOut:
1438 QSGItemPrivate::get(item)->deliverFocusEvent(static_cast<QFocusEvent *>(e));
1440 case QEvent::MouseButtonPress:
1441 case QEvent::MouseButtonRelease:
1442 case QEvent::MouseButtonDblClick:
1443 case QEvent::MouseMove:
1444 // XXX todo - should sendEvent be doing this? how does it relate to forwarded events?
1446 QMouseEvent *se = static_cast<QMouseEvent *>(e);
1447 if (!d->sendFilteredMouseEvent(item->parentItem(), item, se)) {
1449 QSGItemPrivate::get(item)->deliverMouseEvent(se);
1454 QSGItemPrivate::get(item)->deliverWheelEvent(static_cast<QWheelEvent *>(e));
1456 case QEvent::HoverEnter:
1457 case QEvent::HoverLeave:
1458 case QEvent::HoverMove:
1459 QSGItemPrivate::get(item)->deliverHoverEvent(static_cast<QHoverEvent *>(e));
1461 case QEvent::TouchBegin:
1462 case QEvent::TouchUpdate:
1463 case QEvent::TouchEnd:
1464 QSGItemPrivate::get(item)->deliverTouchEvent(static_cast<QTouchEvent *>(e));
1466 case QSGEvent::SGDragEnter:
1467 case QSGEvent::SGDragExit:
1468 case QSGEvent::SGDragMove:
1469 case QSGEvent::SGDragDrop:
1470 QSGItemPrivate::get(item)->deliverDragEvent(static_cast<QSGDragEvent *>(e));
1479 void QSGCanvasPrivate::cleanupNodes()
1481 for (int ii = 0; ii < cleanupNodeList.count(); ++ii)
1482 delete cleanupNodeList.at(ii);
1483 cleanupNodeList.clear();
1486 void QSGCanvasPrivate::updateDirtyNodes()
1489 qWarning() << "QSGCanvasPrivate::updateDirtyNodes():";
1494 QSGItem *updateList = dirtyItemList;
1496 if (updateList) QSGItemPrivate::get(updateList)->prevDirtyItem = &updateList;
1498 while (updateList) {
1499 QSGItem *item = updateList;
1500 QSGItemPrivate *itemPriv = QSGItemPrivate::get(item);
1501 itemPriv->removeFromDirtyList();
1504 qWarning() << " QSGNode:" << item << qPrintable(itemPriv->dirtyToString());
1506 updateDirtyNode(item);
1510 void QSGCanvasPrivate::updateDirtyNode(QSGItem *item)
1512 #ifdef QML_RUNTIME_TESTING
1513 bool didFlash = false;
1516 QSGItemPrivate *itemPriv = QSGItemPrivate::get(item);
1517 quint32 dirty = itemPriv->dirtyAttributes;
1518 itemPriv->dirtyAttributes = 0;
1520 if ((dirty & QSGItemPrivate::TransformUpdateMask) ||
1521 (dirty & QSGItemPrivate::Size && itemPriv->origin != QSGItem::TopLeft &&
1522 (itemPriv->scale != 1. || itemPriv->rotation != 0.))) {
1526 if (itemPriv->x != 0. || itemPriv->y != 0.)
1527 matrix.translate(itemPriv->x, itemPriv->y);
1529 for (int ii = itemPriv->transforms.count() - 1; ii >= 0; --ii)
1530 itemPriv->transforms.at(ii)->applyTo(&matrix);
1532 if (itemPriv->scale != 1. || itemPriv->rotation != 0.) {
1533 QPointF origin = item->transformOriginPoint();
1534 matrix.translate(origin.x(), origin.y());
1535 if (itemPriv->scale != 1.)
1536 matrix.scale(itemPriv->scale, itemPriv->scale);
1537 if (itemPriv->rotation != 0.)
1538 matrix.rotate(itemPriv->rotation, 0, 0, 1);
1539 matrix.translate(-origin.x(), -origin.y());
1542 itemPriv->itemNode()->setMatrix(matrix);
1545 bool clipEffectivelyChanged = dirty & QSGItemPrivate::Clip &&
1546 ((item->clip() == false) != (itemPriv->clipNode == 0));
1547 bool effectRefEffectivelyChanged = dirty & QSGItemPrivate::EffectReference &&
1548 ((itemPriv->effectRefCount == 0) != (itemPriv->rootNode == 0));
1550 if (clipEffectivelyChanged) {
1551 QSGNode *parent = itemPriv->opacityNode ? (QSGNode *) itemPriv->opacityNode : (QSGNode *)itemPriv->itemNode();
1552 QSGNode *child = itemPriv->rootNode ? (QSGNode *)itemPriv->rootNode : (QSGNode *)itemPriv->groupNode;
1555 Q_ASSERT(itemPriv->clipNode == 0);
1556 itemPriv->clipNode = new QSGDefaultClipNode(item->boundingRect());
1557 itemPriv->clipNode->update();
1560 parent->removeChildNode(child);
1561 parent->appendChildNode(itemPriv->clipNode);
1563 itemPriv->clipNode->appendChildNode(child);
1566 Q_ASSERT(itemPriv->clipNode != 0);
1567 parent->removeChildNode(itemPriv->clipNode);
1569 itemPriv->clipNode->removeChildNode(child);
1570 delete itemPriv->clipNode;
1571 itemPriv->clipNode = 0;
1573 parent->appendChildNode(child);
1577 if (dirty & QSGItemPrivate::ChildrenUpdateMask)
1578 itemPriv->childContainerNode()->removeAllChildNodes();
1580 if (effectRefEffectivelyChanged) {
1581 QSGNode *parent = itemPriv->clipNode;
1583 parent = itemPriv->opacityNode;
1585 parent = itemPriv->itemNode();
1586 QSGNode *child = itemPriv->groupNode;
1588 if (itemPriv->effectRefCount) {
1589 Q_ASSERT(itemPriv->rootNode == 0);
1590 itemPriv->rootNode = new QSGRootNode;
1593 parent->removeChildNode(child);
1594 parent->appendChildNode(itemPriv->rootNode);
1596 itemPriv->rootNode->appendChildNode(child);
1598 Q_ASSERT(itemPriv->rootNode != 0);
1599 parent->removeChildNode(itemPriv->rootNode);
1601 itemPriv->rootNode->removeChildNode(child);
1602 delete itemPriv->rootNode;
1603 itemPriv->rootNode = 0;
1605 parent->appendChildNode(child);
1609 if (dirty & QSGItemPrivate::ChildrenUpdateMask) {
1610 QSGNode *groupNode = itemPriv->groupNode;
1612 groupNode->removeAllChildNodes();
1614 QList<QSGItem *> orderedChildren = itemPriv->paintOrderChildItems();
1617 for (; ii < orderedChildren.count() && orderedChildren.at(ii)->z() < 0; ++ii) {
1618 QSGItemPrivate *childPrivate = QSGItemPrivate::get(orderedChildren.at(ii));
1619 if (!childPrivate->explicitVisible && !childPrivate->effectRefCount)
1621 if (childPrivate->itemNode()->parent())
1622 childPrivate->itemNode()->parent()->removeChildNode(childPrivate->itemNode());
1624 itemPriv->childContainerNode()->appendChildNode(childPrivate->itemNode());
1626 itemPriv->beforePaintNode = itemPriv->groupNode ? itemPriv->groupNode->lastChild() : 0;
1628 if (itemPriv->paintNode)
1629 itemPriv->childContainerNode()->appendChildNode(itemPriv->paintNode);
1631 for (; ii < orderedChildren.count(); ++ii) {
1632 QSGItemPrivate *childPrivate = QSGItemPrivate::get(orderedChildren.at(ii));
1633 if (!childPrivate->explicitVisible && !childPrivate->effectRefCount)
1635 if (childPrivate->itemNode()->parent())
1636 childPrivate->itemNode()->parent()->removeChildNode(childPrivate->itemNode());
1638 itemPriv->childContainerNode()->appendChildNode(childPrivate->itemNode());
1642 if ((dirty & QSGItemPrivate::Size) && itemPriv->clipNode) {
1643 itemPriv->clipNode->setRect(item->boundingRect());
1644 itemPriv->clipNode->update();
1647 if (dirty & (QSGItemPrivate::OpacityValue | QSGItemPrivate::Visible | QSGItemPrivate::HideReference)) {
1648 qreal opacity = itemPriv->explicitVisible && itemPriv->hideRefCount == 0
1649 ? itemPriv->opacity : qreal(0);
1651 if (opacity != 1 && !itemPriv->opacityNode) {
1652 itemPriv->opacityNode = new QSGOpacityNode;
1654 QSGNode *parent = itemPriv->itemNode();
1655 QSGNode *child = itemPriv->clipNode;
1657 child = itemPriv->rootNode;
1659 child = itemPriv->groupNode;
1662 parent->removeChildNode(child);
1663 parent->appendChildNode(itemPriv->opacityNode);
1665 itemPriv->opacityNode->appendChildNode(child);
1667 if (itemPriv->opacityNode)
1668 itemPriv->opacityNode->setOpacity(opacity);
1671 if (dirty & QSGItemPrivate::ContentUpdateMask) {
1673 if (itemPriv->flags & QSGItem::ItemHasContents) {
1674 updatePaintNodeData.transformNode = itemPriv->itemNode();
1675 itemPriv->paintNode = item->updatePaintNode(itemPriv->paintNode, &updatePaintNodeData);
1677 Q_ASSERT(itemPriv->paintNode == 0 ||
1678 itemPriv->paintNode->parent() == 0 ||
1679 itemPriv->paintNode->parent() == itemPriv->childContainerNode());
1681 if (itemPriv->paintNode && itemPriv->paintNode->parent() == 0) {
1682 if (itemPriv->beforePaintNode)
1683 itemPriv->childContainerNode()->insertChildNodeAfter(itemPriv->paintNode, itemPriv->beforePaintNode);
1685 itemPriv->childContainerNode()->prependChildNode(itemPriv->paintNode);
1687 } else if (itemPriv->paintNode) {
1688 delete itemPriv->paintNode;
1689 itemPriv->paintNode = 0;
1694 // Check consistency.
1695 const QSGNode *nodeChain[] = {
1696 itemPriv->itemNodeInstance,
1697 itemPriv->opacityNode,
1700 itemPriv->groupNode,
1701 itemPriv->paintNode,
1706 while (ip < 5 && nodeChain[ip] == 0)
1711 while (ic < 5 && nodeChain[ic] == 0)
1713 const QSGNode *parent = nodeChain[ip];
1714 const QSGNode *child = nodeChain[ic];
1716 Q_ASSERT(parent == itemPriv->groupNode || parent->childCount() == 0);
1718 Q_ASSERT(parent == itemPriv->groupNode || parent->childCount() == 1);
1719 Q_ASSERT(child->parent() == parent);
1720 bool containsChild = false;
1721 for (QSGNode *n = parent->firstChild(); n; n = n->nextSibling())
1722 containsChild |= (n == child);
1723 Q_ASSERT(containsChild);
1729 #ifdef QML_RUNTIME_TESTING
1730 if (itemPriv->sceneGraphContext()->isFlashModeEnabled()) {
1731 QSGFlashNode *flash = new QSGFlashNode();
1732 flash->setRect(item->boundingRect());
1733 itemPriv->childContainerNode()->appendChildNode(flash);
1744 void QSGCanvas::maybeUpdate()
1748 if (d->thread && d->thread->isRunning())
1749 d->thread->maybeUpdate();
1753 \fn void QSGEngine::sceneGraphInitialized();
1755 This signal is emitted when the scene graph has been initialized.
1757 This signal will be emitted from the scene graph rendering thread.
1761 Returns the QSGEngine used for this scene.
1763 The engine will only be available once the scene graph has been
1764 initialized. Register for the sceneGraphEngine() signal to get
1765 notification about this.
1768 QSGEngine *QSGCanvas::sceneGraphEngine() const
1770 Q_D(const QSGCanvas);
1771 if (d->context && d->context->isReady())
1772 return d->context->engine();
1779 Sets the render target for this canvas to be \a fbo.
1781 The specified fbo must be created in the context of the canvas
1782 or one that shares with it.
1785 This function can only be called from the thread doing
1789 void QSGCanvas::setRenderTarget(QOpenGLFramebufferObject *fbo)
1792 if (d->context && d->context && QThread::currentThread() != d->context->thread()) {
1793 qWarning("QSGCanvas::setRenderThread: Cannot set render target from outside the rendering thread");
1797 d->renderTarget = fbo;
1803 Returns the render target for this canvas.
1805 The default is to render to the surface of the canvas, in which
1806 case the render target is 0.
1808 QOpenGLFramebufferObject *QSGCanvas::renderTarget() const
1810 Q_D(const QSGCanvas);
1811 return d->renderTarget;
1816 Grabs the contents of the framebuffer and returns it as an image.
1818 This function might not work if the view is not visible.
1820 \warning Calling this function will cause performance problems.
1822 \warning This function can only be called from the GUI thread.
1824 QImage QSGCanvas::grabFrameBuffer()
1827 return d->thread ? d->thread->grab() : QImage();
1832 void QSGCanvasRenderLoop::createGLContext()
1834 gl = new QOpenGLContext();
1835 gl->setFormat(d->context->defaultSurfaceFormat());
1840 void QSGCanvasRenderThread::run()
1843 qDebug("QML Rendering Thread Started");
1849 initializeSceneGraph();
1854 while (!shouldExit) {
1857 bool sizeChanged = false;
1858 isExternalUpdatePending = false;
1860 if (renderedSize != windowSize) {
1862 printf(" RenderThread: window has changed size...\n");
1864 glViewport(0, 0, windowSize.width(), windowSize.height());
1869 printf(" RenderThread: preparing to sync...\n");
1872 if (!isGuiBlocked) {
1873 isGuiBlockPending = true;
1876 printf(" RenderThread: aquired sync lock...\n");
1878 QCoreApplication::postEvent(this, new QEvent(QEvent::User));
1880 printf(" RenderThread: going to sleep...\n");
1884 isGuiBlockPending = false;
1888 printf(" RenderThread: Doing locked sync\n");
1894 // Wake GUI after sync to let it continue animating and event processing.
1898 printf(" RenderThread: sync done\n");
1904 printf(" RenderThread: rendering... %d x %d\n", windowSize.width(), windowSize.height());
1907 renderSceneGraph(windowSize);
1909 // The content of the target buffer is undefined after swap() so grab needs
1910 // to happen before swap();
1913 printf(" RenderThread: doing a grab...\n");
1915 grabContent = qt_gl_read_framebuffer(windowSize, false, false);
1920 printf(" RenderThread: wait for swap...\n");
1925 printf(" RenderThread: swap complete...\n");
1929 isPaintCompleted = true;
1931 renderedSize = windowSize;
1933 // Wake the GUI thread now that rendering is complete, to signal that painting
1934 // is done, resizing is done or grabbing is completed. For grabbing, we're
1935 // signalling this much later than needed (we could have done it before swap)
1936 // but we don't want to lock an extra time.
1939 if (!animationRunning && !isExternalUpdatePending && !shouldExit && !doGrab) {
1941 printf(" RenderThread: nothing to do, going to sleep...\n");
1943 isRenderBlocked = true;
1945 isRenderBlocked = false;
1950 // Process any "deleteLater" objects...
1951 QCoreApplication::processEvents();
1955 printf(" RenderThread: render loop exited... Good Night!\n");
1963 printf(" RenderThread: waking GUI for final sleep..\n");
1969 printf(" RenderThread: All done...\n");
1975 bool QSGCanvasRenderThread::event(QEvent *e)
1977 Q_ASSERT(QThread::currentThread() == qApp->thread());
1979 if (e->type() == QEvent::User) {
1980 if (!syncAlreadyHappened)
1983 syncAlreadyHappened = false;
1985 if (animationRunning && animationDriver()) {
1987 qDebug("GUI: Advancing animations...\n");
1990 animationDriver()->advance();
1993 qDebug("GUI: Animations advanced...\n");
2000 return QThread::event(e);
2005 void QSGCanvasRenderThread::exhaustSyncEvent()
2007 if (isGuiBlockPending) {
2009 syncAlreadyHappened = true;
2015 void QSGCanvasRenderThread::sync(bool guiAlreadyLocked)
2018 printf("GUI: sync - %s\n", guiAlreadyLocked ? "outside event" : "inside event");
2020 if (!guiAlreadyLocked)
2023 renderThreadAwakened = false;
2030 if (!guiAlreadyLocked)
2038 Acquires the mutex for the GUI thread. The function uses the isGuiBlocked
2039 variable to keep track of how many recursion levels the gui is locket with.
2040 We only actually acquire the mutex for the first level to avoid deadlocking
2044 void QSGCanvasRenderThread::lockInGui()
2046 // We must avoid recursive locking in the GUI thread, hence we
2047 // only lock when we are the first one to try to block.
2054 printf("GUI: aquired lock... %d\n", isGuiBlocked);
2060 void QSGCanvasRenderThread::unlockInGui()
2063 printf("GUI: releasing lock... %d\n", isGuiBlocked);
2073 void QSGCanvasRenderThread::animationStarted()
2076 printf("GUI: animationStarted()\n");
2081 animationRunning = true;
2083 if (isRenderBlocked)
2091 void QSGCanvasRenderThread::animationStopped()
2094 printf("GUI: animationStopped()...\n");
2098 animationRunning = false;
2103 void QSGCanvasRenderThread::paint()
2106 printf("GUI: paint called..\n");
2112 isPaintCompleted = false;
2113 while (isRunning() && !isPaintCompleted) {
2114 if (isRenderBlocked)
2123 void QSGCanvasRenderThread::resize(const QSize &size)
2126 printf("GUI: Resize Event: %dx%d\n", size.width(), size.height());
2139 while (isRunning() && renderedSize != windowSize) {
2140 if (isRenderBlocked)
2149 void QSGCanvasRenderThread::startRendering()
2152 printf("GUI: Starting Render Thread\n");
2157 isGuiBlockPending = false;
2163 void QSGCanvasRenderThread::stopRendering()
2166 printf("GUI: stopping render thread\n");
2173 if (isRenderBlocked) {
2175 printf("GUI: waking up render thread\n");
2180 while (!hasExited) {
2182 printf("GUI: waiting for render thread to have exited..\n");
2190 printf("GUI: waiting for render thread to terminate..\n");
2192 // Actually wait for the thread to terminate. Otherwise we can delete it
2193 // too early and crash.
2197 printf("GUI: thread has terminated and we're all good..\n");
2204 QImage QSGCanvasRenderThread::grab()
2209 if (QThread::currentThread() != qApp->thread()) {
2210 qWarning("QSGCanvas::grabFrameBuffer: can only be called from the GUI thread");
2215 printf("GUI: doing a pixelwise grab..\n");
2222 isPaintCompleted = false;
2223 while (isRunning() && !isPaintCompleted) {
2224 if (isRenderBlocked)
2229 QImage grabbed = grabContent;
2230 grabContent = QImage();
2239 void QSGCanvasRenderThread::maybeUpdate()
2241 Q_ASSERT_X(QThread::currentThread() == QCoreApplication::instance()->thread() || inSync,
2242 "QSGCanvas::update",
2243 "Function can only be called from GUI thread or during QSGItem::updatePaintNode()");
2246 isExternalUpdatePending = true;
2248 } else if (!renderThreadAwakened) {
2250 printf("GUI: doing update...\n");
2252 renderThreadAwakened = true;
2254 isExternalUpdatePending = true;
2255 if (isRenderBlocked)
2262 #include "moc_qsgcanvas.cpp"