From 6ce6b8a378b0d97ba950240ffb048a4b7e485235 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samuel=20R=C3=B8dal?= Date: Mon, 20 Jun 2011 13:29:26 +0200 Subject: Rename QWindowSurface -> QBackingStore and split into platform / public. Also get rid of GL window surface and related classes. --- src/widgets/kernel/kernel.pri | 4 +- src/widgets/kernel/qapplication.cpp | 12 +- src/widgets/kernel/qbackingstore.cpp | 1665 ---------------------------- src/widgets/kernel/qbackingstore_p.h | 278 ----- src/widgets/kernel/qwidget.cpp | 259 +---- src/widgets/kernel/qwidget.h | 6 +- src/widgets/kernel/qwidget_p.h | 29 +- src/widgets/kernel/qwidget_qpa.cpp | 38 +- src/widgets/kernel/qwidgetbackingstore.cpp | 1396 +++++++++++++++++++++++ src/widgets/kernel/qwidgetbackingstore_p.h | 268 +++++ 10 files changed, 1732 insertions(+), 2223 deletions(-) delete mode 100644 src/widgets/kernel/qbackingstore.cpp delete mode 100644 src/widgets/kernel/qbackingstore_p.h create mode 100644 src/widgets/kernel/qwidgetbackingstore.cpp create mode 100644 src/widgets/kernel/qwidgetbackingstore_p.h (limited to 'src/widgets') diff --git a/src/widgets/kernel/kernel.pri b/src/widgets/kernel/kernel.pri index 6be316bae9..a4754018b2 100644 --- a/src/widgets/kernel/kernel.pri +++ b/src/widgets/kernel/kernel.pri @@ -11,7 +11,7 @@ HEADERS += \ kernel/qactiongroup.h \ kernel/qapplication.h \ kernel/qapplication_p.h \ - kernel/qbackingstore_p.h \ + kernel/qwidgetbackingstore_p.h \ kernel/qboxlayout.h \ kernel/qdesktopwidget.h \ kernel/qformlayout.h \ @@ -46,7 +46,7 @@ SOURCES += \ kernel/qaction.cpp \ kernel/qactiongroup.cpp \ kernel/qapplication.cpp \ - kernel/qbackingstore.cpp \ + kernel/qwidgetbackingstore.cpp \ kernel/qboxlayout.cpp \ kernel/qformlayout.cpp \ kernel/qgridlayout.cpp \ diff --git a/src/widgets/kernel/qapplication.cpp b/src/widgets/kernel/qapplication.cpp index ec7c3a5712..7595290d11 100644 --- a/src/widgets/kernel/qapplication.cpp +++ b/src/widgets/kernel/qapplication.cpp @@ -503,17 +503,7 @@ bool qt_tabletChokeMouse = false; inline bool QApplicationPrivate::isAlien(QWidget *widget) { - if (!widget) - return false; -#if defined(Q_WS_QWS) || defined(Q_WS_QPA) - return !widget->isWindow() -# ifdef Q_BACKINGSTORE_SUBSURFACES - && !(widget->d_func()->maybeTopData() && widget->d_func()->maybeTopData()->windowSurface) -# endif - ; -#else - return !widget->internalWinId(); -#endif + return widget && !widget->isWindow(); } // ######## move to QApplicationPrivate diff --git a/src/widgets/kernel/qbackingstore.cpp b/src/widgets/kernel/qbackingstore.cpp deleted file mode 100644 index 1d5fbbc8f9..0000000000 --- a/src/widgets/kernel/qbackingstore.cpp +++ /dev/null @@ -1,1665 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the QtGui module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** GNU Lesser General Public License Usage -** This file may be used under the terms of the GNU Lesser General Public -** License version 2.1 as published by the Free Software Foundation and -** appearing in the file LICENSE.LGPL included in the packaging of this -** file. Please review the following information to ensure the GNU Lesser -** General Public License version 2.1 requirements will be met: -** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Nokia gives you certain additional -** rights. These rights are described in the Nokia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU General -** Public License version 3.0 as published by the Free Software Foundation -** and appearing in the file LICENSE.GPL included in the packaging of this -** file. Please review the following information to ensure the GNU General -** Public License version 3.0 requirements will be met: -** http://www.gnu.org/copyleft/gpl.html. -** -** Other Usage -** Alternatively, this file may be used in accordance with the terms and -** conditions contained in a signed written agreement between you and Nokia. -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - - -#include "qplatformdefs.h" - -#include "qbackingstore_p.h" - -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#ifdef Q_WS_QWS -#include -#include -#endif - -QT_BEGIN_NAMESPACE - -extern QRegion qt_dirtyRegion(QWidget *); - -/* - A version of QRect::intersects() that does not normalize the rects. -*/ -static inline bool qRectIntersects(const QRect &r1, const QRect &r2) -{ - return (qMax(r1.left(), r2.left()) <= qMin(r1.right(), r2.right()) - && qMax(r1.top(), r2.top()) <= qMin(r1.bottom(), r2.bottom())); -} - -/** - * Flushes the contents of the \a windowSurface into the screen area of \a widget. - * \a tlwOffset is the position of the top level widget relative to the window surface. - * \a region is the region to be updated in \a widget coordinates. - */ -static inline void qt_flush(QWidget *widget, const QRegion ®ion, QWindowSurface *windowSurface, - QWidget *tlw, const QPoint &tlwOffset) -{ - Q_ASSERT(widget); - Q_ASSERT(!region.isEmpty()); - Q_ASSERT(windowSurface); - Q_ASSERT(tlw); - -#if !defined(QT_NO_PAINT_DEBUG) && !defined(Q_WS_QWS) - // QWS does flush update in QWindowSurface::flush (because it needs to lock the surface etc). - static int flushUpdate = qgetenv("QT_FLUSH_UPDATE").toInt(); - if (flushUpdate > 0) - QWidgetBackingStore::showYellowThing(widget, region, flushUpdate * 10, false); -#endif - - //The performance hit by doing this should be negligible. However, be aware that - //using this FPS when you have > 1 windowsurface can give you inaccurate FPS - static bool fpsDebug = qgetenv("QT_DEBUG_FPS").toInt(); - if (fpsDebug) { - static QTime time = QTime::currentTime(); - static int frames = 0; - - frames++; - - if(time.elapsed() > 5000) { - double fps = double(frames * 1000) /time.restart(); - fprintf(stderr,"FPS: %.1f\n",fps); - frames = 0; - } - } - if (widget != tlw) - windowSurface->flush(widget->windowHandle(), region, tlwOffset + widget->mapTo(tlw, QPoint())); - else - windowSurface->flush(widget->windowHandle(), region, tlwOffset); -} - -#ifndef QT_NO_PAINT_DEBUG -#ifdef Q_WS_WIN -static void showYellowThing_win(QWidget *widget, const QRegion ®ion, int msec) -{ - HBRUSH brush; - static int i = 0; - switch (i) { - case 0: - brush = CreateSolidBrush(RGB(255, 255, 0)); - break; - case 1: - brush = CreateSolidBrush(RGB(255, 200, 55)); - break; - case 2: - brush = CreateSolidBrush(RGB(200, 255, 55)); - break; - case 3: - brush = CreateSolidBrush(RGB(200, 200, 0)); - break; - } - i = (i + 1) & 3; - - HDC hdc = widget->getDC(); - - const QVector &rects = region.rects(); - foreach (QRect rect, rects) { - RECT winRect; - SetRect(&winRect, rect.left(), rect.top(), rect.right(), rect.bottom()); - FillRect(hdc, &winRect, brush); - } - - widget->releaseDC(hdc); - ::Sleep(msec); -} -#endif - -void QWidgetBackingStore::showYellowThing(QWidget *widget, const QRegion &toBePainted, int msec, bool unclipped) -{ -#ifdef Q_WS_QWS - Q_UNUSED(widget); - Q_UNUSED(unclipped); - static QWSYellowSurface surface(true); - surface.setDelay(msec); - surface.flush(widget, toBePainted, QPoint()); -#else - QRegion paintRegion = toBePainted; - QRect widgetRect = widget->rect(); - - if (!widget->internalWinId()) { - QWidget *nativeParent = widget->nativeParentWidget(); - const QPoint offset = widget->mapTo(nativeParent, QPoint(0, 0)); - paintRegion.translate(offset); - widgetRect.translate(offset); - widget = nativeParent; - } - -#ifdef Q_WS_WIN - 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()); - - QApplication::syncX(); - -#if defined(Q_OS_UNIX) - ::usleep(1000 * msec); -#endif -#endif // Q_WS_WIN -#endif // Q_WS_QWS -} - -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 = qgetenv("QT_FLUSH_PAINT_EVENT").toInt(); - if (!flushPaintEvent) - return false; - delay = flushPaintEvent; - } else { - static int flushPaint = qgetenv("QT_FLUSH_PAINT").toInt(); - 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; - - const QPoint offset = widget->mapTo(tlw, QPoint()); - qt_flush(widget, rgn, tlwExtra->backingStore->windowSurface, tlw, offset); -} -#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(tlwOffset + widget->mapTo(tlw, rect.topLeft())); - const QRect tlwRect(QRect(pos, rect.size())); - if (fullUpdatePending || dirty.intersects(tlwRect)) - return false; // We don't want to scroll junk. - return windowSurface->scroll(tlwRect, dx, dy); -} - -void QWidgetBackingStore::releaseBuffer() -{ - if (windowSurface) -#if defined(Q_WS_QPA) - windowSurface->resize(QSize()); -#else - windowSurface->setGeometry(QRect()); -#endif -#ifdef Q_BACKINGSTORE_SUBSURFACES - for (int i = 0; i < subSurfaces.size(); ++i) - subSurfaces.at(i)->setGeometry(QRect()); -#endif -} - -/*! - 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, QWindowSurface *windowSurface, - BeginPaintInfo *returnInfo, bool toCleanIsInTopLevelCoordinates) -{ -#ifdef Q_WS_QWS - QWSWindowSurface *surface = static_cast(windowSurface); - QWidget *surfaceWidget = surface->window(); - - if (!surface->isValid()) { - // this looks strange but it really just releases the surface - surface->releaseSurface(); - // the old window surface is deleted in setWindowSurface, which is - // called from QWindowSurface constructor. - windowSurface = tlw->d_func()->createDefaultWindowSurface(); - surface = static_cast(windowSurface); - // createDefaultWindowSurface() will set topdata->windowSurface on the - // widget to zero. However, if this is a sub-surface, it should point - // to the widget's sub windowSurface, so we set that here: - if (!surfaceWidget->isWindow()) - surfaceWidget->d_func()->topData()->windowSurface = windowSurface; - surface->setGeometry(topLevelRect()); - returnInfo->windowSurfaceRecreated = true; - } - - const QRegion toCleanUnclipped(toClean); - - if (surfaceWidget->isWindow()) - tlwOffset = surface->painterOffset(); -#ifdef Q_BACKINGSTORE_SUBSURFACES - else if (toCleanIsInTopLevelCoordinates) - toClean &= surface->clipRegion().translated(surfaceWidget->mapTo(tlw, QPoint())); - if (!toCleanIsInTopLevelCoordinates && windowSurface == this->windowSurface) - toClean &= surface->clipRegion().translated(-widget->mapTo(surfaceWidget, QPoint())); -#else - toClean &= surface->clipRegion(); -#endif - - if (toClean.isEmpty()) { - if (surfaceWidget->isWindow()) { - dirtyFromPreviousSync += toCleanUnclipped; - hasDirtyFromPreviousSync = true; - } - - returnInfo->nothingToPaint = true; - // Nothing to repaint. However, we might have newly exposed areas on the - // screen, so we have to make sure those are flushed. - flush(); - return; - } - - if (surfaceWidget->isWindow()) { - if (toCleanUnclipped != toClean) { - dirtyFromPreviousSync += (toCleanUnclipped - surface->clipRegion()); - hasDirtyFromPreviousSync = true; - } - if (hasDirtyFromPreviousSync) { - dirtyFromPreviousSync -= toClean; - hasDirtyFromPreviousSync = !dirtyFromPreviousSync.isEmpty(); - } - } - -#endif // Q_WS_QWS - - Q_UNUSED(widget); - Q_UNUSED(toCleanIsInTopLevelCoordinates); - - // Always flush repainted areas. - dirtyOnScreen += toClean; - -#if defined(Q_WS_QWS) && !defined(Q_BACKINGSTORE_SUBSURFACES) - toClean.translate(tlwOffset); -#endif - -#ifdef QT_NO_PAINT_DEBUG - windowSurface->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) - windowSurface->beginPaint(toClean); -#endif - - Q_UNUSED(returnInfo); -} - -void QWidgetBackingStore::endPaint(const QRegion &cleaned, QWindowSurface *windowSurface, - BeginPaintInfo *beginPaintInfo) -{ -#ifndef QT_NO_PAINT_DEBUG - if (!beginPaintInfo->wasFlushed) - windowSurface->endPaint(cleaned); - else - QWidgetBackingStore::unflushPaint(tlw, cleaned); -#else - Q_UNUSED(beginPaintInfo); - windowSurface->endPaint(cleaned); -#endif - -#ifdef Q_BACKINGSTORE_SUBSURFACES - flush(static_cast(windowSurface)->window(), windowSurface); -#else - flush(); -#endif -} - -/*! - 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()); -#if defined(Q_WS_QPA) - const QRect surfaceGeometry(tlwRect.topLeft(), windowSurface->size()); -#else - const QRect surfaceGeometry(windowSurface->geometry()); -#endif - if (fullUpdatePending || (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; - - if (dirtyOnScreenWidgets) { // Only in use with native child widgets. - for (int i = 0; i < dirtyOnScreenWidgets->size(); ++i) { - QWidget *w = dirtyOnScreenWidgets->at(i); - 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)) { -#if defined(Q_WS_QPA) - const QSize surfaceGeometry(windowSurface->size()); -#else - const QRect surfaceGeometry(windowSurface->geometry()); -#endif - 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; -} - -static inline void sendUpdateRequest(QWidget *widget, bool updateImmediately) -{ - if (!widget) - return; - - if (updateImmediately) { - QEvent event(QEvent::UpdateRequest); - QApplication::sendEvent(widget, &event); - } else { - QApplication::postEvent(widget, new QEvent(QEvent::UpdateRequest), Qt::LowEventPriority); - } -} - -/*! - 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 updateImmediately is true, the event is sent immediately instead of posted. - - If invalidateBuffer is true, 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 invalidateBuffer is completely ignored. - - ### Qt 4.6: Merge into a template function (after MSVC isn't supported anymore). -*/ -void QWidgetBackingStore::markDirty(const QRegion &rgn, QWidget *widget, bool updateImmediately, - bool invalidateBuffer) -{ - 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(!rgn.isEmpty()); - -#ifndef QT_NO_GRAPHICSEFFECT - widget->d_func()->invalidateGraphicsEffectsRecursively(); -#endif //QT_NO_GRAPHICSEFFECT - - if (widget->d_func()->paintOnScreen()) { - if (widget->d_func()->dirty.isEmpty()) { - widget->d_func()->dirty = rgn; - sendUpdateRequest(widget, updateImmediately); - return; - } else if (qt_region_strictContains(widget->d_func()->dirty, widget->rect())) { - if (updateImmediately) - sendUpdateRequest(widget, updateImmediately); - return; // Already dirty. - } - - const bool eventAlreadyPosted = !widget->d_func()->dirty.isEmpty(); - widget->d_func()->dirty += rgn; - if (!eventAlreadyPosted || updateImmediately) - sendUpdateRequest(widget, updateImmediately); - return; - } - - if (fullUpdatePending) { - if (updateImmediately) - sendUpdateRequest(tlw, updateImmediately); - return; - } - - if (!windowSurface->hasFeature(QWindowSurface::PartialUpdates)) { - fullUpdatePending = true; - sendUpdateRequest(tlw, updateImmediately); - return; - } - - const QPoint offset = widget->mapTo(tlw, QPoint()); - const QRect widgetRect = widget->d_func()->effectiveRectFor(widget->rect()); - if (qt_region_strictContains(dirty, widgetRect.translated(offset))) { - if (updateImmediately) - sendUpdateRequest(tlw, updateImmediately); - return; // Already dirty. - } - - if (invalidateBuffer) { - const bool eventAlreadyPosted = !dirty.isEmpty(); -#ifndef QT_NO_GRAPHICSEFFECT - if (widget->d_func()->graphicsEffect) - dirty += widget->d_func()->effectiveRectFor(rgn.boundingRect()).translated(offset); - else -#endif //QT_NO_GRAPHICSEFFECT - dirty += rgn.translated(offset); - if (!eventAlreadyPosted || updateImmediately) - sendUpdateRequest(tlw, updateImmediately); - return; - } - - if (dirtyWidgets.isEmpty()) { - addDirtyWidget(widget, rgn); - sendUpdateRequest(tlw, updateImmediately); - return; - } - - if (widget->d_func()->inDirtyList) { - if (!qt_region_strictContains(widget->d_func()->dirty, widgetRect)) { -#ifndef QT_NO_GRAPHICSEFFECT - if (widget->d_func()->graphicsEffect) - widget->d_func()->dirty += widget->d_func()->effectiveRectFor(rgn.boundingRect()); - else -#endif //QT_NO_GRAPHICSEFFECT - widget->d_func()->dirty += rgn; - } - } else { - addDirtyWidget(widget, rgn); - } - - if (updateImmediately) - sendUpdateRequest(tlw, updateImmediately); -} - -/*! - This function is equivalent to calling markDirty(QRegion(rect), ...), but - is more efficient as it eliminates QRegion operations/allocations and can - use the rect more precisely for additional cut-offs. - - ### Qt 4.6: Merge into a template function (after MSVC isn't supported anymore). -*/ -void QWidgetBackingStore::markDirty(const QRect &rect, QWidget *widget, bool updateImmediately, - bool invalidateBuffer) -{ - 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(!rect.isEmpty()); - -#ifndef QT_NO_GRAPHICSEFFECT - widget->d_func()->invalidateGraphicsEffectsRecursively(); -#endif //QT_NO_GRAPHICSEFFECT - - if (widget->d_func()->paintOnScreen()) { - if (widget->d_func()->dirty.isEmpty()) { - widget->d_func()->dirty = QRegion(rect); - sendUpdateRequest(widget, updateImmediately); - return; - } else if (qt_region_strictContains(widget->d_func()->dirty, rect)) { - if (updateImmediately) - sendUpdateRequest(widget, updateImmediately); - return; // Already dirty. - } - - const bool eventAlreadyPosted = !widget->d_func()->dirty.isEmpty(); - widget->d_func()->dirty += rect; - if (!eventAlreadyPosted || updateImmediately) - sendUpdateRequest(widget, updateImmediately); - return; - } - - if (fullUpdatePending) { - if (updateImmediately) - sendUpdateRequest(tlw, updateImmediately); - return; - } - - if (!windowSurface->hasFeature(QWindowSurface::PartialUpdates)) { - fullUpdatePending = true; - sendUpdateRequest(tlw, updateImmediately); - return; - } - - const QRect widgetRect = widget->d_func()->effectiveRectFor(rect); - const QRect translatedRect(widgetRect.translated(widget->mapTo(tlw, QPoint()))); - if (qt_region_strictContains(dirty, translatedRect)) { - if (updateImmediately) - sendUpdateRequest(tlw, updateImmediately); - return; // Already dirty - } - - if (invalidateBuffer) { - const bool eventAlreadyPosted = !dirty.isEmpty(); - dirty += translatedRect; - if (!eventAlreadyPosted || updateImmediately) - sendUpdateRequest(tlw, updateImmediately); - return; - } - - if (dirtyWidgets.isEmpty()) { - addDirtyWidget(widget, rect); - sendUpdateRequest(tlw, updateImmediately); - return; - } - - if (widget->d_func()->inDirtyList) { - if (!qt_region_strictContains(widget->d_func()->dirty, widgetRect)) - widget->d_func()->dirty += widgetRect; - } else { - addDirtyWidget(widget, rect); - } - - if (updateImmediately) - sendUpdateRequest(tlw, updateImmediately); -} - -/*! - 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 defined(Q_WS_QWS) || defined(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 (!widget->internalWinId() && !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; - - dirtyWidgetsRemoveAll(w); - dirtyOnScreenWidgetsRemoveAll(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); - } -} - -#if defined(Q_WS_QWS) && !defined(QT_NO_QWS_MANAGER) -bool QWidgetBackingStore::hasDirtyWindowDecoration() const -{ - QTLWExtra *tlwExtra = tlw->d_func()->maybeTopData(); - if (tlwExtra && tlwExtra->qwsManager) - return !tlwExtra->qwsManager->d_func()->dirtyRegions.isEmpty(); - return false; -} - -void QWidgetBackingStore::paintWindowDecoration() -{ - if (!hasDirtyWindowDecoration()) - return; - - QDecoration &decoration = QApplication::qwsDecoration(); - const QRect decorationRect = tlw->rect(); - QRegion decorationRegion = decoration.region(tlw, decorationRect); - - QWSManagerPrivate *managerPrivate = tlw->d_func()->topData()->qwsManager->d_func(); - const bool doClipping = !managerPrivate->entireDecorationNeedsRepaint - && !managerPrivate->dirtyClip.isEmpty(); - - if (doClipping) { - decorationRegion &= static_cast(windowSurface)->clipRegion(); - decorationRegion &= managerPrivate->dirtyClip; - } - - if (decorationRegion.isEmpty()) - return; - - //### The QWS decorations do not always paint the pixels they promise to paint. - // This causes painting problems with QWSMemorySurface. Since none of the other - // window surfaces actually use the region, passing an empty region is a safe - // workaround. - - windowSurface->beginPaint(QRegion()); - - QPaintEngine *engine = windowSurface->paintDevice()->paintEngine(); - Q_ASSERT(engine); - const QRegion oldSystemClip(engine->systemClip()); - engine->setSystemClip(decorationRegion.translated(tlwOffset)); - - QPainter painter(windowSurface->paintDevice()); - painter.setFont(QApplication::font()); - painter.translate(tlwOffset); - - const int numDirty = managerPrivate->dirtyRegions.size(); - for (int i = 0; i < numDirty; ++i) { - const int area = managerPrivate->dirtyRegions.at(i); - - QRegion clipRegion = decoration.region(tlw, decorationRect, area); - if (!clipRegion.isEmpty()) { - // Decoration styles changes the clip and assumes the old clip is non-empty, - // so we have to set it, but in theory it shouldn't be required. - painter.setClipRegion(clipRegion); - decoration.paint(&painter, tlw, area, managerPrivate->dirtyStates.at(i)); - } - } - markDirtyOnScreen(decorationRegion, tlw, QPoint()); - - painter.end(); - windowSurface->endPaint(decorationRegion); - managerPrivate->clearDirtyRegions(); - engine->setSystemClip(oldSystemClip); -} -#endif - -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) - continue; - - updateLists(child); - } - - if (cur->testAttribute(Qt::WA_StaticContents)) - addStaticWidget(cur); - -#ifdef Q_BACKINGSTORE_SUBSURFACES - QTLWExtra *extra = cur->d_func()->maybeTopData(); - if (extra && extra->windowSurface && cur != tlw) - subSurfaces.append(extra->windowSurface); -#endif -} - -QWidgetBackingStore::QWidgetBackingStore(QWidget *topLevel) - : tlw(topLevel), dirtyOnScreenWidgets(0), hasDirtyFromPreviousSync(false) - , fullUpdatePending(0) -{ - windowSurface = tlw->windowSurface(); - if (!windowSurface) - windowSurface = topLevel->d_func()->createDefaultWindowSurface(); - - // The QWindowSurface constructor will call QWidget::setWindowSurface(), - // but automatically created surfaces should not be added to the topdata. -#ifdef Q_BACKINGSTORE_SUBSURFACES - Q_ASSERT(topLevel->d_func()->topData()->windowSurface == windowSurface); -#endif - topLevel->d_func()->topData()->windowSurface = 0; - - // 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)); - } - - delete windowSurface; - windowSurface = 0; - delete dirtyOnScreenWidgets; - dirtyOnScreenWidgets = 0; -} - -//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 int accelEnv = -1; - if (accelEnv == -1) { - accelEnv = qgetenv("QT_NO_FAST_MOVE").toInt() == 0; - } - - QWidget *pw = q->parentWidget(); - QPoint toplevelOffset = pw->mapTo(tlw, QPoint()); - QWidgetPrivate *pd = pw->d_func(); - QRect clipR(pd->clipRect()); -#ifdef Q_WS_QWS - QWidgetBackingStore *wbs = x->backingStore.data(); - QWSWindowSurface *surface = static_cast(wbs->windowSurface); - clipR = clipR.intersected(surface->clipRegion().translated(-toplevelOffset).boundingRect()); -#endif - 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); - - bool accelerateMove = accelEnv && isOpaque -#ifndef QT_NO_GRAPHICSVIEW - // No accelerate move for proxy widgets. - && !tlw->d_func()->extra->proxyWidget -#endif - && !isOverlapped(sourceRect) && !isOverlapped(destRect); - - if (!accelerateMove) { - QRegion parentR(effectiveRectFor(parentRect)); - if (!extra || !extra->hasMask) { - parentR -= newRect; - } else { - // invalidateBuffer() excludes anything outside the mask - parentR += newRect & clipR; - } - pd->invalidateBuffer(parentR); - invalidateBuffer((newRect & clipR).translated(-data.crect.topLeft())); - } else { - - QWidgetBackingStore *wbs = x->backingStore.data(); - QRegion childExpose(newRect & clipR); - - if (sourceRect.isValid() && wbs->bltRect(sourceRect, dx, dy, pw)) - childExpose -= destRect; - - if (!pw->updatesEnabled()) - return; - - const bool childUpdatesEnabled = q->updatesEnabled(); - if (childUpdatesEnabled && !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->backingStore.data(); - if (!wbs) - return; - - static int accelEnv = -1; - if (accelEnv == -1) { - accelEnv = qgetenv("QT_NO_FAST_SCROLL").toInt() == 0; - } - - QRect scrollRect = rect & clipRect(); - bool overlapped = false; - bool accelerateScroll = accelEnv && isOpaque - && !(overlapped = isOverlapped(scrollRect.translated(data.crect.topLeft()))); - -#if defined(Q_WS_QWS) - QWSWindowSurface *surface; - surface = static_cast(wbs->windowSurface); - - if (accelerateScroll && !surface->isBuffered()) { - const QRegion surfaceClip = surface->clipRegion(); - const QRegion outsideClip = QRegion(rect) - surfaceClip; - if (!outsideClip.isEmpty()) { - const QVector clipped = (surfaceClip & rect).rects(); - if (clipped.size() < 8) { - for (int i = 0; i < clipped.size(); ++i) - this->scrollRect(clipped.at(i), dx, dy); - return; - } else { - accelerateScroll = false; - } - } - } -#endif // Q_WS_QWS - - if (!accelerateScroll) { - if (overlapped) { - QRegion region(scrollRect); - subtractOpaqueSiblings(region); - invalidateBuffer(region); - }else { - invalidateBuffer(scrollRect); - } - } else { - const QPoint toplevelOffset = q->mapTo(tlw, QPoint()); -#ifdef Q_WS_QWS - QWSWindowSurface *surface = static_cast(wbs->windowSurface); - const QRegion clip = surface->clipRegion().translated(-toplevelOffset) & scrollRect; - const QRect clipBoundingRect = clip.boundingRect(); - scrollRect &= clipBoundingRect; -#endif - const QRect destRect = scrollRect.translated(dx, dy) & scrollRect; - const QRect sourceRect = destRect.translated(-dx, -dy); - - QRegion childExpose(scrollRect); - if (sourceRect.isValid()) { - if (wbs->bltRect(sourceRect, dx, dy, q)) - childExpose -= destRect; - } - - 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 (!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); - } -} - -static inline bool discardSyncRequest(QWidget *tlw, QTLWExtra *tlwExtra) -{ - if (!tlw || !tlwExtra) - return true; - -#ifdef Q_WS_X11 - // Delay the sync until we get an Expose event from X11 (initial show). - // Qt::WA_Mapped is set to true, but the actual mapping has not yet occurred. - // However, we must repaint immediately regardless of the state if someone calls repaint(). - if (tlwExtra->waitingForMapNotify && !tlwExtra->inRepaint) - return true; -#endif - - if (!tlw->testAttribute(Qt::WA_Mapped)) - return true; - - if (!tlw->isVisible() -#ifndef Q_WS_X11 - // If we're minimized on X11, WA_Mapped will be false and we - // will return in the case above. Some window managers on X11 - // sends us the PropertyNotify to change the minimized state - // *AFTER* we've received the expose event, which is baaad. - || tlw->isMinimized() -#endif - ) - return true; - - return false; -} - -/*! - 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 (discardSyncRequest(tlw, tlwExtra) || tlwExtra->inTopLevelResize) - return; - - if (!exposedWidget || !exposedWidget->internalWinId() || !exposedWidget->isVisible() - || !exposedWidget->updatesEnabled() || exposedRegion.isEmpty()) { - return; - } - - // If there's no preserved contents support we always need - // to do a full repaint before flushing - if (!windowSurface->hasFeature(QWindowSurface::PreservedContents)) - fullUpdatePending = true; - - // Nothing to repaint. - if (!isDirty()) { - qt_flush(exposedWidget, exposedRegion, windowSurface, tlw, tlwOffset); - return; - } - - if (exposedWidget != tlw) - markDirtyOnScreen(exposedRegion, exposedWidget, exposedWidget->mapTo(tlw, QPoint())); - else - markDirtyOnScreen(exposedRegion, exposedWidget, QPoint()); - sync(); -} - -/*! - Synchronizes the backing store, i.e. dirty areas are repainted and flushed. -*/ -void QWidgetBackingStore::sync() -{ - 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(); - fullUpdatePending = false; - } - return; - } - - const bool updatesDisabled = !tlw->updatesEnabled(); - bool repaintAllWidgets = false; - - const bool inTopLevelResize = tlwExtra->inTopLevelResize; - const QRect tlwRect(topLevelRect()); -#ifdef Q_WS_QPA - const QRect surfaceGeometry(tlwRect.topLeft(), windowSurface->size()); -#else - const QRect surfaceGeometry(windowSurface->geometry()); -#endif - if ((fullUpdatePending || inTopLevelResize || surfaceGeometry.size() != tlwRect.size()) && !updatesDisabled) { - if (hasStaticContents()) { - // 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; - windowSurface->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; - } - } - -#ifdef Q_WS_QPA - if (inTopLevelResize || surfaceGeometry.size() != tlwRect.size()) - windowSurface->resize(tlwRect.size()); -#else - if (inTopLevelResize || surfaceGeometry != tlwRect) - windowSurface->setGeometry(tlwRect); -#endif - - if (updatesDisabled) - return; - - if (hasDirtyFromPreviousSync) - dirty += dirtyFromPreviousSync; - - // 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); - // Scrolled and moved widgets must draw all children. - if (!wd->isScrolled && !wd->isMoved) - wd->subtractOpaqueChildren(wd->dirty, w->rect()); - - if (wd->dirty.isEmpty()) { - resetWidget(w); - continue; - } - - const QRegion widgetDirty(w != tlw ? wd->dirty.translated(w->mapTo(tlw, QPoint())) - : wd->dirty); - toClean += widgetDirty; - -#ifndef QT_NO_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(); - - fullUpdatePending = false; - - if (toClean.isEmpty()) { - // Nothing to repaint. However, 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. - flush(); - return; - } - -#ifndef QT_NO_GRAPHICSVIEW - if (tlw->d_func()->extra->proxyWidget) { - updateStaticContentsSize(); - dirty = QRegion(); - const QVector rects(toClean.rects()); - for (int i = 0; i < rects.size(); ++i) - tlw->d_func()->extra->proxyWidget->update(rects.at(i)); - return; - } -#endif - -#ifndef Q_BACKINGSTORE_SUBSURFACES - BeginPaintInfo beginPaintInfo; - beginPaint(toClean, tlw, windowSurface, &beginPaintInfo); - if (beginPaintInfo.nothingToPaint) { - for (int i = 0; i < opaqueNonOverlappedWidgets.size(); ++i) - resetWidget(opaqueNonOverlappedWidgets[i]); - dirty = QRegion(); - return; - } -#endif - - // Must do this before sending any paint events because - // the size may change in the paint event. - updateStaticContentsSize(); - const QRegion dirtyCopy(dirty); - dirty = QRegion(); - - // 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); - -#ifdef Q_BACKINGSTORE_SUBSURFACES - QWindowSurface *subSurface = w->windowSurface(); - BeginPaintInfo beginPaintInfo; - - QPoint off = w->mapTo(tlw, QPoint()); - toBePainted.translate(off); - beginPaint(toBePainted, w, subSurface, &beginPaintInfo, true); - toBePainted.translate(-off); - - if (beginPaintInfo.nothingToPaint) - continue; - - if (beginPaintInfo.windowSurfaceRecreated) { - // Eep the window surface has changed. The old one may have been - // deleted, in which case we will segfault on the call to - // painterOffset() below. Use the new window surface instead. - subSurface = w->windowSurface(); - } - - QPoint offset(tlwOffset); - if (subSurface == windowSurface) - offset += w->mapTo(tlw, QPoint()); - else - offset = static_cast(subSurface)->painterOffset(); - wd->drawWidget(subSurface->paintDevice(), toBePainted, offset, flags, 0, this); - - endPaint(toBePainted, subSurface, &beginPaintInfo); -#else - QPoint offset(tlwOffset); - if (w != tlw) - offset += w->mapTo(tlw, QPoint()); - wd->drawWidget(windowSurface->paintDevice(), toBePainted, offset, flags, 0, this); -#endif - } - - // Paint the rest with composition. -#ifndef Q_BACKINGSTORE_SUBSURFACES - if (repaintAllWidgets || !dirtyCopy.isEmpty()) { - const int flags = QWidgetPrivate::DrawAsRoot | QWidgetPrivate::DrawRecursive; - tlw->d_func()->drawWidget(windowSurface->paintDevice(), dirtyCopy, tlwOffset, flags, 0, this); - } - - endPaint(toClean, windowSurface, &beginPaintInfo); -#else - if (!repaintAllWidgets && dirtyCopy.isEmpty()) - return; // Nothing more to paint. - - QList surfaceList(subSurfaces); - surfaceList.prepend(windowSurface); - const QRect dirtyBoundingRect(dirtyCopy.boundingRect()); - - // Loop through all window surfaces (incl. the top-level surface) and - // repaint those intersecting with the bounding rect of the dirty region. - for (int i = 0; i < surfaceList.size(); ++i) { - QWindowSurface *subSurface = surfaceList.at(i); - QWidget *w = subSurface->window(); - QWidgetPrivate *wd = w->d_func(); - - const QRect clipRect = wd->clipRect().translated(w->mapTo(tlw, QPoint())); - if (!qRectIntersects(dirtyBoundingRect, clipRect)) - continue; - - toClean = dirtyCopy; - BeginPaintInfo beginPaintInfo; - beginPaint(toClean, w, subSurface, &beginPaintInfo); - if (beginPaintInfo.nothingToPaint) - continue; - - if (beginPaintInfo.windowSurfaceRecreated) { - // Eep the window surface has changed. The old one may have been - // deleted, in which case we will segfault on the call to - // painterOffset() below. Use the new window surface instead. - subSurface = w->windowSurface(); - } - - int flags = QWidgetPrivate::DrawRecursive; - if (w == tlw) - flags |= QWidgetPrivate::DrawAsRoot; - const QPoint painterOffset = static_cast(subSurface)->painterOffset(); - wd->drawWidget(subSurface->paintDevice(), toClean, painterOffset, flags, 0, this); - - endPaint(toClean, subSurface, &beginPaintInfo); - } -#endif -} - -/*! - 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, QWindowSurface *surface) -{ -#if defined(Q_WS_QWS) && !defined(QT_NO_QWS_MANAGER) - paintWindowDecoration(); -#endif - - if (!dirtyOnScreen.isEmpty()) { - QWidget *target = widget ? widget : tlw; - QWindowSurface *source = surface ? surface : windowSurface; - qt_flush(target, dirtyOnScreen, source, tlw, tlwOffset); - dirtyOnScreen = QRegion(); - } - - if (!dirtyOnScreenWidgets || dirtyOnScreenWidgets->isEmpty()) - return; - - for (int i = 0; i < dirtyOnScreenWidgets->size(); ++i) { - QWidget *w = dirtyOnScreenWidgets->at(i); - QWidgetPrivate *wd = w->d_func(); - Q_ASSERT(wd->needsFlush); - qt_flush(w, *wd->needsFlush, windowSurface, tlw, tlwOffset); - *wd->needsFlush = QRegion(); - } - dirtyOnScreenWidgets->clear(); -} - -static inline bool discardInvalidateBufferRequest(QWidget *widget, QTLWExtra *tlwExtra) -{ - Q_ASSERT(widget); - if (QApplication::closingDown()) - return true; - - if (!tlwExtra || tlwExtra->inTopLevelResize || !tlwExtra->backingStore) - return true; - - if (!widget->isVisible() || !widget->updatesEnabled()) - return true; - - return false; -} - -/*! - Invalidates the buffer when the widget is resized. - Static areas are never invalidated unless absolutely needed. -*/ -void QWidgetPrivate::invalidateBuffer_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; - invalidateBuffer(dirty); - } else { - // Entire widget needs repaint. - invalidateBuffer(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()->invalidateBuffer(parentExpose); - } else { - if (hasStaticChildren && !graphicsEffect) { - QRegion parentExpose(QRect(oldPos, oldSize)); - parentExpose -= data.crect; // Offset is unchanged, safe to do this. - q->parentWidget()->d_func()->invalidateBuffer(parentExpose); - } else { - q->parentWidget()->d_func()->invalidateBuffer(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; - invalidateBuffer(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()->invalidateBuffer(parentExpose); - } else { - QRegion parentExpose(oldRect); - parentExpose -= data.crect; - q->parentWidget()->d_func()->invalidateBuffer(parentExpose); - } -} - -/*! - Invalidates the \a rgn (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. - - ### Qt 4.6: Merge into a template function (after MSVC isn't supported anymore). -*/ -void QWidgetPrivate::invalidateBuffer(const QRegion &rgn) -{ - Q_Q(QWidget); - - QTLWExtra *tlwExtra = q->window()->d_func()->maybeTopData(); - if (discardInvalidateBufferRequest(q, tlwExtra) || rgn.isEmpty()) - return; - - QRegion wrgn(rgn); - wrgn &= clipRect(); - if (!graphicsEffect && extra && extra->hasMask) - wrgn &= extra->mask; - if (wrgn.isEmpty()) - return; - - tlwExtra->backingStore->markDirty(wrgn, q, false, true); -} - -/*! - This function is equivalent to calling invalidateBuffer(QRegion(rect), ...), but - is more efficient as it eliminates QRegion operations/allocations and can - use the rect more precisely for additional cut-offs. - - ### Qt 4.6: Merge into a template function (after MSVC isn't supported anymore). -*/ -void QWidgetPrivate::invalidateBuffer(const QRect &rect) -{ - Q_Q(QWidget); - - QTLWExtra *tlwExtra = q->window()->d_func()->maybeTopData(); - if (discardInvalidateBufferRequest(q, tlwExtra) || rect.isEmpty()) - return; - - QRect wRect(rect); - wRect &= clipRect(); - if (wRect.isEmpty()) - return; - - if (graphicsEffect || !extra || !extra->hasMask) { - tlwExtra->backingStore->markDirty(wRect, q, false, true); - return; - } - - QRegion wRgn(extra->mask); - wRgn &= wRect; - if (wRgn.isEmpty()) - return; - - tlwExtra->backingStore->markDirty(wRgn, q, false, true); -} - -void QWidgetPrivate::repaint_sys(const QRegion &rgn) -{ - if (data.in_destructor) - return; - - Q_Q(QWidget); - if (q->testAttribute(Qt::WA_StaticContents)) { - if (!extra) - createExtra(); - extra->staticContentsSize = data.crect.size(); - } - -#ifdef Q_WS_QPA //Dont even call q->p - QPaintEngine *engine = 0; -#else - QPaintEngine *engine = q->paintEngine(); -#endif - // 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); - -#ifdef 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->testAttribute(Qt::WA_PaintOutsidePaintEvent) && q->paintingActive()) - qWarning("QWidget::repaint: It is dangerous to leave painters active on a widget outside of the PaintEvent"); -} - - -QT_END_NAMESPACE diff --git a/src/widgets/kernel/qbackingstore_p.h b/src/widgets/kernel/qbackingstore_p.h deleted file mode 100644 index cd41af31e1..0000000000 --- a/src/widgets/kernel/qbackingstore_p.h +++ /dev/null @@ -1,278 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). -** All rights reserved. -** Contact: Nokia Corporation (qt-info@nokia.com) -** -** This file is part of the QtGui module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** GNU Lesser General Public License Usage -** This file may be used under the terms of the GNU Lesser General Public -** License version 2.1 as published by the Free Software Foundation and -** appearing in the file LICENSE.LGPL included in the packaging of this -** file. Please review the following information to ensure the GNU Lesser -** General Public License version 2.1 requirements will be met: -** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Nokia gives you certain additional -** rights. These rights are described in the Nokia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU General -** Public License version 3.0 as published by the Free Software Foundation -** and appearing in the file LICENSE.GPL included in the packaging of this -** file. Please review the following information to ensure the GNU General -** Public License version 3.0 requirements will be met: -** http://www.gnu.org/copyleft/gpl.html. -** -** Other Usage -** Alternatively, this file may be used in accordance with the terms and -** conditions contained in a signed written agreement between you and Nokia. -** -** -** -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QBACKINGSTORE_P_H -#define QBACKINGSTORE_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 -#ifdef Q_WS_QWS -#include -#endif - -QT_BEGIN_NAMESPACE - -class QWindowSurface; - -struct BeginPaintInfo { - inline BeginPaintInfo() : wasFlushed(0), nothingToPaint(0), windowSurfaceRecreated(0) {} - uint wasFlushed : 1; - uint nothingToPaint : 1; - uint windowSurfaceRecreated : 1; -}; - -class Q_AUTOTEST_EXPORT QWidgetBackingStore -{ -public: - 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 = 0, QWindowSurface *surface = 0); - - inline QPoint topLevelOffset() const { return tlwOffset; } - - QWindowSurface *surface() const { return windowSurface; } - - inline bool isDirty() const - { - return !(dirtyWidgets.isEmpty() && dirty.isEmpty() && !hasDirtyFromPreviousSync - && !fullUpdatePending -#if defined(Q_WS_QWS) && !defined(QT_NO_QWS_MANAGER) - && !hasDirtyWindowDecoration() -#endif - ); - } - - // ### Qt 4.6: Merge into a template function (after MSVC isn't supported anymore). - void markDirty(const QRegion &rgn, QWidget *widget, bool updateImmediately = false, - bool invalidateBuffer = false); - void markDirty(const QRect &rect, QWidget *widget, bool updateImmediately = false, - bool invalidateBuffer = false); - -private: - QWidget *tlw; - QRegion dirtyOnScreen; // needsFlush - QRegion dirty; // needsRepaint - QRegion dirtyFromPreviousSync; - QVector dirtyWidgets; - QVector *dirtyOnScreenWidgets; - QList staticWidgets; - QWindowSurface *windowSurface; -#ifdef Q_BACKINGSTORE_SUBSURFACES - QList subSurfaces; -#endif - uint hasDirtyFromPreviousSync : 1; - uint fullUpdatePending : 1; - - QPoint tlwOffset; - - static bool flushPaint(QWidget *widget, const QRegion &rgn); - static void unflushPaint(QWidget *widget, const QRegion &rgn); - - bool bltRect(const QRect &rect, int dx, int dy, QWidget *widget); - void releaseBuffer(); - - void beginPaint(QRegion &toClean, QWidget *widget, QWindowSurface *windowSurface, - BeginPaintInfo *returnInfo, bool toCleanIsInTopLevelCoordinates = true); - void endPaint(const QRegion &cleaned, QWindowSurface *windowSurface, BeginPaintInfo *beginPaintInfo); - - QRegion dirtyRegion(QWidget *widget = 0) const; - QRegion staticContents(QWidget *widget = 0, const QRect &withinClipRect = QRect()) const; - - void markDirtyOnScreen(const QRegion &dirtyOnScreen, QWidget *widget, const QPoint &topLevelOffset); - - void removeDirtyWidget(QWidget *w); - -#if defined(Q_WS_QWS) && !defined(QT_NO_QWS_MANAGER) - bool hasDirtyWindowDecoration() const; - void paintWindowDecoration(); -#endif - void updateLists(QWidget *widget); - - inline void addDirtyWidget(QWidget *widget, const QRegion &rgn) - { - if (widget && !widget->d_func()->inDirtyList && !widget->data->in_destructor) { - QWidgetPrivate *widgetPrivate = widget->d_func(); -#ifndef QT_NO_GRAPHICSEFFECT - if (widgetPrivate->graphicsEffect) - widgetPrivate->dirty = widgetPrivate->effectiveRectFor(rgn.boundingRect()); - else -#endif //QT_NO_GRAPHICSEFFECT - widgetPrivate->dirty = rgn; - dirtyWidgets.append(widget); - widgetPrivate->inDirtyList = true; - } - } - - inline void dirtyWidgetsRemoveAll(QWidget *widget) - { - int i = 0; - while (i < dirtyWidgets.size()) { - if (dirtyWidgets.at(i) == widget) - dirtyWidgets.remove(i); - else - ++i; - } - } - - 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 - { -#ifdef Q_WS_QWS - return tlw->frameGeometry(); -#else - return tlw->data->crect; -#endif - } - - inline void appendDirtyOnScreenWidget(QWidget *widget) - { - if (!widget) - return; - - if (!dirtyOnScreenWidgets) { - dirtyOnScreenWidgets = new QVector; - dirtyOnScreenWidgets->append(widget); - } else if (!dirtyOnScreenWidgets->contains(widget)) { - dirtyOnScreenWidgets->append(widget); - } - } - - inline void dirtyOnScreenWidgetsRemoveAll(QWidget *widget) - { - if (!widget || !dirtyOnScreenWidgets) - return; - - int i = 0; - while (i < dirtyOnScreenWidgets->size()) { - if (dirtyOnScreenWidgets->at(i) == widget) - dirtyOnScreenWidgets->remove(i); - else - ++i; - } - } - - 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 - { return !staticWidgets.isEmpty() && windowSurface->hasFeature(QWindowSurface::StaticContents); } - - friend QRegion qt_dirtyRegion(QWidget *); - friend class QWidgetPrivate; - friend class QWidget; - friend class QWSManagerPrivate; - friend class QETWidget; - friend class QWindowSurface; - friend class QWSWindowSurface; -}; - -QT_END_NAMESPACE - -#endif // QBACKINGSTORE_P_H diff --git a/src/widgets/kernel/qwidget.cpp b/src/widgets/kernel/qwidget.cpp index 9a7ad9fc0c..9889a66aa6 100644 --- a/src/widgets/kernel/qwidget.cpp +++ b/src/widgets/kernel/qwidget.cpp @@ -70,12 +70,6 @@ # include "qtoolbar.h" # include #endif -#if defined(Q_WS_QWS) -# include "qwsdisplay_qws.h" -# include "qwsmanager_qws.h" -# include "qpaintengine.h" // for PorterDuff -# include "private/qwindowsurface_qws_p.h" -#endif #if defined(Q_WS_QPA) #include "qplatformwindow_qpa.h" #include "private/qwidgetwindow_qpa_p.h" @@ -100,8 +94,8 @@ #endif #include -#include -#include +#include +#include #ifdef Q_WS_MAC # include #endif @@ -137,9 +131,7 @@ QT_BEGIN_NAMESPACE -#if !defined(Q_WS_QWS) static bool qt_enable_backingstore = true; -#endif #ifdef Q_WS_X11 // for compatibility with Qt 4.0 Q_WIDGETS_EXPORT void qt_x11_set_global_double_buffer(bool enable) @@ -352,32 +344,6 @@ QWidgetPrivate::~QWidgetPrivate() #endif //QT_NO_GRAPHICSEFFECT } -class QDummyWindowSurface : public QWindowSurface -{ -public: - QDummyWindowSurface(QWindow *window) : QWindowSurface(window) {} - QPaintDevice *paintDevice() { return static_cast(window())->widget(); } - void flush(QWindow *, const QRegion &, const QPoint &) {} -}; - -QWindowSurface *QWidgetPrivate::createDefaultWindowSurface() -{ - Q_Q(QWidget); - - QWindowSurface *surface; -#ifndef QT_NO_PROPERTIES - if (q->property("_q_DummyWindowSurface").toBool()) { - surface = new QDummyWindowSurface(q->windowHandle()); - } else -#endif - { - QWindow *win = topData()->window; - surface = QGuiApplicationPrivate::platformIntegration()->createWindowSurface(win, win->winId()); - } - - return surface; -} - /*! \internal */ @@ -393,10 +359,8 @@ void QWidgetPrivate::scrollChildren(int dx, int dy) QPoint oldp = w->pos(); QRect r(w->pos() + pd, w->size()); w->data->crect = r; -#ifndef Q_WS_QWS if (w->testAttribute(Qt::WA_WState_Created)) w->d_func()->setWSGeometry(); -#endif w->d_func()->setDirtyOpaqueRegion(); QMoveEvent e(r.topLeft(), oldp); QApplication::sendEvent(w, &e); @@ -1508,9 +1472,9 @@ void QWidget::create(WId window, bool initializeWindow, bool destroyOldWindow) // a real toplevel window needs a backing store if (isWindow() && windowType() != Qt::Desktop) { - d->topData()->backingStore.destroy(); + d->topData()->backingStoreTracker.destroy(); if (hasBackingStoreSupport()) - d->topData()->backingStore.create(this); + d->topData()->backingStoreTracker.create(this); } d->setModal_sys(); @@ -1625,7 +1589,7 @@ QWidget::~QWidget() else if (!internalWinId() && isVisible()) { qApp->d_func()->sendSyntheticEnterLeave(this); } -#elif defined(Q_WS_QWS) || defined(Q_WS_QPA) +#elif defined(Q_WS_QPA) else if (isVisible()) { qApp->d_func()->sendSyntheticEnterLeave(this); } @@ -1732,7 +1696,7 @@ void QWidgetPrivate::createTLExtra() QTLWExtra* x = extra->topextra = new QTLWExtra; x->icon = 0; x->iconPixmap = 0; - x->windowSurface = 0; + x->backingStore = 0; x->sharedPainter = 0; x->incw = x->inch = 0; x->basew = x->baseh = 0; @@ -1815,13 +1779,10 @@ void QWidgetPrivate::deleteExtra() #endif if (extra->topextra) { deleteTLSysExtra(); - extra->topextra->backingStore.destroy(); + extra->topextra->backingStoreTracker.destroy(); delete extra->topextra->icon; delete extra->topextra->iconPixmap; -#if defined(Q_WS_QWS) && !defined(QT_NO_QWS_MANAGER) - delete extra->topextra->qwsManager; -#endif - delete extra->topextra->windowSurface; + delete extra->topextra->backingStore; delete extra->topextra; } delete extra; @@ -2212,9 +2173,7 @@ void QWidgetPrivate::clipToEffectiveMask(QRegion ®ion) const bool QWidgetPrivate::paintOnScreen() const { -#if defined(Q_WS_QWS) - return false; -#elif defined(QT_NO_BACKINGSTORE) +#if defined(QT_NO_BACKINGSTORE) return true; #else Q_Q(const QWidget); @@ -2376,10 +2335,6 @@ void QWidgetPrivate::paintBackground(QPainter *painter, const QRegion &rgn, int if ((flags & DrawAsRoot) && !(q->autoFillBackground() && autoFillBrush.isOpaque())) { const QBrush bg = q->palette().brush(QPalette::Window); -#ifdef Q_WS_QWS - if (!(flags & DontSetCompositionMode) && painter->paintEngine()->hasFeature(QPaintEngine::PorterDuff)) - painter->setCompositionMode(QPainter::CompositionMode_Source); //copy alpha straight in -#endif fillRegion(painter, rgn, bg); } @@ -3891,19 +3846,6 @@ bool QWidgetPrivate::setMinimumSize_helper(int &minw, int &minh) { Q_Q(QWidget); -#ifdef Q_WS_QWS - if (q->isWindow()) { - const QRect maxWindowRect = QApplication::desktop()->availableGeometry(QApplication::desktop()->screenNumber(q)); - if (!maxWindowRect.isEmpty()) { - // ### This is really just a work-around. Layout shouldn't be - // asking for minimum sizes bigger than the screen. - if (minw > maxWindowRect.width()) - minw = maxWindowRect.width(); - if (minh > maxWindowRect.height()) - minh = maxWindowRect.height(); - } - } -#endif int mw = minw, mh = minh; if (mw == QWIDGETSIZE_MAX) mw = 0; @@ -4094,15 +4036,7 @@ void QWidget::setFixedSize(const QSize & s) void QWidget::setFixedSize(int w, int h) { Q_D(QWidget); -#ifdef Q_WS_QWS - // temporary fix for 4.3.x. - // Should move the embedded spesific contraints in setMinimumSize_helper into QLayout - int tmpW = w; - int tmpH = h; - bool minSizeSet = d->setMinimumSize_helper(tmpW, tmpH); -#else bool minSizeSet = d->setMinimumSize_helper(w, h); -#endif bool maxSizeSet = d->setMaximumSize_helper(w, h); if (!minSizeSet && !maxSizeSet) return; @@ -5050,7 +4984,7 @@ void QWidget::setCursor(const QCursor &cursor) { Q_D(QWidget); // On Mac we must set the cursor even if it is the ArrowCursor. -#if !defined(Q_WS_MAC) && !defined(Q_WS_QWS) +#if !defined(Q_WS_MAC) if (cursor.shape() != Qt::ArrowCursor || (d->extra && d->extra->curs)) #endif @@ -5538,7 +5472,7 @@ void QWidgetPrivate::drawWidget(QPaintDevice *pdev, const QRegion &rgn, const QP //actually send the paint event QPaintEvent e(toBePainted); QCoreApplication::sendSpontaneousEvent(q, &e); -#if !defined(Q_WS_QWS) && !defined(Q_WS_QPA) +#if !defined(Q_WS_QPA) if (backingStore && !onScreen && !asRoot && (q->internalWinId() || !q->nativeParentWidget()->isWindow())) backingStore->markDirtyOnScreen(toBePainted, q, offset); #endif @@ -5587,9 +5521,6 @@ void QWidgetPrivate::drawWidget(QPaintDevice *pdev, const QRegion &rgn, const QP if (recursive && !children.isEmpty()) { paintSiblingsRecursive(pdev, children, children.size() - 1, rgn, offset, flags & ~DrawAsRoot -#ifdef Q_BACKINGSTORE_SUBSURFACES - , q->windowSurface() -#endif , sharedPainter, backingStore); } } @@ -5661,10 +5592,6 @@ void QWidgetPrivate::render(QPaintDevice *target, const QPoint &targetOffset, else flags |= DontSubtractOpaqueChildren; -#ifdef Q_WS_QWS - flags |= DontSetCompositionMode; -#endif - if (target->devType() == QInternal::Printer) { QPainter p(target); render_helper(&p, targetOffset, paintRegion, renderFlags); @@ -5686,9 +5613,6 @@ 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 -#ifdef Q_BACKINGSTORE_SUBSURFACES - , const QWindowSurface *currentSurface -#endif , QPainter *sharedPainter, QWidgetBackingStore *backingStore) { QWidget *w = 0; @@ -5707,13 +5631,8 @@ void QWidgetPrivate::paintSiblingsRecursive(QPaintDevice *pdev, const QObjectLis } if (qRectIntersects(boundingRect, x->d_func()->effectiveRectFor(x->data->crect))) { -#ifdef Q_BACKINGSTORE_SUBSURFACES - if (x->windowSurface() == currentSurface) -#endif - { - w = x; - break; - } + w = x; + break; } } --index; @@ -5730,9 +5649,6 @@ void QWidgetPrivate::paintSiblingsRecursive(QPaintDevice *pdev, const QObjectLis if (wd->isOpaque) wr -= hasMask ? wd->extra->mask.translated(widgetPos) : w->data->crect; paintSiblingsRecursive(pdev, siblings, --index, wr, offset, flags -#ifdef Q_BACKINGSTORE_SUBSURFACES - , currentSurface -#endif , sharedPainter, backingStore); } @@ -7582,7 +7498,7 @@ void QWidgetPrivate::hide_helper() // next bit tries to move the focus if the focus widget is now // hidden. if (wasVisible) { -#if defined(Q_WS_WIN) || defined(Q_WS_X11) || defined (Q_WS_QWS) || defined(Q_WS_MAC) || defined(Q_WS_QPA) +#if defined(Q_WS_WIN) || defined(Q_WS_X11) || defined(Q_WS_MAC) || defined(Q_WS_QPA) qApp->d_func()->sendSyntheticEnterLeave(q); #endif @@ -7653,9 +7569,6 @@ void QWidget::setVisible(bool visible) #if defined(Q_WS_X11) if (windowType() == Qt::Window) QApplicationPrivate::applyX11SpecificCommandLineArguments(this); -#elif defined(Q_WS_QWS) - if (windowType() == Qt::Window) - QApplicationPrivate::applyQWSSpecificCommandLineArguments(this); #endif bool wasResized = testAttribute(Qt::WA_Resized); @@ -7714,7 +7627,7 @@ void QWidget::setVisible(bool visible) d->show_helper(); -#if defined(Q_WS_WIN) || defined(Q_WS_X11) || defined (Q_WS_QWS) || defined(Q_WS_MAC) || defined(Q_WS_QPA) +#if defined(Q_WS_WIN) || defined(Q_WS_X11) || defined(Q_WS_MAC) || defined(Q_WS_QPA) qApp->d_func()->sendSyntheticEnterLeave(this); #endif } @@ -7846,7 +7759,7 @@ void QWidgetPrivate::hideChildren(bool spontaneous) widget->d_func()->hide_sys(); } } -#if defined(Q_WS_WIN) || defined(Q_WS_X11) || defined (Q_WS_QWS) || defined(Q_WS_MAC) || defined(Q_WS_QPA) +#if defined(Q_WS_WIN) || defined(Q_WS_X11) || defined(Q_WS_MAC) || defined(Q_WS_QPA) qApp->d_func()->sendSyntheticEnterLeave(widget); #endif #ifndef QT_NO_ACCESSIBILITY @@ -8049,13 +7962,6 @@ QRegion QWidget::visibleRegion() const QRegion r(clipRect); d->subtractOpaqueChildren(r, clipRect); d->subtractOpaqueSiblings(r); -#ifdef Q_WS_QWS - const QWSWindowSurface *surface = static_cast(windowSurface()); - if (surface) { - const QPoint offset = mapTo(surface->window(), QPoint()); - r &= surface->clipRegion().translated(-offset); - } -#endif return r; } @@ -8807,10 +8713,6 @@ void QWidget::changeEvent(QEvent * event) updateGeometry(); if (d->layout) d->layout->invalidate(); -#ifdef Q_WS_QWS - if (isWindow()) - d->data.fstrut_dirty = true; -#endif break; } @@ -9588,31 +9490,6 @@ bool QWidget::x11Event(XEvent *) } #endif -#if defined(Q_WS_QWS) - -/*! - \fn bool QWidget::qwsEvent(QWSEvent *event) - - This special event handler can be reimplemented in a subclass to - receive native Qt for Embedded Linux events which are passed in the - \a event parameter. - - In your reimplementation of this function, if you want to stop the - event being handled by Qt, return true. If you return false, this - native event is passed back to Qt, which translates the event into - a Qt event and sends it to the widget. - - \warning This function is not portable. - - \sa QApplication::qwsEventFilter() -*/ -bool QWidget::qwsEvent(QWSEvent *) -{ - return false; -} - -#endif - /*! Ensures that the widget has been polished by QStyle (i.e., has a @@ -10132,34 +10009,18 @@ void QWidget::setParent(QWidget *parent, Qt::WindowFlags f) focusWidget()->clearFocus(); QTLWExtra *oldTopExtra = window()->d_func()->maybeTopData(); - QWidgetBackingStoreTracker *oldBsTracker = oldTopExtra ? &oldTopExtra->backingStore : 0; + QWidgetBackingStoreTracker *oldBsTracker = oldTopExtra ? &oldTopExtra->backingStoreTracker : 0; d->setParent_sys(parent, f); QTLWExtra *topExtra = window()->d_func()->maybeTopData(); - QWidgetBackingStoreTracker *bsTracker = topExtra ? &topExtra->backingStore : 0; + QWidgetBackingStoreTracker *bsTracker = topExtra ? &topExtra->backingStoreTracker : 0; if (oldBsTracker && oldBsTracker != bsTracker) oldBsTracker->unregisterWidgetSubtree(this); if (desktopWidget) parent = 0; -#ifdef Q_BACKINGSTORE_SUBSURFACES - QTLWExtra *extra = d->maybeTopData(); - QWindowSurface *windowSurface = (extra ? extra->windowSurface : 0); - if (newParent && windowSurface) { - QWidgetBackingStore *oldBs = oldtlw->d_func()->maybeBackingStore(); - if (oldBs) - oldBs->subSurfaces.removeAll(windowSurface); - - if (parent) { - QWidgetBackingStore *newBs = parent->d_func()->maybeBackingStore(); - if (newBs) - newBs->subSurfaces.append(windowSurface); - } - } -#endif - if (QWidgetBackingStore *oldBs = oldtlw->d_func()->maybeBackingStore()) { if (newParent) oldBs->removeDirtyWidget(this); @@ -10405,7 +10266,7 @@ void QWidget::repaint(const QRect &rect) QTLWExtra *tlwExtra = window()->d_func()->maybeTopData(); if (tlwExtra && !tlwExtra->inTopLevelResize && tlwExtra->backingStore) { tlwExtra->inRepaint = true; - tlwExtra->backingStore->markDirty(rect, this, true); + tlwExtra->backingStoreTracker->markDirty(rect, this, true); tlwExtra->inRepaint = false; } } else { @@ -10440,7 +10301,7 @@ void QWidget::repaint(const QRegion &rgn) QTLWExtra *tlwExtra = window()->d_func()->maybeTopData(); if (tlwExtra && !tlwExtra->inTopLevelResize && tlwExtra->backingStore) { tlwExtra->inRepaint = true; - tlwExtra->backingStore->markDirty(rgn, this, true); + tlwExtra->backingStoreTracker->markDirty(rgn, this, true); tlwExtra->inRepaint = false; } } else { @@ -10502,7 +10363,7 @@ void QWidget::update(const QRect &rect) #endif // QT_MAC_USE_COCOA QTLWExtra *tlwExtra = window()->d_func()->maybeTopData(); if (tlwExtra && !tlwExtra->inTopLevelResize && tlwExtra->backingStore) - tlwExtra->backingStore->markDirty(rect, this); + tlwExtra->backingStoreTracker->markDirty(rect, this); } else { d_func()->repaint_sys(rect); } @@ -10532,7 +10393,7 @@ void QWidget::update(const QRegion &rgn) #endif // QT_MAC_USE_COCOA QTLWExtra *tlwExtra = window()->d_func()->maybeTopData(); if (tlwExtra && !tlwExtra->inTopLevelResize && tlwExtra->backingStore) - tlwExtra->backingStore->markDirty(rgn, this); + tlwExtra->backingStoreTracker->markDirty(rgn, this); } else { d_func()->repaint_sys(rgn); } @@ -10873,14 +10734,6 @@ void QWidget::setAttribute(Qt::WidgetAttribute attribute, bool on) // from the desktop. show_sys will only update platform specific // attributes at this point. d->hide_sys(); -#ifdef Q_WS_QWS - // Release the region for this window from qws if the widget has - // been shown before the attribute was set. - if (QWSWindowSurface *surface = static_cast(windowSurface())) { - QWidget::qwsDisplay()->requestRegion(surface->winId(), surface->key(), - surface->permanentState(), QRegion()); - } -#endif d->show_sys(); } break; @@ -11029,10 +10882,8 @@ void QWidget::setWindowOpacity(qreal opacity) extra->opacity = uint(opacity * 255); setAttribute(Qt::WA_WState_WindowOpacitySet); -#ifndef Q_WS_QWS if (!testAttribute(Qt::WA_WState_Created)) return; -#endif #ifndef QT_NO_GRAPHICSVIEW if (QGraphicsProxyWidget *proxy = graphicsProxyWidget()) { @@ -11322,7 +11173,7 @@ void QWidget::setShortcutAutoRepeat(int id, bool enable) */ void QWidget::updateMicroFocus() { -#if !defined(QT_NO_IM) && (defined(Q_WS_X11) || defined(Q_WS_QWS) || defined(Q_OS_SYMBIAN)) +#if !defined(QT_NO_IM) && (defined(Q_WS_X11) || defined(Q_OS_SYMBIAN)) Q_D(QWidget); // and optimization to update input context only it has already been created. if (d->assignedInputContext() || qApp->d_func()->inputContext) { @@ -12102,83 +11953,55 @@ bool QWidgetPrivate::inTabWidget(QWidget *widget) #endif /*! - \preliminary - \since 4.2 - \obsolete + \since 5.0 + \internal - Sets the window surface to be the \a surface specified. - The QWidget takes will ownership of the \a surface. - widget itself is deleted. + Sets the backing store to be the \a store specified. + The QWidget will take ownership of the \a store. */ -void QWidget::setWindowSurface(QWindowSurface *surface) +void QWidget::setBackingStore(QBackingStore *store) { // ### createWinId() ?? -#ifndef Q_BACKINGSTORE_SUBSURFACES if (!isTopLevel()) return; -#endif Q_D(QWidget); QTLWExtra *topData = d->topData(); - if (topData->windowSurface == surface) + if (topData->backingStore == store) return; - QWindowSurface *oldSurface = topData->windowSurface; - delete topData->windowSurface; - topData->windowSurface = surface; + QBackingStore *oldStore = topData->backingStore; + delete topData->backingStore; + topData->backingStore = store; QWidgetBackingStore *bs = d->maybeBackingStore(); if (!bs) return; if (isTopLevel()) { - if (bs->windowSurface != oldSurface && bs->windowSurface != surface) - delete bs->windowSurface; - bs->windowSurface = surface; - } -#ifdef Q_BACKINGSTORE_SUBSURFACES - else { - bs->subSurfaces.append(surface); + if (bs->store != oldStore && bs->store != store) + delete bs->store; + bs->store = store; } - bs->subSurfaces.removeOne(oldSurface); -#endif } /*! - \preliminary - \since 4.2 + \since 5.0 - Returns the QWindowSurface this widget will be drawn into. + Returns the QBackingStore this widget will be drawn into. */ -QWindowSurface *QWidget::windowSurface() const +QBackingStore *QWidget::backingStore() const { Q_D(const QWidget); QTLWExtra *extra = d->maybeTopData(); - if (extra && extra->windowSurface) - return extra->windowSurface; + if (extra && extra->backingStore) + return extra->backingStore; QWidgetBackingStore *bs = d->maybeBackingStore(); -#ifdef Q_BACKINGSTORE_SUBSURFACES - if (bs && bs->subSurfaces.isEmpty()) - return bs->windowSurface; - - if (!isTopLevel()) { - const QWidget *w = parentWidget(); - while (w) { - QTLWExtra *extra = w->d_func()->maybeTopData(); - if (extra && extra->windowSurface) - return extra->windowSurface; - if (w->isTopLevel()) - break; - w = w->parentWidget(); - } - } -#endif // Q_BACKINGSTORE_SUBSURFACES - - return bs ? bs->windowSurface : 0; + return bs ? bs->store : 0; } 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 2be5e5f52d..755feb8849 100644 --- a/src/widgets/kernel/qwidget.h +++ b/src/widgets/kernel/qwidget.h @@ -97,7 +97,7 @@ class QShowEvent; class QHideEvent; class QInputContext; class QIcon; -class QWindowSurface; +class QBackingStore; class QPlatformWindow; class QLocale; class QGraphicsProxyWidget; @@ -631,8 +631,7 @@ public: bool autoFillBackground() const; void setAutoFillBackground(bool enabled); - void setWindowSurface(QWindowSurface *surface); - QWindowSurface *windowSurface() const; + QBackingStore *backingStore() const; #if defined(Q_WS_QPA) void setWindowHandle(QWindow *window); @@ -730,6 +729,7 @@ protected: protected: QWidget(QWidgetPrivate &d, QWidget* parent, Qt::WindowFlags f); private: + void setBackingStore(QBackingStore *store); bool testAttribute_helper(Qt::WidgetAttribute) const; diff --git a/src/widgets/kernel/qwidget_p.h b/src/widgets/kernel/qwidget_p.h index 46c5ecfe33..f3838ee3dd 100644 --- a/src/widgets/kernel/qwidget_p.h +++ b/src/widgets/kernel/qwidget_p.h @@ -163,8 +163,8 @@ struct QTLWExtra { // Regular pointers (keep them together to avoid gaps on 64 bits architectures). QIcon *icon; // widget icon QPixmap *iconPixmap; - QWidgetBackingStoreTracker backingStore; - QWindowSurface *windowSurface; + QWidgetBackingStoreTracker backingStoreTracker; + QBackingStore *backingStore; QPainter *sharedPainter; // Implicit pointers (shared_null). @@ -458,11 +458,8 @@ public: void paintSiblingsRecursive(QPaintDevice *pdev, const QObjectList& children, int index, - const QRegion &rgn, const QPoint &offset, int flags -#ifdef Q_BACKINGSTORE_SUBSURFACES - , const QWindowSurface *currentSurface -#endif - , QPainter *sharedPainter, QWidgetBackingStore *backingStore); + const QRegion &rgn, const QPoint &offset, int flags, + QPainter *sharedPainter, QWidgetBackingStore *backingStore); QPainter *beginSharedPainter(); @@ -470,8 +467,6 @@ public: #ifndef QT_NO_GRAPHICSVIEW static QGraphicsProxyWidget * nearestGraphicsProxyWidget(const QWidget *origin); #endif - QWindowSurface *createDefaultWindowSurface(); - QWindowSurface *createDefaultWindowSurface_sys(); void repaint_sys(const QRegion &rgn); QRect clipRect() const; @@ -884,20 +879,6 @@ public: static OSStatus qt_widget_event(EventHandlerCallRef er, EventRef event, void *); static bool qt_widget_rgn(QWidget *, short, RgnHandle, bool); void registerTouchWindow(bool enable = true); -#elif defined(Q_WS_QWS) // <--------------------------------------------------------- QWS - void setMaxWindowState_helper(); - void setFullScreenSize_helper(); - void moveSurface(QWindowSurface *surface, const QPoint &offset); - QRegion localRequestedRegion() const; - QRegion localAllocatedRegion() const; - - friend class QWSManager; - friend class QWSManagerPrivate; - friend class QDecoration; -#ifndef QT_NO_CURSOR - void updateCursor() const; -#endif - QScreen* getScreen() const; #elif defined(Q_WS_QPA) // <--------------------------------------------------------- QPA void setMaxWindowState_helper(); void setFullScreenSize_helper(); @@ -1026,7 +1007,7 @@ inline QWidgetBackingStore *QWidgetPrivate::maybeBackingStore() const { Q_Q(const QWidget); QTLWExtra *x = q->window()->d_func()->maybeTopData(); - return x ? x->backingStore.data() : 0; + return x ? x->backingStoreTracker.data() : 0; } QT_END_NAMESPACE diff --git a/src/widgets/kernel/qwidget_qpa.cpp b/src/widgets/kernel/qwidget_qpa.cpp index caaa065b64..9aa7e5afb6 100644 --- a/src/widgets/kernel/qwidget_qpa.cpp +++ b/src/widgets/kernel/qwidget_qpa.cpp @@ -42,7 +42,7 @@ #include "QtWidgets/qwidget.h" #include "QtGui/qevent.h" #include "QtWidgets/qapplication.h" -#include "private/qbackingstore_p.h" +#include "private/qwidgetbackingstore_p.h" #include "private/qwidget_p.h" #include "private/qwidgetwindow_qpa_p.h" #include "private/qapplication_p.h" @@ -94,8 +94,6 @@ void QWidgetPrivate::create_sys(WId window, bool initializeWindow, bool destroyO if (!q->testAttribute(Qt::WA_NativeWindow) && !q->isWindow()) return; // we only care about real toplevels - QWindowSurface *surface = q->windowSurface(); - QWindow *win = topData()->window; // topData() ensures the extra is created but does not ensure 'window' is non-null // in case the extra was already valid. @@ -129,13 +127,13 @@ void QWidgetPrivate::create_sys(WId window, bool initializeWindow, bool destroyO data.window_flags = win->windowFlags(); - if (!surface ) { - if (win) { - surface = QGuiApplicationPrivate::platformIntegration()->createWindowSurface(win, win->winId()); - q->setWindowSurface(surface); - } else { - q->setAttribute(Qt::WA_PaintOnScreen,true); - } + QBackingStore *store = q->backingStore(); + + if (!store) { + if (win) + q->setBackingStore(new QBackingStore(win)); + else + q->setAttribute(Qt::WA_PaintOnScreen, true); } setWinId(win->winId()); @@ -449,12 +447,15 @@ void QWidgetPrivate::show_sys() if (windowRect != geomRect) { window->setGeometry(geomRect); } - if (QWindowSurface *surface = q->windowSurface()) { - if (windowRect.size() != geomRect.size()) { - surface->resize(geomRect.size()); + + if (QBackingStore *store = q->backingStore()) { + if (store->size() != geomRect.size()) { + store->resize(geomRect.size()); } } + invalidateBuffer(q->rect()); + if (window) window->setVisible(true); } @@ -663,9 +664,9 @@ void QWidgetPrivate::setGeometry_sys(int x, int y, int w, int h, bool isMove) q->windowHandle()->setGeometry(QRect(posInNativeParent,r.size())); } const QWidgetBackingStore *bs = maybeBackingStore(); - if (bs->windowSurface) { + if (bs->store) { if (isResize) - bs->windowSurface->resize(r.size()); + bs->store->resize(r.size()); } if (needsShow) @@ -854,13 +855,6 @@ QPaintEngine *QWidget::paintEngine() const return 0; //##### @@@ } -QWindowSurface *QWidgetPrivate::createDefaultWindowSurface_sys() -{ - //This function should not be called. - Q_ASSERT(false); - return 0; -} - void QWidgetPrivate::setModal_sys() { Q_Q(QWidget); diff --git a/src/widgets/kernel/qwidgetbackingstore.cpp b/src/widgets/kernel/qwidgetbackingstore.cpp new file mode 100644 index 0000000000..1cb6f51619 --- /dev/null +++ b/src/widgets/kernel/qwidgetbackingstore.cpp @@ -0,0 +1,1396 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#include "qplatformdefs.h" + +#include "qwidgetbackingstore_p.h" + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#ifdef Q_WS_QWS +#include +#include +#endif + +QT_BEGIN_NAMESPACE + +extern QRegion qt_dirtyRegion(QWidget *); + +/* + A version of QRect::intersects() that does not normalize the rects. +*/ +static inline bool qRectIntersects(const QRect &r1, const QRect &r2) +{ + return (qMax(r1.left(), r2.left()) <= qMin(r1.right(), r2.right()) + && qMax(r1.top(), r2.top()) <= qMin(r1.bottom(), r2.bottom())); +} + +/** + * Flushes the contents of the \a backingStore into the screen area of \a widget. + * \a tlwOffset is the position of the top level widget relative to the window surface. + * \a region is the region to be updated in \a widget coordinates. + */ +static inline void qt_flush(QWidget *widget, const QRegion ®ion, QBackingStore *backingStore, + QWidget *tlw, const QPoint &tlwOffset) +{ + Q_ASSERT(widget); + Q_ASSERT(!region.isEmpty()); + Q_ASSERT(backingStore); + Q_ASSERT(tlw); + +#if !defined(QT_NO_PAINT_DEBUG) + static int flushUpdate = qgetenv("QT_FLUSH_UPDATE").toInt(); + if (flushUpdate > 0) + QWidgetBackingStore::showYellowThing(widget, region, flushUpdate * 10, false); +#endif + + //The performance hit by doing this should be negligible. However, be aware that + //using this FPS when you have > 1 windowsurface can give you inaccurate FPS + static bool fpsDebug = qgetenv("QT_DEBUG_FPS").toInt(); + if (fpsDebug) { + static QTime time = QTime::currentTime(); + static int frames = 0; + + frames++; + + if(time.elapsed() > 5000) { + double fps = double(frames * 1000) /time.restart(); + fprintf(stderr,"FPS: %.1f\n",fps); + frames = 0; + } + } + if (widget != tlw) + backingStore->flush(region, widget->windowHandle(), tlwOffset + widget->mapTo(tlw, QPoint())); + else + backingStore->flush(region, widget->windowHandle(), tlwOffset); +} + +#ifndef QT_NO_PAINT_DEBUG +#ifdef Q_WS_WIN +static void showYellowThing_win(QWidget *widget, const QRegion ®ion, int msec) +{ + HBRUSH brush; + static int i = 0; + switch (i) { + case 0: + brush = CreateSolidBrush(RGB(255, 255, 0)); + break; + case 1: + brush = CreateSolidBrush(RGB(255, 200, 55)); + break; + case 2: + brush = CreateSolidBrush(RGB(200, 255, 55)); + break; + case 3: + brush = CreateSolidBrush(RGB(200, 200, 0)); + break; + } + i = (i + 1) & 3; + + HDC hdc = widget->getDC(); + + const QVector &rects = region.rects(); + foreach (QRect rect, rects) { + RECT winRect; + SetRect(&winRect, rect.left(), rect.top(), rect.right(), rect.bottom()); + FillRect(hdc, &winRect, brush); + } + + widget->releaseDC(hdc); + ::Sleep(msec); +} +#endif + +void QWidgetBackingStore::showYellowThing(QWidget *widget, const QRegion &toBePainted, int msec, bool unclipped) +{ +#ifdef Q_WS_QWS + Q_UNUSED(widget); + Q_UNUSED(unclipped); + static QWSYellowSurface surface(true); + surface.setDelay(msec); + surface.flush(widget, toBePainted, QPoint()); +#else + QRegion paintRegion = toBePainted; + QRect widgetRect = widget->rect(); + + if (!widget->internalWinId()) { + QWidget *nativeParent = widget->nativeParentWidget(); + const QPoint offset = widget->mapTo(nativeParent, QPoint(0, 0)); + paintRegion.translate(offset); + widgetRect.translate(offset); + widget = nativeParent; + } + +#ifdef Q_WS_WIN + 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()); + + QApplication::syncX(); + +#if defined(Q_OS_UNIX) + ::usleep(1000 * msec); +#endif +#endif // Q_WS_WIN +#endif // Q_WS_QWS +} + +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 = qgetenv("QT_FLUSH_PAINT_EVENT").toInt(); + if (!flushPaintEvent) + return false; + delay = flushPaintEvent; + } else { + static int flushPaint = qgetenv("QT_FLUSH_PAINT").toInt(); + 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; + + const QPoint offset = widget->mapTo(tlw, QPoint()); + qt_flush(widget, rgn, tlwExtra->backingStoreTracker->store, tlw, offset); +} +#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(tlwOffset + widget->mapTo(tlw, rect.topLeft())); + const QRect tlwRect(QRect(pos, rect.size())); + if (fullUpdatePending || dirty.intersects(tlwRect)) + return false; // We don't want to scroll junk. + return store->scroll(tlwRect, dx, dy); +} + +void QWidgetBackingStore::releaseBuffer() +{ + if (store) + store->resize(QSize()); +} + +/*! + 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 (fullUpdatePending || (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; + + if (dirtyOnScreenWidgets) { // Only in use with native child widgets. + for (int i = 0; i < dirtyOnScreenWidgets->size(); ++i) { + QWidget *w = dirtyOnScreenWidgets->at(i); + 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; +} + +static inline void sendUpdateRequest(QWidget *widget, bool updateImmediately) +{ + if (!widget) + return; + + if (updateImmediately) { + QEvent event(QEvent::UpdateRequest); + QApplication::sendEvent(widget, &event); + } else { + QApplication::postEvent(widget, new QEvent(QEvent::UpdateRequest), Qt::LowEventPriority); + } +} + +/*! + 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 updateImmediately is true, the event is sent immediately instead of posted. + + If invalidateBuffer is true, 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 invalidateBuffer is completely ignored. + + ### Qt 4.6: Merge into a template function (after MSVC isn't supported anymore). +*/ +void QWidgetBackingStore::markDirty(const QRegion &rgn, QWidget *widget, bool updateImmediately, + bool invalidateBuffer) +{ + 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(!rgn.isEmpty()); + +#ifndef QT_NO_GRAPHICSEFFECT + widget->d_func()->invalidateGraphicsEffectsRecursively(); +#endif //QT_NO_GRAPHICSEFFECT + + if (widget->d_func()->paintOnScreen()) { + if (widget->d_func()->dirty.isEmpty()) { + widget->d_func()->dirty = rgn; + sendUpdateRequest(widget, updateImmediately); + return; + } else if (qt_region_strictContains(widget->d_func()->dirty, widget->rect())) { + if (updateImmediately) + sendUpdateRequest(widget, updateImmediately); + return; // Already dirty. + } + + const bool eventAlreadyPosted = !widget->d_func()->dirty.isEmpty(); + widget->d_func()->dirty += rgn; + if (!eventAlreadyPosted || updateImmediately) + sendUpdateRequest(widget, updateImmediately); + return; + } + + if (fullUpdatePending) { + if (updateImmediately) + sendUpdateRequest(tlw, updateImmediately); + return; + } + + const QPoint offset = widget->mapTo(tlw, QPoint()); + const QRect widgetRect = widget->d_func()->effectiveRectFor(widget->rect()); + if (qt_region_strictContains(dirty, widgetRect.translated(offset))) { + if (updateImmediately) + sendUpdateRequest(tlw, updateImmediately); + return; // Already dirty. + } + + if (invalidateBuffer) { + const bool eventAlreadyPosted = !dirty.isEmpty(); +#ifndef QT_NO_GRAPHICSEFFECT + if (widget->d_func()->graphicsEffect) + dirty += widget->d_func()->effectiveRectFor(rgn.boundingRect()).translated(offset); + else +#endif //QT_NO_GRAPHICSEFFECT + dirty += rgn.translated(offset); + if (!eventAlreadyPosted || updateImmediately) + sendUpdateRequest(tlw, updateImmediately); + return; + } + + if (dirtyWidgets.isEmpty()) { + addDirtyWidget(widget, rgn); + sendUpdateRequest(tlw, updateImmediately); + return; + } + + if (widget->d_func()->inDirtyList) { + if (!qt_region_strictContains(widget->d_func()->dirty, widgetRect)) { +#ifndef QT_NO_GRAPHICSEFFECT + if (widget->d_func()->graphicsEffect) + widget->d_func()->dirty += widget->d_func()->effectiveRectFor(rgn.boundingRect()); + else +#endif //QT_NO_GRAPHICSEFFECT + widget->d_func()->dirty += rgn; + } + } else { + addDirtyWidget(widget, rgn); + } + + if (updateImmediately) + sendUpdateRequest(tlw, updateImmediately); +} + +/*! + This function is equivalent to calling markDirty(QRegion(rect), ...), but + is more efficient as it eliminates QRegion operations/allocations and can + use the rect more precisely for additional cut-offs. + + ### Qt 4.6: Merge into a template function (after MSVC isn't supported anymore). +*/ +void QWidgetBackingStore::markDirty(const QRect &rect, QWidget *widget, bool updateImmediately, + bool invalidateBuffer) +{ + 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(!rect.isEmpty()); + +#ifndef QT_NO_GRAPHICSEFFECT + widget->d_func()->invalidateGraphicsEffectsRecursively(); +#endif //QT_NO_GRAPHICSEFFECT + + if (widget->d_func()->paintOnScreen()) { + if (widget->d_func()->dirty.isEmpty()) { + widget->d_func()->dirty = QRegion(rect); + sendUpdateRequest(widget, updateImmediately); + return; + } else if (qt_region_strictContains(widget->d_func()->dirty, rect)) { + if (updateImmediately) + sendUpdateRequest(widget, updateImmediately); + return; // Already dirty. + } + + const bool eventAlreadyPosted = !widget->d_func()->dirty.isEmpty(); + widget->d_func()->dirty += rect; + if (!eventAlreadyPosted || updateImmediately) + sendUpdateRequest(widget, updateImmediately); + return; + } + + if (fullUpdatePending) { + if (updateImmediately) + sendUpdateRequest(tlw, updateImmediately); + return; + } + + const QRect widgetRect = widget->d_func()->effectiveRectFor(rect); + const QRect translatedRect(widgetRect.translated(widget->mapTo(tlw, QPoint()))); + if (qt_region_strictContains(dirty, translatedRect)) { + if (updateImmediately) + sendUpdateRequest(tlw, updateImmediately); + return; // Already dirty + } + + if (invalidateBuffer) { + const bool eventAlreadyPosted = !dirty.isEmpty(); + dirty += translatedRect; + if (!eventAlreadyPosted || updateImmediately) + sendUpdateRequest(tlw, updateImmediately); + return; + } + + if (dirtyWidgets.isEmpty()) { + addDirtyWidget(widget, rect); + sendUpdateRequest(tlw, updateImmediately); + return; + } + + if (widget->d_func()->inDirtyList) { + if (!qt_region_strictContains(widget->d_func()->dirty, widgetRect)) + widget->d_func()->dirty += widgetRect; + } else { + addDirtyWidget(widget, rect); + } + + if (updateImmediately) + sendUpdateRequest(tlw, updateImmediately); +} + +/*! + 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 defined(Q_WS_QWS) || defined(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 (!widget->internalWinId() && !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; + + dirtyWidgetsRemoveAll(w); + dirtyOnScreenWidgetsRemoveAll(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) + continue; + + updateLists(child); + } + + if (cur->testAttribute(Qt::WA_StaticContents)) + addStaticWidget(cur); +} + +QWidgetBackingStore::QWidgetBackingStore(QWidget *topLevel) + : tlw(topLevel), dirtyOnScreenWidgets(0), hasDirtyFromPreviousSync(false) + , fullUpdatePending(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)); + } + + delete dirtyOnScreenWidgets; + dirtyOnScreenWidgets = 0; +} + +//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 int accelEnv = -1; + if (accelEnv == -1) { + accelEnv = qgetenv("QT_NO_FAST_MOVE").toInt() == 0; + } + + QWidget *pw = q->parentWidget(); + QPoint toplevelOffset = pw->mapTo(tlw, QPoint()); + QWidgetPrivate *pd = pw->d_func(); + QRect clipR(pd->clipRect()); +#ifdef Q_WS_QWS + QWidgetBackingStore *wbs = x->backingStore.data(); + QWSWindowSurface *surface = static_cast(wbs->windowSurface); + clipR = clipR.intersected(surface->clipRegion().translated(-toplevelOffset).boundingRect()); +#endif + 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); + + bool accelerateMove = accelEnv && isOpaque +#ifndef QT_NO_GRAPHICSVIEW + // No accelerate move for proxy widgets. + && !tlw->d_func()->extra->proxyWidget +#endif + && !isOverlapped(sourceRect) && !isOverlapped(destRect); + + if (!accelerateMove) { + QRegion parentR(effectiveRectFor(parentRect)); + if (!extra || !extra->hasMask) { + parentR -= newRect; + } else { + // invalidateBuffer() excludes anything outside the mask + parentR += newRect & clipR; + } + pd->invalidateBuffer(parentR); + invalidateBuffer((newRect & clipR).translated(-data.crect.topLeft())); + } else { + + QWidgetBackingStore *wbs = x->backingStoreTracker.data(); + QRegion childExpose(newRect & clipR); + + if (sourceRect.isValid() && wbs->bltRect(sourceRect, dx, dy, pw)) + childExpose -= destRect; + + if (!pw->updatesEnabled()) + return; + + const bool childUpdatesEnabled = q->updatesEnabled(); + if (childUpdatesEnabled && !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->backingStoreTracker.data(); + if (!wbs) + return; + + static int accelEnv = -1; + if (accelEnv == -1) { + accelEnv = qgetenv("QT_NO_FAST_SCROLL").toInt() == 0; + } + + QRect scrollRect = rect & clipRect(); + bool overlapped = false; + bool accelerateScroll = accelEnv && isOpaque + && !(overlapped = isOverlapped(scrollRect.translated(data.crect.topLeft()))); + +#if defined(Q_WS_QWS) + QWSWindowSurface *surface; + surface = static_cast(wbs->windowSurface); + + if (accelerateScroll && !surface->isBuffered()) { + const QRegion surfaceClip = surface->clipRegion(); + const QRegion outsideClip = QRegion(rect) - surfaceClip; + if (!outsideClip.isEmpty()) { + const QVector clipped = (surfaceClip & rect).rects(); + if (clipped.size() < 8) { + for (int i = 0; i < clipped.size(); ++i) + this->scrollRect(clipped.at(i), dx, dy); + return; + } else { + accelerateScroll = false; + } + } + } +#endif // Q_WS_QWS + + if (!accelerateScroll) { + if (overlapped) { + QRegion region(scrollRect); + subtractOpaqueSiblings(region); + invalidateBuffer(region); + }else { + invalidateBuffer(scrollRect); + } + } else { + const QPoint toplevelOffset = q->mapTo(tlw, QPoint()); +#ifdef Q_WS_QWS + QWSWindowSurface *surface = static_cast(wbs->windowSurface); + const QRegion clip = surface->clipRegion().translated(-toplevelOffset) & scrollRect; + const QRect clipBoundingRect = clip.boundingRect(); + scrollRect &= clipBoundingRect; +#endif + const QRect destRect = scrollRect.translated(dx, dy) & scrollRect; + const QRect sourceRect = destRect.translated(-dx, -dy); + + QRegion childExpose(scrollRect); + if (sourceRect.isValid()) { + if (wbs->bltRect(sourceRect, dx, dy, q)) + childExpose -= destRect; + } + + 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 (!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); + } +} + +static inline bool discardSyncRequest(QWidget *tlw, QTLWExtra *tlwExtra) +{ + if (!tlw || !tlwExtra) + return true; + +#ifdef Q_WS_X11 + // Delay the sync until we get an Expose event from X11 (initial show). + // Qt::WA_Mapped is set to true, but the actual mapping has not yet occurred. + // However, we must repaint immediately regardless of the state if someone calls repaint(). + if (tlwExtra->waitingForMapNotify && !tlwExtra->inRepaint) + return true; +#endif + + if (!tlw->testAttribute(Qt::WA_Mapped)) + return true; + + if (!tlw->isVisible() +#ifndef Q_WS_X11 + // If we're minimized on X11, WA_Mapped will be false and we + // will return in the case above. Some window managers on X11 + // sends us the PropertyNotify to change the minimized state + // *AFTER* we've received the expose event, which is baaad. + || tlw->isMinimized() +#endif + ) + return true; + + return false; +} + +/*! + 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 (discardSyncRequest(tlw, tlwExtra) || tlwExtra->inTopLevelResize) + return; + + if (!exposedWidget || !exposedWidget->internalWinId() || !exposedWidget->isVisible() + || !exposedWidget->updatesEnabled() || exposedRegion.isEmpty()) { + return; + } + + // Nothing to repaint. + if (!isDirty()) { + qt_flush(exposedWidget, exposedRegion, store, tlw, tlwOffset); + return; + } + + if (exposedWidget != tlw) + markDirtyOnScreen(exposedRegion, exposedWidget, exposedWidget->mapTo(tlw, QPoint())); + else + markDirtyOnScreen(exposedRegion, exposedWidget, QPoint()); + sync(); +} + +/*! + Synchronizes the backing store, i.e. dirty areas are repainted and flushed. +*/ +void QWidgetBackingStore::sync() +{ + 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(); + fullUpdatePending = false; + } + return; + } + + const bool updatesDisabled = !tlw->updatesEnabled(); + bool repaintAllWidgets = false; + + const bool inTopLevelResize = tlwExtra->inTopLevelResize; + const QRect tlwRect(topLevelRect()); + const QRect surfaceGeometry(tlwRect.topLeft(), store->size()); + if ((fullUpdatePending || inTopLevelResize || surfaceGeometry.size() != tlwRect.size()) && !updatesDisabled) { + if (hasStaticContents()) { + // 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; + + if (hasDirtyFromPreviousSync) + dirty += dirtyFromPreviousSync; + + // 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); + // Scrolled and moved widgets must draw all children. + if (!wd->isScrolled && !wd->isMoved) + wd->subtractOpaqueChildren(wd->dirty, w->rect()); + + if (wd->dirty.isEmpty()) { + resetWidget(w); + continue; + } + + const QRegion widgetDirty(w != tlw ? wd->dirty.translated(w->mapTo(tlw, QPoint())) + : wd->dirty); + toClean += widgetDirty; + +#ifndef QT_NO_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(); + + fullUpdatePending = false; + + if (toClean.isEmpty()) { + // Nothing to repaint. However, 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. + flush(); + return; + } + +#ifndef QT_NO_GRAPHICSVIEW + if (tlw->d_func()->extra->proxyWidget) { + updateStaticContentsSize(); + dirty = QRegion(); + const QVector rects(toClean.rects()); + for (int i = 0; i < rects.size(); ++i) + tlw->d_func()->extra->proxyWidget->update(rects.at(i)); + 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(); + 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(); + + // 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(tlwOffset); + 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, tlwOffset, 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, QBackingStore *backingStore) +{ + if (!dirtyOnScreen.isEmpty()) { + QWidget *target = widget ? widget : tlw; + QBackingStore *source = store ? store : backingStore; + qt_flush(target, dirtyOnScreen, source, tlw, tlwOffset); + dirtyOnScreen = QRegion(); + } + + if (!dirtyOnScreenWidgets || dirtyOnScreenWidgets->isEmpty()) + return; + + for (int i = 0; i < dirtyOnScreenWidgets->size(); ++i) { + QWidget *w = dirtyOnScreenWidgets->at(i); + QWidgetPrivate *wd = w->d_func(); + Q_ASSERT(wd->needsFlush); + qt_flush(w, *wd->needsFlush, backingStore, tlw, tlwOffset); + *wd->needsFlush = QRegion(); + } + dirtyOnScreenWidgets->clear(); +} + +static inline bool discardInvalidateBufferRequest(QWidget *widget, QTLWExtra *tlwExtra) +{ + Q_ASSERT(widget); + if (QApplication::closingDown()) + return true; + + if (!tlwExtra || tlwExtra->inTopLevelResize || !tlwExtra->backingStore) + return true; + + if (!widget->isVisible() || !widget->updatesEnabled()) + return true; + + return false; +} + +/*! + Invalidates the buffer when the widget is resized. + Static areas are never invalidated unless absolutely needed. +*/ +void QWidgetPrivate::invalidateBuffer_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; + invalidateBuffer(dirty); + } else { + // Entire widget needs repaint. + invalidateBuffer(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()->invalidateBuffer(parentExpose); + } else { + if (hasStaticChildren && !graphicsEffect) { + QRegion parentExpose(QRect(oldPos, oldSize)); + parentExpose -= data.crect; // Offset is unchanged, safe to do this. + q->parentWidget()->d_func()->invalidateBuffer(parentExpose); + } else { + q->parentWidget()->d_func()->invalidateBuffer(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; + invalidateBuffer(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()->invalidateBuffer(parentExpose); + } else { + QRegion parentExpose(oldRect); + parentExpose -= data.crect; + q->parentWidget()->d_func()->invalidateBuffer(parentExpose); + } +} + +/*! + Invalidates the \a rgn (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. + + ### Qt 4.6: Merge into a template function (after MSVC isn't supported anymore). +*/ +void QWidgetPrivate::invalidateBuffer(const QRegion &rgn) +{ + Q_Q(QWidget); + + QTLWExtra *tlwExtra = q->window()->d_func()->maybeTopData(); + if (discardInvalidateBufferRequest(q, tlwExtra) || rgn.isEmpty()) + return; + + QRegion wrgn(rgn); + wrgn &= clipRect(); + if (!graphicsEffect && extra && extra->hasMask) + wrgn &= extra->mask; + if (wrgn.isEmpty()) + return; + + tlwExtra->backingStoreTracker->markDirty(wrgn, q, false, true); +} + +/*! + This function is equivalent to calling invalidateBuffer(QRegion(rect), ...), but + is more efficient as it eliminates QRegion operations/allocations and can + use the rect more precisely for additional cut-offs. + + ### Qt 4.6: Merge into a template function (after MSVC isn't supported anymore). +*/ +void QWidgetPrivate::invalidateBuffer(const QRect &rect) +{ + Q_Q(QWidget); + + QTLWExtra *tlwExtra = q->window()->d_func()->maybeTopData(); + if (discardInvalidateBufferRequest(q, tlwExtra) || rect.isEmpty()) + return; + + QRect wRect(rect); + wRect &= clipRect(); + if (wRect.isEmpty()) + return; + + if (graphicsEffect || !extra || !extra->hasMask) { + tlwExtra->backingStoreTracker->markDirty(wRect, q, false, true); + return; + } + + QRegion wRgn(extra->mask); + wRgn &= wRect; + if (wRgn.isEmpty()) + return; + + tlwExtra->backingStoreTracker->markDirty(wRgn, q, false, true); +} + +void QWidgetPrivate::repaint_sys(const QRegion &rgn) +{ + if (data.in_destructor) + return; + + Q_Q(QWidget); + if (q->testAttribute(Qt::WA_StaticContents)) { + if (!extra) + createExtra(); + extra->staticContentsSize = data.crect.size(); + } + +#ifdef Q_WS_QPA //Dont even call q->p + QPaintEngine *engine = 0; +#else + QPaintEngine *engine = q->paintEngine(); +#endif + // 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); + +#ifdef 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->testAttribute(Qt::WA_PaintOutsidePaintEvent) && q->paintingActive()) + qWarning("QWidget::repaint: It is dangerous to leave painters active on a widget outside of the PaintEvent"); +} + + +QT_END_NAMESPACE diff --git a/src/widgets/kernel/qwidgetbackingstore_p.h b/src/widgets/kernel/qwidgetbackingstore_p.h new file mode 100644 index 0000000000..4d43a90322 --- /dev/null +++ b/src/widgets/kernel/qwidgetbackingstore_p.h @@ -0,0 +1,268 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#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 + +QT_BEGIN_NAMESPACE + +struct BeginPaintInfo { + inline BeginPaintInfo() : wasFlushed(0), nothingToPaint(0), backingStoreRecreated(0) {} + uint wasFlushed : 1; + uint nothingToPaint : 1; + uint backingStoreRecreated : 1; +}; + +class Q_AUTOTEST_EXPORT QWidgetBackingStore +{ +public: + 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 = 0, QBackingStore *store = 0); + + inline QPoint topLevelOffset() const { return tlwOffset; } + + QBackingStore *backingStore() const { return store; } + + inline bool isDirty() const + { + return !(dirtyWidgets.isEmpty() && dirty.isEmpty() && !hasDirtyFromPreviousSync + && !fullUpdatePending +#if defined(Q_WS_QWS) && !defined(QT_NO_QWS_MANAGER) + && !hasDirtyWindowDecoration() +#endif + ); + } + + // ### Qt 4.6: Merge into a template function (after MSVC isn't supported anymore). + void markDirty(const QRegion &rgn, QWidget *widget, bool updateImmediately = false, + bool invalidateBuffer = false); + void markDirty(const QRect &rect, QWidget *widget, bool updateImmediately = false, + bool invalidateBuffer = false); + +private: + QWidget *tlw; + QRegion dirtyOnScreen; // needsFlush + QRegion dirty; // needsRepaint + QRegion dirtyFromPreviousSync; + QVector dirtyWidgets; + QVector *dirtyOnScreenWidgets; + QList staticWidgets; + QBackingStore *store; + uint hasDirtyFromPreviousSync : 1; + uint fullUpdatePending : 1; + + QPoint tlwOffset; + + static bool flushPaint(QWidget *widget, const QRegion &rgn); + static void unflushPaint(QWidget *widget, const QRegion &rgn); + + bool bltRect(const QRect &rect, int dx, int dy, QWidget *widget); + void releaseBuffer(); + + 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 = 0) const; + QRegion staticContents(QWidget *widget = 0, const QRect &withinClipRect = QRect()) const; + + void markDirtyOnScreen(const QRegion &dirtyOnScreen, QWidget *widget, const QPoint &topLevelOffset); + + void removeDirtyWidget(QWidget *w); + +#if defined(Q_WS_QWS) && !defined(QT_NO_QWS_MANAGER) + bool hasDirtyWindowDecoration() const; + void paintWindowDecoration(); +#endif + void updateLists(QWidget *widget); + + inline void addDirtyWidget(QWidget *widget, const QRegion &rgn) + { + if (widget && !widget->d_func()->inDirtyList && !widget->data->in_destructor) { + QWidgetPrivate *widgetPrivate = widget->d_func(); +#ifndef QT_NO_GRAPHICSEFFECT + if (widgetPrivate->graphicsEffect) + widgetPrivate->dirty = widgetPrivate->effectiveRectFor(rgn.boundingRect()); + else +#endif //QT_NO_GRAPHICSEFFECT + widgetPrivate->dirty = rgn; + dirtyWidgets.append(widget); + widgetPrivate->inDirtyList = true; + } + } + + inline void dirtyWidgetsRemoveAll(QWidget *widget) + { + int i = 0; + while (i < dirtyWidgets.size()) { + if (dirtyWidgets.at(i) == widget) + dirtyWidgets.remove(i); + else + ++i; + } + } + + 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 + { +#ifdef Q_WS_QWS + return tlw->frameGeometry(); +#else + return tlw->data->crect; +#endif + } + + inline void appendDirtyOnScreenWidget(QWidget *widget) + { + if (!widget) + return; + + if (!dirtyOnScreenWidgets) { + dirtyOnScreenWidgets = new QVector; + dirtyOnScreenWidgets->append(widget); + } else if (!dirtyOnScreenWidgets->contains(widget)) { + dirtyOnScreenWidgets->append(widget); + } + } + + inline void dirtyOnScreenWidgetsRemoveAll(QWidget *widget) + { + if (!widget || !dirtyOnScreenWidgets) + return; + + int i = 0; + while (i < dirtyOnScreenWidgets->size()) { + if (dirtyOnScreenWidgets->at(i) == widget) + dirtyOnScreenWidgets->remove(i); + else + ++i; + } + } + + 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 + { return !staticWidgets.isEmpty(); } + + friend QRegion qt_dirtyRegion(QWidget *); + friend class QWidgetPrivate; + friend class QWidget; + friend class QETWidget; + friend class QBackingStore; +}; + +QT_END_NAMESPACE + +#endif // QBACKINGSTORE_P_H -- cgit v1.2.3