From 6d9d4e6817b116676320a1aba0ea0af1633205bc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tor=20Arne=20Vestb=C3=B8?= Date: Wed, 14 Aug 2019 14:07:55 +0200 Subject: Rename QWidgetBackingStore to QWidgetRepaintManager MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Quoting a blog from 2009, "this class is responsible for figuring out which parts of the window surface needs to be updated prior to showing it to screen, so it's really a repaint manager." https://blog.qt.io/blog/2009/12/16/qt-graphics-and-performance-an-overview/ What better time to do the rename than 10 years later! Change-Id: Ibf3c3bc8c7df64ac03d72e1f71d296b62d832fee Reviewed-by: Tor Arne Vestbø --- src/plugins/platforms/cocoa/qcocoabackingstore.mm | 2 +- .../platforms/qnx/qqnxscreeneventhandler.cpp | 2 +- src/widgets/kernel/kernel.pri | 4 +- src/widgets/kernel/qwidget.cpp | 97 +- src/widgets/kernel/qwidget.h | 2 +- src/widgets/kernel/qwidget_p.h | 20 +- src/widgets/kernel/qwidgetbackingstore.cpp | 1595 -------------------- src/widgets/kernel/qwidgetbackingstore_p.h | 281 ---- src/widgets/kernel/qwidgetrepaintmanager.cpp | 1595 ++++++++++++++++++++ src/widgets/kernel/qwidgetrepaintmanager_p.h | 281 ++++ src/widgets/kernel/qwidgetwindow.cpp | 6 +- tests/auto/widgets/kernel/qwidget/tst_qwidget.cpp | 18 +- 12 files changed, 1951 insertions(+), 1952 deletions(-) delete mode 100644 src/widgets/kernel/qwidgetbackingstore.cpp delete mode 100644 src/widgets/kernel/qwidgetbackingstore_p.h create mode 100644 src/widgets/kernel/qwidgetrepaintmanager.cpp create mode 100644 src/widgets/kernel/qwidgetrepaintmanager_p.h diff --git a/src/plugins/platforms/cocoa/qcocoabackingstore.mm b/src/plugins/platforms/cocoa/qcocoabackingstore.mm index 6015257f4e..15e0236107 100644 --- a/src/plugins/platforms/cocoa/qcocoabackingstore.mm +++ b/src/plugins/platforms/cocoa/qcocoabackingstore.mm @@ -153,7 +153,7 @@ void QNSWindowBackingStore::flush(QWindow *window, const QRegion ®ion, const // context is set up correctly (coordinate system, clipping, etc). Outside // of the normal display cycle there is no focused view, as explained above, // so we have to handle it manually. There's also a corner case inside the - // normal display cycle due to way QWidgetBackingStore composits native child + // normal display cycle due to way QWidgetRepaintManager composits native child // widgets, where we'll get a flush of a native child during the drawRect of // its parent/ancestor, and the parent/ancestor being the one locked by AppKit. // In this case we also need to lock and unlock focus manually. diff --git a/src/plugins/platforms/qnx/qqnxscreeneventhandler.cpp b/src/plugins/platforms/qnx/qqnxscreeneventhandler.cpp index 56131dcc48..a9b5860187 100644 --- a/src/plugins/platforms/qnx/qqnxscreeneventhandler.cpp +++ b/src/plugins/platforms/qnx/qqnxscreeneventhandler.cpp @@ -636,7 +636,7 @@ void QQnxScreenEventHandler::handleDisplayEvent(screen_event_t event) // We never remove the primary display, the qpa plugin doesn't support that and it crashes. // To support it, this would be needed: // - Adjust all qnx qpa code which uses screens - // - Make QWidgetBackingStore not dereference a null paint device + // - Make QWidgetRepaintManager not dereference a null paint device // - Create platform resources ( QQnxWindow ) for all QWindow because they would be deleted // when you delete the screen diff --git a/src/widgets/kernel/kernel.pri b/src/widgets/kernel/kernel.pri index a4b81335c5..693af7eb80 100644 --- a/src/widgets/kernel/kernel.pri +++ b/src/widgets/kernel/kernel.pri @@ -12,7 +12,7 @@ HEADERS += \ kernel/qactiongroup.h \ kernel/qapplication.h \ kernel/qapplication_p.h \ - kernel/qwidgetbackingstore_p.h \ + kernel/qwidgetrepaintmanager_p.h \ kernel/qboxlayout.h \ kernel/qdesktopwidget.h \ kernel/qgridlayout.h \ @@ -41,7 +41,7 @@ SOURCES += \ kernel/qaction.cpp \ kernel/qactiongroup.cpp \ kernel/qapplication.cpp \ - kernel/qwidgetbackingstore.cpp \ + kernel/qwidgetrepaintmanager.cpp \ kernel/qboxlayout.cpp \ kernel/qgridlayout.cpp \ kernel/qlayout.cpp \ diff --git a/src/widgets/kernel/qwidget.cpp b/src/widgets/kernel/qwidget.cpp index 066dfaa183..d1ad0c0a01 100644 --- a/src/widgets/kernel/qwidget.cpp +++ b/src/widgets/kernel/qwidget.cpp @@ -90,7 +90,7 @@ #include #endif #include -#include +#include #if 0 // Used to be included in Qt4 for Q_WS_MAC # include #endif @@ -869,11 +869,11 @@ QRegion qt_dirtyRegion(QWidget *widget) if (!widget) return QRegion(); - QWidgetBackingStore *bs = qt_widget_private(widget)->maybeBackingStore(); - if (!bs) + QWidgetRepaintManager *repaintManager = qt_widget_private(widget)->maybeRepaintManager(); + if (!repaintManager) return QRegion(); - return bs->dirtyRegion(widget); + return repaintManager->dirtyRegion(widget); } /***************************************************************************** @@ -1278,9 +1278,9 @@ void QWidget::create(WId window, bool initializeWindow, bool destroyOldWindow) setAttribute(Qt::WA_WState_Created); // set created flag d->create(); - // a real toplevel window needs a backing store + // A real toplevel window needs a paint manager if (isWindow() && windowType() != Qt::Desktop) - d->topData()->widgetBackingStore.reset(new QWidgetBackingStore(this)); + d->topData()->repaintManager.reset(new QWidgetRepaintManager(this)); d->setModal_sys(); @@ -1585,10 +1585,10 @@ QWidget::~QWidget() qApp->d_func()->sendSyntheticEnterLeave(this); } - if (QWidgetBackingStore *bs = d->maybeBackingStore()) { - bs->removeDirtyWidget(this); + if (QWidgetRepaintManager *repaintManager = d->maybeRepaintManager()) { + repaintManager->removeDirtyWidget(this); if (testAttribute(Qt::WA_StaticContents)) - bs->removeStaticWidget(this); + repaintManager->removeStaticWidget(this); } delete d->needsFlush; @@ -1804,7 +1804,7 @@ void QWidgetPrivate::deleteTLSysExtra() //the qplatformbackingstore may hold a reference to the window, so the backingstore //needs to be deleted first. - extra->topextra->widgetBackingStore.reset(nullptr); + extra->topextra->repaintManager.reset(nullptr); deleteBackingStore(this); #ifndef QT_NO_OPENGL extra->topextra->widgetTextures.clear(); @@ -1875,8 +1875,8 @@ void QWidgetPrivate::syncBackingStore() if (paintOnScreen()) { repaint_sys(dirty); dirty = QRegion(); - } else if (QWidgetBackingStore *bs = maybeBackingStore()) { - bs->sync(); + } else if (QWidgetRepaintManager *repaintManager = maybeRepaintManager()) { + repaintManager->sync(); } } @@ -1884,8 +1884,8 @@ void QWidgetPrivate::syncBackingStore(const QRegion ®ion) { if (paintOnScreen()) repaint_sys(region); - else if (QWidgetBackingStore *bs = maybeBackingStore()) { - bs->sync(q_func(), region); + else if (QWidgetRepaintManager *repaintManager = maybeRepaintManager()) { + repaintManager->sync(q_func(), region); } } @@ -5411,7 +5411,7 @@ void QWidgetPrivate::render_helper(QPainter *painter, const QPoint &targetOffset } void QWidgetPrivate::drawWidget(QPaintDevice *pdev, const QRegion &rgn, const QPoint &offset, int flags, - QPainter *sharedPainter, QWidgetBackingStore *backingStore) + QPainter *sharedPainter, QWidgetRepaintManager *repaintManager) { if (rgn.isEmpty()) return; @@ -5426,7 +5426,7 @@ void QWidgetPrivate::drawWidget(QPaintDevice *pdev, const QRegion &rgn, const QP QWidgetEffectSourcePrivate *sourced = static_cast (source->d_func()); if (!sourced->context) { - QWidgetPaintContext context(pdev, rgn, offset, flags, sharedPainter, backingStore); + QWidgetPaintContext context(pdev, rgn, offset, flags, sharedPainter, repaintManager); sourced->context = &context; if (!sharedPainter) { setSystemClip(pdev->paintEngine(), pdev->devicePixelRatioF(), rgn.translated(offset)); @@ -5452,8 +5452,8 @@ void QWidgetPrivate::drawWidget(QPaintDevice *pdev, const QRegion &rgn, const QP // Native widgets need to be marked dirty on screen so painting will be done in correct context // Same check as in the no effects case below. - if (backingStore && !onScreen && !asRoot && (q->internalWinId() || !q->nativeParentWidget()->isWindow())) - backingStore->markDirtyOnScreen(rgn, q, offset); + if (repaintManager && !onScreen && !asRoot && (q->internalWinId() || !q->nativeParentWidget()->isWindow())) + repaintManager->markDirtyOnScreen(rgn, q, offset); return; } @@ -5481,7 +5481,7 @@ void QWidgetPrivate::drawWidget(QPaintDevice *pdev, const QRegion &rgn, const QP //clip away the new area #ifndef QT_NO_PAINT_DEBUG - bool flushed = QWidgetBackingStore::flushPaint(q, toBePainted); + bool flushed = QWidgetRepaintManager::flushPaint(q, toBePainted); #endif QPaintEngine *paintEngine = pdev->paintEngine(); if (paintEngine) { @@ -5543,11 +5543,11 @@ void QWidgetPrivate::drawWidget(QPaintDevice *pdev, const QRegion &rgn, const QP // This widget renders into a texture which is composed later. We just need to // punch a hole in the backingstore, so the texture will be visible. beginBackingStorePainting(); - if (!q->testAttribute(Qt::WA_AlwaysStackOnTop) && backingStore) { + if (!q->testAttribute(Qt::WA_AlwaysStackOnTop) && repaintManager) { QPainter p(q); p.setCompositionMode(QPainter::CompositionMode_Source); p.fillRect(q->rect(), Qt::transparent); - } else if (!backingStore) { + } else if (!repaintManager) { // We are not drawing to a backingstore: fall back to QImage QImage img = grabFramebuffer(); // grabFramebuffer() always sets the format to RGB32 @@ -5572,8 +5572,8 @@ void QWidgetPrivate::drawWidget(QPaintDevice *pdev, const QRegion &rgn, const QP } // Native widgets need to be marked dirty on screen so painting will be done in correct context - if (backingStore && !onScreen && !asRoot && (q->internalWinId() || (q->nativeParentWidget() && !q->nativeParentWidget()->isWindow()))) - backingStore->markDirtyOnScreen(toBePainted, q, offset); + if (repaintManager && !onScreen && !asRoot && (q->internalWinId() || (q->nativeParentWidget() && !q->nativeParentWidget()->isWindow()))) + repaintManager->markDirtyOnScreen(toBePainted, q, offset); //restore if (paintEngine) { @@ -5599,7 +5599,7 @@ void QWidgetPrivate::drawWidget(QPaintDevice *pdev, const QRegion &rgn, const QP #ifndef QT_NO_PAINT_DEBUG if (flushed) - QWidgetBackingStore::unflushPaint(q, toBePainted); + QWidgetRepaintManager::unflushPaint(q, toBePainted); #endif } else if (q->isWindow()) { QPaintEngine *engine = pdev->paintEngine(); @@ -5619,8 +5619,8 @@ void QWidgetPrivate::drawWidget(QPaintDevice *pdev, const QRegion &rgn, const QP } if (recursive && !children.isEmpty()) { - paintSiblingsRecursive(pdev, children, children.size() - 1, rgn, offset, flags & ~DrawAsRoot - , sharedPainter, backingStore); + paintSiblingsRecursive(pdev, children, children.size() - 1, rgn, offset, flags & ~DrawAsRoot, + sharedPainter, repaintManager); } } @@ -5712,7 +5712,7 @@ void QWidgetPrivate::render(QPaintDevice *target, const QPoint &targetOffset, void QWidgetPrivate::paintSiblingsRecursive(QPaintDevice *pdev, const QObjectList& siblings, int index, const QRegion &rgn, const QPoint &offset, int flags - , QPainter *sharedPainter, QWidgetBackingStore *backingStore) + , QPainter *sharedPainter, QWidgetRepaintManager *repaintManager) { QWidget *w = 0; QRect boundingRect; @@ -5747,8 +5747,8 @@ void QWidgetPrivate::paintSiblingsRecursive(QPaintDevice *pdev, const QObjectLis QRegion wr(rgn); if (wd->isOpaque) wr -= hasMask ? wd->extra->mask.translated(widgetPos) : w->data->crect; - paintSiblingsRecursive(pdev, siblings, --index, wr, offset, flags - , sharedPainter, backingStore); + paintSiblingsRecursive(pdev, siblings, --index, wr, offset, flags, + sharedPainter, repaintManager); } if (w->updatesEnabled() @@ -5761,7 +5761,7 @@ void QWidgetPrivate::paintSiblingsRecursive(QPaintDevice *pdev, const QObjectLis wRegion.translate(-widgetPos); if (hasMask) wRegion &= wd->extra->mask; - wd->drawWidget(pdev, wRegion, offset + widgetPos, flags, sharedPainter, backingStore); + wd->drawWidget(pdev, wRegion, offset + widgetPos, flags, sharedPainter, repaintManager); } } @@ -5796,7 +5796,7 @@ void QWidgetEffectSourcePrivate::draw(QPainter *painter) toBePainted &= wd->extra->mask; wd->drawWidget(context->pdev, toBePainted, context->offset, context->flags, - context->sharedPainter, context->backingStore); + context->sharedPainter, context->repaintManager); } QPixmap QWidgetEffectSourcePrivate::pixmap(Qt::CoordinateSystem system, QPoint *offset, @@ -8173,8 +8173,8 @@ void QWidgetPrivate::hide_helper() } } - if (QWidgetBackingStore *bs = maybeBackingStore()) - bs->removeDirtyWidget(q); + if (QWidgetRepaintManager *repaintManager = maybeRepaintManager()) + repaintManager->removeDirtyWidget(q); #ifndef QT_NO_ACCESSIBILITY if (wasVisible) { @@ -10680,12 +10680,12 @@ void QWidget::setParent(QWidget *parent, Qt::WindowFlags f) } #endif - if (QWidgetBackingStore *oldBs = oldtlw->d_func()->maybeBackingStore()) { + if (QWidgetRepaintManager *oldPaintManager = oldtlw->d_func()->maybeRepaintManager()) { if (newParent) - oldBs->removeDirtyWidget(this); + oldPaintManager->removeDirtyWidget(this); // Move the widget and all its static children from // the old backing store to the new one. - oldBs->moveStaticWidgets(this); + oldPaintManager->moveStaticWidgets(this); } // ### fixme: Qt 6: Remove AA_ImmediateWidgetCreation. @@ -11039,7 +11039,7 @@ void QWidgetPrivate::repaint(T r) QTLWExtra *tlwExtra = q->window()->d_func()->maybeTopData(); if (tlwExtra && !tlwExtra->inTopLevelResize && tlwExtra->backingStore) - tlwExtra->widgetBackingStore->markDirty(r, q, QWidgetBackingStore::UpdateNow); + tlwExtra->repaintManager->markDirty(r, q, QWidgetRepaintManager::UpdateNow); } /*! @@ -11114,7 +11114,7 @@ void QWidgetPrivate::update(T r) QTLWExtra *tlwExtra = q->window()->d_func()->maybeTopData(); if (tlwExtra && !tlwExtra->inTopLevelResize && tlwExtra->backingStore) - tlwExtra->widgetBackingStore->markDirty(clipped, q); + tlwExtra->repaintManager->markDirty(clipped, q); } /*! @@ -11395,11 +11395,11 @@ void QWidget::setAttribute(Qt::WidgetAttribute attribute, bool on) break; case Qt::WA_StaticContents: - if (QWidgetBackingStore *bs = d->maybeBackingStore()) { + if (QWidgetRepaintManager *repaintManager = d->maybeRepaintManager()) { if (on) - bs->addStaticWidget(this); + repaintManager->addStaticWidget(this); else - bs->removeStaticWidget(this); + repaintManager->removeStaticWidget(this); } break; case Qt::WA_TranslucentBackground: @@ -12204,14 +12204,14 @@ void QWidget::setBackingStore(QBackingStore *store) deleteBackingStore(d); topData->backingStore = store; - QWidgetBackingStore *bs = d->maybeBackingStore(); - if (!bs) + QWidgetRepaintManager *repaintManager = d->maybeRepaintManager(); + if (!repaintManager) return; if (isTopLevel()) { - if (bs->store != oldStore && bs->store != store) - delete bs->store; - bs->store = store; + if (repaintManager->store != oldStore && repaintManager->store != store) + delete repaintManager->store; + repaintManager->store = store; } } @@ -12227,9 +12227,8 @@ QBackingStore *QWidget::backingStore() const if (extra && extra->backingStore) return extra->backingStore; - QWidgetBackingStore *bs = d->maybeBackingStore(); - - return bs ? bs->store : 0; + QWidgetRepaintManager *repaintManager = d->maybeRepaintManager(); + return repaintManager ? repaintManager->store : nullptr; } void QWidgetPrivate::getLayoutItemMargins(int *left, int *top, int *right, int *bottom) const diff --git a/src/widgets/kernel/qwidget.h b/src/widgets/kernel/qwidget.h index 0777bed65c..f7ec7f9cf1 100644 --- a/src/widgets/kernel/qwidget.h +++ b/src/widgets/kernel/qwidget.h @@ -697,7 +697,7 @@ private: QLayout *takeLayout(); friend class QBackingStoreDevice; - friend class QWidgetBackingStore; + friend class QWidgetRepaintManager; friend class QApplication; friend class QApplicationPrivate; friend class QGuiApplication; diff --git a/src/widgets/kernel/qwidget_p.h b/src/widgets/kernel/qwidget_p.h index b4a9d283db..c3b3a2ed93 100644 --- a/src/widgets/kernel/qwidget_p.h +++ b/src/widgets/kernel/qwidget_p.h @@ -86,7 +86,7 @@ QT_BEGIN_NAMESPACE class QWidgetWindow; class QPaintEngine; class QPixmap; -class QWidgetBackingStore; +class QWidgetRepaintManager; class QGraphicsProxyWidget; class QWidgetItemV2; class QOpenGLContext; @@ -121,7 +121,7 @@ struct QTLWExtra { // Regular pointers (keep them together to avoid gaps on 64 bits architectures). std::unique_ptr icon; // widget icon - std::unique_ptr widgetBackingStore; + std::unique_ptr repaintManager; QBackingStore *backingStore; QPainter *sharedPainter; QWidgetWindow *window; @@ -305,7 +305,7 @@ public: QTLWExtra *maybeTopData() const; QPainter *sharedPainter() const; void setSharedPainter(QPainter *painter); - QWidgetBackingStore *maybeBackingStore() const; + QWidgetRepaintManager *maybeRepaintManager() const; enum class WindowHandleMode { Direct, @@ -384,13 +384,13 @@ public: void render(QPaintDevice *target, const QPoint &targetOffset, const QRegion &sourceRegion, QWidget::RenderFlags renderFlags); void drawWidget(QPaintDevice *pdev, const QRegion &rgn, const QPoint &offset, int flags, - QPainter *sharedPainter = nullptr, QWidgetBackingStore *backingStore = nullptr); + QPainter *sharedPainter = nullptr, QWidgetRepaintManager *repaintManager = nullptr); void sendPaintEvent(const QRegion &toBePainted); void paintSiblingsRecursive(QPaintDevice *pdev, const QObjectList& children, int index, const QRegion &rgn, const QPoint &offset, int flags, - QPainter *sharedPainter, QWidgetBackingStore *backingStore); + QPainter *sharedPainter, QWidgetRepaintManager *repaintManager); #if QT_CONFIG(graphicsview) static QGraphicsProxyWidget * nearestGraphicsProxyWidget(const QWidget *origin); @@ -876,15 +876,15 @@ public: struct QWidgetPaintContext { inline QWidgetPaintContext(QPaintDevice *d, const QRegion &r, const QPoint &o, int f, - QPainter *p, QWidgetBackingStore *b) - : pdev(d), rgn(r), offset(o), flags(f), sharedPainter(p), backingStore(b), painter(nullptr) {} + QPainter *p, QWidgetRepaintManager *rpm) + : pdev(d), rgn(r), offset(o), flags(f), sharedPainter(p), repaintManager(rpm), painter(nullptr) {} QPaintDevice *pdev; QRegion rgn; QPoint offset; int flags; QPainter *sharedPainter; - QWidgetBackingStore *backingStore; + QWidgetRepaintManager *repaintManager; QPainter *painter; }; @@ -980,11 +980,11 @@ inline bool QWidgetPrivate::pointInsideRectAndMask(const QPoint &p) const || extra->mask.contains(p)); } -inline QWidgetBackingStore *QWidgetPrivate::maybeBackingStore() const +inline QWidgetRepaintManager *QWidgetPrivate::maybeRepaintManager() const { Q_Q(const QWidget); QTLWExtra *x = q->window()->d_func()->maybeTopData(); - return x ? x->widgetBackingStore.get() : nullptr; + return x ? x->repaintManager.get() : nullptr; } QT_END_NAMESPACE diff --git a/src/widgets/kernel/qwidgetbackingstore.cpp b/src/widgets/kernel/qwidgetbackingstore.cpp deleted file mode 100644 index 1b963010d1..0000000000 --- a/src/widgets/kernel/qwidgetbackingstore.cpp +++ /dev/null @@ -1,1595 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtWidgets module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - - -#include "qplatformdefs.h" - -#include "qwidgetbackingstore_p.h" - -#include -#include -#include -#include -#include -#include -#if QT_CONFIG(graphicsview) -#include -#endif - -#include -#include -#include -#if QT_CONFIG(graphicseffect) -#include -#endif -#include -#include - -#include - -#if defined(Q_OS_WIN) && !defined(QT_NO_PAINT_DEBUG) -# include -# include -#endif - -#include - -QT_BEGIN_NAMESPACE - -extern QRegion qt_dirtyRegion(QWidget *); - -#ifndef QT_NO_OPENGL -Q_GLOBAL_STATIC(QPlatformTextureList, qt_dummy_platformTextureList) -#endif - -static bool hasPlatformWindow(QWidget *widget) -{ - return widget && widget->windowHandle() && widget->windowHandle()->handle(); -} - -/** - * Flushes the contents of the \a backingStore into the screen area of \a widget. - * \a region is the region to be updated in \a widget coordinates. - */ -void QWidgetBackingStore::qt_flush(QWidget *widget, const QRegion ®ion, QBackingStore *backingStore, - QWidget *tlw, QPlatformTextureList *widgetTextures, - QWidgetBackingStore *widgetBackingStore) -{ -#ifdef QT_NO_OPENGL - Q_UNUSED(widgetTextures); - Q_ASSERT(!region.isEmpty()); -#else - Q_ASSERT(!region.isEmpty() || widgetTextures); -#endif - Q_ASSERT(widget); - Q_ASSERT(backingStore); - Q_ASSERT(tlw); -#if !defined(QT_NO_PAINT_DEBUG) - static int flushUpdate = qEnvironmentVariableIntValue("QT_FLUSH_UPDATE"); - if (flushUpdate > 0) - QWidgetBackingStore::showYellowThing(widget, region, flushUpdate * 10, false); -#endif - - if (tlw->testAttribute(Qt::WA_DontShowOnScreen) || widget->testAttribute(Qt::WA_DontShowOnScreen)) - return; - - // Foreign Windows do not have backing store content and must not be flushed - if (QWindow *widgetWindow = widget->windowHandle()) { - if (widgetWindow->type() == Qt::ForeignWindow) - return; - } - - static bool fpsDebug = qEnvironmentVariableIntValue("QT_DEBUG_FPS"); - if (fpsDebug) { - if (!widgetBackingStore->perfFrames++) - widgetBackingStore->perfTime.start(); - if (widgetBackingStore->perfTime.elapsed() > 5000) { - double fps = double(widgetBackingStore->perfFrames * 1000) / widgetBackingStore->perfTime.restart(); - qDebug("FPS: %.1f\n", fps); - widgetBackingStore->perfFrames = 0; - } - } - - QPoint offset; - if (widget != tlw) - offset += widget->mapTo(tlw, QPoint()); - - QRegion effectiveRegion = region; -#ifndef QT_NO_OPENGL - const bool compositionWasActive = widget->d_func()->renderToTextureComposeActive; - if (!widgetTextures) { - widget->d_func()->renderToTextureComposeActive = false; - // Detect the case of falling back to the normal flush path when no - // render-to-texture widgets are visible anymore. We will force one - // last flush to go through the OpenGL-based composition to prevent - // artifacts. The next flush after this one will use the normal path. - if (compositionWasActive) - widgetTextures = qt_dummy_platformTextureList; - } else { - widget->d_func()->renderToTextureComposeActive = true; - } - // When changing the composition status, make sure the dirty region covers - // the entire widget. Just having e.g. the shown/hidden render-to-texture - // widget's area marked as dirty is incorrect when changing flush paths. - if (compositionWasActive != widget->d_func()->renderToTextureComposeActive) - effectiveRegion = widget->rect(); - - // re-test since we may have been forced to this path via the dummy texture list above - if (widgetTextures) { - qt_window_private(tlw->windowHandle())->compositing = true; - widget->window()->d_func()->sendComposeStatus(widget->window(), false); - // A window may have alpha even when the app did not request - // WA_TranslucentBackground. Therefore the compositor needs to know whether the app intends - // to rely on translucency, in order to decide if it should clear to transparent or opaque. - const bool translucentBackground = widget->testAttribute(Qt::WA_TranslucentBackground); - backingStore->handle()->composeAndFlush(widget->windowHandle(), effectiveRegion, offset, - widgetTextures, translucentBackground); - widget->window()->d_func()->sendComposeStatus(widget->window(), true); - } else -#endif - backingStore->flush(effectiveRegion, widget->windowHandle(), offset); -} - -#ifndef QT_NO_PAINT_DEBUG -#if defined(Q_OS_WIN) && !defined(Q_OS_WINRT) - -static void showYellowThing_win(QWidget *widget, const QRegion ®ion, int msec) -{ - // We expect to be passed a native parent. - QWindow *nativeWindow = widget->windowHandle(); - if (!nativeWindow) - return; - void *hdcV = QGuiApplication::platformNativeInterface()->nativeResourceForWindow(QByteArrayLiteral("getDC"), nativeWindow); - if (!hdcV) - return; - const HDC hdc = reinterpret_cast(hdcV); - - static const COLORREF colors[] = {RGB(255, 255, 0), RGB(255, 200, 55), RGB(200, 255, 55), RGB(200, 200, 0)}; - - static size_t i = 0; - const HBRUSH brush = CreateSolidBrush(colors[i]); - i = (i + 1) % (sizeof(colors) / sizeof(colors[0])); - - for (const QRect &rect : region) { - RECT winRect; - SetRect(&winRect, rect.left(), rect.top(), rect.right(), rect.bottom()); - FillRect(hdc, &winRect, brush); - } - DeleteObject(brush); - QGuiApplication::platformNativeInterface()->nativeResourceForWindow(QByteArrayLiteral("releaseDC"), nativeWindow); - ::Sleep(msec); -} -#endif // defined(Q_OS_WIN) && !defined(Q_OS_WINRT) - -void QWidgetBackingStore::showYellowThing(QWidget *widget, const QRegion &toBePainted, int msec, bool unclipped) -{ -#ifdef Q_OS_WINRT - Q_UNUSED(msec) -#endif - QRegion paintRegion = toBePainted; - QRect widgetRect = widget->rect(); - - if (!hasPlatformWindow(widget)) { - QWidget *nativeParent = widget->nativeParentWidget(); - const QPoint offset = widget->mapTo(nativeParent, QPoint(0, 0)); - paintRegion.translate(offset); - widgetRect.translate(offset); - widget = nativeParent; - } - -#if defined(Q_OS_WIN) && !defined(Q_OS_WINRT) - Q_UNUSED(unclipped); - showYellowThing_win(widget, paintRegion, msec); -#else - //flags to fool painter - bool paintUnclipped = widget->testAttribute(Qt::WA_PaintUnclipped); - if (unclipped && !widget->d_func()->paintOnScreen()) - widget->setAttribute(Qt::WA_PaintUnclipped); - - const bool setFlag = !widget->testAttribute(Qt::WA_WState_InPaintEvent); - if (setFlag) - widget->setAttribute(Qt::WA_WState_InPaintEvent); - - //setup the engine - QPaintEngine *pe = widget->paintEngine(); - if (pe) { - pe->setSystemClip(paintRegion); - { - QPainter p(widget); - p.setClipRegion(paintRegion); - static int i = 0; - switch (i) { - case 0: - p.fillRect(widgetRect, QColor(255,255,0)); - break; - case 1: - p.fillRect(widgetRect, QColor(255,200,55)); - break; - case 2: - p.fillRect(widgetRect, QColor(200,255,55)); - break; - case 3: - p.fillRect(widgetRect, QColor(200,200,0)); - break; - } - i = (i+1) & 3; - p.end(); - } - } - - if (setFlag) - widget->setAttribute(Qt::WA_WState_InPaintEvent, false); - - //restore - widget->setAttribute(Qt::WA_PaintUnclipped, paintUnclipped); - - if (pe) - pe->setSystemClip(QRegion()); - -#if defined(Q_OS_UNIX) - ::usleep(1000 * msec); -#endif -#endif // !Q_OS_WIN -} - -bool QWidgetBackingStore::flushPaint(QWidget *widget, const QRegion &rgn) -{ - if (!widget) - return false; - - int delay = 0; - if (widget->testAttribute(Qt::WA_WState_InPaintEvent)) { - static int flushPaintEvent = qEnvironmentVariableIntValue("QT_FLUSH_PAINT_EVENT"); - if (!flushPaintEvent) - return false; - delay = flushPaintEvent; - } else { - static int flushPaint = qEnvironmentVariableIntValue("QT_FLUSH_PAINT"); - if (!flushPaint) - return false; - delay = flushPaint; - } - - QWidgetBackingStore::showYellowThing(widget, rgn, delay * 10, true); - return true; -} - -void QWidgetBackingStore::unflushPaint(QWidget *widget, const QRegion &rgn) -{ - if (widget->d_func()->paintOnScreen() || rgn.isEmpty()) - return; - - QWidget *tlw = widget->window(); - QTLWExtra *tlwExtra = tlw->d_func()->maybeTopData(); - if (!tlwExtra) - return; - - qt_flush(widget, rgn, tlwExtra->widgetBackingStore->store, tlw, 0, tlw->d_func()->maybeBackingStore()); -} -#endif // QT_NO_PAINT_DEBUG - -/* - Moves the whole rect by (dx, dy) in widget's coordinate system. - Doesn't generate any updates. -*/ -bool QWidgetBackingStore::bltRect(const QRect &rect, int dx, int dy, QWidget *widget) -{ - const QPoint pos(widget->mapTo(tlw, rect.topLeft())); - const QRect tlwRect(QRect(pos, rect.size())); - if (dirty.intersects(tlwRect)) - return false; // We don't want to scroll junk. - return store->scroll(tlwRect, dx, dy); -} - -/*! - Prepares the window surface to paint a\ toClean region of the \a widget and - updates the BeginPaintInfo struct accordingly. - - The \a toClean region might be clipped by the window surface. -*/ -void QWidgetBackingStore::beginPaint(QRegion &toClean, QWidget *widget, QBackingStore *backingStore, - BeginPaintInfo *returnInfo, bool toCleanIsInTopLevelCoordinates) -{ - Q_UNUSED(widget); - Q_UNUSED(toCleanIsInTopLevelCoordinates); - - // Always flush repainted areas. - dirtyOnScreen += toClean; - -#ifdef QT_NO_PAINT_DEBUG - backingStore->beginPaint(toClean); -#else - returnInfo->wasFlushed = QWidgetBackingStore::flushPaint(tlw, toClean); - // Avoid deadlock with QT_FLUSH_PAINT: the server will wait for - // the BackingStore lock, so if we hold that, the server will - // never release the Communication lock that we are waiting for in - // sendSynchronousCommand - if (!returnInfo->wasFlushed) - backingStore->beginPaint(toClean); -#endif - - Q_UNUSED(returnInfo); -} - -void QWidgetBackingStore::endPaint(const QRegion &cleaned, QBackingStore *backingStore, - BeginPaintInfo *beginPaintInfo) -{ -#ifndef QT_NO_PAINT_DEBUG - if (!beginPaintInfo->wasFlushed) - backingStore->endPaint(); - else - QWidgetBackingStore::unflushPaint(tlw, cleaned); -#else - Q_UNUSED(beginPaintInfo); - Q_UNUSED(cleaned); - backingStore->endPaint(); -#endif - - flush(); -} - -/*! - Returns the region (in top-level coordinates) that needs repaint and/or flush. - - If the widget is non-zero, only the dirty region for the widget is returned - and the region will be in widget coordinates. -*/ -QRegion QWidgetBackingStore::dirtyRegion(QWidget *widget) const -{ - const bool widgetDirty = widget && widget != tlw; - const QRect tlwRect(topLevelRect()); - const QRect surfaceGeometry(tlwRect.topLeft(), store->size()); - if (surfaceGeometry != tlwRect && surfaceGeometry.size() != tlwRect.size()) { - if (widgetDirty) { - const QRect dirtyTlwRect = QRect(QPoint(), tlwRect.size()); - const QPoint offset(widget->mapTo(tlw, QPoint())); - const QRect dirtyWidgetRect(dirtyTlwRect & widget->rect().translated(offset)); - return dirtyWidgetRect.translated(-offset); - } - return QRect(QPoint(), tlwRect.size()); - } - - // Calculate the region that needs repaint. - QRegion r(dirty); - for (int i = 0; i < dirtyWidgets.size(); ++i) { - QWidget *w = dirtyWidgets.at(i); - if (widgetDirty && w != widget && !widget->isAncestorOf(w)) - continue; - r += w->d_func()->dirty.translated(w->mapTo(tlw, QPoint())); - } - - // Append the region that needs flush. - r += dirtyOnScreen; - - for (QWidget *w : dirtyOnScreenWidgets) { - if (widgetDirty && w != widget && !widget->isAncestorOf(w)) - continue; - QWidgetPrivate *wd = w->d_func(); - Q_ASSERT(wd->needsFlush); - r += wd->needsFlush->translated(w->mapTo(tlw, QPoint())); - } - - if (widgetDirty) { - // Intersect with the widget geometry and translate to its coordinates. - const QPoint offset(widget->mapTo(tlw, QPoint())); - r &= widget->rect().translated(offset); - r.translate(-offset); - } - return r; -} - -/*! - Returns the static content inside the \a parent if non-zero; otherwise the static content - for the entire backing store is returned. The content will be clipped to \a withinClipRect - if non-empty. -*/ -QRegion QWidgetBackingStore::staticContents(QWidget *parent, const QRect &withinClipRect) const -{ - if (!parent && tlw->testAttribute(Qt::WA_StaticContents)) { - const QSize surfaceGeometry(store->size()); - QRect surfaceRect(0, 0, surfaceGeometry.width(), surfaceGeometry.height()); - if (!withinClipRect.isEmpty()) - surfaceRect &= withinClipRect; - return QRegion(surfaceRect); - } - - QRegion region; - if (parent && parent->d_func()->children.isEmpty()) - return region; - - const bool clipToRect = !withinClipRect.isEmpty(); - const int count = staticWidgets.count(); - for (int i = 0; i < count; ++i) { - QWidget *w = staticWidgets.at(i); - QWidgetPrivate *wd = w->d_func(); - if (!wd->isOpaque || !wd->extra || wd->extra->staticContentsSize.isEmpty() - || !w->isVisible() || (parent && !parent->isAncestorOf(w))) { - continue; - } - - QRect rect(0, 0, wd->extra->staticContentsSize.width(), wd->extra->staticContentsSize.height()); - const QPoint offset = w->mapTo(parent ? parent : tlw, QPoint()); - if (clipToRect) - rect &= withinClipRect.translated(-offset); - if (rect.isEmpty()) - continue; - - rect &= wd->clipRect(); - if (rect.isEmpty()) - continue; - - QRegion visible(rect); - wd->clipToEffectiveMask(visible); - if (visible.isEmpty()) - continue; - wd->subtractOpaqueSiblings(visible, 0, /*alsoNonOpaque=*/true); - - visible.translate(offset); - region += visible; - } - - return region; -} - -void QWidgetBackingStore::sendUpdateRequest(QWidget *widget, UpdateTime updateTime) -{ - if (!widget) - return; - -#ifndef QT_NO_OPENGL - // Having every repaint() leading to a sync/flush is bad as it causes - // compositing and waiting for vsync each and every time. Change to - // UpdateLater, except for approx. once per frame to prevent starvation in - // case the control does not get back to the event loop. - QWidget *w = widget->window(); - if (updateTime == UpdateNow && w && w->windowHandle() && QWindowPrivate::get(w->windowHandle())->compositing) { - int refresh = 60; - QScreen *ws = w->windowHandle()->screen(); - if (ws) - refresh = ws->refreshRate(); - QWindowPrivate *wd = QWindowPrivate::get(w->windowHandle()); - if (wd->lastComposeTime.isValid()) { - const qint64 elapsed = wd->lastComposeTime.elapsed(); - if (elapsed <= qint64(1000.0f / refresh)) - updateTime = UpdateLater; - } - } -#endif - - switch (updateTime) { - case UpdateLater: - updateRequestSent = true; - QCoreApplication::postEvent(widget, new QEvent(QEvent::UpdateRequest), Qt::LowEventPriority); - break; - case UpdateNow: { - QEvent event(QEvent::UpdateRequest); - QCoreApplication::sendEvent(widget, &event); - break; - } - } -} - -static inline QRect widgetRectFor(QWidget *, const QRect &r) { return r; } -static inline QRect widgetRectFor(QWidget *widget, const QRegion &) { return widget->rect(); } - -/*! - Marks the region of the widget as dirty (if not already marked as dirty) and - posts an UpdateRequest event to the top-level widget (if not already posted). - - If updateTime is UpdateNow, the event is sent immediately instead of posted. - - If bufferState is BufferInvalid, all widgets intersecting with the region will be dirty. - - If the widget paints directly on screen, the event is sent to the widget - instead of the top-level widget, and bufferState is completely ignored. -*/ -template -void QWidgetBackingStore::markDirty(const T &r, QWidget *widget, UpdateTime updateTime, BufferState bufferState) -{ - Q_ASSERT(tlw->d_func()->extra); - Q_ASSERT(tlw->d_func()->extra->topextra); - Q_ASSERT(!tlw->d_func()->extra->topextra->inTopLevelResize); - Q_ASSERT(widget->isVisible() && widget->updatesEnabled()); - Q_ASSERT(widget->window() == tlw); - Q_ASSERT(!r.isEmpty()); - -#if QT_CONFIG(graphicseffect) - widget->d_func()->invalidateGraphicsEffectsRecursively(); -#endif - - QRect widgetRect = widgetRectFor(widget, r); - - // --------------------------------------------------------------------------- - - if (widget->d_func()->paintOnScreen()) { - if (widget->d_func()->dirty.isEmpty()) { - widget->d_func()->dirty = r; - sendUpdateRequest(widget, updateTime); - return; - } else if (qt_region_strictContains(widget->d_func()->dirty, widgetRect)) { - if (updateTime == UpdateNow) - sendUpdateRequest(widget, updateTime); - return; // Already dirty - } - - const bool eventAlreadyPosted = !widget->d_func()->dirty.isEmpty(); - widget->d_func()->dirty += r; - if (!eventAlreadyPosted || updateTime == UpdateNow) - sendUpdateRequest(widget, updateTime); - return; - } - - // --------------------------------------------------------------------------- - - if (QWidgetPrivate::get(widget)->renderToTexture) { - if (!widget->d_func()->inDirtyList) - addDirtyRenderToTextureWidget(widget); - if (!updateRequestSent || updateTime == UpdateNow) - sendUpdateRequest(tlw, updateTime); - return; - } - - // --------------------------------------------------------------------------- - - QRect effectiveWidgetRect = widget->d_func()->effectiveRectFor(widgetRect); - const QPoint offset = widget->mapTo(tlw, QPoint()); - QRect translatedRect = effectiveWidgetRect.translated(offset); -#if QT_CONFIG(graphicseffect) - // Graphics effects may exceed window size, clamp - translatedRect = translatedRect.intersected(QRect(QPoint(), tlw->size())); -#endif - if (qt_region_strictContains(dirty, translatedRect)) { - if (updateTime == UpdateNow) - sendUpdateRequest(tlw, updateTime); - return; // Already dirty - } - - // --------------------------------------------------------------------------- - - if (bufferState == BufferInvalid) { - const bool eventAlreadyPosted = !dirty.isEmpty() || updateRequestSent; -#if QT_CONFIG(graphicseffect) - if (widget->d_func()->graphicsEffect) - dirty += widget->d_func()->effectiveRectFor(r).translated(offset); - else -#endif - dirty += r.translated(offset); - - if (!eventAlreadyPosted || updateTime == UpdateNow) - sendUpdateRequest(tlw, updateTime); - return; - } - - // --------------------------------------------------------------------------- - - if (dirtyWidgets.isEmpty()) { - addDirtyWidget(widget, r); - sendUpdateRequest(tlw, updateTime); - return; - } - - // --------------------------------------------------------------------------- - - if (widget->d_func()->inDirtyList) { - if (!qt_region_strictContains(widget->d_func()->dirty, effectiveWidgetRect)) { -#if QT_CONFIG(graphicseffect) - if (widget->d_func()->graphicsEffect) - widget->d_func()->dirty += widget->d_func()->effectiveRectFor(r); - else -#endif - widget->d_func()->dirty += r; - } - } else { - addDirtyWidget(widget, r); - } - - // --------------------------------------------------------------------------- - - if (updateTime == UpdateNow) - sendUpdateRequest(tlw, updateTime); -} -template void QWidgetBackingStore::markDirty(const QRect &, QWidget *, UpdateTime, BufferState); -template void QWidgetBackingStore::markDirty(const QRegion &, QWidget *, UpdateTime, BufferState); - -/*! - Marks the \a region of the \a widget as dirty on screen. The \a region will be copied from - the backing store to the \a widget's native parent next time flush() is called. - - Paint on screen widgets are ignored. -*/ -void QWidgetBackingStore::markDirtyOnScreen(const QRegion ®ion, QWidget *widget, const QPoint &topLevelOffset) -{ - if (!widget || widget->d_func()->paintOnScreen() || region.isEmpty()) - return; - -#if 0 // Used to be included in Qt4 for Q_WS_MAC - if (!widget->testAttribute(Qt::WA_WState_InPaintEvent)) - dirtyOnScreen += region.translated(topLevelOffset); - return; -#endif - - // Top-level. - if (widget == tlw) { - if (!widget->testAttribute(Qt::WA_WState_InPaintEvent)) - dirtyOnScreen += region; - return; - } - - // Alien widgets. - if (!hasPlatformWindow(widget) && !widget->isWindow()) { - QWidget *nativeParent = widget->nativeParentWidget(); // Alien widgets with the top-level as the native parent (common case). - if (nativeParent == tlw) { - if (!widget->testAttribute(Qt::WA_WState_InPaintEvent)) - dirtyOnScreen += region.translated(topLevelOffset); - return; - } - - // Alien widgets with native parent != tlw. - QWidgetPrivate *nativeParentPrivate = nativeParent->d_func(); - if (!nativeParentPrivate->needsFlush) - nativeParentPrivate->needsFlush = new QRegion; - const QPoint nativeParentOffset = widget->mapTo(nativeParent, QPoint()); - *nativeParentPrivate->needsFlush += region.translated(nativeParentOffset); - appendDirtyOnScreenWidget(nativeParent); - return; - } - - // Native child widgets. - QWidgetPrivate *widgetPrivate = widget->d_func(); - if (!widgetPrivate->needsFlush) - widgetPrivate->needsFlush = new QRegion; - *widgetPrivate->needsFlush += region; - appendDirtyOnScreenWidget(widget); -} - -void QWidgetBackingStore::removeDirtyWidget(QWidget *w) -{ - if (!w) - return; - - dirtyWidgets.removeAll(w); - dirtyOnScreenWidgets.removeAll(w); - dirtyRenderToTextureWidgets.removeAll(w); - resetWidget(w); - - QWidgetPrivate *wd = w->d_func(); - const int n = wd->children.count(); - for (int i = 0; i < n; ++i) { - if (QWidget *child = qobject_cast(wd->children.at(i))) - removeDirtyWidget(child); - } -} - -void QWidgetBackingStore::updateLists(QWidget *cur) -{ - if (!cur) - return; - - QList children = cur->children(); - for (int i = 0; i < children.size(); ++i) { - QWidget *child = qobject_cast(children.at(i)); - if (!child || child->isWindow()) - continue; - - updateLists(child); - } - - if (cur->testAttribute(Qt::WA_StaticContents)) - addStaticWidget(cur); -} - -QWidgetBackingStore::QWidgetBackingStore(QWidget *topLevel) - : tlw(topLevel), - updateRequestSent(0), - textureListWatcher(0), - perfFrames(0) -{ - store = tlw->backingStore(); - Q_ASSERT(store); - - // Ensure all existing subsurfaces and static widgets are added to their respective lists. - updateLists(topLevel); -} - -QWidgetBackingStore::~QWidgetBackingStore() -{ - for (int c = 0; c < dirtyWidgets.size(); ++c) - resetWidget(dirtyWidgets.at(c)); - for (int c = 0; c < dirtyRenderToTextureWidgets.size(); ++c) - resetWidget(dirtyRenderToTextureWidgets.at(c)); -} - -static QVector getSortedRectsToScroll(const QRegion ®ion, int dx, int dy) -{ - QVector rects; - std::copy(region.begin(), region.end(), std::back_inserter(rects)); - if (rects.count() > 1) { - std::sort(rects.begin(), rects.end(), [=](const QRect &r1, const QRect &r2) { - if (r1.y() == r2.y()) { - if (dx > 0) - return r1.x() > r2.x(); - return r1.x() < r2.x(); - } - if (dy > 0) - return r1.y() > r2.y(); - return r1.y() < r2.y(); - }); - } - return rects; -} - -//parent's coordinates; move whole rect; update parent and widget -//assume the screen blt has already been done, so we don't need to refresh that part -void QWidgetPrivate::moveRect(const QRect &rect, int dx, int dy) -{ - Q_Q(QWidget); - if (!q->isVisible() || (dx == 0 && dy == 0)) - return; - - QWidget *tlw = q->window(); - QTLWExtra* x = tlw->d_func()->topData(); - if (x->inTopLevelResize) - return; - - static const bool accelEnv = qEnvironmentVariableIntValue("QT_NO_FAST_MOVE") == 0; - - QWidget *pw = q->parentWidget(); - QPoint toplevelOffset = pw->mapTo(tlw, QPoint()); - QWidgetPrivate *pd = pw->d_func(); - QRect clipR(pd->clipRect()); - const QRect newRect(rect.translated(dx, dy)); - QRect destRect = rect.intersected(clipR); - if (destRect.isValid()) - destRect = destRect.translated(dx, dy).intersected(clipR); - const QRect sourceRect(destRect.translated(-dx, -dy)); - const QRect parentRect(rect & clipR); - const bool nativeWithTextureChild = textureChildSeen && hasPlatformWindow(q); - - const bool accelerateMove = accelEnv && isOpaque && !nativeWithTextureChild -#if QT_CONFIG(graphicsview) - // No accelerate move for proxy widgets. - && !tlw->d_func()->extra->proxyWidget -#endif - ; - - if (!accelerateMove) { - QRegion parentR(effectiveRectFor(parentRect)); - if (!extra || !extra->hasMask) { - parentR -= newRect; - } else { - // invalidateBackingStore() excludes anything outside the mask - parentR += newRect & clipR; - } - pd->invalidateBackingStore(parentR); - invalidateBackingStore((newRect & clipR).translated(-data.crect.topLeft())); - } else { - - QWidgetBackingStore *wbs = x->widgetBackingStore.get(); - QRegion childExpose(newRect & clipR); - QRegion overlappedExpose; - - if (sourceRect.isValid()) { - overlappedExpose = (overlappedRegion(sourceRect) | overlappedRegion(destRect)) & clipR; - - const qreal factor = QHighDpiScaling::factor(q->windowHandle()); - if (overlappedExpose.isEmpty() || qFloor(factor) == factor) { - const QVector rectsToScroll - = getSortedRectsToScroll(QRegion(sourceRect) - overlappedExpose, dx, dy); - for (QRect rect : rectsToScroll) { - if (wbs->bltRect(rect, dx, dy, pw)) { - childExpose -= rect.translated(dx, dy); - } - } - } - - childExpose -= overlappedExpose; - } - - if (!pw->updatesEnabled()) - return; - - const bool childUpdatesEnabled = q->updatesEnabled(); - if (childUpdatesEnabled) { - if (!overlappedExpose.isEmpty()) { - overlappedExpose.translate(-data.crect.topLeft()); - invalidateBackingStore(overlappedExpose); - } - if (!childExpose.isEmpty()) { - childExpose.translate(-data.crect.topLeft()); - wbs->markDirty(childExpose, q); - isMoved = true; - } - } - - QRegion parentExpose(parentRect); - parentExpose -= newRect; - if (extra && extra->hasMask) - parentExpose += QRegion(newRect) - extra->mask.translated(data.crect.topLeft()); - - if (!parentExpose.isEmpty()) { - wbs->markDirty(parentExpose, pw); - pd->isMoved = true; - } - - if (childUpdatesEnabled) { - QRegion needsFlush(sourceRect); - needsFlush += destRect; - wbs->markDirtyOnScreen(needsFlush, pw, toplevelOffset); - } - } -} - -//widget's coordinates; scroll within rect; only update widget -void QWidgetPrivate::scrollRect(const QRect &rect, int dx, int dy) -{ - Q_Q(QWidget); - QWidget *tlw = q->window(); - QTLWExtra* x = tlw->d_func()->topData(); - if (x->inTopLevelResize) - return; - - QWidgetBackingStore *wbs = x->widgetBackingStore.get(); - if (!wbs) - return; - - static const bool accelEnv = qEnvironmentVariableIntValue("QT_NO_FAST_SCROLL") == 0; - - const QRect clipR = clipRect(); - const QRect scrollRect = rect & clipR; - const bool accelerateScroll = accelEnv && isOpaque && !q_func()->testAttribute(Qt::WA_WState_InPaintEvent); - - if (!accelerateScroll) { - if (!overlappedRegion(scrollRect.translated(data.crect.topLeft()), true).isEmpty()) { - QRegion region(scrollRect); - subtractOpaqueSiblings(region); - invalidateBackingStore(region); - }else { - invalidateBackingStore(scrollRect); - } - } else { - const QPoint toplevelOffset = q->mapTo(tlw, QPoint()); - const QRect destRect = scrollRect.translated(dx, dy) & scrollRect; - const QRect sourceRect = destRect.translated(-dx, -dy); - - const QRegion overlappedExpose = (overlappedRegion(scrollRect.translated(data.crect.topLeft()))) - .translated(-data.crect.topLeft()) & clipR; - QRegion childExpose(scrollRect); - - const qreal factor = QHighDpiScaling::factor(q->windowHandle()); - if (overlappedExpose.isEmpty() || qFloor(factor) == factor) { - const QVector rectsToScroll - = getSortedRectsToScroll(QRegion(sourceRect) - overlappedExpose, dx, dy); - for (const QRect &rect : rectsToScroll) { - if (wbs->bltRect(rect, dx, dy, q)) { - childExpose -= rect.translated(dx, dy); - } - } - } - - childExpose -= overlappedExpose; - - if (inDirtyList) { - if (rect == q->rect()) { - dirty.translate(dx, dy); - } else { - QRegion dirtyScrollRegion = dirty.intersected(scrollRect); - if (!dirtyScrollRegion.isEmpty()) { - dirty -= dirtyScrollRegion; - dirtyScrollRegion.translate(dx, dy); - dirty += dirtyScrollRegion; - } - } - } - - if (!q->updatesEnabled()) - return; - - if (!overlappedExpose.isEmpty()) - invalidateBackingStore(overlappedExpose); - if (!childExpose.isEmpty()) { - wbs->markDirty(childExpose, q); - isScrolled = true; - } - - // Instead of using native scroll-on-screen, we copy from - // backingstore, giving only one screen update for each - // scroll, and a solid appearance - wbs->markDirtyOnScreen(destRect, q, toplevelOffset); - } -} - -#ifndef QT_NO_OPENGL -static void findTextureWidgetsRecursively(QWidget *tlw, QWidget *widget, QPlatformTextureList *widgetTextures, QVector *nativeChildren) -{ - QWidgetPrivate *wd = QWidgetPrivate::get(widget); - if (wd->renderToTexture) { - QPlatformTextureList::Flags flags = wd->textureListFlags(); - const QRect rect(widget->mapTo(tlw, QPoint()), widget->size()); - widgetTextures->appendTexture(widget, wd->textureId(), rect, wd->clipRect(), flags); - } - - for (int i = 0; i < wd->children.size(); ++i) { - QWidget *w = qobject_cast(wd->children.at(i)); - // Stop at native widgets but store them. Stop at hidden widgets too. - if (w && !w->isWindow() && hasPlatformWindow(w)) - nativeChildren->append(w); - if (w && !w->isWindow() && !hasPlatformWindow(w) && !w->isHidden() && QWidgetPrivate::get(w)->textureChildSeen) - findTextureWidgetsRecursively(tlw, w, widgetTextures, nativeChildren); - } -} - -static void findAllTextureWidgetsRecursively(QWidget *tlw, QWidget *widget) -{ - // textureChildSeen does not take native child widgets into account and that's good. - if (QWidgetPrivate::get(widget)->textureChildSeen) { - QVector nativeChildren; - auto tl = qt_make_unique(); - // Look for texture widgets (incl. widget itself) from 'widget' down, - // but skip subtrees with a parent of a native child widget. - findTextureWidgetsRecursively(tlw, widget, tl.get(), &nativeChildren); - // tl may be empty regardless of textureChildSeen if we have native or hidden children. - if (!tl->isEmpty()) - QWidgetPrivate::get(tlw)->topData()->widgetTextures.push_back(std::move(tl)); - // Native child widgets, if there was any, get their own separate QPlatformTextureList. - for (QWidget *ncw : qAsConst(nativeChildren)) { - if (QWidgetPrivate::get(ncw)->textureChildSeen) - findAllTextureWidgetsRecursively(tlw, ncw); - } - } -} - -static QPlatformTextureList *widgetTexturesFor(QWidget *tlw, QWidget *widget) -{ - for (const auto &tl : QWidgetPrivate::get(tlw)->topData()->widgetTextures) { - Q_ASSERT(!tl->isEmpty()); - for (int i = 0; i < tl->count(); ++i) { - QWidget *w = static_cast(tl->source(i)); - if ((hasPlatformWindow(w) && w == widget) || (!hasPlatformWindow(w) && w->nativeParentWidget() == widget)) - return tl.get(); - } - } - - if (QWidgetPrivate::get(widget)->textureChildSeen) { - // No render-to-texture widgets in the (sub-)tree due to hidden or native - // children. Returning null results in using the normal backingstore flush path - // without OpenGL-based compositing. This is very desirable normally. However, - // some platforms cannot handle switching between the non-GL and GL paths for - // their windows so it has to be opt-in. - static bool switchableWidgetComposition = - QGuiApplicationPrivate::instance()->platformIntegration() - ->hasCapability(QPlatformIntegration::SwitchableWidgetComposition); - if (!switchableWidgetComposition) - return qt_dummy_platformTextureList(); - } - - return 0; -} - -// Watches one or more QPlatformTextureLists for changes in the lock state and -// triggers a backingstore sync when all the registered lists turn into -// unlocked state. This is essential when a custom composeAndFlush() -// implementation in a platform plugin is not synchronous and keeps -// holding on to the textures for some time even after returning from there. -QPlatformTextureListWatcher::QPlatformTextureListWatcher(QWidgetBackingStore *backingStore) - : m_backingStore(backingStore) -{ -} - -void QPlatformTextureListWatcher::watch(QPlatformTextureList *textureList) -{ - connect(textureList, SIGNAL(locked(bool)), SLOT(onLockStatusChanged(bool))); - m_locked[textureList] = textureList->isLocked(); -} - -bool QPlatformTextureListWatcher::isLocked() const -{ - foreach (bool v, m_locked) { - if (v) - return true; - } - return false; -} - -void QPlatformTextureListWatcher::onLockStatusChanged(bool locked) -{ - QPlatformTextureList *tl = static_cast(sender()); - m_locked[tl] = locked; - if (!isLocked()) - m_backingStore->sync(); -} - -#else - -static QPlatformTextureList *widgetTexturesFor(QWidget *tlw, QWidget *widget) -{ - Q_UNUSED(tlw); - Q_UNUSED(widget); - return nullptr; -} - -#endif // QT_NO_OPENGL - -static inline bool discardSyncRequest(QWidget *tlw, QTLWExtra *tlwExtra) -{ - if (!tlw || !tlwExtra || !tlw->testAttribute(Qt::WA_Mapped) || !tlw->isVisible()) - return true; - - return false; -} - -bool QWidgetBackingStore::syncAllowed() -{ -#ifndef QT_NO_OPENGL - QTLWExtra *tlwExtra = tlw->d_func()->maybeTopData(); - if (textureListWatcher && !textureListWatcher->isLocked()) { - textureListWatcher->deleteLater(); - textureListWatcher = 0; - } else if (!tlwExtra->widgetTextures.empty()) { - bool skipSync = false; - for (const auto &tl : tlwExtra->widgetTextures) { - if (tl->isLocked()) { - if (!textureListWatcher) - textureListWatcher = new QPlatformTextureListWatcher(this); - if (!textureListWatcher->isLocked()) - textureListWatcher->watch(tl.get()); - skipSync = true; - } - } - if (skipSync) // cannot compose due to widget textures being in use - return false; - } -#endif - return true; -} - -/*! - Synchronizes the \a exposedRegion of the \a exposedWidget with the backing store. - - If there's nothing to repaint, the area is flushed and painting does not occur; - otherwise the area is marked as dirty on screen and will be flushed right after - we are done with all painting. -*/ -void QWidgetBackingStore::sync(QWidget *exposedWidget, const QRegion &exposedRegion) -{ - QTLWExtra *tlwExtra = tlw->d_func()->maybeTopData(); - if (!tlw->isVisible() || !tlwExtra || tlwExtra->inTopLevelResize) - return; - - if (!exposedWidget || !hasPlatformWindow(exposedWidget) - || !exposedWidget->isVisible() || !exposedWidget->testAttribute(Qt::WA_Mapped) - || !exposedWidget->updatesEnabled() || exposedRegion.isEmpty()) { - return; - } - - // Nothing to repaint. - if (!isDirty() && store->size().isValid()) { - QPlatformTextureList *tl = widgetTexturesFor(tlw, exposedWidget); - qt_flush(exposedWidget, tl ? QRegion() : exposedRegion, store, tlw, tl, this); - return; - } - - if (exposedWidget != tlw) - markDirtyOnScreen(exposedRegion, exposedWidget, exposedWidget->mapTo(tlw, QPoint())); - else - markDirtyOnScreen(exposedRegion, exposedWidget, QPoint()); - - if (syncAllowed()) - doSync(); -} - -/*! - Synchronizes the backing store, i.e. dirty areas are repainted and flushed. -*/ -void QWidgetBackingStore::sync() -{ - updateRequestSent = false; - QTLWExtra *tlwExtra = tlw->d_func()->maybeTopData(); - if (discardSyncRequest(tlw, tlwExtra)) { - // If the top-level is minimized, it's not visible on the screen so we can delay the - // update until it's shown again. In order to do that we must keep the dirty states. - // These will be cleared when we receive the first expose after showNormal(). - // However, if the widget is not visible (isVisible() returns false), everything will - // be invalidated once the widget is shown again, so clear all dirty states. - if (!tlw->isVisible()) { - dirty = QRegion(); - for (int i = 0; i < dirtyWidgets.size(); ++i) - resetWidget(dirtyWidgets.at(i)); - dirtyWidgets.clear(); - } - return; - } - - if (syncAllowed()) - doSync(); -} - -void QWidgetBackingStore::doSync() -{ - const bool updatesDisabled = !tlw->updatesEnabled(); - bool repaintAllWidgets = false; - - const bool inTopLevelResize = tlw->d_func()->maybeTopData()->inTopLevelResize; - const QRect tlwRect(topLevelRect()); - const QRect surfaceGeometry(tlwRect.topLeft(), store->size()); - if ((inTopLevelResize || surfaceGeometry.size() != tlwRect.size()) && !updatesDisabled) { - if (hasStaticContents() && !store->size().isEmpty() ) { - // Repaint existing dirty area and newly visible area. - const QRect clipRect(0, 0, surfaceGeometry.width(), surfaceGeometry.height()); - const QRegion staticRegion(staticContents(0, clipRect)); - QRegion newVisible(0, 0, tlwRect.width(), tlwRect.height()); - newVisible -= staticRegion; - dirty += newVisible; - store->setStaticContents(staticRegion); - } else { - // Repaint everything. - dirty = QRegion(0, 0, tlwRect.width(), tlwRect.height()); - for (int i = 0; i < dirtyWidgets.size(); ++i) - resetWidget(dirtyWidgets.at(i)); - dirtyWidgets.clear(); - repaintAllWidgets = true; - } - } - - if (inTopLevelResize || surfaceGeometry.size() != tlwRect.size()) - store->resize(tlwRect.size()); - - if (updatesDisabled) - return; - - // Contains everything that needs repaint. - QRegion toClean(dirty); - - // Loop through all update() widgets and remove them from the list before they are - // painted (in case someone calls update() in paintEvent). If the widget is opaque - // and does not have transparent overlapping siblings, append it to the - // opaqueNonOverlappedWidgets list and paint it directly without composition. - QVarLengthArray opaqueNonOverlappedWidgets; - for (int i = 0; i < dirtyWidgets.size(); ++i) { - QWidget *w = dirtyWidgets.at(i); - QWidgetPrivate *wd = w->d_func(); - if (wd->data.in_destructor) - continue; - - // Clip with mask() and clipRect(). - wd->dirty &= wd->clipRect(); - wd->clipToEffectiveMask(wd->dirty); - - // Subtract opaque siblings and children. - bool hasDirtySiblingsAbove = false; - // We know for sure that the widget isn't overlapped if 'isMoved' is true. - if (!wd->isMoved) - wd->subtractOpaqueSiblings(wd->dirty, &hasDirtySiblingsAbove); - - // Make a copy of the widget's dirty region, to restore it in case there is an opaque - // render-to-texture child that completely covers the widget, because otherwise the - // render-to-texture child won't be visible, due to its parent widget not being redrawn - // with a proper blending mask. - const QRegion dirtyBeforeSubtractedOpaqueChildren = wd->dirty; - - // Scrolled and moved widgets must draw all children. - if (!wd->isScrolled && !wd->isMoved) - wd->subtractOpaqueChildren(wd->dirty, w->rect()); - - if (wd->dirty.isEmpty() && wd->textureChildSeen) - wd->dirty = dirtyBeforeSubtractedOpaqueChildren; - - if (wd->dirty.isEmpty()) { - resetWidget(w); - continue; - } - - const QRegion widgetDirty(w != tlw ? wd->dirty.translated(w->mapTo(tlw, QPoint())) - : wd->dirty); - toClean += widgetDirty; - -#if QT_CONFIG(graphicsview) - if (tlw->d_func()->extra->proxyWidget) { - resetWidget(w); - continue; - } -#endif - - if (!hasDirtySiblingsAbove && wd->isOpaque && !dirty.intersects(widgetDirty.boundingRect())) { - opaqueNonOverlappedWidgets.append(w); - } else { - resetWidget(w); - dirty += widgetDirty; - } - } - dirtyWidgets.clear(); - -#ifndef QT_NO_OPENGL - // Find all render-to-texture child widgets (including self). - // The search is cut at native widget boundaries, meaning that each native child widget - // has its own list for the subtree below it. - QTLWExtra *tlwExtra = tlw->d_func()->topData(); - tlwExtra->widgetTextures.clear(); - findAllTextureWidgetsRecursively(tlw, tlw); - qt_window_private(tlw->windowHandle())->compositing = false; // will get updated in qt_flush() -#endif - - if (toClean.isEmpty()) { - // Nothing to repaint. However renderToTexture widgets are handled - // specially, they are not in the regular dirty list, in order to - // prevent triggering unnecessary backingstore painting when only the - // OpenGL content changes. Check if we have such widgets in the special - // dirty list. - QVarLengthArray paintPending; - const int numPaintPending = dirtyRenderToTextureWidgets.count(); - paintPending.reserve(numPaintPending); - for (int i = 0; i < numPaintPending; ++i) { - QWidget *w = dirtyRenderToTextureWidgets.at(i); - paintPending << w; - resetWidget(w); - } - dirtyRenderToTextureWidgets.clear(); - for (int i = 0; i < numPaintPending; ++i) { - QWidget *w = paintPending[i]; - w->d_func()->sendPaintEvent(w->rect()); - if (w != tlw) { - QWidget *npw = w->nativeParentWidget(); - if (hasPlatformWindow(w) || (npw && npw != tlw)) { - if (!hasPlatformWindow(w)) - w = npw; - QWidgetPrivate *wPrivate = w->d_func(); - if (!wPrivate->needsFlush) - wPrivate->needsFlush = new QRegion; - appendDirtyOnScreenWidget(w); - } - } - } - - // We might have newly exposed areas on the screen if this function was - // called from sync(QWidget *, QRegion)), so we have to make sure those - // are flushed. We also need to composite the renderToTexture widgets. - flush(); - - return; - } - -#ifndef QT_NO_OPENGL - for (const auto &tl : tlwExtra->widgetTextures) { - for (int i = 0; i < tl->count(); ++i) { - QWidget *w = static_cast(tl->source(i)); - if (dirtyRenderToTextureWidgets.contains(w)) { - const QRect rect = tl->geometry(i); // mapped to the tlw already - // Set a flag to indicate that the paint event for this - // render-to-texture widget must not to be optimized away. - w->d_func()->renderToTextureReallyDirty = 1; - dirty += rect; - toClean += rect; - } - } - } - for (int i = 0; i < dirtyRenderToTextureWidgets.count(); ++i) - resetWidget(dirtyRenderToTextureWidgets.at(i)); - dirtyRenderToTextureWidgets.clear(); -#endif - -#if QT_CONFIG(graphicsview) - if (tlw->d_func()->extra->proxyWidget) { - updateStaticContentsSize(); - dirty = QRegion(); - updateRequestSent = false; - for (const QRect &rect : toClean) - tlw->d_func()->extra->proxyWidget->update(rect); - return; - } -#endif - - BeginPaintInfo beginPaintInfo; - beginPaint(toClean, tlw, store, &beginPaintInfo); - if (beginPaintInfo.nothingToPaint) { - for (int i = 0; i < opaqueNonOverlappedWidgets.size(); ++i) - resetWidget(opaqueNonOverlappedWidgets[i]); - dirty = QRegion(); - updateRequestSent = false; - return; - } - - // Must do this before sending any paint events because - // the size may change in the paint event. - updateStaticContentsSize(); - const QRegion dirtyCopy(dirty); - dirty = QRegion(); - updateRequestSent = false; - - // Paint opaque non overlapped widgets. - for (int i = 0; i < opaqueNonOverlappedWidgets.size(); ++i) { - QWidget *w = opaqueNonOverlappedWidgets[i]; - QWidgetPrivate *wd = w->d_func(); - - int flags = QWidgetPrivate::DrawRecursive; - // Scrolled and moved widgets must draw all children. - if (!wd->isScrolled && !wd->isMoved) - flags |= QWidgetPrivate::DontDrawOpaqueChildren; - if (w == tlw) - flags |= QWidgetPrivate::DrawAsRoot; - - QRegion toBePainted(wd->dirty); - resetWidget(w); - - QPoint offset; - if (w != tlw) - offset += w->mapTo(tlw, QPoint()); - wd->drawWidget(store->paintDevice(), toBePainted, offset, flags, 0, this); - } - - // Paint the rest with composition. - if (repaintAllWidgets || !dirtyCopy.isEmpty()) { - const int flags = QWidgetPrivate::DrawAsRoot | QWidgetPrivate::DrawRecursive; - tlw->d_func()->drawWidget(store->paintDevice(), dirtyCopy, QPoint(), flags, 0, this); - } - - endPaint(toClean, store, &beginPaintInfo); -} - -/*! - Flushes the contents of the backing store into the top-level widget. - If the \a widget is non-zero, the content is flushed to the \a widget. - If the \a surface is non-zero, the content of the \a surface is flushed. -*/ -void QWidgetBackingStore::flush(QWidget *widget) -{ - const bool hasDirtyOnScreenWidgets = !dirtyOnScreenWidgets.isEmpty(); - bool flushed = false; - - // Flush the region in dirtyOnScreen. - if (!dirtyOnScreen.isEmpty()) { - QWidget *target = widget ? widget : tlw; - qt_flush(target, dirtyOnScreen, store, tlw, widgetTexturesFor(tlw, tlw), this); - dirtyOnScreen = QRegion(); - flushed = true; - } - - // Render-to-texture widgets are not in dirtyOnScreen so flush if we have not done it above. - if (!flushed && !hasDirtyOnScreenWidgets) { -#ifndef QT_NO_OPENGL - if (!tlw->d_func()->topData()->widgetTextures.empty()) { - QPlatformTextureList *tl = widgetTexturesFor(tlw, tlw); - if (tl) { - QWidget *target = widget ? widget : tlw; - qt_flush(target, QRegion(), store, tlw, tl, this); - } - } -#endif - } - - if (!hasDirtyOnScreenWidgets) - return; - - for (QWidget *w : qExchange(dirtyOnScreenWidgets, {})) { - QWidgetPrivate *wd = w->d_func(); - Q_ASSERT(wd->needsFlush); - QPlatformTextureList *widgetTexturesForNative = wd->textureChildSeen ? widgetTexturesFor(tlw, w) : 0; - qt_flush(w, *wd->needsFlush, store, tlw, widgetTexturesForNative, this); - *wd->needsFlush = QRegion(); - } -} - -/*! - Invalidates the backing store when the widget is resized. - Static areas are never invalidated unless absolutely needed. -*/ -void QWidgetPrivate::invalidateBackingStore_resizeHelper(const QPoint &oldPos, const QSize &oldSize) -{ - Q_Q(QWidget); - Q_ASSERT(!q->isWindow()); - Q_ASSERT(q->parentWidget()); - - const bool staticContents = q->testAttribute(Qt::WA_StaticContents); - const bool sizeDecreased = (data.crect.width() < oldSize.width()) - || (data.crect.height() < oldSize.height()); - - const QPoint offset(data.crect.x() - oldPos.x(), data.crect.y() - oldPos.y()); - const bool parentAreaExposed = !offset.isNull() || sizeDecreased; - const QRect newWidgetRect(q->rect()); - const QRect oldWidgetRect(0, 0, oldSize.width(), oldSize.height()); - - if (!staticContents || graphicsEffect) { - QRegion staticChildren; - QWidgetBackingStore *bs = 0; - if (offset.isNull() && (bs = maybeBackingStore())) - staticChildren = bs->staticContents(q, oldWidgetRect); - const bool hasStaticChildren = !staticChildren.isEmpty(); - - if (hasStaticChildren) { - QRegion dirty(newWidgetRect); - dirty -= staticChildren; - invalidateBackingStore(dirty); - } else { - // Entire widget needs repaint. - invalidateBackingStore(newWidgetRect); - } - - if (!parentAreaExposed) - return; - - // Invalidate newly exposed area of the parent. - if (!graphicsEffect && extra && extra->hasMask) { - QRegion parentExpose(extra->mask.translated(oldPos)); - parentExpose &= QRect(oldPos, oldSize); - if (hasStaticChildren) - parentExpose -= data.crect; // Offset is unchanged, safe to do this. - q->parentWidget()->d_func()->invalidateBackingStore(parentExpose); - } else { - if (hasStaticChildren && !graphicsEffect) { - QRegion parentExpose(QRect(oldPos, oldSize)); - parentExpose -= data.crect; // Offset is unchanged, safe to do this. - q->parentWidget()->d_func()->invalidateBackingStore(parentExpose); - } else { - q->parentWidget()->d_func()->invalidateBackingStore(effectiveRectFor(QRect(oldPos, oldSize))); - } - } - return; - } - - // Move static content to its new position. - if (!offset.isNull()) { - if (sizeDecreased) { - const QSize minSize(qMin(oldSize.width(), data.crect.width()), - qMin(oldSize.height(), data.crect.height())); - moveRect(QRect(oldPos, minSize), offset.x(), offset.y()); - } else { - moveRect(QRect(oldPos, oldSize), offset.x(), offset.y()); - } - } - - // Invalidate newly visible area of the widget. - if (!sizeDecreased || !oldWidgetRect.contains(newWidgetRect)) { - QRegion newVisible(newWidgetRect); - newVisible -= oldWidgetRect; - invalidateBackingStore(newVisible); - } - - if (!parentAreaExposed) - return; - - // Invalidate newly exposed area of the parent. - const QRect oldRect(oldPos, oldSize); - if (extra && extra->hasMask) { - QRegion parentExpose(oldRect); - parentExpose &= extra->mask.translated(oldPos); - parentExpose -= (extra->mask.translated(data.crect.topLeft()) & data.crect); - q->parentWidget()->d_func()->invalidateBackingStore(parentExpose); - } else { - QRegion parentExpose(oldRect); - parentExpose -= data.crect; - q->parentWidget()->d_func()->invalidateBackingStore(parentExpose); - } -} - -/*! - Invalidates the \a r (in widget's coordinates) of the backing store, i.e. - all widgets intersecting with the region will be repainted when the backing - store is synced. -*/ -template -void QWidgetPrivate::invalidateBackingStore(const T &r) -{ - if (r.isEmpty()) - return; - - if (QCoreApplication::closingDown()) - return; - - Q_Q(QWidget); - if (!q->isVisible() || !q->updatesEnabled()) - return; - - QTLWExtra *tlwExtra = q->window()->d_func()->maybeTopData(); - if (!tlwExtra || tlwExtra->inTopLevelResize || !tlwExtra->backingStore) - return; - - T clipped(r); - clipped &= clipRect(); - if (clipped.isEmpty()) - return; - - if (!graphicsEffect && extra && extra->hasMask) { - QRegion masked(extra->mask); - masked &= clipped; - if (masked.isEmpty()) - return; - - tlwExtra->widgetBackingStore->markDirty(masked, q, - QWidgetBackingStore::UpdateLater, QWidgetBackingStore::BufferInvalid); - } else { - tlwExtra->widgetBackingStore->markDirty(clipped, q, - QWidgetBackingStore::UpdateLater, QWidgetBackingStore::BufferInvalid); - } -} -// Needed by tst_QWidget -template Q_AUTOTEST_EXPORT void QWidgetPrivate::invalidateBackingStore(const QRect &r); - -void QWidgetPrivate::repaint_sys(const QRegion &rgn) -{ - if (data.in_destructor) - return; - - Q_Q(QWidget); - if (discardSyncRequest(q, maybeTopData())) - return; - - if (q->testAttribute(Qt::WA_StaticContents)) { - if (!extra) - createExtra(); - extra->staticContentsSize = data.crect.size(); - } - - QPaintEngine *engine = q->paintEngine(); - - // QGLWidget does not support partial updates if: - // 1) The context is double buffered - // 2) The context is single buffered and auto-fill background is enabled. - const bool noPartialUpdateSupport = (engine && (engine->type() == QPaintEngine::OpenGL - || engine->type() == QPaintEngine::OpenGL2)) - && (usesDoubleBufferedGLContext || q->autoFillBackground()); - QRegion toBePainted(noPartialUpdateSupport ? q->rect() : rgn); - -#if 0 // Used to be included in Qt4 for Q_WS_MAC - // No difference between update() and repaint() on the Mac. - update_sys(toBePainted); - return; -#endif - - toBePainted &= clipRect(); - clipToEffectiveMask(toBePainted); - if (toBePainted.isEmpty()) - return; // Nothing to repaint. - -#ifndef QT_NO_PAINT_DEBUG - bool flushed = QWidgetBackingStore::flushPaint(q, toBePainted); -#endif - - drawWidget(q, toBePainted, QPoint(), QWidgetPrivate::DrawAsRoot | QWidgetPrivate::DrawPaintOnScreen, 0); - -#ifndef QT_NO_PAINT_DEBUG - if (flushed) - QWidgetBackingStore::unflushPaint(q, toBePainted); -#endif - - if (Q_UNLIKELY(q->paintingActive())) - qWarning("QWidget::repaint: It is dangerous to leave painters active on a widget outside of the PaintEvent"); -} - - -QT_END_NAMESPACE - -#include "moc_qwidgetbackingstore_p.cpp" diff --git a/src/widgets/kernel/qwidgetbackingstore_p.h b/src/widgets/kernel/qwidgetbackingstore_p.h deleted file mode 100644 index 08042445bf..0000000000 --- a/src/widgets/kernel/qwidgetbackingstore_p.h +++ /dev/null @@ -1,281 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtWidgets module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QWIDGETBACKINGSTORE_P_H -#define QWIDGETBACKINGSTORE_P_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include -#include -#include -#include -#include - -QT_BEGIN_NAMESPACE - -class QPlatformTextureList; -class QPlatformTextureListWatcher; -class QWidgetBackingStore; - -struct BeginPaintInfo { - inline BeginPaintInfo() : wasFlushed(0), nothingToPaint(0), backingStoreRecreated(0) {} - uint wasFlushed : 1; - uint nothingToPaint : 1; - uint backingStoreRecreated : 1; -}; - -#ifndef QT_NO_OPENGL -class QPlatformTextureListWatcher : public QObject -{ - Q_OBJECT - -public: - QPlatformTextureListWatcher(QWidgetBackingStore *backingStore); - void watch(QPlatformTextureList *textureList); - bool isLocked() const; - -private slots: - void onLockStatusChanged(bool locked); - -private: - QHash m_locked; - QWidgetBackingStore *m_backingStore; -}; -#endif - -class Q_AUTOTEST_EXPORT QWidgetBackingStore -{ -public: - enum UpdateTime { - UpdateNow, - UpdateLater - }; - - enum BufferState{ - BufferValid, - BufferInvalid - }; - - QWidgetBackingStore(QWidget *t); - ~QWidgetBackingStore(); - - static void showYellowThing(QWidget *widget, const QRegion &rgn, int msec, bool); - - void sync(QWidget *exposedWidget, const QRegion &exposedRegion); - void sync(); - void flush(QWidget *widget = nullptr); - - QBackingStore *backingStore() const { return store; } - - inline bool isDirty() const - { - return !(dirtyWidgets.isEmpty() && dirty.isEmpty() && dirtyRenderToTextureWidgets.isEmpty()); - } - - template - void markDirty(const T &r, QWidget *widget, UpdateTime updateTime = UpdateLater, - BufferState bufferState = BufferValid); - -private: - QWidget *tlw; - QRegion dirtyOnScreen; // needsFlush - QRegion dirty; // needsRepaint - QRegion dirtyFromPreviousSync; - QVector dirtyWidgets; - QVector dirtyRenderToTextureWidgets; - QVector dirtyOnScreenWidgets; - QList staticWidgets; - QBackingStore *store; - uint updateRequestSent : 1; - - QPlatformTextureListWatcher *textureListWatcher; - QElapsedTimer perfTime; - int perfFrames; - - void sendUpdateRequest(QWidget *widget, UpdateTime updateTime); - - static bool flushPaint(QWidget *widget, const QRegion &rgn); - static void unflushPaint(QWidget *widget, const QRegion &rgn); - static void qt_flush(QWidget *widget, const QRegion ®ion, QBackingStore *backingStore, - QWidget *tlw, - QPlatformTextureList *widgetTextures, - QWidgetBackingStore *widgetBackingStore); - - void doSync(); - bool bltRect(const QRect &rect, int dx, int dy, QWidget *widget); - - void beginPaint(QRegion &toClean, QWidget *widget, QBackingStore *backingStore, - BeginPaintInfo *returnInfo, bool toCleanIsInTopLevelCoordinates = true); - void endPaint(const QRegion &cleaned, QBackingStore *backingStore, BeginPaintInfo *beginPaintInfo); - - QRegion dirtyRegion(QWidget *widget = nullptr) const; - QRegion staticContents(QWidget *widget = nullptr, const QRect &withinClipRect = QRect()) const; - - void markDirtyOnScreen(const QRegion &dirtyOnScreen, QWidget *widget, const QPoint &topLevelOffset); - - void removeDirtyWidget(QWidget *w); - - void updateLists(QWidget *widget); - - bool syncAllowed(); - - inline void addDirtyWidget(QWidget *widget, const QRegion &rgn) - { - if (widget && !widget->d_func()->inDirtyList && !widget->data->in_destructor) { - QWidgetPrivate *widgetPrivate = widget->d_func(); -#if QT_CONFIG(graphicseffect) - if (widgetPrivate->graphicsEffect) - widgetPrivate->dirty = widgetPrivate->effectiveRectFor(rgn.boundingRect()); - else -#endif // QT_CONFIG(graphicseffect) - widgetPrivate->dirty = rgn; - dirtyWidgets.append(widget); - widgetPrivate->inDirtyList = true; - } - } - - inline void addDirtyRenderToTextureWidget(QWidget *widget) - { - if (widget && !widget->d_func()->inDirtyList && !widget->data->in_destructor) { - QWidgetPrivate *widgetPrivate = widget->d_func(); - Q_ASSERT(widgetPrivate->renderToTexture); - dirtyRenderToTextureWidgets.append(widget); - widgetPrivate->inDirtyList = true; - } - } - - inline void addStaticWidget(QWidget *widget) - { - if (!widget) - return; - - Q_ASSERT(widget->testAttribute(Qt::WA_StaticContents)); - if (!staticWidgets.contains(widget)) - staticWidgets.append(widget); - } - - inline void removeStaticWidget(QWidget *widget) - { staticWidgets.removeAll(widget); } - - // Move the reparented widget and all its static children from this backing store - // to the new backing store if reparented into another top-level / backing store. - inline void moveStaticWidgets(QWidget *reparented) - { - Q_ASSERT(reparented); - QWidgetBackingStore *newBs = reparented->d_func()->maybeBackingStore(); - if (newBs == this) - return; - - int i = 0; - while (i < staticWidgets.size()) { - QWidget *w = staticWidgets.at(i); - if (reparented == w || reparented->isAncestorOf(w)) { - staticWidgets.removeAt(i); - if (newBs) - newBs->addStaticWidget(w); - } else { - ++i; - } - } - } - - inline QRect topLevelRect() const - { - return tlw->data->crect; - } - - inline void appendDirtyOnScreenWidget(QWidget *widget) - { - if (!widget) - return; - - if (!dirtyOnScreenWidgets.contains(widget)) - dirtyOnScreenWidgets.append(widget); - } - - inline void resetWidget(QWidget *widget) - { - if (widget) { - widget->d_func()->inDirtyList = false; - widget->d_func()->isScrolled = false; - widget->d_func()->isMoved = false; - widget->d_func()->dirty = QRegion(); - } - } - - inline void updateStaticContentsSize() - { - for (int i = 0; i < staticWidgets.size(); ++i) { - QWidgetPrivate *wd = staticWidgets.at(i)->d_func(); - if (!wd->extra) - wd->createExtra(); - wd->extra->staticContentsSize = wd->data.crect.size(); - } - } - - inline bool hasStaticContents() const - { -#if defined(Q_OS_WIN) - return !staticWidgets.isEmpty(); -#else - return !staticWidgets.isEmpty() && false; -#endif - } - - friend QRegion qt_dirtyRegion(QWidget *); - friend class QWidgetPrivate; - friend class QWidget; - friend class QBackingStore; - - Q_DISABLE_COPY_MOVE(QWidgetBackingStore) -}; - -QT_END_NAMESPACE - -#endif // QBACKINGSTORE_P_H diff --git a/src/widgets/kernel/qwidgetrepaintmanager.cpp b/src/widgets/kernel/qwidgetrepaintmanager.cpp new file mode 100644 index 0000000000..30d92551f7 --- /dev/null +++ b/src/widgets/kernel/qwidgetrepaintmanager.cpp @@ -0,0 +1,1595 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtWidgets module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#include "qplatformdefs.h" + +#include "qwidgetrepaintmanager_p.h" + +#include +#include +#include +#include +#include +#include +#if QT_CONFIG(graphicsview) +#include +#endif + +#include +#include +#include +#if QT_CONFIG(graphicseffect) +#include +#endif +#include +#include + +#include + +#if defined(Q_OS_WIN) && !defined(QT_NO_PAINT_DEBUG) +# include +# include +#endif + +#include + +QT_BEGIN_NAMESPACE + +extern QRegion qt_dirtyRegion(QWidget *); + +#ifndef QT_NO_OPENGL +Q_GLOBAL_STATIC(QPlatformTextureList, qt_dummy_platformTextureList) +#endif + +static bool hasPlatformWindow(QWidget *widget) +{ + return widget && widget->windowHandle() && widget->windowHandle()->handle(); +} + +/** + * Flushes the contents of the \a backingStore into the screen area of \a widget. + * \a region is the region to be updated in \a widget coordinates. + */ +void QWidgetRepaintManager::qt_flush(QWidget *widget, const QRegion ®ion, QBackingStore *backingStore, + QWidget *tlw, QPlatformTextureList *widgetTextures, + QWidgetRepaintManager *repaintManager) +{ +#ifdef QT_NO_OPENGL + Q_UNUSED(widgetTextures); + Q_ASSERT(!region.isEmpty()); +#else + Q_ASSERT(!region.isEmpty() || widgetTextures); +#endif + Q_ASSERT(widget); + Q_ASSERT(backingStore); + Q_ASSERT(tlw); +#if !defined(QT_NO_PAINT_DEBUG) + static int flushUpdate = qEnvironmentVariableIntValue("QT_FLUSH_UPDATE"); + if (flushUpdate > 0) + QWidgetRepaintManager::showYellowThing(widget, region, flushUpdate * 10, false); +#endif + + if (tlw->testAttribute(Qt::WA_DontShowOnScreen) || widget->testAttribute(Qt::WA_DontShowOnScreen)) + return; + + // Foreign Windows do not have backing store content and must not be flushed + if (QWindow *widgetWindow = widget->windowHandle()) { + if (widgetWindow->type() == Qt::ForeignWindow) + return; + } + + static bool fpsDebug = qEnvironmentVariableIntValue("QT_DEBUG_FPS"); + if (fpsDebug) { + if (!repaintManager->perfFrames++) + repaintManager->perfTime.start(); + if (repaintManager->perfTime.elapsed() > 5000) { + double fps = double(repaintManager->perfFrames * 1000) / repaintManager->perfTime.restart(); + qDebug("FPS: %.1f\n", fps); + repaintManager->perfFrames = 0; + } + } + + QPoint offset; + if (widget != tlw) + offset += widget->mapTo(tlw, QPoint()); + + QRegion effectiveRegion = region; +#ifndef QT_NO_OPENGL + const bool compositionWasActive = widget->d_func()->renderToTextureComposeActive; + if (!widgetTextures) { + widget->d_func()->renderToTextureComposeActive = false; + // Detect the case of falling back to the normal flush path when no + // render-to-texture widgets are visible anymore. We will force one + // last flush to go through the OpenGL-based composition to prevent + // artifacts. The next flush after this one will use the normal path. + if (compositionWasActive) + widgetTextures = qt_dummy_platformTextureList; + } else { + widget->d_func()->renderToTextureComposeActive = true; + } + // When changing the composition status, make sure the dirty region covers + // the entire widget. Just having e.g. the shown/hidden render-to-texture + // widget's area marked as dirty is incorrect when changing flush paths. + if (compositionWasActive != widget->d_func()->renderToTextureComposeActive) + effectiveRegion = widget->rect(); + + // re-test since we may have been forced to this path via the dummy texture list above + if (widgetTextures) { + qt_window_private(tlw->windowHandle())->compositing = true; + widget->window()->d_func()->sendComposeStatus(widget->window(), false); + // A window may have alpha even when the app did not request + // WA_TranslucentBackground. Therefore the compositor needs to know whether the app intends + // to rely on translucency, in order to decide if it should clear to transparent or opaque. + const bool translucentBackground = widget->testAttribute(Qt::WA_TranslucentBackground); + backingStore->handle()->composeAndFlush(widget->windowHandle(), effectiveRegion, offset, + widgetTextures, translucentBackground); + widget->window()->d_func()->sendComposeStatus(widget->window(), true); + } else +#endif + backingStore->flush(effectiveRegion, widget->windowHandle(), offset); +} + +#ifndef QT_NO_PAINT_DEBUG +#if defined(Q_OS_WIN) && !defined(Q_OS_WINRT) + +static void showYellowThing_win(QWidget *widget, const QRegion ®ion, int msec) +{ + // We expect to be passed a native parent. + QWindow *nativeWindow = widget->windowHandle(); + if (!nativeWindow) + return; + void *hdcV = QGuiApplication::platformNativeInterface()->nativeResourceForWindow(QByteArrayLiteral("getDC"), nativeWindow); + if (!hdcV) + return; + const HDC hdc = reinterpret_cast(hdcV); + + static const COLORREF colors[] = {RGB(255, 255, 0), RGB(255, 200, 55), RGB(200, 255, 55), RGB(200, 200, 0)}; + + static size_t i = 0; + const HBRUSH brush = CreateSolidBrush(colors[i]); + i = (i + 1) % (sizeof(colors) / sizeof(colors[0])); + + for (const QRect &rect : region) { + RECT winRect; + SetRect(&winRect, rect.left(), rect.top(), rect.right(), rect.bottom()); + FillRect(hdc, &winRect, brush); + } + DeleteObject(brush); + QGuiApplication::platformNativeInterface()->nativeResourceForWindow(QByteArrayLiteral("releaseDC"), nativeWindow); + ::Sleep(msec); +} +#endif // defined(Q_OS_WIN) && !defined(Q_OS_WINRT) + +void QWidgetRepaintManager::showYellowThing(QWidget *widget, const QRegion &toBePainted, int msec, bool unclipped) +{ +#ifdef Q_OS_WINRT + Q_UNUSED(msec) +#endif + QRegion paintRegion = toBePainted; + QRect widgetRect = widget->rect(); + + if (!hasPlatformWindow(widget)) { + QWidget *nativeParent = widget->nativeParentWidget(); + const QPoint offset = widget->mapTo(nativeParent, QPoint(0, 0)); + paintRegion.translate(offset); + widgetRect.translate(offset); + widget = nativeParent; + } + +#if defined(Q_OS_WIN) && !defined(Q_OS_WINRT) + Q_UNUSED(unclipped); + showYellowThing_win(widget, paintRegion, msec); +#else + //flags to fool painter + bool paintUnclipped = widget->testAttribute(Qt::WA_PaintUnclipped); + if (unclipped && !widget->d_func()->paintOnScreen()) + widget->setAttribute(Qt::WA_PaintUnclipped); + + const bool setFlag = !widget->testAttribute(Qt::WA_WState_InPaintEvent); + if (setFlag) + widget->setAttribute(Qt::WA_WState_InPaintEvent); + + //setup the engine + QPaintEngine *pe = widget->paintEngine(); + if (pe) { + pe->setSystemClip(paintRegion); + { + QPainter p(widget); + p.setClipRegion(paintRegion); + static int i = 0; + switch (i) { + case 0: + p.fillRect(widgetRect, QColor(255,255,0)); + break; + case 1: + p.fillRect(widgetRect, QColor(255,200,55)); + break; + case 2: + p.fillRect(widgetRect, QColor(200,255,55)); + break; + case 3: + p.fillRect(widgetRect, QColor(200,200,0)); + break; + } + i = (i+1) & 3; + p.end(); + } + } + + if (setFlag) + widget->setAttribute(Qt::WA_WState_InPaintEvent, false); + + //restore + widget->setAttribute(Qt::WA_PaintUnclipped, paintUnclipped); + + if (pe) + pe->setSystemClip(QRegion()); + +#if defined(Q_OS_UNIX) + ::usleep(1000 * msec); +#endif +#endif // !Q_OS_WIN +} + +bool QWidgetRepaintManager::flushPaint(QWidget *widget, const QRegion &rgn) +{ + if (!widget) + return false; + + int delay = 0; + if (widget->testAttribute(Qt::WA_WState_InPaintEvent)) { + static int flushPaintEvent = qEnvironmentVariableIntValue("QT_FLUSH_PAINT_EVENT"); + if (!flushPaintEvent) + return false; + delay = flushPaintEvent; + } else { + static int flushPaint = qEnvironmentVariableIntValue("QT_FLUSH_PAINT"); + if (!flushPaint) + return false; + delay = flushPaint; + } + + QWidgetRepaintManager::showYellowThing(widget, rgn, delay * 10, true); + return true; +} + +void QWidgetRepaintManager::unflushPaint(QWidget *widget, const QRegion &rgn) +{ + if (widget->d_func()->paintOnScreen() || rgn.isEmpty()) + return; + + QWidget *tlw = widget->window(); + QTLWExtra *tlwExtra = tlw->d_func()->maybeTopData(); + if (!tlwExtra) + return; + + qt_flush(widget, rgn, tlwExtra->repaintManager->store, tlw, 0, tlw->d_func()->maybeRepaintManager()); +} +#endif // QT_NO_PAINT_DEBUG + +/* + Moves the whole rect by (dx, dy) in widget's coordinate system. + Doesn't generate any updates. +*/ +bool QWidgetRepaintManager::bltRect(const QRect &rect, int dx, int dy, QWidget *widget) +{ + const QPoint pos(widget->mapTo(tlw, rect.topLeft())); + const QRect tlwRect(QRect(pos, rect.size())); + if (dirty.intersects(tlwRect)) + return false; // We don't want to scroll junk. + return store->scroll(tlwRect, dx, dy); +} + +/*! + Prepares the window surface to paint a\ toClean region of the \a widget and + updates the BeginPaintInfo struct accordingly. + + The \a toClean region might be clipped by the window surface. +*/ +void QWidgetRepaintManager::beginPaint(QRegion &toClean, QWidget *widget, QBackingStore *backingStore, + BeginPaintInfo *returnInfo, bool toCleanIsInTopLevelCoordinates) +{ + Q_UNUSED(widget); + Q_UNUSED(toCleanIsInTopLevelCoordinates); + + // Always flush repainted areas. + dirtyOnScreen += toClean; + +#ifdef QT_NO_PAINT_DEBUG + backingStore->beginPaint(toClean); +#else + returnInfo->wasFlushed = QWidgetRepaintManager::flushPaint(tlw, toClean); + // Avoid deadlock with QT_FLUSH_PAINT: the server will wait for + // the BackingStore lock, so if we hold that, the server will + // never release the Communication lock that we are waiting for in + // sendSynchronousCommand + if (!returnInfo->wasFlushed) + backingStore->beginPaint(toClean); +#endif + + Q_UNUSED(returnInfo); +} + +void QWidgetRepaintManager::endPaint(const QRegion &cleaned, QBackingStore *backingStore, + BeginPaintInfo *beginPaintInfo) +{ +#ifndef QT_NO_PAINT_DEBUG + if (!beginPaintInfo->wasFlushed) + backingStore->endPaint(); + else + QWidgetRepaintManager::unflushPaint(tlw, cleaned); +#else + Q_UNUSED(beginPaintInfo); + Q_UNUSED(cleaned); + backingStore->endPaint(); +#endif + + flush(); +} + +/*! + Returns the region (in top-level coordinates) that needs repaint and/or flush. + + If the widget is non-zero, only the dirty region for the widget is returned + and the region will be in widget coordinates. +*/ +QRegion QWidgetRepaintManager::dirtyRegion(QWidget *widget) const +{ + const bool widgetDirty = widget && widget != tlw; + const QRect tlwRect(topLevelRect()); + const QRect surfaceGeometry(tlwRect.topLeft(), store->size()); + if (surfaceGeometry != tlwRect && surfaceGeometry.size() != tlwRect.size()) { + if (widgetDirty) { + const QRect dirtyTlwRect = QRect(QPoint(), tlwRect.size()); + const QPoint offset(widget->mapTo(tlw, QPoint())); + const QRect dirtyWidgetRect(dirtyTlwRect & widget->rect().translated(offset)); + return dirtyWidgetRect.translated(-offset); + } + return QRect(QPoint(), tlwRect.size()); + } + + // Calculate the region that needs repaint. + QRegion r(dirty); + for (int i = 0; i < dirtyWidgets.size(); ++i) { + QWidget *w = dirtyWidgets.at(i); + if (widgetDirty && w != widget && !widget->isAncestorOf(w)) + continue; + r += w->d_func()->dirty.translated(w->mapTo(tlw, QPoint())); + } + + // Append the region that needs flush. + r += dirtyOnScreen; + + for (QWidget *w : dirtyOnScreenWidgets) { + if (widgetDirty && w != widget && !widget->isAncestorOf(w)) + continue; + QWidgetPrivate *wd = w->d_func(); + Q_ASSERT(wd->needsFlush); + r += wd->needsFlush->translated(w->mapTo(tlw, QPoint())); + } + + if (widgetDirty) { + // Intersect with the widget geometry and translate to its coordinates. + const QPoint offset(widget->mapTo(tlw, QPoint())); + r &= widget->rect().translated(offset); + r.translate(-offset); + } + return r; +} + +/*! + Returns the static content inside the \a parent if non-zero; otherwise the static content + for the entire backing store is returned. The content will be clipped to \a withinClipRect + if non-empty. +*/ +QRegion QWidgetRepaintManager::staticContents(QWidget *parent, const QRect &withinClipRect) const +{ + if (!parent && tlw->testAttribute(Qt::WA_StaticContents)) { + const QSize surfaceGeometry(store->size()); + QRect surfaceRect(0, 0, surfaceGeometry.width(), surfaceGeometry.height()); + if (!withinClipRect.isEmpty()) + surfaceRect &= withinClipRect; + return QRegion(surfaceRect); + } + + QRegion region; + if (parent && parent->d_func()->children.isEmpty()) + return region; + + const bool clipToRect = !withinClipRect.isEmpty(); + const int count = staticWidgets.count(); + for (int i = 0; i < count; ++i) { + QWidget *w = staticWidgets.at(i); + QWidgetPrivate *wd = w->d_func(); + if (!wd->isOpaque || !wd->extra || wd->extra->staticContentsSize.isEmpty() + || !w->isVisible() || (parent && !parent->isAncestorOf(w))) { + continue; + } + + QRect rect(0, 0, wd->extra->staticContentsSize.width(), wd->extra->staticContentsSize.height()); + const QPoint offset = w->mapTo(parent ? parent : tlw, QPoint()); + if (clipToRect) + rect &= withinClipRect.translated(-offset); + if (rect.isEmpty()) + continue; + + rect &= wd->clipRect(); + if (rect.isEmpty()) + continue; + + QRegion visible(rect); + wd->clipToEffectiveMask(visible); + if (visible.isEmpty()) + continue; + wd->subtractOpaqueSiblings(visible, 0, /*alsoNonOpaque=*/true); + + visible.translate(offset); + region += visible; + } + + return region; +} + +void QWidgetRepaintManager::sendUpdateRequest(QWidget *widget, UpdateTime updateTime) +{ + if (!widget) + return; + +#ifndef QT_NO_OPENGL + // Having every repaint() leading to a sync/flush is bad as it causes + // compositing and waiting for vsync each and every time. Change to + // UpdateLater, except for approx. once per frame to prevent starvation in + // case the control does not get back to the event loop. + QWidget *w = widget->window(); + if (updateTime == UpdateNow && w && w->windowHandle() && QWindowPrivate::get(w->windowHandle())->compositing) { + int refresh = 60; + QScreen *ws = w->windowHandle()->screen(); + if (ws) + refresh = ws->refreshRate(); + QWindowPrivate *wd = QWindowPrivate::get(w->windowHandle()); + if (wd->lastComposeTime.isValid()) { + const qint64 elapsed = wd->lastComposeTime.elapsed(); + if (elapsed <= qint64(1000.0f / refresh)) + updateTime = UpdateLater; + } + } +#endif + + switch (updateTime) { + case UpdateLater: + updateRequestSent = true; + QCoreApplication::postEvent(widget, new QEvent(QEvent::UpdateRequest), Qt::LowEventPriority); + break; + case UpdateNow: { + QEvent event(QEvent::UpdateRequest); + QCoreApplication::sendEvent(widget, &event); + break; + } + } +} + +static inline QRect widgetRectFor(QWidget *, const QRect &r) { return r; } +static inline QRect widgetRectFor(QWidget *widget, const QRegion &) { return widget->rect(); } + +/*! + Marks the region of the widget as dirty (if not already marked as dirty) and + posts an UpdateRequest event to the top-level widget (if not already posted). + + If updateTime is UpdateNow, the event is sent immediately instead of posted. + + If bufferState is BufferInvalid, all widgets intersecting with the region will be dirty. + + If the widget paints directly on screen, the event is sent to the widget + instead of the top-level widget, and bufferState is completely ignored. +*/ +template +void QWidgetRepaintManager::markDirty(const T &r, QWidget *widget, UpdateTime updateTime, BufferState bufferState) +{ + Q_ASSERT(tlw->d_func()->extra); + Q_ASSERT(tlw->d_func()->extra->topextra); + Q_ASSERT(!tlw->d_func()->extra->topextra->inTopLevelResize); + Q_ASSERT(widget->isVisible() && widget->updatesEnabled()); + Q_ASSERT(widget->window() == tlw); + Q_ASSERT(!r.isEmpty()); + +#if QT_CONFIG(graphicseffect) + widget->d_func()->invalidateGraphicsEffectsRecursively(); +#endif + + QRect widgetRect = widgetRectFor(widget, r); + + // --------------------------------------------------------------------------- + + if (widget->d_func()->paintOnScreen()) { + if (widget->d_func()->dirty.isEmpty()) { + widget->d_func()->dirty = r; + sendUpdateRequest(widget, updateTime); + return; + } else if (qt_region_strictContains(widget->d_func()->dirty, widgetRect)) { + if (updateTime == UpdateNow) + sendUpdateRequest(widget, updateTime); + return; // Already dirty + } + + const bool eventAlreadyPosted = !widget->d_func()->dirty.isEmpty(); + widget->d_func()->dirty += r; + if (!eventAlreadyPosted || updateTime == UpdateNow) + sendUpdateRequest(widget, updateTime); + return; + } + + // --------------------------------------------------------------------------- + + if (QWidgetPrivate::get(widget)->renderToTexture) { + if (!widget->d_func()->inDirtyList) + addDirtyRenderToTextureWidget(widget); + if (!updateRequestSent || updateTime == UpdateNow) + sendUpdateRequest(tlw, updateTime); + return; + } + + // --------------------------------------------------------------------------- + + QRect effectiveWidgetRect = widget->d_func()->effectiveRectFor(widgetRect); + const QPoint offset = widget->mapTo(tlw, QPoint()); + QRect translatedRect = effectiveWidgetRect.translated(offset); +#if QT_CONFIG(graphicseffect) + // Graphics effects may exceed window size, clamp + translatedRect = translatedRect.intersected(QRect(QPoint(), tlw->size())); +#endif + if (qt_region_strictContains(dirty, translatedRect)) { + if (updateTime == UpdateNow) + sendUpdateRequest(tlw, updateTime); + return; // Already dirty + } + + // --------------------------------------------------------------------------- + + if (bufferState == BufferInvalid) { + const bool eventAlreadyPosted = !dirty.isEmpty() || updateRequestSent; +#if QT_CONFIG(graphicseffect) + if (widget->d_func()->graphicsEffect) + dirty += widget->d_func()->effectiveRectFor(r).translated(offset); + else +#endif + dirty += r.translated(offset); + + if (!eventAlreadyPosted || updateTime == UpdateNow) + sendUpdateRequest(tlw, updateTime); + return; + } + + // --------------------------------------------------------------------------- + + if (dirtyWidgets.isEmpty()) { + addDirtyWidget(widget, r); + sendUpdateRequest(tlw, updateTime); + return; + } + + // --------------------------------------------------------------------------- + + if (widget->d_func()->inDirtyList) { + if (!qt_region_strictContains(widget->d_func()->dirty, effectiveWidgetRect)) { +#if QT_CONFIG(graphicseffect) + if (widget->d_func()->graphicsEffect) + widget->d_func()->dirty += widget->d_func()->effectiveRectFor(r); + else +#endif + widget->d_func()->dirty += r; + } + } else { + addDirtyWidget(widget, r); + } + + // --------------------------------------------------------------------------- + + if (updateTime == UpdateNow) + sendUpdateRequest(tlw, updateTime); +} +template void QWidgetRepaintManager::markDirty(const QRect &, QWidget *, UpdateTime, BufferState); +template void QWidgetRepaintManager::markDirty(const QRegion &, QWidget *, UpdateTime, BufferState); + +/*! + Marks the \a region of the \a widget as dirty on screen. The \a region will be copied from + the backing store to the \a widget's native parent next time flush() is called. + + Paint on screen widgets are ignored. +*/ +void QWidgetRepaintManager::markDirtyOnScreen(const QRegion ®ion, QWidget *widget, const QPoint &topLevelOffset) +{ + if (!widget || widget->d_func()->paintOnScreen() || region.isEmpty()) + return; + +#if 0 // Used to be included in Qt4 for Q_WS_MAC + if (!widget->testAttribute(Qt::WA_WState_InPaintEvent)) + dirtyOnScreen += region.translated(topLevelOffset); + return; +#endif + + // Top-level. + if (widget == tlw) { + if (!widget->testAttribute(Qt::WA_WState_InPaintEvent)) + dirtyOnScreen += region; + return; + } + + // Alien widgets. + if (!hasPlatformWindow(widget) && !widget->isWindow()) { + QWidget *nativeParent = widget->nativeParentWidget(); // Alien widgets with the top-level as the native parent (common case). + if (nativeParent == tlw) { + if (!widget->testAttribute(Qt::WA_WState_InPaintEvent)) + dirtyOnScreen += region.translated(topLevelOffset); + return; + } + + // Alien widgets with native parent != tlw. + QWidgetPrivate *nativeParentPrivate = nativeParent->d_func(); + if (!nativeParentPrivate->needsFlush) + nativeParentPrivate->needsFlush = new QRegion; + const QPoint nativeParentOffset = widget->mapTo(nativeParent, QPoint()); + *nativeParentPrivate->needsFlush += region.translated(nativeParentOffset); + appendDirtyOnScreenWidget(nativeParent); + return; + } + + // Native child widgets. + QWidgetPrivate *widgetPrivate = widget->d_func(); + if (!widgetPrivate->needsFlush) + widgetPrivate->needsFlush = new QRegion; + *widgetPrivate->needsFlush += region; + appendDirtyOnScreenWidget(widget); +} + +void QWidgetRepaintManager::removeDirtyWidget(QWidget *w) +{ + if (!w) + return; + + dirtyWidgets.removeAll(w); + dirtyOnScreenWidgets.removeAll(w); + dirtyRenderToTextureWidgets.removeAll(w); + resetWidget(w); + + QWidgetPrivate *wd = w->d_func(); + const int n = wd->children.count(); + for (int i = 0; i < n; ++i) { + if (QWidget *child = qobject_cast(wd->children.at(i))) + removeDirtyWidget(child); + } +} + +void QWidgetRepaintManager::updateLists(QWidget *cur) +{ + if (!cur) + return; + + QList children = cur->children(); + for (int i = 0; i < children.size(); ++i) { + QWidget *child = qobject_cast(children.at(i)); + if (!child || child->isWindow()) + continue; + + updateLists(child); + } + + if (cur->testAttribute(Qt::WA_StaticContents)) + addStaticWidget(cur); +} + +QWidgetRepaintManager::QWidgetRepaintManager(QWidget *topLevel) + : tlw(topLevel), + updateRequestSent(0), + textureListWatcher(0), + perfFrames(0) +{ + store = tlw->backingStore(); + Q_ASSERT(store); + + // Ensure all existing subsurfaces and static widgets are added to their respective lists. + updateLists(topLevel); +} + +QWidgetRepaintManager::~QWidgetRepaintManager() +{ + for (int c = 0; c < dirtyWidgets.size(); ++c) + resetWidget(dirtyWidgets.at(c)); + for (int c = 0; c < dirtyRenderToTextureWidgets.size(); ++c) + resetWidget(dirtyRenderToTextureWidgets.at(c)); +} + +static QVector getSortedRectsToScroll(const QRegion ®ion, int dx, int dy) +{ + QVector rects; + std::copy(region.begin(), region.end(), std::back_inserter(rects)); + if (rects.count() > 1) { + std::sort(rects.begin(), rects.end(), [=](const QRect &r1, const QRect &r2) { + if (r1.y() == r2.y()) { + if (dx > 0) + return r1.x() > r2.x(); + return r1.x() < r2.x(); + } + if (dy > 0) + return r1.y() > r2.y(); + return r1.y() < r2.y(); + }); + } + return rects; +} + +//parent's coordinates; move whole rect; update parent and widget +//assume the screen blt has already been done, so we don't need to refresh that part +void QWidgetPrivate::moveRect(const QRect &rect, int dx, int dy) +{ + Q_Q(QWidget); + if (!q->isVisible() || (dx == 0 && dy == 0)) + return; + + QWidget *tlw = q->window(); + QTLWExtra* x = tlw->d_func()->topData(); + if (x->inTopLevelResize) + return; + + static const bool accelEnv = qEnvironmentVariableIntValue("QT_NO_FAST_MOVE") == 0; + + QWidget *pw = q->parentWidget(); + QPoint toplevelOffset = pw->mapTo(tlw, QPoint()); + QWidgetPrivate *pd = pw->d_func(); + QRect clipR(pd->clipRect()); + const QRect newRect(rect.translated(dx, dy)); + QRect destRect = rect.intersected(clipR); + if (destRect.isValid()) + destRect = destRect.translated(dx, dy).intersected(clipR); + const QRect sourceRect(destRect.translated(-dx, -dy)); + const QRect parentRect(rect & clipR); + const bool nativeWithTextureChild = textureChildSeen && hasPlatformWindow(q); + + const bool accelerateMove = accelEnv && isOpaque && !nativeWithTextureChild +#if QT_CONFIG(graphicsview) + // No accelerate move for proxy widgets. + && !tlw->d_func()->extra->proxyWidget +#endif + ; + + if (!accelerateMove) { + QRegion parentR(effectiveRectFor(parentRect)); + if (!extra || !extra->hasMask) { + parentR -= newRect; + } else { + // invalidateBackingStore() excludes anything outside the mask + parentR += newRect & clipR; + } + pd->invalidateBackingStore(parentR); + invalidateBackingStore((newRect & clipR).translated(-data.crect.topLeft())); + } else { + + QWidgetRepaintManager *wbs = x->repaintManager.get(); + QRegion childExpose(newRect & clipR); + QRegion overlappedExpose; + + if (sourceRect.isValid()) { + overlappedExpose = (overlappedRegion(sourceRect) | overlappedRegion(destRect)) & clipR; + + const qreal factor = QHighDpiScaling::factor(q->windowHandle()); + if (overlappedExpose.isEmpty() || qFloor(factor) == factor) { + const QVector rectsToScroll + = getSortedRectsToScroll(QRegion(sourceRect) - overlappedExpose, dx, dy); + for (QRect rect : rectsToScroll) { + if (wbs->bltRect(rect, dx, dy, pw)) { + childExpose -= rect.translated(dx, dy); + } + } + } + + childExpose -= overlappedExpose; + } + + if (!pw->updatesEnabled()) + return; + + const bool childUpdatesEnabled = q->updatesEnabled(); + if (childUpdatesEnabled) { + if (!overlappedExpose.isEmpty()) { + overlappedExpose.translate(-data.crect.topLeft()); + invalidateBackingStore(overlappedExpose); + } + if (!childExpose.isEmpty()) { + childExpose.translate(-data.crect.topLeft()); + wbs->markDirty(childExpose, q); + isMoved = true; + } + } + + QRegion parentExpose(parentRect); + parentExpose -= newRect; + if (extra && extra->hasMask) + parentExpose += QRegion(newRect) - extra->mask.translated(data.crect.topLeft()); + + if (!parentExpose.isEmpty()) { + wbs->markDirty(parentExpose, pw); + pd->isMoved = true; + } + + if (childUpdatesEnabled) { + QRegion needsFlush(sourceRect); + needsFlush += destRect; + wbs->markDirtyOnScreen(needsFlush, pw, toplevelOffset); + } + } +} + +//widget's coordinates; scroll within rect; only update widget +void QWidgetPrivate::scrollRect(const QRect &rect, int dx, int dy) +{ + Q_Q(QWidget); + QWidget *tlw = q->window(); + QTLWExtra* x = tlw->d_func()->topData(); + if (x->inTopLevelResize) + return; + + QWidgetRepaintManager *wbs = x->repaintManager.get(); + if (!wbs) + return; + + static const bool accelEnv = qEnvironmentVariableIntValue("QT_NO_FAST_SCROLL") == 0; + + const QRect clipR = clipRect(); + const QRect scrollRect = rect & clipR; + const bool accelerateScroll = accelEnv && isOpaque && !q_func()->testAttribute(Qt::WA_WState_InPaintEvent); + + if (!accelerateScroll) { + if (!overlappedRegion(scrollRect.translated(data.crect.topLeft()), true).isEmpty()) { + QRegion region(scrollRect); + subtractOpaqueSiblings(region); + invalidateBackingStore(region); + }else { + invalidateBackingStore(scrollRect); + } + } else { + const QPoint toplevelOffset = q->mapTo(tlw, QPoint()); + const QRect destRect = scrollRect.translated(dx, dy) & scrollRect; + const QRect sourceRect = destRect.translated(-dx, -dy); + + const QRegion overlappedExpose = (overlappedRegion(scrollRect.translated(data.crect.topLeft()))) + .translated(-data.crect.topLeft()) & clipR; + QRegion childExpose(scrollRect); + + const qreal factor = QHighDpiScaling::factor(q->windowHandle()); + if (overlappedExpose.isEmpty() || qFloor(factor) == factor) { + const QVector rectsToScroll + = getSortedRectsToScroll(QRegion(sourceRect) - overlappedExpose, dx, dy); + for (const QRect &rect : rectsToScroll) { + if (wbs->bltRect(rect, dx, dy, q)) { + childExpose -= rect.translated(dx, dy); + } + } + } + + childExpose -= overlappedExpose; + + if (inDirtyList) { + if (rect == q->rect()) { + dirty.translate(dx, dy); + } else { + QRegion dirtyScrollRegion = dirty.intersected(scrollRect); + if (!dirtyScrollRegion.isEmpty()) { + dirty -= dirtyScrollRegion; + dirtyScrollRegion.translate(dx, dy); + dirty += dirtyScrollRegion; + } + } + } + + if (!q->updatesEnabled()) + return; + + if (!overlappedExpose.isEmpty()) + invalidateBackingStore(overlappedExpose); + if (!childExpose.isEmpty()) { + wbs->markDirty(childExpose, q); + isScrolled = true; + } + + // Instead of using native scroll-on-screen, we copy from + // backingstore, giving only one screen update for each + // scroll, and a solid appearance + wbs->markDirtyOnScreen(destRect, q, toplevelOffset); + } +} + +#ifndef QT_NO_OPENGL +static void findTextureWidgetsRecursively(QWidget *tlw, QWidget *widget, QPlatformTextureList *widgetTextures, QVector *nativeChildren) +{ + QWidgetPrivate *wd = QWidgetPrivate::get(widget); + if (wd->renderToTexture) { + QPlatformTextureList::Flags flags = wd->textureListFlags(); + const QRect rect(widget->mapTo(tlw, QPoint()), widget->size()); + widgetTextures->appendTexture(widget, wd->textureId(), rect, wd->clipRect(), flags); + } + + for (int i = 0; i < wd->children.size(); ++i) { + QWidget *w = qobject_cast(wd->children.at(i)); + // Stop at native widgets but store them. Stop at hidden widgets too. + if (w && !w->isWindow() && hasPlatformWindow(w)) + nativeChildren->append(w); + if (w && !w->isWindow() && !hasPlatformWindow(w) && !w->isHidden() && QWidgetPrivate::get(w)->textureChildSeen) + findTextureWidgetsRecursively(tlw, w, widgetTextures, nativeChildren); + } +} + +static void findAllTextureWidgetsRecursively(QWidget *tlw, QWidget *widget) +{ + // textureChildSeen does not take native child widgets into account and that's good. + if (QWidgetPrivate::get(widget)->textureChildSeen) { + QVector nativeChildren; + auto tl = qt_make_unique(); + // Look for texture widgets (incl. widget itself) from 'widget' down, + // but skip subtrees with a parent of a native child widget. + findTextureWidgetsRecursively(tlw, widget, tl.get(), &nativeChildren); + // tl may be empty regardless of textureChildSeen if we have native or hidden children. + if (!tl->isEmpty()) + QWidgetPrivate::get(tlw)->topData()->widgetTextures.push_back(std::move(tl)); + // Native child widgets, if there was any, get their own separate QPlatformTextureList. + for (QWidget *ncw : qAsConst(nativeChildren)) { + if (QWidgetPrivate::get(ncw)->textureChildSeen) + findAllTextureWidgetsRecursively(tlw, ncw); + } + } +} + +static QPlatformTextureList *widgetTexturesFor(QWidget *tlw, QWidget *widget) +{ + for (const auto &tl : QWidgetPrivate::get(tlw)->topData()->widgetTextures) { + Q_ASSERT(!tl->isEmpty()); + for (int i = 0; i < tl->count(); ++i) { + QWidget *w = static_cast(tl->source(i)); + if ((hasPlatformWindow(w) && w == widget) || (!hasPlatformWindow(w) && w->nativeParentWidget() == widget)) + return tl.get(); + } + } + + if (QWidgetPrivate::get(widget)->textureChildSeen) { + // No render-to-texture widgets in the (sub-)tree due to hidden or native + // children. Returning null results in using the normal backingstore flush path + // without OpenGL-based compositing. This is very desirable normally. However, + // some platforms cannot handle switching between the non-GL and GL paths for + // their windows so it has to be opt-in. + static bool switchableWidgetComposition = + QGuiApplicationPrivate::instance()->platformIntegration() + ->hasCapability(QPlatformIntegration::SwitchableWidgetComposition); + if (!switchableWidgetComposition) + return qt_dummy_platformTextureList(); + } + + return 0; +} + +// Watches one or more QPlatformTextureLists for changes in the lock state and +// triggers a backingstore sync when all the registered lists turn into +// unlocked state. This is essential when a custom composeAndFlush() +// implementation in a platform plugin is not synchronous and keeps +// holding on to the textures for some time even after returning from there. +QPlatformTextureListWatcher::QPlatformTextureListWatcher(QWidgetRepaintManager *paintManager) + : m_repaintManager(paintManager) +{ +} + +void QPlatformTextureListWatcher::watch(QPlatformTextureList *textureList) +{ + connect(textureList, SIGNAL(locked(bool)), SLOT(onLockStatusChanged(bool))); + m_locked[textureList] = textureList->isLocked(); +} + +bool QPlatformTextureListWatcher::isLocked() const +{ + foreach (bool v, m_locked) { + if (v) + return true; + } + return false; +} + +void QPlatformTextureListWatcher::onLockStatusChanged(bool locked) +{ + QPlatformTextureList *tl = static_cast(sender()); + m_locked[tl] = locked; + if (!isLocked()) + m_repaintManager->sync(); +} + +#else + +static QPlatformTextureList *widgetTexturesFor(QWidget *tlw, QWidget *widget) +{ + Q_UNUSED(tlw); + Q_UNUSED(widget); + return nullptr; +} + +#endif // QT_NO_OPENGL + +static inline bool discardSyncRequest(QWidget *tlw, QTLWExtra *tlwExtra) +{ + if (!tlw || !tlwExtra || !tlw->testAttribute(Qt::WA_Mapped) || !tlw->isVisible()) + return true; + + return false; +} + +bool QWidgetRepaintManager::syncAllowed() +{ +#ifndef QT_NO_OPENGL + QTLWExtra *tlwExtra = tlw->d_func()->maybeTopData(); + if (textureListWatcher && !textureListWatcher->isLocked()) { + textureListWatcher->deleteLater(); + textureListWatcher = 0; + } else if (!tlwExtra->widgetTextures.empty()) { + bool skipSync = false; + for (const auto &tl : tlwExtra->widgetTextures) { + if (tl->isLocked()) { + if (!textureListWatcher) + textureListWatcher = new QPlatformTextureListWatcher(this); + if (!textureListWatcher->isLocked()) + textureListWatcher->watch(tl.get()); + skipSync = true; + } + } + if (skipSync) // cannot compose due to widget textures being in use + return false; + } +#endif + return true; +} + +/*! + Synchronizes the \a exposedRegion of the \a exposedWidget with the backing store. + + If there's nothing to repaint, the area is flushed and painting does not occur; + otherwise the area is marked as dirty on screen and will be flushed right after + we are done with all painting. +*/ +void QWidgetRepaintManager::sync(QWidget *exposedWidget, const QRegion &exposedRegion) +{ + QTLWExtra *tlwExtra = tlw->d_func()->maybeTopData(); + if (!tlw->isVisible() || !tlwExtra || tlwExtra->inTopLevelResize) + return; + + if (!exposedWidget || !hasPlatformWindow(exposedWidget) + || !exposedWidget->isVisible() || !exposedWidget->testAttribute(Qt::WA_Mapped) + || !exposedWidget->updatesEnabled() || exposedRegion.isEmpty()) { + return; + } + + // Nothing to repaint. + if (!isDirty() && store->size().isValid()) { + QPlatformTextureList *tl = widgetTexturesFor(tlw, exposedWidget); + qt_flush(exposedWidget, tl ? QRegion() : exposedRegion, store, tlw, tl, this); + return; + } + + if (exposedWidget != tlw) + markDirtyOnScreen(exposedRegion, exposedWidget, exposedWidget->mapTo(tlw, QPoint())); + else + markDirtyOnScreen(exposedRegion, exposedWidget, QPoint()); + + if (syncAllowed()) + doSync(); +} + +/*! + Synchronizes the backing store, i.e. dirty areas are repainted and flushed. +*/ +void QWidgetRepaintManager::sync() +{ + updateRequestSent = false; + QTLWExtra *tlwExtra = tlw->d_func()->maybeTopData(); + if (discardSyncRequest(tlw, tlwExtra)) { + // If the top-level is minimized, it's not visible on the screen so we can delay the + // update until it's shown again. In order to do that we must keep the dirty states. + // These will be cleared when we receive the first expose after showNormal(). + // However, if the widget is not visible (isVisible() returns false), everything will + // be invalidated once the widget is shown again, so clear all dirty states. + if (!tlw->isVisible()) { + dirty = QRegion(); + for (int i = 0; i < dirtyWidgets.size(); ++i) + resetWidget(dirtyWidgets.at(i)); + dirtyWidgets.clear(); + } + return; + } + + if (syncAllowed()) + doSync(); +} + +void QWidgetRepaintManager::doSync() +{ + const bool updatesDisabled = !tlw->updatesEnabled(); + bool repaintAllWidgets = false; + + const bool inTopLevelResize = tlw->d_func()->maybeTopData()->inTopLevelResize; + const QRect tlwRect(topLevelRect()); + const QRect surfaceGeometry(tlwRect.topLeft(), store->size()); + if ((inTopLevelResize || surfaceGeometry.size() != tlwRect.size()) && !updatesDisabled) { + if (hasStaticContents() && !store->size().isEmpty() ) { + // Repaint existing dirty area and newly visible area. + const QRect clipRect(0, 0, surfaceGeometry.width(), surfaceGeometry.height()); + const QRegion staticRegion(staticContents(0, clipRect)); + QRegion newVisible(0, 0, tlwRect.width(), tlwRect.height()); + newVisible -= staticRegion; + dirty += newVisible; + store->setStaticContents(staticRegion); + } else { + // Repaint everything. + dirty = QRegion(0, 0, tlwRect.width(), tlwRect.height()); + for (int i = 0; i < dirtyWidgets.size(); ++i) + resetWidget(dirtyWidgets.at(i)); + dirtyWidgets.clear(); + repaintAllWidgets = true; + } + } + + if (inTopLevelResize || surfaceGeometry.size() != tlwRect.size()) + store->resize(tlwRect.size()); + + if (updatesDisabled) + return; + + // Contains everything that needs repaint. + QRegion toClean(dirty); + + // Loop through all update() widgets and remove them from the list before they are + // painted (in case someone calls update() in paintEvent). If the widget is opaque + // and does not have transparent overlapping siblings, append it to the + // opaqueNonOverlappedWidgets list and paint it directly without composition. + QVarLengthArray opaqueNonOverlappedWidgets; + for (int i = 0; i < dirtyWidgets.size(); ++i) { + QWidget *w = dirtyWidgets.at(i); + QWidgetPrivate *wd = w->d_func(); + if (wd->data.in_destructor) + continue; + + // Clip with mask() and clipRect(). + wd->dirty &= wd->clipRect(); + wd->clipToEffectiveMask(wd->dirty); + + // Subtract opaque siblings and children. + bool hasDirtySiblingsAbove = false; + // We know for sure that the widget isn't overlapped if 'isMoved' is true. + if (!wd->isMoved) + wd->subtractOpaqueSiblings(wd->dirty, &hasDirtySiblingsAbove); + + // Make a copy of the widget's dirty region, to restore it in case there is an opaque + // render-to-texture child that completely covers the widget, because otherwise the + // render-to-texture child won't be visible, due to its parent widget not being redrawn + // with a proper blending mask. + const QRegion dirtyBeforeSubtractedOpaqueChildren = wd->dirty; + + // Scrolled and moved widgets must draw all children. + if (!wd->isScrolled && !wd->isMoved) + wd->subtractOpaqueChildren(wd->dirty, w->rect()); + + if (wd->dirty.isEmpty() && wd->textureChildSeen) + wd->dirty = dirtyBeforeSubtractedOpaqueChildren; + + if (wd->dirty.isEmpty()) { + resetWidget(w); + continue; + } + + const QRegion widgetDirty(w != tlw ? wd->dirty.translated(w->mapTo(tlw, QPoint())) + : wd->dirty); + toClean += widgetDirty; + +#if QT_CONFIG(graphicsview) + if (tlw->d_func()->extra->proxyWidget) { + resetWidget(w); + continue; + } +#endif + + if (!hasDirtySiblingsAbove && wd->isOpaque && !dirty.intersects(widgetDirty.boundingRect())) { + opaqueNonOverlappedWidgets.append(w); + } else { + resetWidget(w); + dirty += widgetDirty; + } + } + dirtyWidgets.clear(); + +#ifndef QT_NO_OPENGL + // Find all render-to-texture child widgets (including self). + // The search is cut at native widget boundaries, meaning that each native child widget + // has its own list for the subtree below it. + QTLWExtra *tlwExtra = tlw->d_func()->topData(); + tlwExtra->widgetTextures.clear(); + findAllTextureWidgetsRecursively(tlw, tlw); + qt_window_private(tlw->windowHandle())->compositing = false; // will get updated in qt_flush() +#endif + + if (toClean.isEmpty()) { + // Nothing to repaint. However renderToTexture widgets are handled + // specially, they are not in the regular dirty list, in order to + // prevent triggering unnecessary backingstore painting when only the + // OpenGL content changes. Check if we have such widgets in the special + // dirty list. + QVarLengthArray paintPending; + const int numPaintPending = dirtyRenderToTextureWidgets.count(); + paintPending.reserve(numPaintPending); + for (int i = 0; i < numPaintPending; ++i) { + QWidget *w = dirtyRenderToTextureWidgets.at(i); + paintPending << w; + resetWidget(w); + } + dirtyRenderToTextureWidgets.clear(); + for (int i = 0; i < numPaintPending; ++i) { + QWidget *w = paintPending[i]; + w->d_func()->sendPaintEvent(w->rect()); + if (w != tlw) { + QWidget *npw = w->nativeParentWidget(); + if (hasPlatformWindow(w) || (npw && npw != tlw)) { + if (!hasPlatformWindow(w)) + w = npw; + QWidgetPrivate *wPrivate = w->d_func(); + if (!wPrivate->needsFlush) + wPrivate->needsFlush = new QRegion; + appendDirtyOnScreenWidget(w); + } + } + } + + // We might have newly exposed areas on the screen if this function was + // called from sync(QWidget *, QRegion)), so we have to make sure those + // are flushed. We also need to composite the renderToTexture widgets. + flush(); + + return; + } + +#ifndef QT_NO_OPENGL + for (const auto &tl : tlwExtra->widgetTextures) { + for (int i = 0; i < tl->count(); ++i) { + QWidget *w = static_cast(tl->source(i)); + if (dirtyRenderToTextureWidgets.contains(w)) { + const QRect rect = tl->geometry(i); // mapped to the tlw already + // Set a flag to indicate that the paint event for this + // render-to-texture widget must not to be optimized away. + w->d_func()->renderToTextureReallyDirty = 1; + dirty += rect; + toClean += rect; + } + } + } + for (int i = 0; i < dirtyRenderToTextureWidgets.count(); ++i) + resetWidget(dirtyRenderToTextureWidgets.at(i)); + dirtyRenderToTextureWidgets.clear(); +#endif + +#if QT_CONFIG(graphicsview) + if (tlw->d_func()->extra->proxyWidget) { + updateStaticContentsSize(); + dirty = QRegion(); + updateRequestSent = false; + for (const QRect &rect : toClean) + tlw->d_func()->extra->proxyWidget->update(rect); + return; + } +#endif + + BeginPaintInfo beginPaintInfo; + beginPaint(toClean, tlw, store, &beginPaintInfo); + if (beginPaintInfo.nothingToPaint) { + for (int i = 0; i < opaqueNonOverlappedWidgets.size(); ++i) + resetWidget(opaqueNonOverlappedWidgets[i]); + dirty = QRegion(); + updateRequestSent = false; + return; + } + + // Must do this before sending any paint events because + // the size may change in the paint event. + updateStaticContentsSize(); + const QRegion dirtyCopy(dirty); + dirty = QRegion(); + updateRequestSent = false; + + // Paint opaque non overlapped widgets. + for (int i = 0; i < opaqueNonOverlappedWidgets.size(); ++i) { + QWidget *w = opaqueNonOverlappedWidgets[i]; + QWidgetPrivate *wd = w->d_func(); + + int flags = QWidgetPrivate::DrawRecursive; + // Scrolled and moved widgets must draw all children. + if (!wd->isScrolled && !wd->isMoved) + flags |= QWidgetPrivate::DontDrawOpaqueChildren; + if (w == tlw) + flags |= QWidgetPrivate::DrawAsRoot; + + QRegion toBePainted(wd->dirty); + resetWidget(w); + + QPoint offset; + if (w != tlw) + offset += w->mapTo(tlw, QPoint()); + wd->drawWidget(store->paintDevice(), toBePainted, offset, flags, 0, this); + } + + // Paint the rest with composition. + if (repaintAllWidgets || !dirtyCopy.isEmpty()) { + const int flags = QWidgetPrivate::DrawAsRoot | QWidgetPrivate::DrawRecursive; + tlw->d_func()->drawWidget(store->paintDevice(), dirtyCopy, QPoint(), flags, 0, this); + } + + endPaint(toClean, store, &beginPaintInfo); +} + +/*! + Flushes the contents of the backing store into the top-level widget. + If the \a widget is non-zero, the content is flushed to the \a widget. + If the \a surface is non-zero, the content of the \a surface is flushed. +*/ +void QWidgetRepaintManager::flush(QWidget *widget) +{ + const bool hasDirtyOnScreenWidgets = !dirtyOnScreenWidgets.isEmpty(); + bool flushed = false; + + // Flush the region in dirtyOnScreen. + if (!dirtyOnScreen.isEmpty()) { + QWidget *target = widget ? widget : tlw; + qt_flush(target, dirtyOnScreen, store, tlw, widgetTexturesFor(tlw, tlw), this); + dirtyOnScreen = QRegion(); + flushed = true; + } + + // Render-to-texture widgets are not in dirtyOnScreen so flush if we have not done it above. + if (!flushed && !hasDirtyOnScreenWidgets) { +#ifndef QT_NO_OPENGL + if (!tlw->d_func()->topData()->widgetTextures.empty()) { + QPlatformTextureList *tl = widgetTexturesFor(tlw, tlw); + if (tl) { + QWidget *target = widget ? widget : tlw; + qt_flush(target, QRegion(), store, tlw, tl, this); + } + } +#endif + } + + if (!hasDirtyOnScreenWidgets) + return; + + for (QWidget *w : qExchange(dirtyOnScreenWidgets, {})) { + QWidgetPrivate *wd = w->d_func(); + Q_ASSERT(wd->needsFlush); + QPlatformTextureList *widgetTexturesForNative = wd->textureChildSeen ? widgetTexturesFor(tlw, w) : 0; + qt_flush(w, *wd->needsFlush, store, tlw, widgetTexturesForNative, this); + *wd->needsFlush = QRegion(); + } +} + +/*! + Invalidates the backing store when the widget is resized. + Static areas are never invalidated unless absolutely needed. +*/ +void QWidgetPrivate::invalidateBackingStore_resizeHelper(const QPoint &oldPos, const QSize &oldSize) +{ + Q_Q(QWidget); + Q_ASSERT(!q->isWindow()); + Q_ASSERT(q->parentWidget()); + + const bool staticContents = q->testAttribute(Qt::WA_StaticContents); + const bool sizeDecreased = (data.crect.width() < oldSize.width()) + || (data.crect.height() < oldSize.height()); + + const QPoint offset(data.crect.x() - oldPos.x(), data.crect.y() - oldPos.y()); + const bool parentAreaExposed = !offset.isNull() || sizeDecreased; + const QRect newWidgetRect(q->rect()); + const QRect oldWidgetRect(0, 0, oldSize.width(), oldSize.height()); + + if (!staticContents || graphicsEffect) { + QRegion staticChildren; + QWidgetRepaintManager *bs = 0; + if (offset.isNull() && (bs = maybeRepaintManager())) + staticChildren = bs->staticContents(q, oldWidgetRect); + const bool hasStaticChildren = !staticChildren.isEmpty(); + + if (hasStaticChildren) { + QRegion dirty(newWidgetRect); + dirty -= staticChildren; + invalidateBackingStore(dirty); + } else { + // Entire widget needs repaint. + invalidateBackingStore(newWidgetRect); + } + + if (!parentAreaExposed) + return; + + // Invalidate newly exposed area of the parent. + if (!graphicsEffect && extra && extra->hasMask) { + QRegion parentExpose(extra->mask.translated(oldPos)); + parentExpose &= QRect(oldPos, oldSize); + if (hasStaticChildren) + parentExpose -= data.crect; // Offset is unchanged, safe to do this. + q->parentWidget()->d_func()->invalidateBackingStore(parentExpose); + } else { + if (hasStaticChildren && !graphicsEffect) { + QRegion parentExpose(QRect(oldPos, oldSize)); + parentExpose -= data.crect; // Offset is unchanged, safe to do this. + q->parentWidget()->d_func()->invalidateBackingStore(parentExpose); + } else { + q->parentWidget()->d_func()->invalidateBackingStore(effectiveRectFor(QRect(oldPos, oldSize))); + } + } + return; + } + + // Move static content to its new position. + if (!offset.isNull()) { + if (sizeDecreased) { + const QSize minSize(qMin(oldSize.width(), data.crect.width()), + qMin(oldSize.height(), data.crect.height())); + moveRect(QRect(oldPos, minSize), offset.x(), offset.y()); + } else { + moveRect(QRect(oldPos, oldSize), offset.x(), offset.y()); + } + } + + // Invalidate newly visible area of the widget. + if (!sizeDecreased || !oldWidgetRect.contains(newWidgetRect)) { + QRegion newVisible(newWidgetRect); + newVisible -= oldWidgetRect; + invalidateBackingStore(newVisible); + } + + if (!parentAreaExposed) + return; + + // Invalidate newly exposed area of the parent. + const QRect oldRect(oldPos, oldSize); + if (extra && extra->hasMask) { + QRegion parentExpose(oldRect); + parentExpose &= extra->mask.translated(oldPos); + parentExpose -= (extra->mask.translated(data.crect.topLeft()) & data.crect); + q->parentWidget()->d_func()->invalidateBackingStore(parentExpose); + } else { + QRegion parentExpose(oldRect); + parentExpose -= data.crect; + q->parentWidget()->d_func()->invalidateBackingStore(parentExpose); + } +} + +/*! + Invalidates the \a r (in widget's coordinates) of the backing store, i.e. + all widgets intersecting with the region will be repainted when the backing + store is synced. +*/ +template +void QWidgetPrivate::invalidateBackingStore(const T &r) +{ + if (r.isEmpty()) + return; + + if (QCoreApplication::closingDown()) + return; + + Q_Q(QWidget); + if (!q->isVisible() || !q->updatesEnabled()) + return; + + QTLWExtra *tlwExtra = q->window()->d_func()->maybeTopData(); + if (!tlwExtra || tlwExtra->inTopLevelResize || !tlwExtra->backingStore) + return; + + T clipped(r); + clipped &= clipRect(); + if (clipped.isEmpty()) + return; + + if (!graphicsEffect && extra && extra->hasMask) { + QRegion masked(extra->mask); + masked &= clipped; + if (masked.isEmpty()) + return; + + tlwExtra->repaintManager->markDirty(masked, q, + QWidgetRepaintManager::UpdateLater, QWidgetRepaintManager::BufferInvalid); + } else { + tlwExtra->repaintManager->markDirty(clipped, q, + QWidgetRepaintManager::UpdateLater, QWidgetRepaintManager::BufferInvalid); + } +} +// Needed by tst_QWidget +template Q_AUTOTEST_EXPORT void QWidgetPrivate::invalidateBackingStore(const QRect &r); + +void QWidgetPrivate::repaint_sys(const QRegion &rgn) +{ + if (data.in_destructor) + return; + + Q_Q(QWidget); + if (discardSyncRequest(q, maybeTopData())) + return; + + if (q->testAttribute(Qt::WA_StaticContents)) { + if (!extra) + createExtra(); + extra->staticContentsSize = data.crect.size(); + } + + QPaintEngine *engine = q->paintEngine(); + + // QGLWidget does not support partial updates if: + // 1) The context is double buffered + // 2) The context is single buffered and auto-fill background is enabled. + const bool noPartialUpdateSupport = (engine && (engine->type() == QPaintEngine::OpenGL + || engine->type() == QPaintEngine::OpenGL2)) + && (usesDoubleBufferedGLContext || q->autoFillBackground()); + QRegion toBePainted(noPartialUpdateSupport ? q->rect() : rgn); + +#if 0 // Used to be included in Qt4 for Q_WS_MAC + // No difference between update() and repaint() on the Mac. + update_sys(toBePainted); + return; +#endif + + toBePainted &= clipRect(); + clipToEffectiveMask(toBePainted); + if (toBePainted.isEmpty()) + return; // Nothing to repaint. + +#ifndef QT_NO_PAINT_DEBUG + bool flushed = QWidgetRepaintManager::flushPaint(q, toBePainted); +#endif + + drawWidget(q, toBePainted, QPoint(), QWidgetPrivate::DrawAsRoot | QWidgetPrivate::DrawPaintOnScreen, 0); + +#ifndef QT_NO_PAINT_DEBUG + if (flushed) + QWidgetRepaintManager::unflushPaint(q, toBePainted); +#endif + + if (Q_UNLIKELY(q->paintingActive())) + qWarning("QWidget::repaint: It is dangerous to leave painters active on a widget outside of the PaintEvent"); +} + + +QT_END_NAMESPACE + +#include "moc_qwidgetrepaintmanager_p.cpp" diff --git a/src/widgets/kernel/qwidgetrepaintmanager_p.h b/src/widgets/kernel/qwidgetrepaintmanager_p.h new file mode 100644 index 0000000000..36f42b7e1a --- /dev/null +++ b/src/widgets/kernel/qwidgetrepaintmanager_p.h @@ -0,0 +1,281 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtWidgets module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QWIDGETREPAINTMANAGER_P_H +#define QWIDGETREPAINTMANAGER_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +class QPlatformTextureList; +class QPlatformTextureListWatcher; +class QWidgetRepaintManager; + +struct BeginPaintInfo { + inline BeginPaintInfo() : wasFlushed(0), nothingToPaint(0), backingStoreRecreated(0) {} + uint wasFlushed : 1; + uint nothingToPaint : 1; + uint backingStoreRecreated : 1; +}; + +#ifndef QT_NO_OPENGL +class QPlatformTextureListWatcher : public QObject +{ + Q_OBJECT + +public: + QPlatformTextureListWatcher(QWidgetRepaintManager *repaintManager); + void watch(QPlatformTextureList *textureList); + bool isLocked() const; + +private slots: + void onLockStatusChanged(bool locked); + +private: + QHash m_locked; + QWidgetRepaintManager *m_repaintManager; +}; +#endif + +class Q_AUTOTEST_EXPORT QWidgetRepaintManager +{ +public: + enum UpdateTime { + UpdateNow, + UpdateLater + }; + + enum BufferState{ + BufferValid, + BufferInvalid + }; + + QWidgetRepaintManager(QWidget *t); + ~QWidgetRepaintManager(); + + static void showYellowThing(QWidget *widget, const QRegion &rgn, int msec, bool); + + void sync(QWidget *exposedWidget, const QRegion &exposedRegion); + void sync(); + void flush(QWidget *widget = nullptr); + + QBackingStore *backingStore() const { return store; } + + inline bool isDirty() const + { + return !(dirtyWidgets.isEmpty() && dirty.isEmpty() && dirtyRenderToTextureWidgets.isEmpty()); + } + + template + void markDirty(const T &r, QWidget *widget, UpdateTime updateTime = UpdateLater, + BufferState bufferState = BufferValid); + +private: + QWidget *tlw; + QRegion dirtyOnScreen; // needsFlush + QRegion dirty; // needsRepaint + QRegion dirtyFromPreviousSync; + QVector dirtyWidgets; + QVector dirtyRenderToTextureWidgets; + QVector dirtyOnScreenWidgets; + QList staticWidgets; + QBackingStore *store; + uint updateRequestSent : 1; + + QPlatformTextureListWatcher *textureListWatcher; + QElapsedTimer perfTime; + int perfFrames; + + void sendUpdateRequest(QWidget *widget, UpdateTime updateTime); + + static bool flushPaint(QWidget *widget, const QRegion &rgn); + static void unflushPaint(QWidget *widget, const QRegion &rgn); + static void qt_flush(QWidget *widget, const QRegion ®ion, QBackingStore *backingStore, + QWidget *tlw, + QPlatformTextureList *widgetTextures, + QWidgetRepaintManager *repaintManager); + + void doSync(); + bool bltRect(const QRect &rect, int dx, int dy, QWidget *widget); + + void beginPaint(QRegion &toClean, QWidget *widget, QBackingStore *backingStore, + BeginPaintInfo *returnInfo, bool toCleanIsInTopLevelCoordinates = true); + void endPaint(const QRegion &cleaned, QBackingStore *backingStore, BeginPaintInfo *beginPaintInfo); + + QRegion dirtyRegion(QWidget *widget = nullptr) const; + QRegion staticContents(QWidget *widget = nullptr, const QRect &withinClipRect = QRect()) const; + + void markDirtyOnScreen(const QRegion &dirtyOnScreen, QWidget *widget, const QPoint &topLevelOffset); + + void removeDirtyWidget(QWidget *w); + + void updateLists(QWidget *widget); + + bool syncAllowed(); + + inline void addDirtyWidget(QWidget *widget, const QRegion &rgn) + { + if (widget && !widget->d_func()->inDirtyList && !widget->data->in_destructor) { + QWidgetPrivate *widgetPrivate = widget->d_func(); +#if QT_CONFIG(graphicseffect) + if (widgetPrivate->graphicsEffect) + widgetPrivate->dirty = widgetPrivate->effectiveRectFor(rgn.boundingRect()); + else +#endif // QT_CONFIG(graphicseffect) + widgetPrivate->dirty = rgn; + dirtyWidgets.append(widget); + widgetPrivate->inDirtyList = true; + } + } + + inline void addDirtyRenderToTextureWidget(QWidget *widget) + { + if (widget && !widget->d_func()->inDirtyList && !widget->data->in_destructor) { + QWidgetPrivate *widgetPrivate = widget->d_func(); + Q_ASSERT(widgetPrivate->renderToTexture); + dirtyRenderToTextureWidgets.append(widget); + widgetPrivate->inDirtyList = true; + } + } + + inline void addStaticWidget(QWidget *widget) + { + if (!widget) + return; + + Q_ASSERT(widget->testAttribute(Qt::WA_StaticContents)); + if (!staticWidgets.contains(widget)) + staticWidgets.append(widget); + } + + inline void removeStaticWidget(QWidget *widget) + { staticWidgets.removeAll(widget); } + + // Move the reparented widget and all its static children from this backing store + // to the new backing store if reparented into another top-level / backing store. + inline void moveStaticWidgets(QWidget *reparented) + { + Q_ASSERT(reparented); + QWidgetRepaintManager *newPaintManager = reparented->d_func()->maybeRepaintManager(); + if (newPaintManager == this) + return; + + int i = 0; + while (i < staticWidgets.size()) { + QWidget *w = staticWidgets.at(i); + if (reparented == w || reparented->isAncestorOf(w)) { + staticWidgets.removeAt(i); + if (newPaintManager) + newPaintManager->addStaticWidget(w); + } else { + ++i; + } + } + } + + inline QRect topLevelRect() const + { + return tlw->data->crect; + } + + inline void appendDirtyOnScreenWidget(QWidget *widget) + { + if (!widget) + return; + + if (!dirtyOnScreenWidgets.contains(widget)) + dirtyOnScreenWidgets.append(widget); + } + + inline void resetWidget(QWidget *widget) + { + if (widget) { + widget->d_func()->inDirtyList = false; + widget->d_func()->isScrolled = false; + widget->d_func()->isMoved = false; + widget->d_func()->dirty = QRegion(); + } + } + + inline void updateStaticContentsSize() + { + for (int i = 0; i < staticWidgets.size(); ++i) { + QWidgetPrivate *wd = staticWidgets.at(i)->d_func(); + if (!wd->extra) + wd->createExtra(); + wd->extra->staticContentsSize = wd->data.crect.size(); + } + } + + inline bool hasStaticContents() const + { +#if defined(Q_OS_WIN) + return !staticWidgets.isEmpty(); +#else + return !staticWidgets.isEmpty() && false; +#endif + } + + friend QRegion qt_dirtyRegion(QWidget *); + friend class QWidgetPrivate; + friend class QWidget; + friend class QBackingStore; + + Q_DISABLE_COPY_MOVE(QWidgetRepaintManager) +}; + +QT_END_NAMESPACE + +#endif // QWIDGETREPAINTMANAGER_P_H diff --git a/src/widgets/kernel/qwidgetwindow.cpp b/src/widgets/kernel/qwidgetwindow.cpp index a01a377956..3a824371f3 100644 --- a/src/widgets/kernel/qwidgetwindow.cpp +++ b/src/widgets/kernel/qwidgetwindow.cpp @@ -46,7 +46,7 @@ #ifndef QT_NO_ACCESSIBILITY #include #endif -#include +#include #include #include #include @@ -770,8 +770,8 @@ void QWidgetWindow::repaintWindow() QTLWExtra *tlwExtra = m_widget->window()->d_func()->maybeTopData(); if (tlwExtra && !tlwExtra->inTopLevelResize && tlwExtra->backingStore) - tlwExtra->widgetBackingStore->markDirty(m_widget->rect(), m_widget, - QWidgetBackingStore::UpdateNow, QWidgetBackingStore::BufferInvalid); + tlwExtra->repaintManager->markDirty(m_widget->rect(), m_widget, + QWidgetRepaintManager::UpdateNow, QWidgetRepaintManager::BufferInvalid); } // Store normal geometry used for saving application settings. diff --git a/tests/auto/widgets/kernel/qwidget/tst_qwidget.cpp b/tests/auto/widgets/kernel/qwidget/tst_qwidget.cpp index 715bbbec0f..22bb488b3d 100644 --- a/tests/auto/widgets/kernel/qwidget/tst_qwidget.cpp +++ b/tests/auto/widgets/kernel/qwidget/tst_qwidget.cpp @@ -46,7 +46,7 @@ #include #include #include -#include +#include #include #include #include @@ -9565,7 +9565,7 @@ void tst_QWidget::destroyBackingStore() QTRY_VERIFY(w.numPaintEvents > 0); w.reset(); w.update(); - qt_widget_private(&w)->topData()->widgetBackingStore.reset(new QWidgetBackingStore(&w)); + qt_widget_private(&w)->topData()->repaintManager.reset(new QWidgetRepaintManager(&w)); w.update(); QApplication::processEvents(); @@ -9580,14 +9580,14 @@ void tst_QWidget::destroyBackingStore() #endif // QT_BUILD_INTERNAL // Helper function -QWidgetBackingStore* backingStore(QWidget &widget) +QWidgetRepaintManager* repaintManager(QWidget &widget) { - QWidgetBackingStore *backingStore = nullptr; + QWidgetRepaintManager *repaintManager = nullptr; #ifdef QT_BUILD_INTERNAL if (QTLWExtra *topExtra = qt_widget_private(&widget)->maybeTopData()) - backingStore = topExtra->widgetBackingStore.get(); + repaintManager = topExtra->repaintManager.get(); #endif - return backingStore; + return repaintManager; } // Tables of 5000 elements do not make sense on Windows Mobile. @@ -9785,12 +9785,12 @@ class scrollWidgetWBS : public QWidget public: void deleteBackingStore() { - static_cast(d_ptr.data())->topData()->widgetBackingStore.reset(nullptr); + static_cast(d_ptr.data())->topData()->repaintManager.reset(nullptr); } void enableBackingStore() { - if (!static_cast(d_ptr.data())->maybeBackingStore()) { - static_cast(d_ptr.data())->topData()->widgetBackingStore.reset(new QWidgetBackingStore(this)); + if (!static_cast(d_ptr.data())->maybeRepaintManager()) { + static_cast(d_ptr.data())->topData()->repaintManager.reset(new QWidgetRepaintManager(this)); static_cast(d_ptr.data())->invalidateBackingStore(this->rect()); repaint(); } -- cgit v1.2.3