diff options
Diffstat (limited to 'src/declarative/items/qquickcanvas.cpp')
-rw-r--r-- | src/declarative/items/qquickcanvas.cpp | 2644 |
1 files changed, 0 insertions, 2644 deletions
diff --git a/src/declarative/items/qquickcanvas.cpp b/src/declarative/items/qquickcanvas.cpp deleted file mode 100644 index 8022af98f4..0000000000 --- a/src/declarative/items/qquickcanvas.cpp +++ /dev/null @@ -1,2644 +0,0 @@ -/**************************************************************************** -** -** 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$ -** GNU Lesser General Public License Usage -** 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. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU General -** Public License version 3.0 as published by the Free Software Foundation -** and appearing in the file LICENSE.GPL included in the packaging of this -** file. Please review the following information to ensure the GNU General -** Public License version 3.0 requirements will be met: -** http://www.gnu.org/copyleft/gpl.html. -** -** Other Usage -** Alternatively, this file may be used in accordance with the terms and -** conditions contained in a signed written agreement between you and Nokia. -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qquickcanvas.h" -#include "qquickcanvas_p.h" - -#include "qquickitem.h" -#include "qquickitem_p.h" - -#include <private/qsgrenderer_p.h> -#include <private/qsgtexture_p.h> -#include <private/qsgflashnode_p.h> -#include <qsgengine.h> - -#include <private/qguiapplication_p.h> -#include <QtGui/QInputPanel> - -#include <private/qabstractanimation_p.h> - -#include <QtGui/qpainter.h> -#include <QtGui/qevent.h> -#include <QtGui/qmatrix4x4.h> -#include <QtCore/qvarlengtharray.h> -#include <QtCore/qabstractanimation.h> -#include <QtDeclarative/qdeclarativeincubator.h> - -#include <private/qdeclarativedebugtrace_p.h> - -QT_BEGIN_NAMESPACE - -#define QQUICK_CANVAS_TIMING -#ifdef QQUICK_CANVAS_TIMING -static bool qquick_canvas_timing = !qgetenv("QML_CANVAS_TIMING").isEmpty(); -static QTime threadTimer; -static int syncTime; -static int renderTime; -static int swapTime; -#endif - -DEFINE_BOOL_CONFIG_OPTION(qmlFixedAnimationStep, QML_FIXED_ANIMATION_STEP) -DEFINE_BOOL_CONFIG_OPTION(qmlNoThreadedRenderer, QML_BAD_GUI_RENDER_LOOP) - -extern Q_GUI_EXPORT QImage qt_gl_read_framebuffer(const QSize &size, bool alpha_format, bool include_alpha); - -void QQuickCanvasPrivate::updateFocusItemTransform() -{ - Q_Q(QQuickCanvas); - QQuickItem *focus = q->activeFocusItem(); - if (focus && qApp->inputPanel()->inputItem() == focus) - qApp->inputPanel()->setInputItemTransform(QQuickItemPrivate::get(focus)->itemToCanvasTransform()); -} - -class QQuickCanvasIncubationController : public QObject, public QDeclarativeIncubationController -{ -public: - QQuickCanvasIncubationController(QQuickCanvasPrivate *canvas) - : m_canvas(canvas), m_eventSent(false) {} - -protected: - virtual bool event(QEvent *e) - { - if (e->type() == QEvent::User) { - Q_ASSERT(m_eventSent); - - bool *amtp = m_canvas->thread->allowMainThreadProcessing(); - while (incubatingObjectCount()) { - if (amtp) - incubateWhile(amtp); - else - incubateFor(5); - QCoreApplication::processEvents(); - } - - m_eventSent = false; - } - return QObject::event(e); - } - - virtual void incubatingObjectCountChanged(int count) - { - if (count && !m_eventSent) { - m_eventSent = true; - QCoreApplication::postEvent(this, new QEvent(QEvent::User)); - } - } - -private: - QQuickCanvasPrivate *m_canvas; - bool m_eventSent; -}; - -class QQuickCanvasPlainRenderLoop : public QObject, public QQuickCanvasRenderLoop -{ -public: - QQuickCanvasPlainRenderLoop() - : updatePending(false) - , animationRunning(false) - { - } - - virtual void paint() { - updatePending = false; - if (animationRunning && animationDriver()) - animationDriver()->advance(); - polishItems(); - syncSceneGraph(); - makeCurrent(); - glViewport(0, 0, size.width(), size.height()); - renderSceneGraph(size); - swapBuffers(); - - if (animationRunning) - maybeUpdate(); - } - - virtual QImage grab() { - return qt_gl_read_framebuffer(size, false, false); - } - - virtual void startRendering() { - if (!glContext()) { - createGLContext(); - makeCurrent(); - initializeSceneGraph(); - } else { - makeCurrent(); - } - maybeUpdate(); - } - - virtual void stopRendering() { - cleanupNodesOnShutdown(); - } - - virtual void maybeUpdate() { - if (!updatePending) { - QCoreApplication::postEvent(this, new QEvent(QEvent::User)); - updatePending = true; - } - } - - virtual void animationStarted() { - animationRunning = true; - maybeUpdate(); - } - - virtual void animationStopped() { - animationRunning = false; - } - - virtual bool isRunning() const { return glContext(); } // Event loop is always running... - virtual void resize(const QSize &s) { size = s; } - virtual void setWindowSize(const QSize &s) { size = s; } - - bool event(QEvent *e) { - if (e->type() == QEvent::User) { - paint(); - return true; - } - return QObject::event(e); - } - - QSize size; - - uint updatePending : 1; - uint animationRunning : 1; -}; - - - -/* -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. -*/ - -/* - Threaded Rendering - ================== - - The threaded rendering uses a number of different variables to track potential - states used to handle resizing, initial paint, grabbing and driving animations - while ALWAYS keeping the GL context in the rendering thread and keeping the - overhead of normal one-shot paints and vblank driven animations at a minimum. - - Resize, initial show and grab suffer slightly in this model as they are locked - to the rendering in the rendering thread, but this is a necessary evil for - the system to work. - - Variables that are used: - - Private::animationRunning: This is true while the animations are running, and only - written to inside locks. - - RenderThread::isGuiBlocked: This is used to indicate that the GUI thread owns the - lock. This variable is an integer to allow for recursive calls to lockInGui() - without using a recursive mutex. See isGuiBlockPending. - - RenderThread::isPaintComplete: This variable is cleared when rendering starts and - set once rendering is complete. It is monitored in the paintEvent(), - resizeEvent() and grab() functions to force them to wait for rendering to - complete. - - RenderThread::isGuiBlockPending: This variable is set in the render thread just - before the sync event is sent to the GUI thread. It is used to avoid deadlocks - in the case where render thread waits while waiting for GUI to pick up the sync - event and GUI thread gets a resizeEvent, the initial paintEvent or a grab. - When this happens, we use the - exhaustSyncEvent() function to do the sync right there and mark the coming - sync event to be discarded. There can only ever be one sync incoming. - - RenderThread::isRenderBlock: This variable is true when animations are not - running and the render thread has gone to sleep, waiting for more to do. - - RenderThread::isExternalUpdatePending: This variable is set to false when - a new render pass is started and to true in maybeUpdate(). It is an - indication to the render thread that another render pass needs to take - place, rather than the render thread going to sleep after completing its swap. - - RenderThread::doGrab: This variable is set by the grab() function and - tells the renderer to do a grab after rendering is complete and before - swapping happens. - - RenderThread::shouldExit: This variable is used to determine if the render - thread should do a nother pass. It is typically set as a result of show() - and unset as a result of hide() or during shutdown() - - RenderThread::hasExited: Used by the GUI thread to synchronize the shutdown - after shouldExit has been set to true. - */ - -// #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 - -QQuickItem::UpdatePaintNodeData::UpdatePaintNodeData() -: transformNode(0) -{ -} - -QQuickRootItem::QQuickRootItem() -{ -} - -void QQuickCanvas::exposeEvent(QExposeEvent *) -{ - Q_D(QQuickCanvas); - d->thread->paint(); -} - -void QQuickCanvas::resizeEvent(QResizeEvent *) -{ - Q_D(QQuickCanvas); - d->thread->resize(size()); -} - -void QQuickCanvas::animationStarted() -{ - d_func()->thread->animationStarted(); -} - -void QQuickCanvas::animationStopped() -{ - d_func()->thread->animationStopped(); -} - -void QQuickCanvas::showEvent(QShowEvent *) -{ - Q_D(QQuickCanvas); - if (d->vsyncAnimations) { - if (!d->animationDriver) { - d->animationDriver = d->context->createAnimationDriver(this); - connect(d->animationDriver, SIGNAL(started()), this, SLOT(animationStarted()), Qt::DirectConnection); - connect(d->animationDriver, SIGNAL(stopped()), this, SLOT(animationStopped()), Qt::DirectConnection); - } - d->animationDriver->install(); - } - - if (!d->thread->isRunning()) { - d->thread->setWindowSize(size()); - d->thread->startRendering(); - } -} - -void QQuickCanvas::hideEvent(QHideEvent *) -{ - Q_D(QQuickCanvas); - d->thread->stopRendering(); -} - -void QQuickCanvas::focusOutEvent(QFocusEvent *) -{ - Q_D(QQuickCanvas); - d->rootItem->setFocus(false); -} - -void QQuickCanvas::focusInEvent(QFocusEvent *) -{ - Q_D(QQuickCanvas); - d->rootItem->setFocus(true); -} - - -/*! - Sets weither this canvas should use vsync driven animations. - - This option can only be set on one single QQuickCanvas, and that it's - vsync signal will then be used to drive all animations in the - process. - - This feature is primarily useful for single QQuickCanvas, QML-only - applications. - - \warning Enabling vsync on multiple QQuickCanvas instances has - undefined behavior. - */ -void QQuickCanvas::setVSyncAnimations(bool enabled) -{ - Q_D(QQuickCanvas); - if (visible()) { - qWarning("QQuickCanvas::setVSyncAnimations: Cannot be changed when widget is shown"); - return; - } - d->vsyncAnimations = enabled; -} - - - -/*! - Returns true if this canvas should use vsync driven animations; - otherwise returns false. - */ -bool QQuickCanvas::vsyncAnimations() const -{ - Q_D(const QQuickCanvas); - return d->vsyncAnimations; -} - -void QQuickCanvasPrivate::initializeSceneGraph() -{ - if (!context) - context = QSGContext::createDefaultContext(); - - if (context->isReady()) - return; - - QOpenGLContext *glctx = const_cast<QOpenGLContext *>(QOpenGLContext::currentContext()); - context->initialize(glctx); - - Q_Q(QQuickCanvas); - - if (!QQuickItemPrivate::get(rootItem)->itemNode()->parent()) { - context->rootNode()->appendChildNode(QQuickItemPrivate::get(rootItem)->itemNode()); - } - - engine = new QSGEngine(); - engine->setCanvas(q); - - emit q_func()->sceneGraphInitialized(); -} - -void QQuickCanvasPrivate::polishItems() -{ - while (!itemsToPolish.isEmpty()) { - QSet<QQuickItem *>::Iterator iter = itemsToPolish.begin(); - QQuickItem *item = *iter; - itemsToPolish.erase(iter); - QQuickItemPrivate::get(item)->polishScheduled = false; - item->updatePolish(); - } - updateFocusItemTransform(); -} - - -void QQuickCanvasPrivate::syncSceneGraph() -{ - updateDirtyNodes(); - - // Copy the current state of clearing from canvas into renderer. - context->renderer()->setClearColor(clearColor); - QSGRenderer::ClearMode mode = QSGRenderer::ClearStencilBuffer | QSGRenderer::ClearDepthBuffer; - if (clearBeforeRendering) - mode |= QSGRenderer::ClearColorBuffer; - context->renderer()->setClearMode(mode); -} - - -void QQuickCanvasPrivate::renderSceneGraph(const QSize &size) -{ - Q_Q(QQuickCanvas); - context->renderer()->setDeviceRect(QRect(QPoint(0, 0), size)); - context->renderer()->setViewportRect(QRect(QPoint(0, 0), renderTarget ? renderTarget->size() : size)); - context->renderer()->setProjectionMatrixToDeviceRect(); - - emit q->beforeRendering(); - context->renderNextFrame(renderTarget); - emit q->afterRendering(); - -#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 -} - - -QQuickCanvasPrivate::QQuickCanvasPrivate() - : rootItem(0) - , activeFocusItem(0) - , mouseGrabberItem(0) - , dirtyItemList(0) - , context(0) - , clearColor(Qt::white) - , vsyncAnimations(false) - , clearBeforeRendering(true) - , thread(0) - , animationDriver(0) - , renderTarget(0) - , incubationController(0) -{ -} - -QQuickCanvasPrivate::~QQuickCanvasPrivate() -{ -} - -void QQuickCanvasPrivate::init(QQuickCanvas *c) -{ - QUnifiedTimer* ut = QUnifiedTimer::instance(true); - if (qmlFixedAnimationStep()) - ut->setConsistentTiming(true); - - q_ptr = c; - - Q_Q(QQuickCanvas); - - rootItem = new QQuickRootItem; - QQuickItemPrivate *rootItemPrivate = QQuickItemPrivate::get(rootItem); - rootItemPrivate->canvas = q; - rootItemPrivate->flags |= QQuickItem::ItemIsFocusScope; - - // In the absence of a focus in event on some platforms assume the window will - // be activated immediately and set focus on the rootItem - // ### Remove when QTBUG-22415 is resolved. - //It is important that this call happens after the rootItem has a canvas.. - rootItem->setFocus(true); - - bool threaded = !qmlNoThreadedRenderer(); - - if (!QGuiApplicationPrivate::platformIntegration()->hasCapability(QPlatformIntegration::ThreadedOpenGL)) { - qWarning("QQuickCanvas: platform does not support threaded rendering!"); - threaded = false; - } - - if (threaded) - thread = new QQuickCanvasRenderThread(); - else - thread = new QQuickCanvasPlainRenderLoop(); - - thread->renderer = q; - thread->d = this; - - context = QSGContext::createDefaultContext(); - thread->moveContextToThread(context); - - q->setSurfaceType(QWindow::OpenGLSurface); - q->setFormat(context->defaultSurfaceFormat()); -} - -QDeclarativeListProperty<QObject> QQuickCanvasPrivate::data() -{ - initRootItem(); - return QQuickItemPrivate::get(rootItem)->data(); -} - -void QQuickCanvasPrivate::initRootItem() -{ - Q_Q(QQuickCanvas); - q->connect(q, SIGNAL(widthChanged(int)), - rootItem, SLOT(setWidth(int))); - q->connect(q, SIGNAL(heightChanged(int)), - rootItem, SLOT(setHeight(int))); - rootItem->setWidth(q->width()); - rootItem->setHeight(q->height()); -} - -void QQuickCanvasPrivate::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())); - } -} - - -/*! -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 QQuickCanvasPrivate::translateTouchEvent(QTouchEvent *touchEvent) -{ -// Q_Q(QQuickCanvas); - -// touchEvent->setWidget(q); // ### refactor... - - 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 QQuickCanvasPrivate::setFocusInScope(QQuickItem *scope, QQuickItem *item, FocusOptions options) -{ - Q_Q(QQuickCanvas); - - Q_ASSERT(item); - Q_ASSERT(scope || item == rootItem); - -#ifdef FOCUS_DEBUG - qWarning() << "QQuickCanvasPrivate::setFocusInScope():"; - qWarning() << " scope:" << (QObject *)scope; - if (scope) - qWarning() << " scopeSubFocusItem:" << (QObject *)QQuickItemPrivate::get(scope)->subFocusItem; - qWarning() << " item:" << (QObject *)item; - qWarning() << " activeFocusItem:" << (QObject *)activeFocusItem; -#endif - - QQuickItemPrivate *scopePrivate = scope ? QQuickItemPrivate::get(scope) : 0; - QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item); - - QQuickItem *oldActiveFocusItem = 0; - QQuickItem *newActiveFocusItem = 0; - - QVarLengthArray<QQuickItem *, 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 - qApp->inputPanel()->commit(); -#endif - - activeFocusItem = 0; - QFocusEvent event(QEvent::FocusOut, Qt::OtherFocusReason); - q->sendEvent(oldActiveFocusItem, &event); - - QQuickItem *afi = oldActiveFocusItem; - while (afi != scope) { - if (QQuickItemPrivate::get(afi)->activeFocus) { - QQuickItemPrivate::get(afi)->activeFocus = false; - changed << afi; - } - afi = afi->parentItem(); - } - } - } - - if (item != rootItem) { - QQuickItem *oldSubFocusItem = scopePrivate->subFocusItem; - // Correct focus chain in scope - if (oldSubFocusItem) { - QQuickItem *sfi = scopePrivate->subFocusItem->parentItem(); - while (sfi != scope) { - QQuickItemPrivate::get(sfi)->subFocusItem = 0; - sfi = sfi->parentItem(); - } - } - { - scopePrivate->subFocusItem = item; - QQuickItem *sfi = scopePrivate->subFocusItem->parentItem(); - while (sfi != scope) { - QQuickItemPrivate::get(sfi)->subFocusItem = item; - sfi = sfi->parentItem(); - } - } - - if (oldSubFocusItem) { - QQuickItemPrivate::get(oldSubFocusItem)->focus = false; - changed << oldSubFocusItem; - } - } - - if (!(options & DontChangeFocusProperty)) { -// if (item != rootItem || QGuiApplication::focusWindow() == q) { // QTBUG-22415 - itemPrivate->focus = true; - changed << item; -// } - } - - if (newActiveFocusItem && rootItem->hasFocus()) { - activeFocusItem = newActiveFocusItem; - - QQuickItemPrivate::get(newActiveFocusItem)->activeFocus = true; - changed << newActiveFocusItem; - - QQuickItem *afi = newActiveFocusItem->parentItem(); - while (afi && afi != scope) { - if (afi->isFocusScope()) { - QQuickItemPrivate::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 QQuickCanvasPrivate::clearFocusInScope(QQuickItem *scope, QQuickItem *item, FocusOptions options) -{ - Q_Q(QQuickCanvas); - - Q_UNUSED(item); - Q_ASSERT(item); - Q_ASSERT(scope || item == rootItem); - -#ifdef FOCUS_DEBUG - qWarning() << "QQuickCanvasPrivate::clearFocusInScope():"; - qWarning() << " scope:" << (QObject *)scope; - qWarning() << " item:" << (QObject *)item; - qWarning() << " activeFocusItem:" << (QObject *)activeFocusItem; -#endif - - QQuickItemPrivate *scopePrivate = scope ? QQuickItemPrivate::get(scope) : 0; - - QQuickItem *oldActiveFocusItem = 0; - QQuickItem *newActiveFocusItem = 0; - - QVarLengthArray<QQuickItem *, 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 - qApp->inputPanel()->commit(); -#endif - - activeFocusItem = 0; - QFocusEvent event(QEvent::FocusOut, Qt::OtherFocusReason); - q->sendEvent(oldActiveFocusItem, &event); - - QQuickItem *afi = oldActiveFocusItem; - while (afi != scope) { - if (QQuickItemPrivate::get(afi)->activeFocus) { - QQuickItemPrivate::get(afi)->activeFocus = false; - changed << afi; - } - afi = afi->parentItem(); - } - } - - if (item != rootItem) { - QQuickItem *oldSubFocusItem = scopePrivate->subFocusItem; - // Correct focus chain in scope - if (oldSubFocusItem) { - QQuickItem *sfi = scopePrivate->subFocusItem->parentItem(); - while (sfi != scope) { - QQuickItemPrivate::get(sfi)->subFocusItem = 0; - sfi = sfi->parentItem(); - } - } - scopePrivate->subFocusItem = 0; - - if (oldSubFocusItem && !(options & DontChangeFocusProperty)) { - QQuickItemPrivate::get(oldSubFocusItem)->focus = false; - changed << oldSubFocusItem; - } - } else if (!(options & DontChangeFocusProperty)) { - QQuickItemPrivate::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 QQuickCanvasPrivate::notifyFocusChangesRecur(QQuickItem **items, int remaining) -{ - QDeclarativeGuard<QQuickItem> item(*items); - - if (remaining) - notifyFocusChangesRecur(items + 1, remaining - 1); - - if (item) { - QQuickItemPrivate *itemPrivate = QQuickItemPrivate::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(QQuickItem::ItemActiveFocusHasChanged, itemPrivate->activeFocus); - emit item->activeFocusChanged(itemPrivate->activeFocus); - } - } -} - -void QQuickCanvasPrivate::updateInputMethodData() -{ - QQuickItem *inputItem = 0; - if (activeFocusItem && activeFocusItem->flags() & QQuickItem::ItemAcceptsInputMethod) - inputItem = activeFocusItem; - qApp->inputPanel()->setInputItem(inputItem); -} - -void QQuickCanvasPrivate::dirtyItem(QQuickItem *) -{ - Q_Q(QQuickCanvas); - q->maybeUpdate(); -} - -void QQuickCanvasPrivate::cleanup(QSGNode *n) -{ - Q_Q(QQuickCanvas); - - Q_ASSERT(!cleanupNodeList.contains(n)); - cleanupNodeList.append(n); - q->maybeUpdate(); -} - - -/*! - \qmlclass Window QQuickCanvas - \inqmlmodule QtQuick.Window 2 - \brief The Window object creates a new top-level window. - - The Window object creates a new top-level window for a QtQuick scene. It automatically sets up the - window for use with QtQuick 2.0 graphical elements. -*/ -/*! - \class QQuickCanvas - \since QtQuick 2.0 - \brief The QQuickCanvas class provides the canvas for displaying a graphical QML scene - - QQuickCanvas provides the graphical scene management needed to interact with and display - a scene of QQuickItems. - - A QQuickCanvas always has a single invisible root item. To add items to this canvas, - reparent the items to the root item or to an existing item in the scene. - - For easily displaying a scene from a QML file, see \l{QQuickView}. -*/ -QQuickCanvas::QQuickCanvas(QWindow *parent) - : QWindow(*(new QQuickCanvasPrivate), parent) -{ - Q_D(QQuickCanvas); - d->init(this); -} - -QQuickCanvas::QQuickCanvas(QQuickCanvasPrivate &dd, QWindow *parent) - : QWindow(dd, parent) -{ - Q_D(QQuickCanvas); - d->init(this); -} - -QQuickCanvas::~QQuickCanvas() -{ - Q_D(QQuickCanvas); - - if (d->thread->isRunning()) - d->thread->stopRendering(); - - // ### should we change ~QQuickItem to handle this better? - // manually cleanup for the root item (item destructor only handles these when an item is parented) - QQuickItemPrivate *rootItemPrivate = QQuickItemPrivate::get(d->rootItem); - rootItemPrivate->removeFromDirtyList(); - - delete d->incubationController; d->incubationController = 0; - - delete d->rootItem; d->rootItem = 0; - - delete d->thread; d->thread = 0; -} - -/*! - Returns the invisible root item of the scene. - - A QQuickCanvas always has a single invisible root item. To add items to this canvas, - reparent the items to the root item or to an existing item in the scene. -*/ -QQuickItem *QQuickCanvas::rootItem() const -{ - Q_D(const QQuickCanvas); - - return d->rootItem; -} - -/*! - Returns the item which currently has active focus. -*/ -QQuickItem *QQuickCanvas::activeFocusItem() const -{ - Q_D(const QQuickCanvas); - - return d->activeFocusItem; -} - -/*! - Returns the item which currently has the mouse grab. -*/ -QQuickItem *QQuickCanvas::mouseGrabberItem() const -{ - Q_D(const QQuickCanvas); - - return d->mouseGrabberItem; -} - - -/*! - \qmlproperty color QtQuick2.Window::Window::color - - The background color for the window. - - Setting this property is more efficient than using a separate Rectangle. -*/ - -bool QQuickCanvasPrivate::clearHover() -{ - if (hoverItems.isEmpty()) - return false; - - QPointF pos = QCursor::pos(); // ### refactor: q->mapFromGlobal(QCursor::pos()); - - bool accepted = false; - foreach (QQuickItem* item, hoverItems) - accepted = sendHoverEvent(QEvent::HoverLeave, item, pos, pos, QGuiApplication::keyboardModifiers(), true) || accepted; - hoverItems.clear(); - return accepted; -} - - -bool QQuickCanvas::event(QEvent *e) -{ - Q_D(QQuickCanvas); - - 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; - break; - } - case QEvent::Leave: - d->clearHover(); - d->lastMousePosition = QPoint(); - break; - case QEvent::DragEnter: - case QEvent::DragLeave: - case QEvent::DragMove: - case QEvent::Drop: - d->deliverDragEvent(&d->dragGrabber, e); - break; - case QEvent::WindowDeactivate: - rootItem()->windowDeactivateEvent(); - break; - default: - break; - } - - return QWindow::event(e); -} - -void QQuickCanvas::keyPressEvent(QKeyEvent *e) -{ - Q_D(QQuickCanvas); - - if (d->activeFocusItem) - sendEvent(d->activeFocusItem, e); -} - -void QQuickCanvas::keyReleaseEvent(QKeyEvent *e) -{ - Q_D(QQuickCanvas); - - if (d->activeFocusItem) - sendEvent(d->activeFocusItem, e); -} - -bool QQuickCanvasPrivate::deliverInitialMousePressEvent(QQuickItem *item, QMouseEvent *event) -{ - Q_Q(QQuickCanvas); - - QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item); - if (itemPrivate->opacity == 0.0) - return false; - - if (itemPrivate->flags & QQuickItem::ItemClipsChildrenToShape) { - QPointF p = item->mapFromScene(event->windowPos()); - if (!QRectF(0, 0, item->width(), item->height()).contains(p)) - return false; - } - - QList<QQuickItem *> children = itemPrivate->paintOrderChildItems(); - for (int ii = children.count() - 1; ii >= 0; --ii) { - QQuickItem *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->windowPos()); - if (QRectF(0, 0, item->width(), item->height()).contains(p)) { - QMouseEvent me(event->type(), p, event->windowPos(), event->screenPos(), - event->button(), event->buttons(), event->modifiers()); - me.accept(); - mouseGrabberItem = item; - q->sendEvent(item, &me); - event->setAccepted(me.isAccepted()); - if (me.isAccepted()) - return true; - mouseGrabberItem->ungrabMouse(); - mouseGrabberItem = 0; - } - } - - return false; -} - -bool QQuickCanvasPrivate::deliverMouseEvent(QMouseEvent *event) -{ - Q_Q(QQuickCanvas); - - lastMousePosition = event->windowPos(); - - if (!mouseGrabberItem && - event->type() == QEvent::MouseButtonPress && - (event->button() & event->buttons()) == event->buttons()) { - return deliverInitialMousePressEvent(rootItem, event); - } - - if (mouseGrabberItem) { - QQuickItemPrivate *mgPrivate = QQuickItemPrivate::get(mouseGrabberItem); - const QTransform &transform = mgPrivate->canvasToItemTransform(); - QMouseEvent me(event->type(), transform.map(event->windowPos()), event->windowPos(), event->screenPos(), - event->button(), event->buttons(), event->modifiers()); - me.accept(); - q->sendEvent(mouseGrabberItem, &me); - event->setAccepted(me.isAccepted()); - if (me.isAccepted()) - return true; - } - - return false; -} - -void QQuickCanvas::mousePressEvent(QMouseEvent *event) -{ - Q_D(QQuickCanvas); - -#ifdef MOUSE_DEBUG - qWarning() << "QQuickCanvas::mousePressEvent()" << event->pos() << event->button() << event->buttons(); -#endif - - d->deliverMouseEvent(event); -} - -void QQuickCanvas::mouseReleaseEvent(QMouseEvent *event) -{ - Q_D(QQuickCanvas); - -#ifdef MOUSE_DEBUG - qWarning() << "QQuickCanvas::mouseReleaseEvent()" << event->pos() << event->button() << event->buttons(); -#endif - - if (!d->mouseGrabberItem) { - QWindow::mouseReleaseEvent(event); - return; - } - - d->deliverMouseEvent(event); - d->mouseGrabberItem = 0; -} - -void QQuickCanvas::mouseDoubleClickEvent(QMouseEvent *event) -{ - Q_D(QQuickCanvas); - -#ifdef MOUSE_DEBUG - qWarning() << "QQuickCanvas::mouseDoubleClickEvent()" << event->pos() << event->button() << event->buttons(); -#endif - - if (!d->mouseGrabberItem && (event->button() & event->buttons()) == event->buttons()) { - if (d->deliverInitialMousePressEvent(d->rootItem, event)) - event->accept(); - else - event->ignore(); - return; - } - - d->deliverMouseEvent(event); -} - -bool QQuickCanvasPrivate::sendHoverEvent(QEvent::Type type, QQuickItem *item, - const QPointF &scenePos, const QPointF &lastScenePos, - Qt::KeyboardModifiers modifiers, bool accepted) -{ - Q_Q(QQuickCanvas); - const QTransform transform = QQuickItemPrivate::get(item)->canvasToItemTransform(); - - //create copy of event - QHoverEvent hoverEvent(type, transform.map(scenePos), transform.map(lastScenePos), modifiers); - hoverEvent.setAccepted(accepted); - - q->sendEvent(item, &hoverEvent); - - return hoverEvent.isAccepted(); -} - -void QQuickCanvas::mouseMoveEvent(QMouseEvent *event) -{ - Q_D(QQuickCanvas); - -#ifdef MOUSE_DEBUG - qWarning() << "QQuickCanvas::mouseMoveEvent()" << event->pos() << event->button() << event->buttons(); -#endif - - if (!d->mouseGrabberItem) { - if (d->lastMousePosition.isNull()) - d->lastMousePosition = event->windowPos(); - QPointF last = d->lastMousePosition; - d->lastMousePosition = event->windowPos(); - - bool accepted = event->isAccepted(); - bool delivered = d->deliverHoverEvent(d->rootItem, event->windowPos(), last, event->modifiers(), accepted); - if (!delivered) { - //take care of any exits - accepted = d->clearHover(); - } - event->setAccepted(accepted); - return; - } - - d->deliverMouseEvent(event); -} - -bool QQuickCanvasPrivate::deliverHoverEvent(QQuickItem *item, const QPointF &scenePos, const QPointF &lastScenePos, - Qt::KeyboardModifiers modifiers, bool &accepted) -{ - QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item); - if (itemPrivate->opacity == 0.0) - return false; - - if (itemPrivate->flags & QQuickItem::ItemClipsChildrenToShape) { - QPointF p = item->mapFromScene(scenePos); - if (!QRectF(0, 0, item->width(), item->height()).contains(p)) - return false; - } - - QList<QQuickItem *> children = itemPrivate->paintOrderChildItems(); - for (int ii = children.count() - 1; ii >= 0; --ii) { - QQuickItem *child = children.at(ii); - if (!child->isVisible() || !child->isEnabled()) - continue; - if (deliverHoverEvent(child, scenePos, lastScenePos, modifiers, accepted)) - return true; - } - - if (itemPrivate->hoverEnabled) { - QPointF p = item->mapFromScene(scenePos); - if (QRectF(0, 0, item->width(), item->height()).contains(p)) { - if (!hoverItems.isEmpty() && hoverItems[0] == item) { - //move - accepted = sendHoverEvent(QEvent::HoverMove, item, scenePos, lastScenePos, modifiers, accepted); - } else { - QList<QQuickItem *> itemsToHover; - QQuickItem* parent = item; - itemsToHover << item; - while ((parent = parent->parentItem())) - itemsToHover << parent; - - // Leaving from previous hovered items until we reach the item or one of its ancestors. - while (!hoverItems.isEmpty() && !itemsToHover.contains(hoverItems[0])) { - sendHoverEvent(QEvent::HoverLeave, hoverItems[0], scenePos, lastScenePos, modifiers, accepted); - hoverItems.removeFirst(); - } - - if (!hoverItems.isEmpty() && hoverItems[0] == item){//Not entering a new Item - // ### Shouldn't we send moves for the parent items as well? - accepted = sendHoverEvent(QEvent::HoverMove, item, scenePos, lastScenePos, modifiers, accepted); - } else { - // Enter items that are not entered yet. - int startIdx = -1; - if (!hoverItems.isEmpty()) - startIdx = itemsToHover.indexOf(hoverItems[0]) - 1; - if (startIdx == -1) - startIdx = itemsToHover.count() - 1; - - for (int i = startIdx; i >= 0; i--) { - QQuickItem *itemToHover = itemsToHover[i]; - if (QQuickItemPrivate::get(itemToHover)->hoverEnabled) { - hoverItems.prepend(itemToHover); - sendHoverEvent(QEvent::HoverEnter, itemToHover, scenePos, lastScenePos, modifiers, accepted); - } - } - } - } - return true; - } - } - - return false; -} - -bool QQuickCanvasPrivate::deliverWheelEvent(QQuickItem *item, QWheelEvent *event) -{ - Q_Q(QQuickCanvas); - QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item); - if (itemPrivate->opacity == 0.0) - return false; - - if (itemPrivate->flags & QQuickItem::ItemClipsChildrenToShape) { - QPointF p = item->mapFromScene(event->posF()); - if (!QRectF(0, 0, item->width(), item->height()).contains(p)) - return false; - } - - QList<QQuickItem *> children = itemPrivate->paintOrderChildItems(); - for (int ii = children.count() - 1; ii >= 0; --ii) { - QQuickItem *child = children.at(ii); - if (!child->isVisible() || !child->isEnabled()) - continue; - if (deliverWheelEvent(child, event)) - return true; - } - - QPointF p = item->mapFromScene(event->posF()); - if (QRectF(0, 0, item->width(), item->height()).contains(p)) { - QWheelEvent wheel(p, event->delta(), event->buttons(), event->modifiers(), event->orientation()); - wheel.accept(); - q->sendEvent(item, &wheel); - if (wheel.isAccepted()) { - event->accept(); - return true; - } - } - - return false; -} - -#ifndef QT_NO_WHEELEVENT -void QQuickCanvas::wheelEvent(QWheelEvent *event) -{ - Q_D(QQuickCanvas); -#ifdef MOUSE_DEBUG - qWarning() << "QQuickCanvas::wheelEvent()" << event->pos() << event->delta() << event->orientation(); -#endif - event->ignore(); - d->deliverWheelEvent(d->rootItem, event); -} -#endif // QT_NO_WHEELEVENT - -bool QQuickCanvasPrivate::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<QQuickItem *, 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; - QQuickItem *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 QQuickCanvasPrivate::deliverTouchPoints(QQuickItem *item, QTouchEvent *event, const QList<QTouchEvent::TouchPoint> &newPoints, QSet<int> *acceptedNewPoints, QHash<QQuickItem *, QList<QTouchEvent::TouchPoint> > *updatedPoints) -{ - Q_Q(QQuickCanvas); - QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item); - - if (itemPrivate->opacity == 0.0) - return false; - - if (itemPrivate->flags & QQuickItem::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<QQuickItem *> children = itemPrivate->paintOrderChildItems(); - for (int ii = children.count() - 1; ii >= 0; --ii) { - QQuickItem *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); // ### refactor: what is the consequence of not setting the widget? - touchEvent.setDeviceType(event->deviceType()); - touchEvent.setModifiers(event->modifiers()); - touchEvent.setTouchPointStates(eventStates); - touchEvent.setTouchPoints(eventPoints); - touchEvent.setTimestamp(event->timestamp()); - - 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; -} - -void QQuickCanvasPrivate::deliverDragEvent(QQuickDragGrabber *grabber, QEvent *event) -{ - Q_Q(QQuickCanvas); - grabber->resetTarget(); - QQuickDragGrabber::iterator grabItem = grabber->begin(); - if (grabItem != grabber->end()) { - Q_ASSERT(event->type() != QEvent::DragEnter); - if (event->type() == QEvent::Drop) { - QDropEvent *e = static_cast<QDropEvent *>(event); - for (e->setAccepted(false); !e->isAccepted() && grabItem != grabber->end(); grabItem = grabber->release(grabItem)) { - QPointF p = (**grabItem)->mapFromScene(e->pos()); - QDropEvent translatedEvent( - p.toPoint(), - e->possibleActions(), - e->mimeData(), - e->mouseButtons(), - e->keyboardModifiers()); - QQuickDropEventEx::copyActions(&translatedEvent, *e); - q->sendEvent(**grabItem, &translatedEvent); - e->setAccepted(translatedEvent.isAccepted()); - e->setDropAction(translatedEvent.dropAction()); - grabber->setTarget(**grabItem); - } - } - if (event->type() != QEvent::DragMove) { // Either an accepted drop or a leave. - QDragLeaveEvent leaveEvent; - for (; grabItem != grabber->end(); grabItem = grabber->release(grabItem)) - q->sendEvent(**grabItem, &leaveEvent); - return; - } else for (; grabItem != grabber->end(); grabItem = grabber->release(grabItem)) { - QDragMoveEvent *moveEvent = static_cast<QDragMoveEvent *>(event); - if (deliverDragEvent(grabber, **grabItem, moveEvent)) { - moveEvent->setAccepted(true); - for (++grabItem; grabItem != grabber->end();) { - QPointF p = (**grabItem)->mapFromScene(moveEvent->pos()); - if (QRectF(0, 0, (**grabItem)->width(), (**grabItem)->height()).contains(p)) { - QDragMoveEvent translatedEvent( - p.toPoint(), - moveEvent->possibleActions(), - moveEvent->mimeData(), - moveEvent->mouseButtons(), - moveEvent->keyboardModifiers()); - QQuickDropEventEx::copyActions(&translatedEvent, *moveEvent); - q->sendEvent(**grabItem, &translatedEvent); - ++grabItem; - } else { - QDragLeaveEvent leaveEvent; - q->sendEvent(**grabItem, &leaveEvent); - grabItem = grabber->release(grabItem); - } - } - return; - } else { - QDragLeaveEvent leaveEvent; - q->sendEvent(**grabItem, &leaveEvent); - } - } - } - if (event->type() == QEvent::DragEnter || event->type() == QEvent::DragMove) { - QDragMoveEvent *e = static_cast<QDragMoveEvent *>(event); - QDragEnterEvent enterEvent( - e->pos(), - e->possibleActions(), - e->mimeData(), - e->mouseButtons(), - e->keyboardModifiers()); - QQuickDropEventEx::copyActions(&enterEvent, *e); - event->setAccepted(deliverDragEvent(grabber, rootItem, &enterEvent)); - } -} - -bool QQuickCanvasPrivate::deliverDragEvent(QQuickDragGrabber *grabber, QQuickItem *item, QDragMoveEvent *event) -{ - Q_Q(QQuickCanvas); - bool accepted = false; - QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item); - if (itemPrivate->opacity == 0.0 || !item->isVisible() || !item->isEnabled()) - return false; - - QPointF p = item->mapFromScene(event->pos()); - if (QRectF(0, 0, item->width(), item->height()).contains(p)) { - if (event->type() == QEvent::DragMove || itemPrivate->flags & QQuickItem::ItemAcceptsDrops) { - QDragMoveEvent translatedEvent( - p.toPoint(), - event->possibleActions(), - event->mimeData(), - event->mouseButtons(), - event->keyboardModifiers(), - event->type()); - QQuickDropEventEx::copyActions(&translatedEvent, *event); - q->sendEvent(item, &translatedEvent); - if (event->type() == QEvent::DragEnter) { - if (translatedEvent.isAccepted()) { - grabber->grab(item); - accepted = true; - } - } else { - accepted = true; - } - } - } else if (itemPrivate->flags & QQuickItem::ItemClipsChildrenToShape) { - return false; - } - - QDragEnterEvent enterEvent( - event->pos(), - event->possibleActions(), - event->mimeData(), - event->mouseButtons(), - event->keyboardModifiers()); - QQuickDropEventEx::copyActions(&enterEvent, *event); - QList<QQuickItem *> children = itemPrivate->paintOrderChildItems(); - for (int ii = children.count() - 1; ii >= 0; --ii) { - if (deliverDragEvent(grabber, children.at(ii), &enterEvent)) - return true; - } - - return accepted; -} - -bool QQuickCanvasPrivate::sendFilteredMouseEvent(QQuickItem *target, QQuickItem *item, QEvent *event) -{ - if (!target) - return false; - - QQuickItemPrivate *targetPrivate = QQuickItemPrivate::get(target); - if (targetPrivate->filtersChildMouseEvents) - if (target->childMouseEventFilter(item, event)) - return true; - - if (sendFilteredMouseEvent(target->parentItem(), item, event)) - return true; - - return false; -} - -/*! - Propagates an event to a QQuickItem on the canvas -*/ -bool QQuickCanvas::sendEvent(QQuickItem *item, QEvent *e) -{ - Q_D(QQuickCanvas); - - if (!item) { - qWarning("QQuickCanvas::sendEvent: Cannot send event to a null item"); - return false; - } - - Q_ASSERT(e); - - switch (e->type()) { - case QEvent::KeyPress: - case QEvent::KeyRelease: - e->accept(); - QQuickItemPrivate::get(item)->deliverKeyEvent(static_cast<QKeyEvent *>(e)); - while (!e->isAccepted() && (item = item->parentItem())) { - e->accept(); - QQuickItemPrivate::get(item)->deliverKeyEvent(static_cast<QKeyEvent *>(e)); - } - break; - case QEvent::FocusIn: - case QEvent::FocusOut: - QQuickItemPrivate::get(item)->deliverFocusEvent(static_cast<QFocusEvent *>(e)); - break; - case QEvent::MouseButtonPress: - case QEvent::MouseButtonRelease: - case QEvent::MouseButtonDblClick: - case QEvent::MouseMove: - // XXX todo - should sendEvent be doing this? how does it relate to forwarded events? - if (!d->sendFilteredMouseEvent(item->parentItem(), item, e)) { - e->accept(); - QQuickItemPrivate::get(item)->deliverMouseEvent(static_cast<QMouseEvent *>(e)); - } - break; - case QEvent::Wheel: - QQuickItemPrivate::get(item)->deliverWheelEvent(static_cast<QWheelEvent *>(e)); - break; - case QEvent::HoverEnter: - case QEvent::HoverLeave: - case QEvent::HoverMove: - QQuickItemPrivate::get(item)->deliverHoverEvent(static_cast<QHoverEvent *>(e)); - break; - case QEvent::TouchBegin: - case QEvent::TouchUpdate: - case QEvent::TouchEnd: - // XXX todo - should sendEvent be doing this? how does it relate to forwarded events? - if (!d->sendFilteredMouseEvent(item->parentItem(), item, e)) { - e->accept(); - QQuickItemPrivate::get(item)->deliverTouchEvent(static_cast<QTouchEvent *>(e)); - } - break; - case QEvent::DragEnter: - case QEvent::DragMove: - case QEvent::DragLeave: - case QEvent::Drop: - QQuickItemPrivate::get(item)->deliverDragEvent(e); - break; - default: - break; - } - - return false; -} - -void QQuickCanvasPrivate::cleanupNodes() -{ - for (int ii = 0; ii < cleanupNodeList.count(); ++ii) - delete cleanupNodeList.at(ii); - cleanupNodeList.clear(); -} - -void QQuickCanvasPrivate::cleanupNodesOnShutdown(QQuickItem *item) -{ - QQuickItemPrivate *p = QQuickItemPrivate::get(item); - if (p->itemNodeInstance) { - delete p->itemNodeInstance; - p->itemNodeInstance = 0; - p->opacityNode = 0; - p->clipNode = 0; - p->groupNode = 0; - p->paintNode = 0; - } - - for (int ii = 0; ii < p->childItems.count(); ++ii) - cleanupNodesOnShutdown(p->childItems.at(ii)); -} - -// This must be called from the render thread, with the main thread frozen -void QQuickCanvasPrivate::cleanupNodesOnShutdown() -{ - cleanupNodes(); - - cleanupNodesOnShutdown(rootItem); -} - -void QQuickCanvasPrivate::updateDirtyNodes() -{ -#ifdef DIRTY_DEBUG - qWarning() << "QQuickCanvasPrivate::updateDirtyNodes():"; -#endif - - cleanupNodes(); - - QQuickItem *updateList = dirtyItemList; - dirtyItemList = 0; - if (updateList) QQuickItemPrivate::get(updateList)->prevDirtyItem = &updateList; - - while (updateList) { - QQuickItem *item = updateList; - QQuickItemPrivate *itemPriv = QQuickItemPrivate::get(item); - itemPriv->removeFromDirtyList(); - -#ifdef DIRTY_DEBUG - qWarning() << " QSGNode:" << item << qPrintable(itemPriv->dirtyToString()); -#endif - updateDirtyNode(item); - } -} - -void QQuickCanvasPrivate::updateDirtyNode(QQuickItem *item) -{ -#ifdef QML_RUNTIME_TESTING - bool didFlash = false; -#endif - - QQuickItemPrivate *itemPriv = QQuickItemPrivate::get(item); - quint32 dirty = itemPriv->dirtyAttributes; - itemPriv->dirtyAttributes = 0; - - if ((dirty & QQuickItemPrivate::TransformUpdateMask) || - (dirty & QQuickItemPrivate::Size && itemPriv->origin != QQuickItem::TopLeft && - (itemPriv->scale != 1. || itemPriv->rotation != 0.))) { - - QMatrix4x4 matrix; - - if (itemPriv->x != 0. || itemPriv->y != 0.) - matrix.translate(itemPriv->x, itemPriv->y); - - 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 = item->transformOriginPoint(); - 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 & QQuickItemPrivate::Clip && - ((item->clip() == false) != (itemPriv->clipNode == 0)); - bool effectRefEffectivelyChanged = dirty & QQuickItemPrivate::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 QQuickDefaultClipNode(item->boundingRect()); - itemPriv->clipNode->update(); - - 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 & QQuickItemPrivate::ChildrenUpdateMask) - itemPriv->childContainerNode()->removeAllChildNodes(); - - 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 & QQuickItemPrivate::ChildrenUpdateMask) { - QSGNode *groupNode = itemPriv->groupNode; - if (groupNode) - groupNode->removeAllChildNodes(); - - QList<QQuickItem *> orderedChildren = itemPriv->paintOrderChildItems(); - int ii = 0; - - for (; ii < orderedChildren.count() && orderedChildren.at(ii)->z() < 0; ++ii) { - QQuickItemPrivate *childPrivate = QQuickItemPrivate::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->beforePaintNode = itemPriv->groupNode ? itemPriv->groupNode->lastChild() : 0; - - if (itemPriv->paintNode) - itemPriv->childContainerNode()->appendChildNode(itemPriv->paintNode); - - for (; ii < orderedChildren.count(); ++ii) { - QQuickItemPrivate *childPrivate = QQuickItemPrivate::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 & QQuickItemPrivate::Size) && itemPriv->clipNode) { - itemPriv->clipNode->setRect(item->boundingRect()); - itemPriv->clipNode->update(); - } - - if (dirty & (QQuickItemPrivate::OpacityValue | QQuickItemPrivate::Visible | QQuickItemPrivate::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 & QQuickItemPrivate::ContentUpdateMask) { - - if (itemPriv->flags & QQuickItem::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->beforePaintNode) - itemPriv->childContainerNode()->insertChildNodeAfter(itemPriv->paintNode, itemPriv->beforePaintNode); - else - itemPriv->childContainerNode()->prependChildNode(itemPriv->paintNode); - } - } 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 (QSGNode *n = parent->firstChild(); n; n = n->nextSibling()) - containsChild |= (n == 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(QQuickCanvas); - if (didFlash) { - q->maybeUpdate(); - } -#endif - -} - -void QQuickCanvas::maybeUpdate() -{ - Q_D(QQuickCanvas); - - if (d->thread && d->thread->isRunning()) - d->thread->maybeUpdate(); -} - -/*! - \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. - - \deprecated - */ - -QSGEngine *QQuickCanvas::sceneGraphEngine() const -{ - Q_D(const QQuickCanvas); - qWarning("QQuickCanvas::sceneGraphEngine() is deprecated, use members of QQuickCanvas instead"); - if (d->context && d->context->isReady()) - return d->engine; - return 0; -} - - - -/*! - Sets the render target for this canvas to be \a fbo. - - The specified fbo must be created in the context of the canvas - or one that shares with it. - - \warning - This function can only be called from the thread doing - the rendering. - */ - -void QQuickCanvas::setRenderTarget(QOpenGLFramebufferObject *fbo) -{ - Q_D(QQuickCanvas); - if (d->context && d->context && QThread::currentThread() != d->context->thread()) { - qWarning("QQuickCanvas::setRenderThread: Cannot set render target from outside the rendering thread"); - return; - } - - d->renderTarget = fbo; -} - - - -/*! - Returns the render target for this canvas. - - The default is to render to the surface of the canvas, in which - case the render target is 0. - */ -QOpenGLFramebufferObject *QQuickCanvas::renderTarget() const -{ - Q_D(const QQuickCanvas); - return d->renderTarget; -} - - -/*! - Grabs the contents of the framebuffer and returns it as an image. - - This function might not work if the view is not visible. - - \warning Calling this function will cause performance problems. - - \warning This function can only be called from the GUI thread. - */ -QImage QQuickCanvas::grabFrameBuffer() -{ - Q_D(QQuickCanvas); - return d->thread ? d->thread->grab() : QImage(); -} - -/*! - Returns an incubation controller that splices incubation between frames - for this canvas. QQuickView automatically installs this controller for you, - otherwise you will need to install it yourself using \l{QDeclarativeEngine::setIncubationController} - - The controller is owned by the canvas and will be destroyed when the canvas - is deleted. -*/ -QDeclarativeIncubationController *QQuickCanvas::incubationController() const -{ - Q_D(const QQuickCanvas); - - if (!d->incubationController) - d->incubationController = new QQuickCanvasIncubationController(const_cast<QQuickCanvasPrivate *>(d)); - return d->incubationController; -} - - - -/*! - \enum QQuickCanvas::CreateTextureOption - - The CreateTextureOption enums are used to customize a texture is wrapped. - - \value TextureHasAlphaChannel The texture has an alpha channel and should - be drawn using blending. - - \value TextureHasMipmaps The texture has mipmaps and can be drawn with - mipmapping enabled. - - \value TextureOwnsGLTexture The texture object owns the texture id and - will delete the GL texture when the texture object is deleted. - */ - -/*! - \fn void QQuickCanvas::beforeRendering() - - This signal is emitted before the scene starts rendering. - - Combined with the modes for clearing the background, this option - can be used to paint using raw GL under QML content. - - The GL context used for rendering the scene graph will be bound - at this point. - - Since this signal is emitted from the scene graph rendering thread, the receiver should - be on the scene graph thread or the connection should be Qt::DirectConnection. - -*/ - -/*! - \fn void QQuickCanvas::afterRendering() - - This signal is emitted after the scene has completed rendering, before swapbuffers is called. - - This signal can be used to paint using raw GL on top of QML content, - or to do screen scraping of the current frame buffer. - - The GL context used for rendering the scene graph will be bound at this point. - - Since this signal is emitted from the scene graph rendering thread, the receiver should - be on the scene graph thread or the connection should be Qt::DirectConnection. - */ - - - -/*! - Sets weither the scene graph rendering of QML should clear the color buffer - before it starts rendering to \a enbled. - - By disabling clearing of the color buffer, it is possible to do GL painting - under the scene graph. - - The color buffer is cleared by default. - - \sa beforeRendering() - */ - -void QQuickCanvas::setClearBeforeRendering(bool enabled) -{ - Q_D(QQuickCanvas); - d->clearBeforeRendering = enabled; -} - - - -/*! - Returns weither clearing of the color buffer is done before rendering or not. - */ - -bool QQuickCanvas::clearBeforeRendering() const -{ - Q_D(const QQuickCanvas); - return d->clearBeforeRendering; -} - - - -/*! - Creates a new QSGTexture from the supplied \a image. If the image has an - alpha channel, the corresponding texture will have an alpha channel. - - The caller of the function is responsible for deleting the returned texture. - The actual GL texture will be deleted when the texture object is deleted. - - \warning This function will return 0 if the scene graph has not yet been - initialized. - - This function can be called both from the GUI thread and the rendering thread. - - \sa sceneGraphInitialized() - */ - -QSGTexture *QQuickCanvas::createTextureFromImage(const QImage &image) const -{ - Q_D(const QQuickCanvas); - if (d->context) - return d->context->createTexture(image); - else - return 0; -} - - - -/*! - Creates a new QSGTexture object from an existing GL texture \a id. - - The caller of the function is responsible for deleting the returned texture. - - Use \a options to customize the texture attributes. - - \warning This function will return 0 if the scenegraph has not yet been - initialized. - - \sa sceneGraphInitialized() - */ -QSGTexture *QQuickCanvas::createTextureFromId(uint id, const QSize &size, CreateTextureOptions options) const -{ - Q_D(const QQuickCanvas); - if (d->context) { - QSGPlainTexture *texture = new QSGPlainTexture(); - texture->setTextureId(id); - texture->setHasAlphaChannel(options & TextureHasAlphaChannel); - texture->setHasMipmaps(options & TextureHasMipmaps); - texture->setOwnsTexture(options & TextureOwnsGLTexture); - texture->setTextureSize(size); - return texture; - } - return 0; -} - - -/*! - Sets the color used to clear the opengl context to \a color. - - Setting the clear color has no effect when clearing is disabled. - - \sa setClearBeforeRendering() - */ - -void QQuickCanvas::setClearColor(const QColor &color) -{ - if (color == d_func()->clearColor) - return; - d_func()->clearColor = color; - emit clearColorChanged(color); -} - - - -/*! - Returns the color used to clear the opengl context. - */ - -QColor QQuickCanvas::clearColor() const -{ - return d_func()->clearColor; -} - - - -void QQuickCanvasRenderLoop::createGLContext() -{ - gl = new QOpenGLContext(); - gl->setFormat(renderer->requestedFormat()); - gl->create(); -} - -void QQuickCanvasRenderThread::run() -{ -#ifdef THREAD_DEBUG - qDebug("QML Rendering Thread Started"); -#endif - - if (!glContext()) { - createGLContext(); - makeCurrent(); - initializeSceneGraph(); - } else { - makeCurrent(); - } - - while (!shouldExit) { - lock(); - - bool sizeChanged = false; - isExternalUpdatePending = false; - - if (renderedSize != windowSize) { -#ifdef THREAD_DEBUG - printf(" RenderThread: window has changed size...\n"); -#endif - glViewport(0, 0, windowSize.width(), windowSize.height()); - sizeChanged = true; - } - -#ifdef THREAD_DEBUG - printf(" RenderThread: preparing to sync...\n"); -#endif - - if (!isGuiBlocked) { - isGuiBlockPending = true; - -#ifdef THREAD_DEBUG - printf(" RenderThread: aquired sync lock...\n"); -#endif - allowMainThreadProcessingFlag = false; - QCoreApplication::postEvent(this, new QEvent(QEvent::User)); -#ifdef THREAD_DEBUG - printf(" RenderThread: going to sleep...\n"); -#endif - wait(); - - isGuiBlockPending = false; - } - -#ifdef THREAD_DEBUG - printf(" RenderThread: Doing locked sync\n"); -#endif -#ifdef QQUICK_CANVAS_TIMING - if (qquick_canvas_timing) - threadTimer.start(); -#endif - inSync = true; - syncSceneGraph(); - inSync = false; - - // Wake GUI after sync to let it continue animating and event processing. - allowMainThreadProcessingFlag = true; - wake(); - unlock(); -#ifdef THREAD_DEBUG - printf(" RenderThread: sync done\n"); -#endif -#ifdef QQUICK_CANVAS_TIMING - if (qquick_canvas_timing) - syncTime = threadTimer.elapsed(); -#endif - -#ifdef THREAD_DEBUG - printf(" RenderThread: rendering... %d x %d\n", windowSize.width(), windowSize.height()); -#endif - - renderSceneGraph(windowSize); -#ifdef QQUICK_CANVAS_TIMING - if (qquick_canvas_timing) - renderTime = threadTimer.elapsed() - syncTime; -#endif - - // The content of the target buffer is undefined after swap() so grab needs - // to happen before swap(); - if (doGrab) { -#ifdef THREAD_DEBUG - printf(" RenderThread: doing a grab...\n"); -#endif - grabContent = qt_gl_read_framebuffer(windowSize, false, false); - doGrab = false; - } - -#ifdef THREAD_DEBUG - printf(" RenderThread: wait for swap...\n"); -#endif - - swapBuffers(); -#ifdef THREAD_DEBUG - printf(" RenderThread: swap complete...\n"); -#endif -#ifdef QQUICK_CANVAS_TIMING - if (qquick_canvas_timing) { - swapTime = threadTimer.elapsed() - renderTime; - qDebug() << "- Breakdown of frame time: sync:" << syncTime - << "ms render:" << renderTime << "ms swap:" << swapTime - << "ms total:" << swapTime + renderTime << "ms"; - } -#endif - - lock(); - isPaintCompleted = true; - if (sizeChanged) - renderedSize = windowSize; - - // Wake the GUI thread now that rendering is complete, to signal that painting - // is done, resizing is done or grabbing is completed. For grabbing, we're - // signalling this much later than needed (we could have done it before swap) - // but we don't want to lock an extra time. - wake(); - - if (!animationRunning && !isExternalUpdatePending && !shouldExit && !doGrab) { -#ifdef THREAD_DEBUG - printf(" RenderThread: nothing to do, going to sleep...\n"); -#endif - isRenderBlocked = true; - wait(); - isRenderBlocked = false; - } - - unlock(); - - QCoreApplication::processEvents(); - - // Process any "deleteLater" objects... - QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete); - } - -#ifdef THREAD_DEBUG - printf(" RenderThread: deleting all outstanding nodes\n"); -#endif - cleanupNodesOnShutdown(); - -#ifdef THREAD_DEBUG - printf(" RenderThread: render loop exited... Good Night!\n"); -#endif - - doneCurrent(); - - lock(); - hasExited = true; -#ifdef THREAD_DEBUG - printf(" RenderThread: waking GUI for final sleep..\n"); -#endif - wake(); - unlock(); - -#ifdef THREAD_DEBUG - printf(" RenderThread: All done...\n"); -#endif -} - - - -bool QQuickCanvasRenderThread::event(QEvent *e) -{ - Q_ASSERT(QThread::currentThread() == qApp->thread()); - - if (e->type() == QEvent::User) { - if (!syncAlreadyHappened) - sync(false); - - syncAlreadyHappened = false; - - if (animationRunning && animationDriver()) { -#ifdef THREAD_DEBUG - qDebug("GUI: Advancing animations...\n"); -#endif - - animationDriver()->advance(); - -#ifdef THREAD_DEBUG - qDebug("GUI: Animations advanced...\n"); -#endif - } - - return true; - } - - return QThread::event(e); -} - - - -void QQuickCanvasRenderThread::exhaustSyncEvent() -{ - if (isGuiBlockPending) { - sync(true); - syncAlreadyHappened = true; - } -} - - - -void QQuickCanvasRenderThread::sync(bool guiAlreadyLocked) -{ -#ifdef THREAD_DEBUG - printf("GUI: sync - %s\n", guiAlreadyLocked ? "outside event" : "inside event"); -#endif - if (!guiAlreadyLocked) - lockInGui(); - - renderThreadAwakened = false; - - polishItems(); - - wake(); - wait(); - - if (!guiAlreadyLocked) - unlockInGui(); -} - - - - -/*! - Acquires the mutex for the GUI thread. The function uses the isGuiBlocked - variable to keep track of how many recursion levels the gui is locked with. - We only actually acquire the mutex for the first level to avoid deadlocking - ourselves. - */ - -void QQuickCanvasRenderThread::lockInGui() -{ - // We must avoid recursive locking in the GUI thread, hence we - // only lock when we are the first one to try to block. - if (!isGuiBlocked) - lock(); - - isGuiBlocked++; - -#ifdef THREAD_DEBUG - printf("GUI: aquired lock... %d\n", isGuiBlocked); -#endif -} - - - -void QQuickCanvasRenderThread::unlockInGui() -{ -#ifdef THREAD_DEBUG - printf("GUI: releasing lock... %d\n", isGuiBlocked); -#endif - --isGuiBlocked; - if (!isGuiBlocked) - unlock(); -} - - - - -void QQuickCanvasRenderThread::animationStarted() -{ -#ifdef THREAD_DEBUG - printf("GUI: animationStarted()\n"); -#endif - - lockInGui(); - - animationRunning = true; - - if (isRenderBlocked) - wake(); - - unlockInGui(); -} - - - -void QQuickCanvasRenderThread::animationStopped() -{ -#ifdef THREAD_DEBUG - printf("GUI: animationStopped()...\n"); -#endif - - lockInGui(); - animationRunning = false; - unlockInGui(); -} - - -void QQuickCanvasRenderThread::paint() -{ -#ifdef THREAD_DEBUG - printf("GUI: paint called..\n"); -#endif - - lockInGui(); - exhaustSyncEvent(); - - isPaintCompleted = false; - while (isRunning() && !isPaintCompleted) { - if (isRenderBlocked) - wake(); - wait(); - } - unlockInGui(); -} - - - -void QQuickCanvasRenderThread::resize(const QSize &size) -{ -#ifdef THREAD_DEBUG - printf("GUI: Resize Event: %dx%d\n", size.width(), size.height()); -#endif - - if (!isRunning()) { - windowSize = size; - return; - } - - lockInGui(); - exhaustSyncEvent(); - - windowSize = size; - - while (isRunning() && renderedSize != windowSize) { - if (isRenderBlocked) - wake(); - wait(); - } - unlockInGui(); -} - - - -void QQuickCanvasRenderThread::startRendering() -{ -#ifdef THREAD_DEBUG - printf("GUI: Starting Render Thread\n"); -#endif - hasExited = false; - shouldExit = false; - isGuiBlocked = 0; - isGuiBlockPending = false; - start(); -} - - - -void QQuickCanvasRenderThread::stopRendering() -{ -#ifdef THREAD_DEBUG - printf("GUI: stopping render thread\n"); -#endif - - lockInGui(); - exhaustSyncEvent(); - shouldExit = true; - - if (isRenderBlocked) { -#ifdef THREAD_DEBUG - printf("GUI: waking up render thread\n"); -#endif - wake(); - } - - while (!hasExited) { -#ifdef THREAD_DEBUG - printf("GUI: waiting for render thread to have exited..\n"); -#endif - wait(); - } - - unlockInGui(); - -#ifdef THREAD_DEBUG - printf("GUI: waiting for render thread to terminate..\n"); -#endif - // Actually wait for the thread to terminate. Otherwise we can delete it - // too early and crash. - QThread::wait(); - -#ifdef THREAD_DEBUG - printf("GUI: thread has terminated and we're all good..\n"); -#endif - -} - - - -QImage QQuickCanvasRenderThread::grab() -{ - if (!isRunning()) - return QImage(); - - if (QThread::currentThread() != qApp->thread()) { - qWarning("QQuickCanvas::grabFrameBuffer: can only be called from the GUI thread"); - return QImage(); - } - -#ifdef THREAD_DEBUG - printf("GUI: doing a pixelwise grab..\n"); -#endif - - lockInGui(); - exhaustSyncEvent(); - - doGrab = true; - isPaintCompleted = false; - while (isRunning() && !isPaintCompleted) { - if (isRenderBlocked) - wake(); - wait(); - } - - QImage grabbed = grabContent; - grabContent = QImage(); - - unlockInGui(); - - return grabbed; -} - - - -void QQuickCanvasRenderThread::maybeUpdate() -{ - Q_ASSERT_X(QThread::currentThread() == QCoreApplication::instance()->thread() || inSync, - "QQuickCanvas::update", - "Function can only be called from GUI thread or during QQuickItem::updatePaintNode()"); - - if (inSync) { - isExternalUpdatePending = true; - - } else if (!renderThreadAwakened) { -#ifdef THREAD_DEBUG - printf("GUI: doing update...\n"); -#endif - renderThreadAwakened = true; - lockInGui(); - isExternalUpdatePending = true; - if (isRenderBlocked) - wake(); - unlockInGui(); - } -} - - -#include "moc_qquickcanvas.cpp" - -QT_END_NAMESPACE |