diff options
Diffstat (limited to 'src/declarative/items/qsgcanvas.cpp')
-rw-r--r-- | src/declarative/items/qsgcanvas.cpp | 1934 |
1 files changed, 1934 insertions, 0 deletions
diff --git a/src/declarative/items/qsgcanvas.cpp b/src/declarative/items/qsgcanvas.cpp new file mode 100644 index 0000000000..16bd8ce44c --- /dev/null +++ b/src/declarative/items/qsgcanvas.cpp @@ -0,0 +1,1934 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtDeclarative module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qsgcanvas.h" +#include "qsgcanvas_p.h" + +#include "qsgitem.h" +#include "qsgitem_p.h" + +#include <private/qsgrenderer_p.h> +#include <private/qsgflashnode_p.h> + +#include <private/qabstractanimation_p.h> + +#include <QtGui/qpainter.h> +#include <QtGui/qgraphicssceneevent.h> +#include <QtGui/qmatrix4x4.h> +#include <QtGui/qinputcontext.h> +#include <QtCore/qvarlengtharray.h> +#include <QtCore/qabstractanimation.h> + +#include <private/qdeclarativedebugtrace_p.h> + +QT_BEGIN_NAMESPACE + +DEFINE_BOOL_CONFIG_OPTION(qmlNoThreadedRenderer, QML_NO_THREADED_RENDERER) +DEFINE_BOOL_CONFIG_OPTION(qmlFixedAnimationStep, QML_FIXED_ANIMATION_STEP) + +/* +Focus behavior +============== + +Prior to being added to a valid canvas items can set and clear focus with no +effect. Only once items are added to a canvas (by way of having a parent set that +already belongs to a canvas) do the focus rules apply. Focus goes back to +having no effect if an item is removed from a canvas. + +When an item is moved into a new focus scope (either being added to a canvas +for the first time, or having its parent changed), if the focus scope already has +a scope focused item that takes precedence over the item being added. Otherwise, +the focus of the added tree is used. In the case of of a tree of items being +added to a canvas for the first time, which may have a conflicted focus state (two +or more items in one scope having focus set), the same rule is applied item by item - +thus the first item that has focus will get it (assuming the scope doesn't already +have a scope focused item), and the other items will have their focus cleared. +*/ + +// #define FOCUS_DEBUG +// #define MOUSE_DEBUG +// #define TOUCH_DEBUG +// #define DIRTY_DEBUG +// #define THREAD_DEBUG + +// #define FRAME_TIMING + +#ifdef FRAME_TIMING +static QTime frameTimer; +int sceneGraphRenderTime; +int readbackTime; +#endif + +QSGItem::UpdatePaintNodeData::UpdatePaintNodeData() +: transformNode(0) +{ +} + +QSGRootItem::QSGRootItem() +{ +} + +void QSGCanvasPrivate::stopRenderingThread() +{ + if (thread->isRunning()) { + mutex.lock(); + exitThread = true; + wait.wakeOne(); + wait.wait(&mutex); + exitThread = false; + mutex.unlock(); + thread->wait(); + } +} + +void QSGCanvasPrivate::_q_animationStarted() +{ +#ifdef THREAD_DEBUG + qWarning("AnimationDriver: Main Thread: started"); +#endif + mutex.lock(); + animationRunning = true; + if (idle) + wait.wakeOne(); + mutex.unlock(); +} + +void QSGCanvasPrivate::_q_animationStopped() +{ +#ifdef THREAD_DEBUG + qWarning("AnimationDriver: Main Thread: stopped"); +#endif + mutex.lock(); + animationRunning = false; + mutex.unlock(); +} + +void QSGCanvas::paintEvent(QPaintEvent *) +{ + Q_D(QSGCanvas); + + if (!d->threadedRendering) { +#ifdef FRAME_TIMING + int lastFrame = frameTimer.restart(); +#endif + + if (d->animationDriver->isRunning()) + d->animationDriver->advance(); + +#ifdef FRAME_TIMING + int animationTime = frameTimer.elapsed(); +#endif + + Q_ASSERT(d->context); + + d->polishItems(); + + QDeclarativeDebugTrace::addEvent(QDeclarativeDebugTrace::FramePaint); + QDeclarativeDebugTrace::startRange(QDeclarativeDebugTrace::Painting); + +#ifdef FRAME_TIMING + int polishTime = frameTimer.elapsed(); +#endif + + makeCurrent(); + +#ifdef FRAME_TIMING + int makecurrentTime = frameTimer.elapsed(); +#endif + + d->syncSceneGraph(); + +#ifdef FRAME_TIMING + int syncTime = frameTimer.elapsed(); +#endif + + d->renderSceneGraph(); + +#ifdef FRAME_TIMING + printf("FrameTimes, last=%d, animations=%d, polish=%d, makeCurrent=%d, sync=%d, sgrender=%d, readback=%d, total=%d\n", + lastFrame, + animationTime, + polishTime - animationTime, + makecurrentTime - polishTime, + syncTime - makecurrentTime, + sceneGraphRenderTime - syncTime, + readbackTime - sceneGraphRenderTime, + frameTimer.elapsed()); +#endif + + QDeclarativeDebugTrace::endRange(QDeclarativeDebugTrace::Painting); + + if (d->animationDriver->isRunning()) + update(); + } +} + +void QSGCanvas::resizeEvent(QResizeEvent *e) +{ + Q_D(QSGCanvas); + if (d->threadedRendering) { + d->mutex.lock(); + QGLWidget::resizeEvent(e); + d->widgetSize = e->size(); + d->mutex.unlock(); + } else { + d->widgetSize = e->size(); + d->viewportSize = d->widgetSize; + QGLWidget::resizeEvent(e); + } +} + +void QSGCanvas::showEvent(QShowEvent *e) +{ + Q_D(QSGCanvas); + + QGLWidget::showEvent(e); + + if (d->threadedRendering) { + d->contextInThread = true; + doneCurrent(); + if (!d->animationDriver) { + d->animationDriver = d->context->createAnimationDriver(this); + connect(d->animationDriver, SIGNAL(started()), this, SLOT(_q_animationStarted()), Qt::DirectConnection); + connect(d->animationDriver, SIGNAL(stopped()), this, SLOT(_q_animationStopped()), Qt::DirectConnection); + } + d->animationDriver->install(); + d->mutex.lock(); + d->thread->start(); + d->wait.wait(&d->mutex); + d->mutex.unlock(); + } else { + makeCurrent(); + + if (!d->context || !d->context->isReady()) { + d->initializeSceneGraph(); + d->animationDriver = d->context->createAnimationDriver(this); + connect(d->animationDriver, SIGNAL(started()), this, SLOT(update())); + } + + d->animationDriver->install(); + } +} + +void QSGCanvas::hideEvent(QHideEvent *e) +{ + Q_D(QSGCanvas); + + if (d->threadedRendering) + d->stopRenderingThread(); + + d->animationDriver->uninstall(); + + QGLWidget::hideEvent(e); +} + +void QSGCanvas::focusOutEvent(QFocusEvent *event) +{ + Q_D(QSGCanvas); + d->rootItem->setFocus(false); + QGLWidget::focusOutEvent(event); +} + +void QSGCanvas::focusInEvent(QFocusEvent *event) +{ + Q_D(QSGCanvas); + d->rootItem->setFocus(true); + QGLWidget::focusInEvent(event); +} + +void QSGCanvasPrivate::initializeSceneGraph() +{ + if (!context) + context = QSGContext::createDefaultContext(); + + if (context->isReady()) + return; + + QGLContext *glctx = const_cast<QGLContext *>(QGLContext::currentContext()); + context->initialize(glctx); + + if (!threadedRendering) { + Q_Q(QSGCanvas); + QObject::connect(context->renderer(), SIGNAL(sceneGraphChanged()), q, SLOT(maybeUpdate()), + Qt::DirectConnection); + } + + if (!QSGItemPrivate::get(rootItem)->itemNode()->parent()) { + context->rootNode()->appendChildNode(QSGItemPrivate::get(rootItem)->itemNode()); + } + + emit q_func()->sceneGraphInitialized(); +} + +void QSGCanvasPrivate::polishItems() +{ + while (!itemsToPolish.isEmpty()) { + QSet<QSGItem *>::Iterator iter = itemsToPolish.begin(); + QSGItem *item = *iter; + itemsToPolish.erase(iter); + QSGItemPrivate::get(item)->polishScheduled = false; + item->updatePolish(); + } +} + + +void QSGCanvasPrivate::syncSceneGraph() +{ + inSync = true; + updateDirtyNodes(); + inSync = false; +} + + +void QSGCanvasPrivate::renderSceneGraph() +{ + QGLContext *glctx = const_cast<QGLContext *>(QGLContext::currentContext()); + + context->renderer()->setDeviceRect(QRect(QPoint(0, 0), viewportSize)); + context->renderer()->setViewportRect(QRect(QPoint(0, 0), viewportSize)); + context->renderer()->setProjectMatrixToDeviceRect(); + + context->renderNextFrame(); + +#ifdef FRAME_TIMING + sceneGraphRenderTime = frameTimer.elapsed(); +#endif + + +#ifdef FRAME_TIMING +// int pixel; +// glReadPixels(0, 0, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, &pixel); + readbackTime = frameTimer.elapsed(); +#endif + + glctx->swapBuffers(); +} + + +void QSGCanvas::sceneGraphChanged() +{ + Q_D(QSGCanvas); + d->needsRepaint = true; +} + + +void QSGCanvasPrivate::runThread() +{ +#ifdef THREAD_DEBUG + qWarning("QSGRenderer: Render thread running"); +#endif + Q_Q(QSGCanvas); + + printf("QSGCanvas::runThread(), rendering in a thread...\n"); + + q->makeCurrent(); + initializeSceneGraph(); + + QObject::connect(context->renderer(), SIGNAL(sceneGraphChanged()), + q, SLOT(sceneGraphChanged()), + Qt::DirectConnection); + + mutex.lock(); + wait.wakeOne(); // Wake the main thread waiting for us to start + + while (true) { + QSize s; + s = widgetSize; + + if (exitThread) + break; + + if (s != viewportSize) { + glViewport(0, 0, s.width(), s.height()); + viewportSize = s; + } + +#ifdef THREAD_DEBUG + qWarning("QSGRenderer: Render Thread: Waiting for main thread to stop"); +#endif + QCoreApplication::postEvent(q, new QEvent(QEvent::User)); + wait.wait(&mutex); + + if (exitThread) { +#ifdef THREAD_DEBUG + qWarning("QSGRenderer: Render Thread: Shutting down..."); +#endif + break; + } + +#ifdef THREAD_DEBUG + qWarning("QSGRenderer: Render Thread: Main thread has stopped, syncing scene"); +#endif + + // Do processing while main thread is frozen + syncSceneGraph(); + +#ifdef THREAD_DEBUG + qWarning("QSGRenderer: Render Thread: Resuming main thread"); +#endif + + // Read animationRunning while inside the locked section + bool continous = animationRunning; + + wait.wakeOne(); + mutex.unlock(); + + bool enterIdle = false; + if (needsRepaint) { +#ifdef THREAD_DEBUG + qWarning("QSGRenderer: Render Thread: rendering scene"); +#endif + renderSceneGraph(); + needsRepaint = false; + } else if (continous) { +#ifdef THREAD_DEBUG + qWarning("QSGRenderer: Render Thread: waiting a while..."); +#endif + MyThread::doWait(); + } else { + enterIdle = true; + } + + mutex.lock(); + + if (enterIdle) { +#ifdef THREAD_DEBUG + qWarning("QSGRenderer: Render Thread: Nothing has changed, going idle..."); +#endif + idle = true; + wait.wait(&mutex); + idle = false; +#ifdef THREAD_DEBUG + qWarning("QSGRenderer: Render Thread: waking up from idle"); +#endif + } + + } + + +#ifdef THREAD_DEBUG + qWarning("QSGRenderer: Render Thread: shutting down, waking up main thread"); +#endif + wait.wakeOne(); + mutex.unlock(); + + q->doneCurrent(); +} + +QSGCanvasPrivate::QSGCanvasPrivate() + : rootItem(0) + , activeFocusItem(0) + , mouseGrabberItem(0) + , hoverItem(0) + , dirtyItemList(0) + , context(0) + , contextInThread(false) + , threadedRendering(false) + , exitThread(false) + , animationRunning(false) + , idle(false) + , needsRepaint(true) + , renderThreadAwakened(false) + , inSync(false) + , thread(new MyThread(this)) + , animationDriver(0) +{ + threadedRendering = !qmlNoThreadedRenderer(); +} + +QSGCanvasPrivate::~QSGCanvasPrivate() +{ +} + +void QSGCanvasPrivate::init(QSGCanvas *c) +{ + QUnifiedTimer::instance(true)->setConsistentTiming(qmlFixedAnimationStep()); + + q_ptr = c; + + Q_Q(QSGCanvas); + + q->setAttribute(Qt::WA_AcceptTouchEvents); + q->setFocusPolicy(Qt::StrongFocus); + + rootItem = new QSGRootItem; + QSGItemPrivate *rootItemPrivate = QSGItemPrivate::get(rootItem); + rootItemPrivate->canvas = q; + rootItemPrivate->flags |= QSGItem::ItemIsFocusScope; + + context = QSGContext::createDefaultContext(); +} + +void QSGCanvasPrivate::sceneMouseEventForTransform(QGraphicsSceneMouseEvent &sceneEvent, + const QTransform &transform) +{ + sceneEvent.setPos(transform.map(sceneEvent.scenePos())); + sceneEvent.setLastPos(transform.map(sceneEvent.lastScenePos())); + for (int ii = 0; ii < 5; ++ii) { + if (sceneEvent.buttons() & (1 << ii)) { + sceneEvent.setButtonDownPos((Qt::MouseButton)(1 << ii), + transform.map(sceneEvent.buttonDownScenePos((Qt::MouseButton)(1 << ii)))); + } + } +} + +void QSGCanvasPrivate::transformTouchPoints(QList<QTouchEvent::TouchPoint> &touchPoints, const QTransform &transform) +{ + for (int i=0; i<touchPoints.count(); i++) { + QTouchEvent::TouchPoint &touchPoint = touchPoints[i]; + touchPoint.setRect(transform.mapRect(touchPoint.sceneRect())); + touchPoint.setStartPos(transform.map(touchPoint.startScenePos())); + touchPoint.setLastPos(transform.map(touchPoint.lastScenePos())); + } +} + +QEvent::Type QSGCanvasPrivate::sceneMouseEventTypeFromMouseEvent(QMouseEvent *event) +{ + switch(event->type()) { + default: + Q_ASSERT(!"Unknown event type"); + case QEvent::MouseButtonPress: + return QEvent::GraphicsSceneMousePress; + case QEvent::MouseButtonRelease: + return QEvent::GraphicsSceneMouseRelease; + case QEvent::MouseButtonDblClick: + return QEvent::GraphicsSceneMouseDoubleClick; + case QEvent::MouseMove: + return QEvent::GraphicsSceneMouseMove; + } +} + +/*! +Fill in the data in \a sceneEvent based on \a event. This method leaves the item local positions in +\a sceneEvent untouched. Use sceneMouseEventForTransform() to fill in those details. +*/ +void QSGCanvasPrivate::sceneMouseEventFromMouseEvent(QGraphicsSceneMouseEvent &sceneEvent, QMouseEvent *event) +{ + Q_Q(QSGCanvas); + + Q_ASSERT(event); + + if (event->type() == QEvent::MouseButtonPress || event->type() == QEvent::MouseButtonDblClick) { + if ((event->button() & event->buttons()) == event->buttons()) { + lastMousePosition = event->pos(); + } + + switch (event->button()) { + default: + Q_ASSERT(!"Unknown button"); + case Qt::LeftButton: + buttonDownPositions[0] = event->pos(); + break; + case Qt::RightButton: + buttonDownPositions[1] = event->pos(); + break; + case Qt::MiddleButton: + buttonDownPositions[2] = event->pos(); + break; + case Qt::XButton1: + buttonDownPositions[3] = event->pos(); + break; + case Qt::XButton2: + buttonDownPositions[4] = event->pos(); + break; + } + } + + sceneEvent.setScenePos(event->pos()); + sceneEvent.setScreenPos(event->globalPos()); + sceneEvent.setLastScenePos(lastMousePosition); + sceneEvent.setLastScreenPos(q->mapToGlobal(lastMousePosition)); + sceneEvent.setButtons(event->buttons()); + sceneEvent.setButton(event->button()); + sceneEvent.setModifiers(event->modifiers()); + sceneEvent.setWidget(q); + + for (int ii = 0; ii < 5; ++ii) { + if (sceneEvent.buttons() & (1 << ii)) { + sceneEvent.setButtonDownScenePos((Qt::MouseButton)(1 << ii), buttonDownPositions[ii]); + sceneEvent.setButtonDownScreenPos((Qt::MouseButton)(1 << ii), q->mapToGlobal(buttonDownPositions[ii])); + } + } + + lastMousePosition = event->pos(); +} + +/*! +Fill in the data in \a hoverEvent based on \a mouseEvent. This method leaves the item local positions in +\a hoverEvent untouched (these are filled in later). +*/ +void QSGCanvasPrivate::sceneHoverEventFromMouseEvent(QGraphicsSceneHoverEvent &hoverEvent, QMouseEvent *mouseEvent) +{ + Q_Q(QSGCanvas); + hoverEvent.setWidget(q); + hoverEvent.setScenePos(mouseEvent->pos()); + hoverEvent.setScreenPos(mouseEvent->globalPos()); + if (lastMousePosition.isNull()) lastMousePosition = mouseEvent->pos(); + hoverEvent.setLastScenePos(lastMousePosition); + hoverEvent.setLastScreenPos(q->mapToGlobal(lastMousePosition)); + hoverEvent.setModifiers(mouseEvent->modifiers()); + hoverEvent.setAccepted(mouseEvent->isAccepted()); + + lastMousePosition = mouseEvent->pos(); +} + +/*! +Translates the data in \a touchEvent to this canvas. This method leaves the item local positions in +\a touchEvent untouched (these are filled in later). +*/ +void QSGCanvasPrivate::translateTouchEvent(QTouchEvent *touchEvent) +{ + Q_Q(QSGCanvas); + + touchEvent->setWidget(q); + + QList<QTouchEvent::TouchPoint> touchPoints = touchEvent->touchPoints(); + for (int i = 0; i < touchPoints.count(); ++i) { + QTouchEvent::TouchPoint &touchPoint = touchPoints[i]; + + touchPoint.setScreenRect(touchPoint.sceneRect()); + touchPoint.setStartScreenPos(touchPoint.startScenePos()); + touchPoint.setLastScreenPos(touchPoint.lastScenePos()); + + touchPoint.setSceneRect(touchPoint.rect()); + touchPoint.setStartScenePos(touchPoint.startPos()); + touchPoint.setLastScenePos(touchPoint.lastPos()); + + if (touchPoint.isPrimary()) + lastMousePosition = touchPoint.pos().toPoint(); + } + touchEvent->setTouchPoints(touchPoints); +} + +void QSGCanvasPrivate::setFocusInScope(QSGItem *scope, QSGItem *item, FocusOptions options) +{ + Q_Q(QSGCanvas); + + Q_ASSERT(item); + Q_ASSERT(scope || item == rootItem); + +#ifdef FOCUS_DEBUG + qWarning() << "QSGCanvasPrivate::setFocusInScope():"; + qWarning() << " scope:" << (QObject *)scope; + if (scope) + qWarning() << " scopeSubFocusItem:" << (QObject *)QSGItemPrivate::get(scope)->subFocusItem; + qWarning() << " item:" << (QObject *)item; + qWarning() << " activeFocusItem:" << (QObject *)activeFocusItem; +#endif + + QSGItemPrivate *scopePrivate = scope ? QSGItemPrivate::get(scope) : 0; + QSGItemPrivate *itemPrivate = QSGItemPrivate::get(item); + + QSGItem *oldActiveFocusItem = 0; + QSGItem *newActiveFocusItem = 0; + + QVarLengthArray<QSGItem *, 20> changed; + + // Does this change the active focus? + if (item == rootItem || scopePrivate->activeFocus) { + oldActiveFocusItem = activeFocusItem; + newActiveFocusItem = item; + while (newActiveFocusItem->isFocusScope() && newActiveFocusItem->scopedFocusItem()) + newActiveFocusItem = newActiveFocusItem->scopedFocusItem(); + + if (oldActiveFocusItem) { +#ifndef QT_NO_IM + if (QInputContext *ic = inputContext()) + ic->reset(); +#endif + + activeFocusItem = 0; + QFocusEvent event(QEvent::FocusOut, Qt::OtherFocusReason); + q->sendEvent(oldActiveFocusItem, &event); + + QSGItem *afi = oldActiveFocusItem; + while (afi != scope) { + if (QSGItemPrivate::get(afi)->activeFocus) { + QSGItemPrivate::get(afi)->activeFocus = false; + changed << afi; + } + afi = afi->parentItem(); + } + } + } + + if (item != rootItem) { + QSGItem *oldSubFocusItem = scopePrivate->subFocusItem; + // Correct focus chain in scope + if (oldSubFocusItem) { + QSGItem *sfi = scopePrivate->subFocusItem->parentItem(); + while (sfi != scope) { + QSGItemPrivate::get(sfi)->subFocusItem = 0; + sfi = sfi->parentItem(); + } + } + { + scopePrivate->subFocusItem = item; + QSGItem *sfi = scopePrivate->subFocusItem->parentItem(); + while (sfi != scope) { + QSGItemPrivate::get(sfi)->subFocusItem = item; + sfi = sfi->parentItem(); + } + } + + if (oldSubFocusItem) { + QSGItemPrivate::get(oldSubFocusItem)->focus = false; + changed << oldSubFocusItem; + } + } + + if (!(options & DontChangeFocusProperty)) { + if (item != rootItem || q->hasFocus()) { + itemPrivate->focus = true; + changed << item; + } + } + + if (newActiveFocusItem && q->hasFocus()) { + activeFocusItem = newActiveFocusItem; + + QSGItemPrivate::get(newActiveFocusItem)->activeFocus = true; + changed << newActiveFocusItem; + + QSGItem *afi = newActiveFocusItem->parentItem(); + while (afi && afi != scope) { + if (afi->isFocusScope()) { + QSGItemPrivate::get(afi)->activeFocus = true; + changed << afi; + } + afi = afi->parentItem(); + } + + updateInputMethodData(); + + QFocusEvent event(QEvent::FocusIn, Qt::OtherFocusReason); + q->sendEvent(newActiveFocusItem, &event); + } else { + updateInputMethodData(); + } + + if (!changed.isEmpty()) + notifyFocusChangesRecur(changed.data(), changed.count() - 1); +} + +void QSGCanvasPrivate::clearFocusInScope(QSGItem *scope, QSGItem *item, FocusOptions options) +{ + Q_Q(QSGCanvas); + + Q_UNUSED(item); + Q_ASSERT(item); + Q_ASSERT(scope || item == rootItem); + +#ifdef FOCUS_DEBUG + qWarning() << "QSGCanvasPrivate::clearFocusInScope():"; + qWarning() << " scope:" << (QObject *)scope; + qWarning() << " item:" << (QObject *)item; + qWarning() << " activeFocusItem:" << (QObject *)activeFocusItem; +#endif + + QSGItemPrivate *scopePrivate = scope ? QSGItemPrivate::get(scope) : 0; + + QSGItem *oldActiveFocusItem = 0; + QSGItem *newActiveFocusItem = 0; + + QVarLengthArray<QSGItem *, 20> changed; + + Q_ASSERT(item == rootItem || item == scopePrivate->subFocusItem); + + // Does this change the active focus? + if (item == rootItem || scopePrivate->activeFocus) { + oldActiveFocusItem = activeFocusItem; + newActiveFocusItem = scope; + + Q_ASSERT(oldActiveFocusItem); + +#ifndef QT_NO_IM + if (QInputContext *ic = inputContext()) + ic->reset(); +#endif + + activeFocusItem = 0; + QFocusEvent event(QEvent::FocusOut, Qt::OtherFocusReason); + q->sendEvent(oldActiveFocusItem, &event); + + QSGItem *afi = oldActiveFocusItem; + while (afi != scope) { + if (QSGItemPrivate::get(afi)->activeFocus) { + QSGItemPrivate::get(afi)->activeFocus = false; + changed << afi; + } + afi = afi->parentItem(); + } + } + + if (item != rootItem) { + QSGItem *oldSubFocusItem = scopePrivate->subFocusItem; + // Correct focus chain in scope + if (oldSubFocusItem) { + QSGItem *sfi = scopePrivate->subFocusItem->parentItem(); + while (sfi != scope) { + QSGItemPrivate::get(sfi)->subFocusItem = 0; + sfi = sfi->parentItem(); + } + } + scopePrivate->subFocusItem = 0; + + if (oldSubFocusItem && !(options & DontChangeFocusProperty)) { + QSGItemPrivate::get(oldSubFocusItem)->focus = false; + changed << oldSubFocusItem; + } + } else if (!(options & DontChangeFocusProperty)) { + QSGItemPrivate::get(item)->focus = false; + changed << item; + } + + if (newActiveFocusItem) { + Q_ASSERT(newActiveFocusItem == scope); + activeFocusItem = scope; + + updateInputMethodData(); + + QFocusEvent event(QEvent::FocusIn, Qt::OtherFocusReason); + q->sendEvent(newActiveFocusItem, &event); + } else { + updateInputMethodData(); + } + + if (!changed.isEmpty()) + notifyFocusChangesRecur(changed.data(), changed.count() - 1); +} + +void QSGCanvasPrivate::notifyFocusChangesRecur(QSGItem **items, int remaining) +{ + QDeclarativeGuard<QSGItem> item(*items); + + if (remaining) + notifyFocusChangesRecur(items + 1, remaining - 1); + + if (item) { + QSGItemPrivate *itemPrivate = QSGItemPrivate::get(item); + + if (itemPrivate->notifiedFocus != itemPrivate->focus) { + itemPrivate->notifiedFocus = itemPrivate->focus; + emit item->focusChanged(itemPrivate->focus); + } + + if (item && itemPrivate->notifiedActiveFocus != itemPrivate->activeFocus) { + itemPrivate->notifiedActiveFocus = itemPrivate->activeFocus; + itemPrivate->itemChange(QSGItem::ItemActiveFocusHasChanged, itemPrivate->activeFocus); + emit item->activeFocusChanged(itemPrivate->activeFocus); + } + } +} + +void QSGCanvasPrivate::updateInputMethodData() +{ + Q_Q(QSGCanvas); + bool enabled = activeFocusItem + && (QSGItemPrivate::get(activeFocusItem)->flags & QSGItem::ItemAcceptsInputMethod); + q->setAttribute(Qt::WA_InputMethodEnabled, enabled); + q->setInputMethodHints(enabled ? activeFocusItem->inputMethodHints() : Qt::ImhNone); +} + +QVariant QSGCanvas::inputMethodQuery(Qt::InputMethodQuery query) const +{ + Q_D(const QSGCanvas); + if (!d->activeFocusItem || !(QSGItemPrivate::get(d->activeFocusItem)->flags & QSGItem::ItemAcceptsInputMethod)) + return QVariant(); + QVariant value = d->activeFocusItem->inputMethodQuery(query); + + //map geometry types + QVariant::Type type = value.type(); + if (type == QVariant::RectF || type == QVariant::Rect) { + const QTransform transform = QSGItemPrivate::get(d->activeFocusItem)->itemToCanvasTransform(); + value = transform.mapRect(value.toRectF()); + } else if (type == QVariant::PointF || type == QVariant::Point) { + const QTransform transform = QSGItemPrivate::get(d->activeFocusItem)->itemToCanvasTransform(); + value = transform.map(value.toPointF()); + } + return value; +} + +void QSGCanvasPrivate::dirtyItem(QSGItem *) +{ + Q_Q(QSGCanvas); + q->maybeUpdate(); +} + +void QSGCanvasPrivate::cleanup(QSGNode *n) +{ + Q_Q(QSGCanvas); + + Q_ASSERT(!cleanupNodeList.contains(n)); + cleanupNodeList.append(n); + q->maybeUpdate(); +} + +static QGLFormat tweakFormat(const QGLFormat &format = QGLFormat::defaultFormat()) +{ + QGLFormat f = format; + f.setSwapInterval(1); + return f; +} + +QSGCanvas::QSGCanvas(QWidget *parent, Qt::WindowFlags f) + : QGLWidget(*(new QSGCanvasPrivate), tweakFormat(), parent, (QGLWidget *) 0, f) +{ + Q_D(QSGCanvas); + + d->init(this); +} + +QSGCanvas::QSGCanvas(const QGLFormat &format, QWidget *parent, Qt::WindowFlags f) + : QGLWidget(*(new QSGCanvasPrivate), tweakFormat(format), parent, (QGLWidget *) 0, f) +{ + Q_D(QSGCanvas); + + d->init(this); +} + +QSGCanvas::QSGCanvas(QSGCanvasPrivate &dd, QWidget *parent, Qt::WindowFlags f) +: QGLWidget(dd, tweakFormat(), parent, 0, f) +{ + Q_D(QSGCanvas); + + d->init(this); +} + +QSGCanvas::QSGCanvas(QSGCanvasPrivate &dd, const QGLFormat &format, QWidget *parent, Qt::WindowFlags f) +: QGLWidget(dd, tweakFormat(format), parent, 0, f) +{ + Q_D(QSGCanvas); + + d->init(this); +} + +QSGCanvas::~QSGCanvas() +{ + Q_D(QSGCanvas); + + if (d->threadedRendering) { + d->stopRenderingThread(); + delete d->thread; + } + + // ### should we change ~QSGItem to handle this better? + // manually cleanup for the root item (item destructor only handles these when an item is parented) + QSGItemPrivate *rootItemPrivate = QSGItemPrivate::get(d->rootItem); + rootItemPrivate->removeFromDirtyList(); + rootItemPrivate->canvas = 0; + + delete d->rootItem; d->rootItem = 0; + d->cleanupNodes(); + + + // We need to remove all references to textures pointing to "our" QSGContext + // from the QDeclarativePixmapCache. Call into the cache to remove the GL / Scene Graph + // part of those cache entries. + // To "play nice" with other GL apps that are potentially running in the GUI thread, + // We get the current context and only temporarily make our own current + QGLContext *currentContext = const_cast<QGLContext *>(QGLContext::currentContext()); + makeCurrent(); + extern void qt_declarative_pixmapstore_clean(QSGContext *context); + qt_declarative_pixmapstore_clean(d->context); + delete d->context; + if (currentContext) + currentContext->makeCurrent(); +} + +QSGItem *QSGCanvas::rootItem() const +{ + Q_D(const QSGCanvas); + + return d->rootItem; +} + +QSGItem *QSGCanvas::activeFocusItem() const +{ + Q_D(const QSGCanvas); + + return d->activeFocusItem; +} + +QSGItem *QSGCanvas::mouseGrabberItem() const +{ + Q_D(const QSGCanvas); + + return d->mouseGrabberItem; +} + + +void QSGCanvasPrivate::clearHover() +{ + Q_Q(QSGCanvas); + if (!hoverItem) + return; + + QGraphicsSceneHoverEvent hoverEvent; + hoverEvent.setWidget(q); + + QPoint cursorPos = QCursor::pos(); + hoverEvent.setScenePos(q->mapFromGlobal(cursorPos)); + hoverEvent.setLastScenePos(hoverEvent.scenePos()); + hoverEvent.setScreenPos(cursorPos); + hoverEvent.setLastScreenPos(hoverEvent.screenPos()); + + QSGItem *item = hoverItem; + hoverItem = 0; + sendHoverEvent(QEvent::GraphicsSceneHoverLeave, item, &hoverEvent); +} + + +bool QSGCanvas::event(QEvent *e) +{ + Q_D(QSGCanvas); + + if (e->type() == QEvent::User) { + Q_ASSERT(d->threadedRendering); + + d->mutex.lock(); +#ifdef THREAD_DEBUG + qWarning("QSGRenderer: Main Thread: Stopped"); +#endif + + d->polishItems(); + + d->renderThreadAwakened = false; + + d->wait.wakeOne(); + + // The thread is exited when the widget has been hidden. We then need to + // skip the waiting, otherwise we would be waiting for a wakeup that never + // comes. + if (d->thread->isRunning()) + d->wait.wait(&d->mutex); +#ifdef THREAD_DEBUG + qWarning("QSGRenderer: Main Thread: Resumed"); +#endif + d->mutex.unlock(); + + if (d->animationRunning) + d->animationDriver->advance(); + } + + switch (e->type()) { + + case QEvent::TouchBegin: + case QEvent::TouchUpdate: + case QEvent::TouchEnd: + { + QTouchEvent *touch = static_cast<QTouchEvent *>(e); + d->translateTouchEvent(touch); + d->deliverTouchEvent(touch); + if (!touch->isAccepted()) + return false; + } + case QEvent::Leave: + d->clearHover(); + d->lastMousePosition = QPoint(); + break; + default: + break; + } + + return QGLWidget::event(e); +} + +void QSGCanvas::keyPressEvent(QKeyEvent *e) +{ + Q_D(QSGCanvas); + + if (d->activeFocusItem) + sendEvent(d->activeFocusItem, e); +} + +void QSGCanvas::keyReleaseEvent(QKeyEvent *e) +{ + Q_D(QSGCanvas); + + if (d->activeFocusItem) + sendEvent(d->activeFocusItem, e); +} + +void QSGCanvas::inputMethodEvent(QInputMethodEvent *e) +{ + Q_D(QSGCanvas); + + if (d->activeFocusItem) + sendEvent(d->activeFocusItem, e); +} + +bool QSGCanvasPrivate::deliverInitialMousePressEvent(QSGItem *item, QGraphicsSceneMouseEvent *event) +{ + Q_Q(QSGCanvas); + + QSGItemPrivate *itemPrivate = QSGItemPrivate::get(item); + if (itemPrivate->opacity == 0.0) + return false; + + if (itemPrivate->flags & QSGItem::ItemClipsChildrenToShape) { + QPointF p = item->mapFromScene(event->scenePos()); + if (!QRectF(0, 0, item->width(), item->height()).contains(p)) + return false; + } + + QList<QSGItem *> children = itemPrivate->paintOrderChildItems(); + for (int ii = children.count() - 1; ii >= 0; --ii) { + QSGItem *child = children.at(ii); + if (!child->isVisible() || !child->isEnabled()) + continue; + if (deliverInitialMousePressEvent(child, event)) + return true; + } + + if (itemPrivate->acceptedMouseButtons & event->button()) { + QPointF p = item->mapFromScene(event->scenePos()); + if (QRectF(0, 0, item->width(), item->height()).contains(p)) { + sceneMouseEventForTransform(*event, itemPrivate->canvasToItemTransform()); + event->accept(); + mouseGrabberItem = item; + q->sendEvent(item, event); + if (event->isAccepted()) + return true; + mouseGrabberItem->ungrabMouse(); + mouseGrabberItem = 0; + } + } + + return false; +} + +bool QSGCanvasPrivate::deliverMouseEvent(QGraphicsSceneMouseEvent *sceneEvent) +{ + Q_Q(QSGCanvas); + + if (!mouseGrabberItem && + sceneEvent->type() == QEvent::GraphicsSceneMousePress && + (sceneEvent->button() & sceneEvent->buttons()) == sceneEvent->buttons()) { + + return deliverInitialMousePressEvent(rootItem, sceneEvent); + } + + if (mouseGrabberItem) { + QSGItemPrivate *mgPrivate = QSGItemPrivate::get(mouseGrabberItem); + sceneMouseEventForTransform(*sceneEvent, mgPrivate->canvasToItemTransform()); + + sceneEvent->accept(); + q->sendEvent(mouseGrabberItem, sceneEvent); + if (sceneEvent->isAccepted()) + return true; + } + + return false; +} + +void QSGCanvas::mousePressEvent(QMouseEvent *event) +{ + Q_D(QSGCanvas); + +#ifdef MOUSE_DEBUG + qWarning() << "QSGCanvas::mousePressEvent()" << event->pos() << event->button() << event->buttons(); +#endif + + QGraphicsSceneMouseEvent sceneEvent(d->sceneMouseEventTypeFromMouseEvent(event)); + d->sceneMouseEventFromMouseEvent(sceneEvent, event); + + d->deliverMouseEvent(&sceneEvent); + event->setAccepted(sceneEvent.isAccepted()); +} + +void QSGCanvas::mouseReleaseEvent(QMouseEvent *event) +{ + Q_D(QSGCanvas); + +#ifdef MOUSE_DEBUG + qWarning() << "QSGCanvas::mouseReleaseEvent()" << event->pos() << event->button() << event->buttons(); +#endif + + if (!d->mouseGrabberItem) { + QGLWidget::mouseReleaseEvent(event); + return; + } + + QGraphicsSceneMouseEvent sceneEvent(d->sceneMouseEventTypeFromMouseEvent(event)); + d->sceneMouseEventFromMouseEvent(sceneEvent, event); + + d->deliverMouseEvent(&sceneEvent); + event->setAccepted(sceneEvent.isAccepted()); + + d->mouseGrabberItem = 0; +} + +void QSGCanvas::mouseDoubleClickEvent(QMouseEvent *event) +{ + Q_D(QSGCanvas); + +#ifdef MOUSE_DEBUG + qWarning() << "QSGCanvas::mouseDoubleClickEvent()" << event->pos() << event->button() << event->buttons(); +#endif + + QGraphicsSceneMouseEvent sceneEvent(d->sceneMouseEventTypeFromMouseEvent(event)); + d->sceneMouseEventFromMouseEvent(sceneEvent, event); + + if (!d->mouseGrabberItem && (event->button() & event->buttons()) == event->buttons()) { + if (d->deliverInitialMousePressEvent(d->rootItem, &sceneEvent)) + event->accept(); + else + event->ignore(); + return; + } + + d->deliverMouseEvent(&sceneEvent); + event->setAccepted(sceneEvent.isAccepted()); +} + +void QSGCanvasPrivate::sendHoverEvent(QEvent::Type type, QSGItem *item, + QGraphicsSceneHoverEvent *event) +{ + Q_Q(QSGCanvas); + const QTransform transform = QSGItemPrivate::get(item)->canvasToItemTransform(); + + //create copy of event + QGraphicsSceneHoverEvent hoverEvent(type); + hoverEvent.setWidget(event->widget()); + hoverEvent.setPos(transform.map(event->scenePos())); + hoverEvent.setScenePos(event->scenePos()); + hoverEvent.setScreenPos(event->screenPos()); + hoverEvent.setLastPos(transform.map(event->lastScenePos())); + hoverEvent.setLastScenePos(event->lastScenePos()); + hoverEvent.setLastScreenPos(event->lastScreenPos()); + hoverEvent.setModifiers(event->modifiers()); + hoverEvent.setAccepted(event->isAccepted()); + + q->sendEvent(item, &hoverEvent); +} + +void QSGCanvas::mouseMoveEvent(QMouseEvent *event) +{ + Q_D(QSGCanvas); + +#ifdef MOUSE_DEBUG + qWarning() << "QSGCanvas::mouseMoveEvent()" << event->pos() << event->button() << event->buttons(); +#endif + + if (!d->mouseGrabberItem) { + QGraphicsSceneHoverEvent hoverEvent; + d->sceneHoverEventFromMouseEvent(hoverEvent, event); + + bool delivered = d->deliverHoverEvent(d->rootItem, &hoverEvent); + if (!delivered) { + //take care of any exits + if (d->hoverItem) { + QSGItem *item = d->hoverItem; + d->hoverItem = 0; + d->sendHoverEvent(QEvent::GraphicsSceneHoverLeave, item, &hoverEvent); + } + } + event->setAccepted(hoverEvent.isAccepted()); + return; + } + + QGraphicsSceneMouseEvent sceneEvent(d->sceneMouseEventTypeFromMouseEvent(event)); + d->sceneMouseEventFromMouseEvent(sceneEvent, event); + + d->deliverMouseEvent(&sceneEvent); + event->setAccepted(sceneEvent.isAccepted()); +} + +bool QSGCanvasPrivate::deliverHoverEvent(QSGItem *item, QGraphicsSceneHoverEvent *event) +{ + QSGItemPrivate *itemPrivate = QSGItemPrivate::get(item); + if (itemPrivate->opacity == 0.0) + return false; + + if (itemPrivate->flags & QSGItem::ItemClipsChildrenToShape) { + QPointF p = item->mapFromScene(event->scenePos()); + if (!QRectF(0, 0, item->width(), item->height()).contains(p)) + return false; + } + + QList<QSGItem *> children = itemPrivate->paintOrderChildItems(); + for (int ii = children.count() - 1; ii >= 0; --ii) { + QSGItem *child = children.at(ii); + if (!child->isEnabled()) + continue; + if (deliverHoverEvent(child, event)) + return true; + } + + if (itemPrivate->hoverEnabled) { + QPointF p = item->mapFromScene(event->scenePos()); + if (QRectF(0, 0, item->width(), item->height()).contains(p)) { + if (hoverItem == item) { + //move + sendHoverEvent(QEvent::GraphicsSceneHoverMove, item, event); + } else { + //exit from previous + if (hoverItem) { + QSGItem *item = hoverItem; + hoverItem = 0; + sendHoverEvent(QEvent::GraphicsSceneHoverLeave, item, event); + } + + //enter new item + hoverItem = item; + sendHoverEvent(QEvent::GraphicsSceneHoverEnter, item, event); + } + return true; + } + } + + return false; +} + +bool QSGCanvasPrivate::deliverWheelEvent(QSGItem *item, QGraphicsSceneWheelEvent *event) +{ + Q_Q(QSGCanvas); + QSGItemPrivate *itemPrivate = QSGItemPrivate::get(item); + if (itemPrivate->opacity == 0.0) + return false; + + if (itemPrivate->flags & QSGItem::ItemClipsChildrenToShape) { + QPointF p = item->mapFromScene(event->scenePos()); + if (!QRectF(0, 0, item->width(), item->height()).contains(p)) + return false; + } + + QList<QSGItem *> children = itemPrivate->paintOrderChildItems(); + for (int ii = children.count() - 1; ii >= 0; --ii) { + QSGItem *child = children.at(ii); + if (!child->isEnabled()) + continue; + if (deliverWheelEvent(child, event)) + return true; + } + + QPointF p = item->mapFromScene(event->scenePos()); + if (QRectF(0, 0, item->width(), item->height()).contains(p)) { + event->setPos(itemPrivate->canvasToItemTransform().map(event->scenePos())); + event->accept(); + q->sendEvent(item, event); + if (event->isAccepted()) + return true; + } + + return false; +} + +#ifndef QT_NO_WHEELEVENT +void QSGCanvas::wheelEvent(QWheelEvent *event) +{ + Q_D(QSGCanvas); +#ifdef MOUSE_DEBUG + qWarning() << "QSGCanvas::wheelEvent()" << event->pos() << event->delta() << event->orientation(); +#endif + QGraphicsSceneWheelEvent wheelEvent(QEvent::GraphicsSceneWheel); + wheelEvent.setWidget(this); + wheelEvent.setScenePos(event->pos()); + wheelEvent.setScreenPos(event->globalPos()); + wheelEvent.setButtons(event->buttons()); + wheelEvent.setModifiers(event->modifiers()); + wheelEvent.setDelta(event->delta()); + wheelEvent.setOrientation(event->orientation()); + wheelEvent.setAccepted(false); + + d->deliverWheelEvent(d->rootItem, &wheelEvent); + event->setAccepted(wheelEvent.isAccepted()); +} +#endif // QT_NO_WHEELEVENT + +bool QSGCanvasPrivate::deliverTouchEvent(QTouchEvent *event) +{ +#ifdef TOUCH_DEBUG + if (event->type() == QEvent::TouchBegin) + qWarning("touchBeginEvent"); + else if (event->type() == QEvent::TouchUpdate) + qWarning("touchUpdateEvent"); + else if (event->type() == QEvent::TouchEnd) + qWarning("touchEndEvent"); +#endif + + QHash<QSGItem *, QList<QTouchEvent::TouchPoint> > updatedPoints; + + if (event->type() == QTouchEvent::TouchBegin) { // all points are new touch points + QSet<int> acceptedNewPoints; + deliverTouchPoints(rootItem, event, event->touchPoints(), &acceptedNewPoints, &updatedPoints); + if (acceptedNewPoints.count() > 0) + event->accept(); + return event->isAccepted(); + } + + const QList<QTouchEvent::TouchPoint> &touchPoints = event->touchPoints(); + QList<QTouchEvent::TouchPoint> newPoints; + QSGItem *item = 0; + for (int i=0; i<touchPoints.count(); i++) { + const QTouchEvent::TouchPoint &touchPoint = touchPoints[i]; + switch (touchPoint.state()) { + case Qt::TouchPointPressed: + newPoints << touchPoint; + break; + case Qt::TouchPointMoved: + case Qt::TouchPointStationary: + case Qt::TouchPointReleased: + if (itemForTouchPointId.contains(touchPoint.id())) { + item = itemForTouchPointId[touchPoint.id()]; + if (item) + updatedPoints[item].append(touchPoint); + } + break; + default: + break; + } + } + + if (newPoints.count() > 0 || updatedPoints.count() > 0) { + QSet<int> acceptedNewPoints; + int prevCount = updatedPoints.count(); + deliverTouchPoints(rootItem, event, newPoints, &acceptedNewPoints, &updatedPoints); + if (acceptedNewPoints.count() > 0 || updatedPoints.count() != prevCount) + event->accept(); + } + + if (event->touchPointStates() & Qt::TouchPointReleased) { + for (int i=0; i<touchPoints.count(); i++) { + if (touchPoints[i].state() == Qt::TouchPointReleased) + itemForTouchPointId.remove(touchPoints[i].id()); + } + } + + return event->isAccepted(); +} + +bool QSGCanvasPrivate::deliverTouchPoints(QSGItem *item, QTouchEvent *event, const QList<QTouchEvent::TouchPoint> &newPoints, QSet<int> *acceptedNewPoints, QHash<QSGItem *, QList<QTouchEvent::TouchPoint> > *updatedPoints) +{ + Q_Q(QSGCanvas); + QSGItemPrivate *itemPrivate = QSGItemPrivate::get(item); + + if (itemPrivate->opacity == 0.0) + return false; + + if (itemPrivate->flags & QSGItem::ItemClipsChildrenToShape) { + QRectF bounds(0, 0, item->width(), item->height()); + for (int i=0; i<newPoints.count(); i++) { + QPointF p = item->mapFromScene(newPoints[i].scenePos()); + if (!bounds.contains(p)) + return false; + } + } + + QList<QSGItem *> children = itemPrivate->paintOrderChildItems(); + for (int ii = children.count() - 1; ii >= 0; --ii) { + QSGItem *child = children.at(ii); + if (!child->isEnabled()) + continue; + if (deliverTouchPoints(child, event, newPoints, acceptedNewPoints, updatedPoints)) + return true; + } + + QList<QTouchEvent::TouchPoint> matchingPoints; + if (newPoints.count() > 0 && acceptedNewPoints->count() < newPoints.count()) { + QRectF bounds(0, 0, item->width(), item->height()); + for (int i=0; i<newPoints.count(); i++) { + if (acceptedNewPoints->contains(newPoints[i].id())) + continue; + QPointF p = item->mapFromScene(newPoints[i].scenePos()); + if (bounds.contains(p)) + matchingPoints << newPoints[i]; + } + } + + if (matchingPoints.count() > 0 || (*updatedPoints)[item].count() > 0) { + QList<QTouchEvent::TouchPoint> &eventPoints = (*updatedPoints)[item]; + eventPoints.append(matchingPoints); + transformTouchPoints(eventPoints, itemPrivate->canvasToItemTransform()); + + Qt::TouchPointStates eventStates; + for (int i=0; i<eventPoints.count(); i++) + eventStates |= eventPoints[i].state(); + // if all points have the same state, set the event type accordingly + QEvent::Type eventType; + switch (eventStates) { + case Qt::TouchPointPressed: + eventType = QEvent::TouchBegin; + break; + case Qt::TouchPointReleased: + eventType = QEvent::TouchEnd; + break; + default: + eventType = QEvent::TouchUpdate; + break; + } + + if (eventStates != Qt::TouchPointStationary) { + QTouchEvent touchEvent(eventType); + touchEvent.setWidget(q); + touchEvent.setDeviceType(event->deviceType()); + touchEvent.setModifiers(event->modifiers()); + touchEvent.setTouchPointStates(eventStates); + touchEvent.setTouchPoints(eventPoints); + + touchEvent.accept(); + q->sendEvent(item, &touchEvent); + + if (touchEvent.isAccepted()) { + for (int i=0; i<matchingPoints.count(); i++) { + itemForTouchPointId[matchingPoints[i].id()] = item; + acceptedNewPoints->insert(matchingPoints[i].id()); + } + } + } + } + + updatedPoints->remove(item); + if (acceptedNewPoints->count() == newPoints.count() && updatedPoints->isEmpty()) + return true; + + return false; +} + +bool QSGCanvasPrivate::sendFilteredMouseEvent(QSGItem *target, QSGItem *item, QGraphicsSceneMouseEvent *event) +{ + if (!target) + return false; + + if (sendFilteredMouseEvent(target->parentItem(), item, event)) + return true; + + QSGItemPrivate *targetPrivate = QSGItemPrivate::get(target); + if (targetPrivate->filtersChildMouseEvents) + if (target->childMouseEventFilter(item, event)) + return true; + + return false; +} + +bool QSGCanvas::sendEvent(QSGItem *item, QEvent *e) +{ + Q_D(QSGCanvas); + + if (!item) { + qWarning("QSGCanvas::sendEvent: Cannot send event to a null item"); + return false; + } + + Q_ASSERT(e); + + switch (e->type()) { + case QEvent::KeyPress: + case QEvent::KeyRelease: + e->accept(); + QSGItemPrivate::get(item)->deliverKeyEvent(static_cast<QKeyEvent *>(e)); + while (!e->isAccepted() && (item = item->parentItem())) { + e->accept(); + QSGItemPrivate::get(item)->deliverKeyEvent(static_cast<QKeyEvent *>(e)); + } + break; + case QEvent::InputMethod: + e->accept(); + QSGItemPrivate::get(item)->deliverInputMethodEvent(static_cast<QInputMethodEvent *>(e)); + while (!e->isAccepted() && (item = item->parentItem())) { + e->accept(); + QSGItemPrivate::get(item)->deliverInputMethodEvent(static_cast<QInputMethodEvent *>(e)); + } + break; + case QEvent::FocusIn: + case QEvent::FocusOut: + QSGItemPrivate::get(item)->deliverFocusEvent(static_cast<QFocusEvent *>(e)); + break; + case QEvent::GraphicsSceneMousePress: + case QEvent::GraphicsSceneMouseRelease: + case QEvent::GraphicsSceneMouseDoubleClick: + case QEvent::GraphicsSceneMouseMove: + // XXX todo - should sendEvent be doing this? how does it relate to forwarded events? + { + QGraphicsSceneMouseEvent *se = static_cast<QGraphicsSceneMouseEvent *>(e); + if (!d->sendFilteredMouseEvent(item->parentItem(), item, se)) { + se->accept(); + QSGItemPrivate::get(item)->deliverMouseEvent(se); + } + } + break; + case QEvent::GraphicsSceneWheel: + QSGItemPrivate::get(item)->deliverWheelEvent(static_cast<QGraphicsSceneWheelEvent *>(e)); + break; + case QEvent::GraphicsSceneHoverEnter: + case QEvent::GraphicsSceneHoverLeave: + case QEvent::GraphicsSceneHoverMove: + QSGItemPrivate::get(item)->deliverHoverEvent(static_cast<QGraphicsSceneHoverEvent *>(e)); + break; + case QEvent::TouchBegin: + case QEvent::TouchUpdate: + case QEvent::TouchEnd: + QSGItemPrivate::get(item)->deliverTouchEvent(static_cast<QTouchEvent *>(e)); + break; + default: + break; + } + + return false; +} + +void QSGCanvasPrivate::cleanupNodes() +{ + for (int ii = 0; ii < cleanupNodeList.count(); ++ii) + delete cleanupNodeList.at(ii); + cleanupNodeList.clear(); +} + +void QSGCanvasPrivate::updateDirtyNodes() +{ +#ifdef DIRTY_DEBUG + qWarning() << "QSGCanvasPrivate::updateDirtyNodes():"; +#endif + + cleanupNodes(); + + QSGItem *updateList = dirtyItemList; + dirtyItemList = 0; + if (updateList) QSGItemPrivate::get(updateList)->prevDirtyItem = &updateList; + + while (updateList) { + QSGItem *item = updateList; + QSGItemPrivate *itemPriv = QSGItemPrivate::get(item); + itemPriv->removeFromDirtyList(); + +#ifdef DIRTY_DEBUG + qWarning() << " QSGNode:" << item << qPrintable(itemPriv->dirtyToString()); +#endif + updateDirtyNode(item); + } +} + +void QSGCanvasPrivate::updateDirtyNode(QSGItem *item) +{ +#ifdef QML_RUNTIME_TESTING + bool didFlash = false; +#endif + + QSGItemPrivate *itemPriv = QSGItemPrivate::get(item); + quint32 dirty = itemPriv->dirtyAttributes; + itemPriv->dirtyAttributes = 0; + + if ((dirty & QSGItemPrivate::TransformUpdateMask) || + (dirty & QSGItemPrivate::Size && itemPriv->origin != QSGItem::TopLeft && + (itemPriv->scale != 1. || itemPriv->rotation != 0.))) { + + QMatrix4x4 matrix; + + if (itemPriv->x != 0. || itemPriv->y != 0.) + matrix.translate(itemPriv->x, itemPriv->y); + + if (dirty & QSGItemPrivate::ComplexTransformUpdateMask) { + for (int ii = itemPriv->transforms.count() - 1; ii >= 0; --ii) + itemPriv->transforms.at(ii)->applyTo(&matrix); + } + + if (itemPriv->scale != 1. || itemPriv->rotation != 0.) { + QPointF origin = itemPriv->computeTransformOrigin(); + matrix.translate(origin.x(), origin.y()); + if (itemPriv->scale != 1.) + matrix.scale(itemPriv->scale, itemPriv->scale); + if (itemPriv->rotation != 0.) + matrix.rotate(itemPriv->rotation, 0, 0, 1); + matrix.translate(-origin.x(), -origin.y()); + } + + itemPriv->itemNode()->setMatrix(matrix); + } + + bool clipEffectivelyChanged = dirty & QSGItemPrivate::Clip && + ((item->clip() == false) != (itemPriv->clipNode == 0)); + bool effectRefEffectivelyChanged = dirty & QSGItemPrivate::EffectReference && + ((itemPriv->effectRefCount == 0) != (itemPriv->rootNode == 0)); + + if (clipEffectivelyChanged) { + QSGNode *parent = itemPriv->opacityNode ? (QSGNode *) itemPriv->opacityNode : (QSGNode *)itemPriv->itemNode(); + QSGNode *child = itemPriv->rootNode ? (QSGNode *)itemPriv->rootNode : (QSGNode *)itemPriv->groupNode; + + if (item->clip()) { + Q_ASSERT(itemPriv->clipNode == 0); + itemPriv->clipNode = new QSGDefaultClipNode(QRectF(0, 0, itemPriv->width, itemPriv->height)); + + if (child) + parent->removeChildNode(child); + parent->appendChildNode(itemPriv->clipNode); + if (child) + itemPriv->clipNode->appendChildNode(child); + + } else { + Q_ASSERT(itemPriv->clipNode != 0); + parent->removeChildNode(itemPriv->clipNode); + if (child) + itemPriv->clipNode->removeChildNode(child); + delete itemPriv->clipNode; + itemPriv->clipNode = 0; + if (child) + parent->appendChildNode(child); + } + } + + if (dirty & QSGItemPrivate::ChildrenUpdateMask) { + while (itemPriv->childContainerNode()->childCount()) + itemPriv->childContainerNode()->removeChildNode(itemPriv->childContainerNode()->childAtIndex(0)); + } + + if (effectRefEffectivelyChanged) { + QSGNode *parent = itemPriv->clipNode; + if (!parent) + parent = itemPriv->opacityNode; + if (!parent) + parent = itemPriv->itemNode(); + QSGNode *child = itemPriv->groupNode; + + if (itemPriv->effectRefCount) { + Q_ASSERT(itemPriv->rootNode == 0); + itemPriv->rootNode = new QSGRootNode; + + if (child) + parent->removeChildNode(child); + parent->appendChildNode(itemPriv->rootNode); + if (child) + itemPriv->rootNode->appendChildNode(child); + } else { + Q_ASSERT(itemPriv->rootNode != 0); + parent->removeChildNode(itemPriv->rootNode); + if (child) + itemPriv->rootNode->removeChildNode(child); + delete itemPriv->rootNode; + itemPriv->rootNode = 0; + if (child) + parent->appendChildNode(child); + } + } + + if (dirty & QSGItemPrivate::ChildrenUpdateMask) { + QSGNode *groupNode = itemPriv->groupNode; + if (groupNode) { + for (int count = groupNode->childCount(); count; --count) + groupNode->removeChildNode(groupNode->childAtIndex(0)); + } + + QList<QSGItem *> orderedChildren = itemPriv->paintOrderChildItems(); + int ii = 0; + + itemPriv->paintNodeIndex = 0; + for (; ii < orderedChildren.count() && orderedChildren.at(ii)->z() < 0; ++ii) { + QSGItemPrivate *childPrivate = QSGItemPrivate::get(orderedChildren.at(ii)); + if (!childPrivate->explicitVisible && !childPrivate->effectRefCount) + continue; + if (childPrivate->itemNode()->parent()) + childPrivate->itemNode()->parent()->removeChildNode(childPrivate->itemNode()); + + itemPriv->childContainerNode()->appendChildNode(childPrivate->itemNode()); + itemPriv->paintNodeIndex++; + } + + if (itemPriv->paintNode) + itemPriv->childContainerNode()->appendChildNode(itemPriv->paintNode); + + for (; ii < orderedChildren.count(); ++ii) { + QSGItemPrivate *childPrivate = QSGItemPrivate::get(orderedChildren.at(ii)); + if (!childPrivate->explicitVisible && !childPrivate->effectRefCount) + continue; + if (childPrivate->itemNode()->parent()) + childPrivate->itemNode()->parent()->removeChildNode(childPrivate->itemNode()); + + itemPriv->childContainerNode()->appendChildNode(childPrivate->itemNode()); + } + } + + if ((dirty & QSGItemPrivate::Size || clipEffectivelyChanged) && itemPriv->clipNode) { + itemPriv->clipNode->setRect(QRectF(0, 0, itemPriv->width, itemPriv->height)); + itemPriv->clipNode->update(); + } + + if (dirty & (QSGItemPrivate::OpacityValue | QSGItemPrivate::Visible | QSGItemPrivate::HideReference)) { + qreal opacity = itemPriv->explicitVisible && itemPriv->hideRefCount == 0 + ? itemPriv->opacity : qreal(0); + + if (opacity != 1 && !itemPriv->opacityNode) { + itemPriv->opacityNode = new QSGOpacityNode; + + QSGNode *parent = itemPriv->itemNode(); + QSGNode *child = itemPriv->clipNode; + if (!child) + child = itemPriv->rootNode; + if (!child) + child = itemPriv->groupNode; + + if (child) + parent->removeChildNode(child); + parent->appendChildNode(itemPriv->opacityNode); + if (child) + itemPriv->opacityNode->appendChildNode(child); + } + if (itemPriv->opacityNode) + itemPriv->opacityNode->setOpacity(opacity); + } + + if (dirty & QSGItemPrivate::ContentUpdateMask) { + + if (itemPriv->flags & QSGItem::ItemHasContents) { + updatePaintNodeData.transformNode = itemPriv->itemNode(); + itemPriv->paintNode = item->updatePaintNode(itemPriv->paintNode, &updatePaintNodeData); + + Q_ASSERT(itemPriv->paintNode == 0 || + itemPriv->paintNode->parent() == 0 || + itemPriv->paintNode->parent() == itemPriv->childContainerNode()); + + if (itemPriv->paintNode && itemPriv->paintNode->parent() == 0) { + if (itemPriv->childContainerNode()->childCount() == itemPriv->paintNodeIndex) + itemPriv->childContainerNode()->appendChildNode(itemPriv->paintNode); + else + itemPriv->childContainerNode()->insertChildNodeBefore(itemPriv->paintNode, itemPriv->childContainerNode()->childAtIndex(itemPriv->paintNodeIndex)); + } + } else if (itemPriv->paintNode) { + delete itemPriv->paintNode; + itemPriv->paintNode = 0; + } + } + +#ifndef QT_NO_DEBUG + // Check consistency. + const QSGNode *nodeChain[] = { + itemPriv->itemNodeInstance, + itemPriv->opacityNode, + itemPriv->clipNode, + itemPriv->rootNode, + itemPriv->groupNode, + itemPriv->paintNode, + }; + + int ip = 0; + for (;;) { + while (ip < 5 && nodeChain[ip] == 0) + ++ip; + if (ip == 5) + break; + int ic = ip + 1; + while (ic < 5 && nodeChain[ic] == 0) + ++ic; + const QSGNode *parent = nodeChain[ip]; + const QSGNode *child = nodeChain[ic]; + if (child == 0) { + Q_ASSERT(parent == itemPriv->groupNode || parent->childCount() == 0); + } else { + Q_ASSERT(parent == itemPriv->groupNode || parent->childCount() == 1); + Q_ASSERT(child->parent() == parent); + bool containsChild = false; + for (int i = 0; i < parent->childCount(); ++i) + containsChild |= (parent->childAtIndex(i) == child); + Q_ASSERT(containsChild); + } + ip = ic; + } +#endif + +#ifdef QML_RUNTIME_TESTING + if (itemPriv->sceneGraphContext()->isFlashModeEnabled()) { + QSGFlashNode *flash = new QSGFlashNode(); + flash->setRect(item->boundingRect()); + itemPriv->childContainerNode()->appendChildNode(flash); + didFlash = true; + } + Q_Q(QSGCanvas); + if (didFlash) { + q->maybeUpdate(); + } +#endif + +} + +void QSGCanvas::maybeUpdate() +{ + Q_D(QSGCanvas); + + if (d->threadedRendering) { + if (!d->renderThreadAwakened) { + d->renderThreadAwakened = true; + bool locked = d->mutex.tryLock(); + if (d->idle && locked) { +#ifdef THREAD_DEBUG + qWarning("QSGRenderer: now maybe I should update..."); +#endif + d->wait.wakeOne(); + } else if (d->inSync) { + // If we are in sync (on scene graph thread) someone has explicitely asked us + // to redraw, hence we tell the render loop to not go idle. + // The primary usecase for this is updatePaintNode() calling update() without + // changing the scene graph. + d->needsRepaint = true; + } + if (locked) + d->mutex.unlock(); + } + } else if (!d->animationDriver || !d->animationDriver->isRunning()) { + update(); + } +} + +/*! + \fn void QSGEngine::sceneGraphInitialized(); + + This signal is emitted when the scene graph has been initialized. + + This signal will be emitted from the scene graph rendering thread. + */ + +/*! + Returns the QSGEngine used for this scene. + + The engine will only be available once the scene graph has been + initialized. Register for the sceneGraphEngine() signal to get + notification about this. + */ + +QSGEngine *QSGCanvas::sceneGraphEngine() const +{ + Q_D(const QSGCanvas); + if (d->context->isReady()) + return d->context->engine(); + return 0; +} + +#include "moc_qsgcanvas.cpp" + +QT_END_NAMESPACE |