1 /****************************************************************************
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/
6 ** This file is part of the QtGui module of the Qt Toolkit.
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** GNU Lesser General Public License Usage
10 ** This file may be used under the terms of the GNU Lesser General Public
11 ** License version 2.1 as published by the Free Software Foundation and
12 ** appearing in the file LICENSE.LGPL included in the packaging of this
13 ** file. Please review the following information to ensure the GNU Lesser
14 ** General Public License version 2.1 requirements will be met:
15 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
17 ** In addition, as a special exception, Nokia gives you certain additional
18 ** rights. These rights are described in the Nokia Qt LGPL Exception
19 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
21 ** GNU General Public License Usage
22 ** Alternatively, this file may be used under the terms of the GNU General
23 ** Public License version 3.0 as published by the Free Software Foundation
24 ** and appearing in the file LICENSE.GPL included in the packaging of this
25 ** file. Please review the following information to ensure the GNU General
26 ** Public License version 3.0 requirements will be met:
27 ** http://www.gnu.org/copyleft/gpl.html.
30 ** Alternatively, this file may be used in accordance with the terms and
31 ** conditions contained in a signed written agreement between you and Nokia.
40 ****************************************************************************/
43 #include "qplatformdefs.h"
45 #include "qwidgetbackingstore_p.h"
47 #include <QtCore/qglobal.h>
48 #include <QtCore/qdebug.h>
49 #include <QtCore/qvarlengtharray.h>
50 #include <QtGui/qevent.h>
51 #include <QtWidgets/qapplication.h>
52 #include <QtGui/qpaintengine.h>
53 #include <QtWidgets/qgraphicsproxywidget.h>
55 #include <private/qwidget_p.h>
56 #include <private/qapplication_p.h>
57 #include <private/qpaintengine_raster_p.h>
58 #include <private/qgraphicseffect_p.h>
62 extern QRegion qt_dirtyRegion(QWidget *);
65 A version of QRect::intersects() that does not normalize the rects.
67 static inline bool qRectIntersects(const QRect &r1, const QRect &r2)
69 return (qMax(r1.left(), r2.left()) <= qMin(r1.right(), r2.right())
70 && qMax(r1.top(), r2.top()) <= qMin(r1.bottom(), r2.bottom()));
74 * Flushes the contents of the \a backingStore into the screen area of \a widget.
75 * \a tlwOffset is the position of the top level widget relative to the window surface.
76 * \a region is the region to be updated in \a widget coordinates.
78 static inline void qt_flush(QWidget *widget, const QRegion ®ion, QBackingStore *backingStore,
79 QWidget *tlw, const QPoint &tlwOffset)
82 Q_ASSERT(!region.isEmpty());
83 Q_ASSERT(backingStore);
86 #if !defined(QT_NO_PAINT_DEBUG)
87 static int flushUpdate = qgetenv("QT_FLUSH_UPDATE").toInt();
89 QWidgetBackingStore::showYellowThing(widget, region, flushUpdate * 10, false);
92 //The performance hit by doing this should be negligible. However, be aware that
93 //using this FPS when you have > 1 windowsurface can give you inaccurate FPS
94 static bool fpsDebug = qgetenv("QT_DEBUG_FPS").toInt();
96 static QTime time = QTime::currentTime();
97 static int frames = 0;
101 if(time.elapsed() > 5000) {
102 double fps = double(frames * 1000) /time.restart();
103 fprintf(stderr,"FPS: %.1f\n",fps);
108 backingStore->flush(region, widget->windowHandle(), tlwOffset + widget->mapTo(tlw, QPoint()));
110 backingStore->flush(region, widget->windowHandle(), tlwOffset);
113 #ifndef QT_NO_PAINT_DEBUG
115 static void showYellowThing_win(QWidget *widget, const QRegion ®ion, int msec)
121 brush = CreateSolidBrush(RGB(255, 255, 0));
124 brush = CreateSolidBrush(RGB(255, 200, 55));
127 brush = CreateSolidBrush(RGB(200, 255, 55));
130 brush = CreateSolidBrush(RGB(200, 200, 0));
135 HDC hdc = widget->getDC();
137 const QVector<QRect> &rects = region.rects();
138 foreach (QRect rect, rects) {
140 SetRect(&winRect, rect.left(), rect.top(), rect.right(), rect.bottom());
141 FillRect(hdc, &winRect, brush);
144 widget->releaseDC(hdc);
149 void QWidgetBackingStore::showYellowThing(QWidget *widget, const QRegion &toBePainted, int msec, bool unclipped)
151 QRegion paintRegion = toBePainted;
152 QRect widgetRect = widget->rect();
154 if (!widget->internalWinId()) {
155 QWidget *nativeParent = widget->nativeParentWidget();
156 const QPoint offset = widget->mapTo(nativeParent, QPoint(0, 0));
157 paintRegion.translate(offset);
158 widgetRect.translate(offset);
159 widget = nativeParent;
164 showYellowThing_win(widget, paintRegion, msec);
166 //flags to fool painter
167 bool paintUnclipped = widget->testAttribute(Qt::WA_PaintUnclipped);
168 if (unclipped && !widget->d_func()->paintOnScreen())
169 widget->setAttribute(Qt::WA_PaintUnclipped);
171 const bool setFlag = !widget->testAttribute(Qt::WA_WState_InPaintEvent);
173 widget->setAttribute(Qt::WA_WState_InPaintEvent);
176 QPaintEngine *pe = widget->paintEngine();
178 pe->setSystemClip(paintRegion);
181 p.setClipRegion(paintRegion);
185 p.fillRect(widgetRect, QColor(255,255,0));
188 p.fillRect(widgetRect, QColor(255,200,55));
191 p.fillRect(widgetRect, QColor(200,255,55));
194 p.fillRect(widgetRect, QColor(200,200,0));
203 widget->setAttribute(Qt::WA_WState_InPaintEvent, false);
206 widget->setAttribute(Qt::WA_PaintUnclipped, paintUnclipped);
209 pe->setSystemClip(QRegion());
211 #if defined(Q_OS_UNIX)
212 ::usleep(1000 * msec);
217 bool QWidgetBackingStore::flushPaint(QWidget *widget, const QRegion &rgn)
223 if (widget->testAttribute(Qt::WA_WState_InPaintEvent)) {
224 static int flushPaintEvent = qgetenv("QT_FLUSH_PAINT_EVENT").toInt();
225 if (!flushPaintEvent)
227 delay = flushPaintEvent;
229 static int flushPaint = qgetenv("QT_FLUSH_PAINT").toInt();
235 QWidgetBackingStore::showYellowThing(widget, rgn, delay * 10, true);
239 void QWidgetBackingStore::unflushPaint(QWidget *widget, const QRegion &rgn)
241 if (widget->d_func()->paintOnScreen() || rgn.isEmpty())
244 QWidget *tlw = widget->window();
245 QTLWExtra *tlwExtra = tlw->d_func()->maybeTopData();
249 const QPoint offset = widget->mapTo(tlw, QPoint());
250 qt_flush(widget, rgn, tlwExtra->backingStoreTracker->store, tlw, offset);
252 #endif // QT_NO_PAINT_DEBUG
255 Moves the whole rect by (dx, dy) in widget's coordinate system.
256 Doesn't generate any updates.
258 bool QWidgetBackingStore::bltRect(const QRect &rect, int dx, int dy, QWidget *widget)
260 const QPoint pos(tlwOffset + widget->mapTo(tlw, rect.topLeft()));
261 const QRect tlwRect(QRect(pos, rect.size()));
262 if (fullUpdatePending || dirty.intersects(tlwRect))
263 return false; // We don't want to scroll junk.
264 return store->scroll(tlwRect, dx, dy);
267 void QWidgetBackingStore::releaseBuffer()
270 store->resize(QSize());
274 Prepares the window surface to paint a\ toClean region of the \a widget and
275 updates the BeginPaintInfo struct accordingly.
277 The \a toClean region might be clipped by the window surface.
279 void QWidgetBackingStore::beginPaint(QRegion &toClean, QWidget *widget, QBackingStore *backingStore,
280 BeginPaintInfo *returnInfo, bool toCleanIsInTopLevelCoordinates)
283 Q_UNUSED(toCleanIsInTopLevelCoordinates);
285 // Always flush repainted areas.
286 dirtyOnScreen += toClean;
288 #ifdef QT_NO_PAINT_DEBUG
289 backingStore->beginPaint(toClean);
291 returnInfo->wasFlushed = QWidgetBackingStore::flushPaint(tlw, toClean);
292 // Avoid deadlock with QT_FLUSH_PAINT: the server will wait for
293 // the BackingStore lock, so if we hold that, the server will
294 // never release the Communication lock that we are waiting for in
295 // sendSynchronousCommand
296 if (!returnInfo->wasFlushed)
297 backingStore->beginPaint(toClean);
300 Q_UNUSED(returnInfo);
303 void QWidgetBackingStore::endPaint(const QRegion &cleaned, QBackingStore *backingStore,
304 BeginPaintInfo *beginPaintInfo)
306 #ifndef QT_NO_PAINT_DEBUG
307 if (!beginPaintInfo->wasFlushed)
308 backingStore->endPaint();
310 QWidgetBackingStore::unflushPaint(tlw, cleaned);
312 Q_UNUSED(beginPaintInfo);
314 backingStore->endPaint();
321 Returns the region (in top-level coordinates) that needs repaint and/or flush.
323 If the widget is non-zero, only the dirty region for the widget is returned
324 and the region will be in widget coordinates.
326 QRegion QWidgetBackingStore::dirtyRegion(QWidget *widget) const
328 const bool widgetDirty = widget && widget != tlw;
329 const QRect tlwRect(topLevelRect());
330 const QRect surfaceGeometry(tlwRect.topLeft(), store->size());
331 if (fullUpdatePending || (surfaceGeometry != tlwRect && surfaceGeometry.size() != tlwRect.size())) {
333 const QRect dirtyTlwRect = QRect(QPoint(), tlwRect.size());
334 const QPoint offset(widget->mapTo(tlw, QPoint()));
335 const QRect dirtyWidgetRect(dirtyTlwRect & widget->rect().translated(offset));
336 return dirtyWidgetRect.translated(-offset);
338 return QRect(QPoint(), tlwRect.size());
341 // Calculate the region that needs repaint.
343 for (int i = 0; i < dirtyWidgets.size(); ++i) {
344 QWidget *w = dirtyWidgets.at(i);
345 if (widgetDirty && w != widget && !widget->isAncestorOf(w))
347 r += w->d_func()->dirty.translated(w->mapTo(tlw, QPoint()));
350 // Append the region that needs flush.
353 if (dirtyOnScreenWidgets) { // Only in use with native child widgets.
354 for (int i = 0; i < dirtyOnScreenWidgets->size(); ++i) {
355 QWidget *w = dirtyOnScreenWidgets->at(i);
356 if (widgetDirty && w != widget && !widget->isAncestorOf(w))
358 QWidgetPrivate *wd = w->d_func();
359 Q_ASSERT(wd->needsFlush);
360 r += wd->needsFlush->translated(w->mapTo(tlw, QPoint()));
365 // Intersect with the widget geometry and translate to its coordinates.
366 const QPoint offset(widget->mapTo(tlw, QPoint()));
367 r &= widget->rect().translated(offset);
368 r.translate(-offset);
374 Returns the static content inside the \a parent if non-zero; otherwise the static content
375 for the entire backing store is returned. The content will be clipped to \a withinClipRect
378 QRegion QWidgetBackingStore::staticContents(QWidget *parent, const QRect &withinClipRect) const
380 if (!parent && tlw->testAttribute(Qt::WA_StaticContents)) {
381 const QSize surfaceGeometry(store->size());
382 QRect surfaceRect(0, 0, surfaceGeometry.width(), surfaceGeometry.height());
383 if (!withinClipRect.isEmpty())
384 surfaceRect &= withinClipRect;
385 return QRegion(surfaceRect);
389 if (parent && parent->d_func()->children.isEmpty())
392 const bool clipToRect = !withinClipRect.isEmpty();
393 const int count = staticWidgets.count();
394 for (int i = 0; i < count; ++i) {
395 QWidget *w = staticWidgets.at(i);
396 QWidgetPrivate *wd = w->d_func();
397 if (!wd->isOpaque || !wd->extra || wd->extra->staticContentsSize.isEmpty()
398 || !w->isVisible() || (parent && !parent->isAncestorOf(w))) {
402 QRect rect(0, 0, wd->extra->staticContentsSize.width(), wd->extra->staticContentsSize.height());
403 const QPoint offset = w->mapTo(parent ? parent : tlw, QPoint());
405 rect &= withinClipRect.translated(-offset);
409 rect &= wd->clipRect();
413 QRegion visible(rect);
414 wd->clipToEffectiveMask(visible);
415 if (visible.isEmpty())
417 wd->subtractOpaqueSiblings(visible, 0, /*alsoNonOpaque=*/true);
419 visible.translate(offset);
426 static inline void sendUpdateRequest(QWidget *widget, bool updateImmediately)
431 if (updateImmediately) {
432 QEvent event(QEvent::UpdateRequest);
433 QApplication::sendEvent(widget, &event);
435 QApplication::postEvent(widget, new QEvent(QEvent::UpdateRequest), Qt::LowEventPriority);
440 Marks the region of the widget as dirty (if not already marked as dirty) and
441 posts an UpdateRequest event to the top-level widget (if not already posted).
443 If updateImmediately is true, the event is sent immediately instead of posted.
445 If invalidateBuffer is true, all widgets intersecting with the region will be dirty.
447 If the widget paints directly on screen, the event is sent to the widget
448 instead of the top-level widget, and invalidateBuffer is completely ignored.
450 ### Qt 4.6: Merge into a template function (after MSVC isn't supported anymore).
452 void QWidgetBackingStore::markDirty(const QRegion &rgn, QWidget *widget, bool updateImmediately,
453 bool invalidateBuffer)
455 Q_ASSERT(tlw->d_func()->extra);
456 Q_ASSERT(tlw->d_func()->extra->topextra);
457 Q_ASSERT(!tlw->d_func()->extra->topextra->inTopLevelResize);
458 Q_ASSERT(widget->isVisible() && widget->updatesEnabled());
459 Q_ASSERT(widget->window() == tlw);
460 Q_ASSERT(!rgn.isEmpty());
462 #ifndef QT_NO_GRAPHICSEFFECT
463 widget->d_func()->invalidateGraphicsEffectsRecursively();
464 #endif //QT_NO_GRAPHICSEFFECT
466 if (widget->d_func()->paintOnScreen()) {
467 if (widget->d_func()->dirty.isEmpty()) {
468 widget->d_func()->dirty = rgn;
469 sendUpdateRequest(widget, updateImmediately);
471 } else if (qt_region_strictContains(widget->d_func()->dirty, widget->rect())) {
472 if (updateImmediately)
473 sendUpdateRequest(widget, updateImmediately);
474 return; // Already dirty.
477 const bool eventAlreadyPosted = !widget->d_func()->dirty.isEmpty();
478 widget->d_func()->dirty += rgn;
479 if (!eventAlreadyPosted || updateImmediately)
480 sendUpdateRequest(widget, updateImmediately);
484 if (fullUpdatePending) {
485 if (updateImmediately)
486 sendUpdateRequest(tlw, updateImmediately);
490 const QPoint offset = widget->mapTo(tlw, QPoint());
491 const QRect widgetRect = widget->d_func()->effectiveRectFor(widget->rect());
492 if (qt_region_strictContains(dirty, widgetRect.translated(offset))) {
493 if (updateImmediately)
494 sendUpdateRequest(tlw, updateImmediately);
495 return; // Already dirty.
498 if (invalidateBuffer) {
499 const bool eventAlreadyPosted = !dirty.isEmpty();
500 #ifndef QT_NO_GRAPHICSEFFECT
501 if (widget->d_func()->graphicsEffect)
502 dirty += widget->d_func()->effectiveRectFor(rgn.boundingRect()).translated(offset);
504 #endif //QT_NO_GRAPHICSEFFECT
505 dirty += rgn.translated(offset);
506 if (!eventAlreadyPosted || updateImmediately)
507 sendUpdateRequest(tlw, updateImmediately);
511 if (dirtyWidgets.isEmpty()) {
512 addDirtyWidget(widget, rgn);
513 sendUpdateRequest(tlw, updateImmediately);
517 if (widget->d_func()->inDirtyList) {
518 if (!qt_region_strictContains(widget->d_func()->dirty, widgetRect)) {
519 #ifndef QT_NO_GRAPHICSEFFECT
520 if (widget->d_func()->graphicsEffect)
521 widget->d_func()->dirty += widget->d_func()->effectiveRectFor(rgn.boundingRect());
523 #endif //QT_NO_GRAPHICSEFFECT
524 widget->d_func()->dirty += rgn;
527 addDirtyWidget(widget, rgn);
530 if (updateImmediately)
531 sendUpdateRequest(tlw, updateImmediately);
535 This function is equivalent to calling markDirty(QRegion(rect), ...), but
536 is more efficient as it eliminates QRegion operations/allocations and can
537 use the rect more precisely for additional cut-offs.
539 ### Qt 4.6: Merge into a template function (after MSVC isn't supported anymore).
541 void QWidgetBackingStore::markDirty(const QRect &rect, QWidget *widget, bool updateImmediately,
542 bool invalidateBuffer)
544 Q_ASSERT(tlw->d_func()->extra);
545 Q_ASSERT(tlw->d_func()->extra->topextra);
546 Q_ASSERT(!tlw->d_func()->extra->topextra->inTopLevelResize);
547 Q_ASSERT(widget->isVisible() && widget->updatesEnabled());
548 Q_ASSERT(widget->window() == tlw);
549 Q_ASSERT(!rect.isEmpty());
551 #ifndef QT_NO_GRAPHICSEFFECT
552 widget->d_func()->invalidateGraphicsEffectsRecursively();
553 #endif //QT_NO_GRAPHICSEFFECT
555 if (widget->d_func()->paintOnScreen()) {
556 if (widget->d_func()->dirty.isEmpty()) {
557 widget->d_func()->dirty = QRegion(rect);
558 sendUpdateRequest(widget, updateImmediately);
560 } else if (qt_region_strictContains(widget->d_func()->dirty, rect)) {
561 if (updateImmediately)
562 sendUpdateRequest(widget, updateImmediately);
563 return; // Already dirty.
566 const bool eventAlreadyPosted = !widget->d_func()->dirty.isEmpty();
567 widget->d_func()->dirty += rect;
568 if (!eventAlreadyPosted || updateImmediately)
569 sendUpdateRequest(widget, updateImmediately);
573 if (fullUpdatePending) {
574 if (updateImmediately)
575 sendUpdateRequest(tlw, updateImmediately);
579 const QRect widgetRect = widget->d_func()->effectiveRectFor(rect);
580 const QRect translatedRect(widgetRect.translated(widget->mapTo(tlw, QPoint())));
581 if (qt_region_strictContains(dirty, translatedRect)) {
582 if (updateImmediately)
583 sendUpdateRequest(tlw, updateImmediately);
584 return; // Already dirty
587 if (invalidateBuffer) {
588 const bool eventAlreadyPosted = !dirty.isEmpty();
589 dirty += translatedRect;
590 if (!eventAlreadyPosted || updateImmediately)
591 sendUpdateRequest(tlw, updateImmediately);
595 if (dirtyWidgets.isEmpty()) {
596 addDirtyWidget(widget, rect);
597 sendUpdateRequest(tlw, updateImmediately);
601 if (widget->d_func()->inDirtyList) {
602 if (!qt_region_strictContains(widget->d_func()->dirty, widgetRect))
603 widget->d_func()->dirty += widgetRect;
605 addDirtyWidget(widget, rect);
608 if (updateImmediately)
609 sendUpdateRequest(tlw, updateImmediately);
613 Marks the \a region of the \a widget as dirty on screen. The \a region will be copied from
614 the backing store to the \a widget's native parent next time flush() is called.
616 Paint on screen widgets are ignored.
618 void QWidgetBackingStore::markDirtyOnScreen(const QRegion ®ion, QWidget *widget, const QPoint &topLevelOffset)
620 if (!widget || widget->d_func()->paintOnScreen() || region.isEmpty())
623 #if defined(Q_WS_MAC)
624 if (!widget->testAttribute(Qt::WA_WState_InPaintEvent))
625 dirtyOnScreen += region.translated(topLevelOffset);
631 if (!widget->testAttribute(Qt::WA_WState_InPaintEvent))
632 dirtyOnScreen += region;
637 if (!widget->internalWinId() && !widget->isWindow()) {
638 QWidget *nativeParent = widget->nativeParentWidget(); // Alien widgets with the top-level as the native parent (common case).
639 if (nativeParent == tlw) {
640 if (!widget->testAttribute(Qt::WA_WState_InPaintEvent))
641 dirtyOnScreen += region.translated(topLevelOffset);
645 // Alien widgets with native parent != tlw.
646 QWidgetPrivate *nativeParentPrivate = nativeParent->d_func();
647 if (!nativeParentPrivate->needsFlush)
648 nativeParentPrivate->needsFlush = new QRegion;
649 const QPoint nativeParentOffset = widget->mapTo(nativeParent, QPoint());
650 *nativeParentPrivate->needsFlush += region.translated(nativeParentOffset);
651 appendDirtyOnScreenWidget(nativeParent);
655 // Native child widgets.
656 QWidgetPrivate *widgetPrivate = widget->d_func();
657 if (!widgetPrivate->needsFlush)
658 widgetPrivate->needsFlush = new QRegion;
659 *widgetPrivate->needsFlush += region;
660 appendDirtyOnScreenWidget(widget);
663 void QWidgetBackingStore::removeDirtyWidget(QWidget *w)
668 dirtyWidgetsRemoveAll(w);
669 dirtyOnScreenWidgetsRemoveAll(w);
672 QWidgetPrivate *wd = w->d_func();
673 const int n = wd->children.count();
674 for (int i = 0; i < n; ++i) {
675 if (QWidget *child = qobject_cast<QWidget*>(wd->children.at(i)))
676 removeDirtyWidget(child);
680 void QWidgetBackingStore::updateLists(QWidget *cur)
685 QList<QObject*> children = cur->children();
686 for (int i = 0; i < children.size(); ++i) {
687 QWidget *child = qobject_cast<QWidget*>(children.at(i));
694 if (cur->testAttribute(Qt::WA_StaticContents))
695 addStaticWidget(cur);
698 QWidgetBackingStore::QWidgetBackingStore(QWidget *topLevel)
699 : tlw(topLevel), dirtyOnScreenWidgets(0), fullUpdatePending(0)
701 store = tlw->backingStore();
704 // Ensure all existing subsurfaces and static widgets are added to their respective lists.
705 updateLists(topLevel);
708 QWidgetBackingStore::~QWidgetBackingStore()
710 for (int c = 0; c < dirtyWidgets.size(); ++c) {
711 resetWidget(dirtyWidgets.at(c));
714 delete dirtyOnScreenWidgets;
715 dirtyOnScreenWidgets = 0;
718 //parent's coordinates; move whole rect; update parent and widget
719 //assume the screen blt has already been done, so we don't need to refresh that part
720 void QWidgetPrivate::moveRect(const QRect &rect, int dx, int dy)
723 if (!q->isVisible() || (dx == 0 && dy == 0))
726 QWidget *tlw = q->window();
727 QTLWExtra* x = tlw->d_func()->topData();
728 if (x->inTopLevelResize)
731 static int accelEnv = -1;
732 if (accelEnv == -1) {
733 accelEnv = qgetenv("QT_NO_FAST_MOVE").toInt() == 0;
736 QWidget *pw = q->parentWidget();
737 QPoint toplevelOffset = pw->mapTo(tlw, QPoint());
738 QWidgetPrivate *pd = pw->d_func();
739 QRect clipR(pd->clipRect());
740 const QRect newRect(rect.translated(dx, dy));
741 QRect destRect = rect.intersected(clipR);
742 if (destRect.isValid())
743 destRect = destRect.translated(dx, dy).intersected(clipR);
744 const QRect sourceRect(destRect.translated(-dx, -dy));
745 const QRect parentRect(rect & clipR);
747 bool accelerateMove = accelEnv && isOpaque
748 #ifndef QT_NO_GRAPHICSVIEW
749 // No accelerate move for proxy widgets.
750 && !tlw->d_func()->extra->proxyWidget
752 && !isOverlapped(sourceRect) && !isOverlapped(destRect);
754 if (!accelerateMove) {
755 QRegion parentR(effectiveRectFor(parentRect));
756 if (!extra || !extra->hasMask) {
759 // invalidateBuffer() excludes anything outside the mask
760 parentR += newRect & clipR;
762 pd->invalidateBuffer(parentR);
763 invalidateBuffer((newRect & clipR).translated(-data.crect.topLeft()));
766 QWidgetBackingStore *wbs = x->backingStoreTracker.data();
767 QRegion childExpose(newRect & clipR);
769 if (sourceRect.isValid() && wbs->bltRect(sourceRect, dx, dy, pw))
770 childExpose -= destRect;
772 if (!pw->updatesEnabled())
775 const bool childUpdatesEnabled = q->updatesEnabled();
776 if (childUpdatesEnabled && !childExpose.isEmpty()) {
777 childExpose.translate(-data.crect.topLeft());
778 wbs->markDirty(childExpose, q);
782 QRegion parentExpose(parentRect);
783 parentExpose -= newRect;
784 if (extra && extra->hasMask)
785 parentExpose += QRegion(newRect) - extra->mask.translated(data.crect.topLeft());
787 if (!parentExpose.isEmpty()) {
788 wbs->markDirty(parentExpose, pw);
792 if (childUpdatesEnabled) {
793 QRegion needsFlush(sourceRect);
794 needsFlush += destRect;
795 wbs->markDirtyOnScreen(needsFlush, pw, toplevelOffset);
800 //widget's coordinates; scroll within rect; only update widget
801 void QWidgetPrivate::scrollRect(const QRect &rect, int dx, int dy)
804 QWidget *tlw = q->window();
805 QTLWExtra* x = tlw->d_func()->topData();
806 if (x->inTopLevelResize)
809 QWidgetBackingStore *wbs = x->backingStoreTracker.data();
813 static int accelEnv = -1;
814 if (accelEnv == -1) {
815 accelEnv = qgetenv("QT_NO_FAST_SCROLL").toInt() == 0;
818 QRect scrollRect = rect & clipRect();
819 bool overlapped = false;
820 bool accelerateScroll = accelEnv && isOpaque
821 && !(overlapped = isOverlapped(scrollRect.translated(data.crect.topLeft())));
823 if (!accelerateScroll) {
825 QRegion region(scrollRect);
826 subtractOpaqueSiblings(region);
827 invalidateBuffer(region);
829 invalidateBuffer(scrollRect);
832 const QPoint toplevelOffset = q->mapTo(tlw, QPoint());
833 const QRect destRect = scrollRect.translated(dx, dy) & scrollRect;
834 const QRect sourceRect = destRect.translated(-dx, -dy);
836 QRegion childExpose(scrollRect);
837 if (sourceRect.isValid()) {
838 if (wbs->bltRect(sourceRect, dx, dy, q))
839 childExpose -= destRect;
843 if (rect == q->rect()) {
844 dirty.translate(dx, dy);
846 QRegion dirtyScrollRegion = dirty.intersected(scrollRect);
847 if (!dirtyScrollRegion.isEmpty()) {
848 dirty -= dirtyScrollRegion;
849 dirtyScrollRegion.translate(dx, dy);
850 dirty += dirtyScrollRegion;
855 if (!q->updatesEnabled())
858 if (!childExpose.isEmpty()) {
859 wbs->markDirty(childExpose, q);
863 // Instead of using native scroll-on-screen, we copy from
864 // backingstore, giving only one screen update for each
865 // scroll, and a solid appearance
866 wbs->markDirtyOnScreen(destRect, q, toplevelOffset);
870 static inline bool discardSyncRequest(QWidget *tlw, QTLWExtra *tlwExtra)
872 if (!tlw || !tlwExtra || !tlw->testAttribute(Qt::WA_Mapped) || !tlw->isVisible())
879 Synchronizes the \a exposedRegion of the \a exposedWidget with the backing store.
881 If there's nothing to repaint, the area is flushed and painting does not occur;
882 otherwise the area is marked as dirty on screen and will be flushed right after
883 we are done with all painting.
885 void QWidgetBackingStore::sync(QWidget *exposedWidget, const QRegion &exposedRegion)
887 QTLWExtra *tlwExtra = tlw->d_func()->maybeTopData();
888 if (discardSyncRequest(tlw, tlwExtra) || tlwExtra->inTopLevelResize)
891 if (!exposedWidget || !exposedWidget->internalWinId() || !exposedWidget->isVisible()
892 || !exposedWidget->updatesEnabled() || exposedRegion.isEmpty()) {
896 // Nothing to repaint.
898 qt_flush(exposedWidget, exposedRegion, store, tlw, tlwOffset);
902 if (exposedWidget != tlw)
903 markDirtyOnScreen(exposedRegion, exposedWidget, exposedWidget->mapTo(tlw, QPoint()));
905 markDirtyOnScreen(exposedRegion, exposedWidget, QPoint());
910 Synchronizes the backing store, i.e. dirty areas are repainted and flushed.
912 void QWidgetBackingStore::sync()
914 QTLWExtra *tlwExtra = tlw->d_func()->maybeTopData();
915 if (discardSyncRequest(tlw, tlwExtra)) {
916 // If the top-level is minimized, it's not visible on the screen so we can delay the
917 // update until it's shown again. In order to do that we must keep the dirty states.
918 // These will be cleared when we receive the first expose after showNormal().
919 // However, if the widget is not visible (isVisible() returns false), everything will
920 // be invalidated once the widget is shown again, so clear all dirty states.
921 if (!tlw->isVisible()) {
923 for (int i = 0; i < dirtyWidgets.size(); ++i)
924 resetWidget(dirtyWidgets.at(i));
925 dirtyWidgets.clear();
926 fullUpdatePending = false;
931 const bool updatesDisabled = !tlw->updatesEnabled();
932 bool repaintAllWidgets = false;
934 const bool inTopLevelResize = tlwExtra->inTopLevelResize;
935 const QRect tlwRect(topLevelRect());
936 const QRect surfaceGeometry(tlwRect.topLeft(), store->size());
937 if ((fullUpdatePending || inTopLevelResize || surfaceGeometry.size() != tlwRect.size()) && !updatesDisabled) {
938 if (hasStaticContents()) {
939 // Repaint existing dirty area and newly visible area.
940 const QRect clipRect(0, 0, surfaceGeometry.width(), surfaceGeometry.height());
941 const QRegion staticRegion(staticContents(0, clipRect));
942 QRegion newVisible(0, 0, tlwRect.width(), tlwRect.height());
943 newVisible -= staticRegion;
945 store->setStaticContents(staticRegion);
947 // Repaint everything.
948 dirty = QRegion(0, 0, tlwRect.width(), tlwRect.height());
949 for (int i = 0; i < dirtyWidgets.size(); ++i)
950 resetWidget(dirtyWidgets.at(i));
951 dirtyWidgets.clear();
952 repaintAllWidgets = true;
956 if (inTopLevelResize || surfaceGeometry.size() != tlwRect.size())
957 store->resize(tlwRect.size());
962 // Contains everything that needs repaint.
963 QRegion toClean(dirty);
965 // Loop through all update() widgets and remove them from the list before they are
966 // painted (in case someone calls update() in paintEvent). If the widget is opaque
967 // and does not have transparent overlapping siblings, append it to the
968 // opaqueNonOverlappedWidgets list and paint it directly without composition.
969 QVarLengthArray<QWidget *, 32> opaqueNonOverlappedWidgets;
970 for (int i = 0; i < dirtyWidgets.size(); ++i) {
971 QWidget *w = dirtyWidgets.at(i);
972 QWidgetPrivate *wd = w->d_func();
973 if (wd->data.in_destructor)
976 // Clip with mask() and clipRect().
977 wd->dirty &= wd->clipRect();
978 wd->clipToEffectiveMask(wd->dirty);
980 // Subtract opaque siblings and children.
981 bool hasDirtySiblingsAbove = false;
982 // We know for sure that the widget isn't overlapped if 'isMoved' is true.
984 wd->subtractOpaqueSiblings(wd->dirty, &hasDirtySiblingsAbove);
985 // Scrolled and moved widgets must draw all children.
986 if (!wd->isScrolled && !wd->isMoved)
987 wd->subtractOpaqueChildren(wd->dirty, w->rect());
989 if (wd->dirty.isEmpty()) {
994 const QRegion widgetDirty(w != tlw ? wd->dirty.translated(w->mapTo(tlw, QPoint()))
996 toClean += widgetDirty;
998 #ifndef QT_NO_GRAPHICSVIEW
999 if (tlw->d_func()->extra->proxyWidget) {
1005 if (!hasDirtySiblingsAbove && wd->isOpaque && !dirty.intersects(widgetDirty.boundingRect())) {
1006 opaqueNonOverlappedWidgets.append(w);
1009 dirty += widgetDirty;
1012 dirtyWidgets.clear();
1014 fullUpdatePending = false;
1016 if (toClean.isEmpty()) {
1017 // Nothing to repaint. However, we might have newly exposed areas on the
1018 // screen if this function was called from sync(QWidget *, QRegion)), so
1019 // we have to make sure those are flushed.
1024 #ifndef QT_NO_GRAPHICSVIEW
1025 if (tlw->d_func()->extra->proxyWidget) {
1026 updateStaticContentsSize();
1028 const QVector<QRect> rects(toClean.rects());
1029 for (int i = 0; i < rects.size(); ++i)
1030 tlw->d_func()->extra->proxyWidget->update(rects.at(i));
1035 BeginPaintInfo beginPaintInfo;
1036 beginPaint(toClean, tlw, store, &beginPaintInfo);
1037 if (beginPaintInfo.nothingToPaint) {
1038 for (int i = 0; i < opaqueNonOverlappedWidgets.size(); ++i)
1039 resetWidget(opaqueNonOverlappedWidgets[i]);
1044 // Must do this before sending any paint events because
1045 // the size may change in the paint event.
1046 updateStaticContentsSize();
1047 const QRegion dirtyCopy(dirty);
1050 // Paint opaque non overlapped widgets.
1051 for (int i = 0; i < opaqueNonOverlappedWidgets.size(); ++i) {
1052 QWidget *w = opaqueNonOverlappedWidgets[i];
1053 QWidgetPrivate *wd = w->d_func();
1055 int flags = QWidgetPrivate::DrawRecursive;
1056 // Scrolled and moved widgets must draw all children.
1057 if (!wd->isScrolled && !wd->isMoved)
1058 flags |= QWidgetPrivate::DontDrawOpaqueChildren;
1060 flags |= QWidgetPrivate::DrawAsRoot;
1062 QRegion toBePainted(wd->dirty);
1065 QPoint offset(tlwOffset);
1067 offset += w->mapTo(tlw, QPoint());
1068 wd->drawWidget(store->paintDevice(), toBePainted, offset, flags, 0, this);
1071 // Paint the rest with composition.
1072 if (repaintAllWidgets || !dirtyCopy.isEmpty()) {
1073 const int flags = QWidgetPrivate::DrawAsRoot | QWidgetPrivate::DrawRecursive;
1074 tlw->d_func()->drawWidget(store->paintDevice(), dirtyCopy, tlwOffset, flags, 0, this);
1077 endPaint(toClean, store, &beginPaintInfo);
1081 Flushes the contents of the backing store into the top-level widget.
1082 If the \a widget is non-zero, the content is flushed to the \a widget.
1083 If the \a surface is non-zero, the content of the \a surface is flushed.
1085 void QWidgetBackingStore::flush(QWidget *widget, QBackingStore *backingStore)
1087 if (!dirtyOnScreen.isEmpty()) {
1088 QWidget *target = widget ? widget : tlw;
1089 QBackingStore *source = store ? store : backingStore;
1090 qt_flush(target, dirtyOnScreen, source, tlw, tlwOffset);
1091 dirtyOnScreen = QRegion();
1094 if (!dirtyOnScreenWidgets || dirtyOnScreenWidgets->isEmpty())
1097 for (int i = 0; i < dirtyOnScreenWidgets->size(); ++i) {
1098 QWidget *w = dirtyOnScreenWidgets->at(i);
1099 QWidgetPrivate *wd = w->d_func();
1100 Q_ASSERT(wd->needsFlush);
1101 qt_flush(w, *wd->needsFlush, backingStore, tlw, tlwOffset);
1102 *wd->needsFlush = QRegion();
1104 dirtyOnScreenWidgets->clear();
1107 static inline bool discardInvalidateBufferRequest(QWidget *widget, QTLWExtra *tlwExtra)
1110 if (QApplication::closingDown())
1113 if (!tlwExtra || tlwExtra->inTopLevelResize || !tlwExtra->backingStore)
1116 if (!widget->isVisible() || !widget->updatesEnabled())
1123 Invalidates the buffer when the widget is resized.
1124 Static areas are never invalidated unless absolutely needed.
1126 void QWidgetPrivate::invalidateBuffer_resizeHelper(const QPoint &oldPos, const QSize &oldSize)
1129 Q_ASSERT(!q->isWindow());
1130 Q_ASSERT(q->parentWidget());
1132 const bool staticContents = q->testAttribute(Qt::WA_StaticContents);
1133 const bool sizeDecreased = (data.crect.width() < oldSize.width())
1134 || (data.crect.height() < oldSize.height());
1136 const QPoint offset(data.crect.x() - oldPos.x(), data.crect.y() - oldPos.y());
1137 const bool parentAreaExposed = !offset.isNull() || sizeDecreased;
1138 const QRect newWidgetRect(q->rect());
1139 const QRect oldWidgetRect(0, 0, oldSize.width(), oldSize.height());
1141 if (!staticContents || graphicsEffect) {
1142 QRegion staticChildren;
1143 QWidgetBackingStore *bs = 0;
1144 if (offset.isNull() && (bs = maybeBackingStore()))
1145 staticChildren = bs->staticContents(q, oldWidgetRect);
1146 const bool hasStaticChildren = !staticChildren.isEmpty();
1148 if (hasStaticChildren) {
1149 QRegion dirty(newWidgetRect);
1150 dirty -= staticChildren;
1151 invalidateBuffer(dirty);
1153 // Entire widget needs repaint.
1154 invalidateBuffer(newWidgetRect);
1157 if (!parentAreaExposed)
1160 // Invalidate newly exposed area of the parent.
1161 if (!graphicsEffect && extra && extra->hasMask) {
1162 QRegion parentExpose(extra->mask.translated(oldPos));
1163 parentExpose &= QRect(oldPos, oldSize);
1164 if (hasStaticChildren)
1165 parentExpose -= data.crect; // Offset is unchanged, safe to do this.
1166 q->parentWidget()->d_func()->invalidateBuffer(parentExpose);
1168 if (hasStaticChildren && !graphicsEffect) {
1169 QRegion parentExpose(QRect(oldPos, oldSize));
1170 parentExpose -= data.crect; // Offset is unchanged, safe to do this.
1171 q->parentWidget()->d_func()->invalidateBuffer(parentExpose);
1173 q->parentWidget()->d_func()->invalidateBuffer(effectiveRectFor(QRect(oldPos, oldSize)));
1179 // Move static content to its new position.
1180 if (!offset.isNull()) {
1181 if (sizeDecreased) {
1182 const QSize minSize(qMin(oldSize.width(), data.crect.width()),
1183 qMin(oldSize.height(), data.crect.height()));
1184 moveRect(QRect(oldPos, minSize), offset.x(), offset.y());
1186 moveRect(QRect(oldPos, oldSize), offset.x(), offset.y());
1190 // Invalidate newly visible area of the widget.
1191 if (!sizeDecreased || !oldWidgetRect.contains(newWidgetRect)) {
1192 QRegion newVisible(newWidgetRect);
1193 newVisible -= oldWidgetRect;
1194 invalidateBuffer(newVisible);
1197 if (!parentAreaExposed)
1200 // Invalidate newly exposed area of the parent.
1201 const QRect oldRect(oldPos, oldSize);
1202 if (extra && extra->hasMask) {
1203 QRegion parentExpose(oldRect);
1204 parentExpose &= extra->mask.translated(oldPos);
1205 parentExpose -= (extra->mask.translated(data.crect.topLeft()) & data.crect);
1206 q->parentWidget()->d_func()->invalidateBuffer(parentExpose);
1208 QRegion parentExpose(oldRect);
1209 parentExpose -= data.crect;
1210 q->parentWidget()->d_func()->invalidateBuffer(parentExpose);
1215 Invalidates the \a rgn (in widget's coordinates) of the backing store, i.e.
1216 all widgets intersecting with the region will be repainted when the backing store
1219 ### Qt 4.6: Merge into a template function (after MSVC isn't supported anymore).
1221 void QWidgetPrivate::invalidateBuffer(const QRegion &rgn)
1225 QTLWExtra *tlwExtra = q->window()->d_func()->maybeTopData();
1226 if (discardInvalidateBufferRequest(q, tlwExtra) || rgn.isEmpty())
1231 if (!graphicsEffect && extra && extra->hasMask)
1232 wrgn &= extra->mask;
1236 tlwExtra->backingStoreTracker->markDirty(wrgn, q, false, true);
1240 This function is equivalent to calling invalidateBuffer(QRegion(rect), ...), but
1241 is more efficient as it eliminates QRegion operations/allocations and can
1242 use the rect more precisely for additional cut-offs.
1244 ### Qt 4.6: Merge into a template function (after MSVC isn't supported anymore).
1246 void QWidgetPrivate::invalidateBuffer(const QRect &rect)
1250 QTLWExtra *tlwExtra = q->window()->d_func()->maybeTopData();
1251 if (discardInvalidateBufferRequest(q, tlwExtra) || rect.isEmpty())
1255 wRect &= clipRect();
1256 if (wRect.isEmpty())
1259 if (graphicsEffect || !extra || !extra->hasMask) {
1260 tlwExtra->backingStoreTracker->markDirty(wRect, q, false, true);
1264 QRegion wRgn(extra->mask);
1269 tlwExtra->backingStoreTracker->markDirty(wRgn, q, false, true);
1272 void QWidgetPrivate::repaint_sys(const QRegion &rgn)
1274 if (data.in_destructor)
1278 if (discardSyncRequest(q, maybeTopData()))
1281 if (q->testAttribute(Qt::WA_StaticContents)) {
1284 extra->staticContentsSize = data.crect.size();
1287 QPaintEngine *engine = q->paintEngine();
1289 // QGLWidget does not support partial updates if:
1290 // 1) The context is double buffered
1291 // 2) The context is single buffered and auto-fill background is enabled.
1292 const bool noPartialUpdateSupport = (engine && (engine->type() == QPaintEngine::OpenGL
1293 || engine->type() == QPaintEngine::OpenGL2))
1294 && (usesDoubleBufferedGLContext || q->autoFillBackground());
1295 QRegion toBePainted(noPartialUpdateSupport ? q->rect() : rgn);
1298 // No difference between update() and repaint() on the Mac.
1299 update_sys(toBePainted);
1303 toBePainted &= clipRect();
1304 clipToEffectiveMask(toBePainted);
1305 if (toBePainted.isEmpty())
1306 return; // Nothing to repaint.
1308 #ifndef QT_NO_PAINT_DEBUG
1309 bool flushed = QWidgetBackingStore::flushPaint(q, toBePainted);
1312 drawWidget(q, toBePainted, QPoint(), QWidgetPrivate::DrawAsRoot | QWidgetPrivate::DrawPaintOnScreen, 0);
1314 #ifndef QT_NO_PAINT_DEBUG
1316 QWidgetBackingStore::unflushPaint(q, toBePainted);
1319 if (q->paintingActive())
1320 qWarning("QWidget::repaint: It is dangerous to leave painters active on a widget outside of the PaintEvent");