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 if (tlw->testAttribute(Qt::WA_DontShowOnScreen) || widget->testAttribute(Qt::WA_DontShowOnScreen))
112 backingStore->flush(region, widget->windowHandle(), tlwOffset + widget->mapTo(tlw, QPoint()));
114 backingStore->flush(region, widget->windowHandle(), tlwOffset);
117 #ifndef QT_NO_PAINT_DEBUG
119 static void showYellowThing_win(QWidget *widget, const QRegion ®ion, int msec)
125 brush = CreateSolidBrush(RGB(255, 255, 0));
128 brush = CreateSolidBrush(RGB(255, 200, 55));
131 brush = CreateSolidBrush(RGB(200, 255, 55));
134 brush = CreateSolidBrush(RGB(200, 200, 0));
139 HDC hdc = widget->getDC();
141 const QVector<QRect> &rects = region.rects();
142 foreach (QRect rect, rects) {
144 SetRect(&winRect, rect.left(), rect.top(), rect.right(), rect.bottom());
145 FillRect(hdc, &winRect, brush);
148 widget->releaseDC(hdc);
153 void QWidgetBackingStore::showYellowThing(QWidget *widget, const QRegion &toBePainted, int msec, bool unclipped)
155 QRegion paintRegion = toBePainted;
156 QRect widgetRect = widget->rect();
158 if (!widget->internalWinId()) {
159 QWidget *nativeParent = widget->nativeParentWidget();
160 const QPoint offset = widget->mapTo(nativeParent, QPoint(0, 0));
161 paintRegion.translate(offset);
162 widgetRect.translate(offset);
163 widget = nativeParent;
168 showYellowThing_win(widget, paintRegion, msec);
170 //flags to fool painter
171 bool paintUnclipped = widget->testAttribute(Qt::WA_PaintUnclipped);
172 if (unclipped && !widget->d_func()->paintOnScreen())
173 widget->setAttribute(Qt::WA_PaintUnclipped);
175 const bool setFlag = !widget->testAttribute(Qt::WA_WState_InPaintEvent);
177 widget->setAttribute(Qt::WA_WState_InPaintEvent);
180 QPaintEngine *pe = widget->paintEngine();
182 pe->setSystemClip(paintRegion);
185 p.setClipRegion(paintRegion);
189 p.fillRect(widgetRect, QColor(255,255,0));
192 p.fillRect(widgetRect, QColor(255,200,55));
195 p.fillRect(widgetRect, QColor(200,255,55));
198 p.fillRect(widgetRect, QColor(200,200,0));
207 widget->setAttribute(Qt::WA_WState_InPaintEvent, false);
210 widget->setAttribute(Qt::WA_PaintUnclipped, paintUnclipped);
213 pe->setSystemClip(QRegion());
215 #if defined(Q_OS_UNIX)
216 ::usleep(1000 * msec);
221 bool QWidgetBackingStore::flushPaint(QWidget *widget, const QRegion &rgn)
227 if (widget->testAttribute(Qt::WA_WState_InPaintEvent)) {
228 static int flushPaintEvent = qgetenv("QT_FLUSH_PAINT_EVENT").toInt();
229 if (!flushPaintEvent)
231 delay = flushPaintEvent;
233 static int flushPaint = qgetenv("QT_FLUSH_PAINT").toInt();
239 QWidgetBackingStore::showYellowThing(widget, rgn, delay * 10, true);
243 void QWidgetBackingStore::unflushPaint(QWidget *widget, const QRegion &rgn)
245 if (widget->d_func()->paintOnScreen() || rgn.isEmpty())
248 QWidget *tlw = widget->window();
249 QTLWExtra *tlwExtra = tlw->d_func()->maybeTopData();
253 const QPoint offset = widget->mapTo(tlw, QPoint());
254 qt_flush(widget, rgn, tlwExtra->backingStoreTracker->store, tlw, offset);
256 #endif // QT_NO_PAINT_DEBUG
259 Moves the whole rect by (dx, dy) in widget's coordinate system.
260 Doesn't generate any updates.
262 bool QWidgetBackingStore::bltRect(const QRect &rect, int dx, int dy, QWidget *widget)
264 const QPoint pos(tlwOffset + widget->mapTo(tlw, rect.topLeft()));
265 const QRect tlwRect(QRect(pos, rect.size()));
266 if (fullUpdatePending || dirty.intersects(tlwRect))
267 return false; // We don't want to scroll junk.
268 return store->scroll(tlwRect, dx, dy);
271 void QWidgetBackingStore::releaseBuffer()
274 store->resize(QSize());
278 Prepares the window surface to paint a\ toClean region of the \a widget and
279 updates the BeginPaintInfo struct accordingly.
281 The \a toClean region might be clipped by the window surface.
283 void QWidgetBackingStore::beginPaint(QRegion &toClean, QWidget *widget, QBackingStore *backingStore,
284 BeginPaintInfo *returnInfo, bool toCleanIsInTopLevelCoordinates)
287 Q_UNUSED(toCleanIsInTopLevelCoordinates);
289 // Always flush repainted areas.
290 dirtyOnScreen += toClean;
292 #ifdef QT_NO_PAINT_DEBUG
293 backingStore->beginPaint(toClean);
295 returnInfo->wasFlushed = QWidgetBackingStore::flushPaint(tlw, toClean);
296 // Avoid deadlock with QT_FLUSH_PAINT: the server will wait for
297 // the BackingStore lock, so if we hold that, the server will
298 // never release the Communication lock that we are waiting for in
299 // sendSynchronousCommand
300 if (!returnInfo->wasFlushed)
301 backingStore->beginPaint(toClean);
304 Q_UNUSED(returnInfo);
307 void QWidgetBackingStore::endPaint(const QRegion &cleaned, QBackingStore *backingStore,
308 BeginPaintInfo *beginPaintInfo)
310 #ifndef QT_NO_PAINT_DEBUG
311 if (!beginPaintInfo->wasFlushed)
312 backingStore->endPaint();
314 QWidgetBackingStore::unflushPaint(tlw, cleaned);
316 Q_UNUSED(beginPaintInfo);
318 backingStore->endPaint();
325 Returns the region (in top-level coordinates) that needs repaint and/or flush.
327 If the widget is non-zero, only the dirty region for the widget is returned
328 and the region will be in widget coordinates.
330 QRegion QWidgetBackingStore::dirtyRegion(QWidget *widget) const
332 const bool widgetDirty = widget && widget != tlw;
333 const QRect tlwRect(topLevelRect());
334 const QRect surfaceGeometry(tlwRect.topLeft(), store->size());
335 if (fullUpdatePending || (surfaceGeometry != tlwRect && surfaceGeometry.size() != tlwRect.size())) {
337 const QRect dirtyTlwRect = QRect(QPoint(), tlwRect.size());
338 const QPoint offset(widget->mapTo(tlw, QPoint()));
339 const QRect dirtyWidgetRect(dirtyTlwRect & widget->rect().translated(offset));
340 return dirtyWidgetRect.translated(-offset);
342 return QRect(QPoint(), tlwRect.size());
345 // Calculate the region that needs repaint.
347 for (int i = 0; i < dirtyWidgets.size(); ++i) {
348 QWidget *w = dirtyWidgets.at(i);
349 if (widgetDirty && w != widget && !widget->isAncestorOf(w))
351 r += w->d_func()->dirty.translated(w->mapTo(tlw, QPoint()));
354 // Append the region that needs flush.
357 if (dirtyOnScreenWidgets) { // Only in use with native child widgets.
358 for (int i = 0; i < dirtyOnScreenWidgets->size(); ++i) {
359 QWidget *w = dirtyOnScreenWidgets->at(i);
360 if (widgetDirty && w != widget && !widget->isAncestorOf(w))
362 QWidgetPrivate *wd = w->d_func();
363 Q_ASSERT(wd->needsFlush);
364 r += wd->needsFlush->translated(w->mapTo(tlw, QPoint()));
369 // Intersect with the widget geometry and translate to its coordinates.
370 const QPoint offset(widget->mapTo(tlw, QPoint()));
371 r &= widget->rect().translated(offset);
372 r.translate(-offset);
378 Returns the static content inside the \a parent if non-zero; otherwise the static content
379 for the entire backing store is returned. The content will be clipped to \a withinClipRect
382 QRegion QWidgetBackingStore::staticContents(QWidget *parent, const QRect &withinClipRect) const
384 if (!parent && tlw->testAttribute(Qt::WA_StaticContents)) {
385 const QSize surfaceGeometry(store->size());
386 QRect surfaceRect(0, 0, surfaceGeometry.width(), surfaceGeometry.height());
387 if (!withinClipRect.isEmpty())
388 surfaceRect &= withinClipRect;
389 return QRegion(surfaceRect);
393 if (parent && parent->d_func()->children.isEmpty())
396 const bool clipToRect = !withinClipRect.isEmpty();
397 const int count = staticWidgets.count();
398 for (int i = 0; i < count; ++i) {
399 QWidget *w = staticWidgets.at(i);
400 QWidgetPrivate *wd = w->d_func();
401 if (!wd->isOpaque || !wd->extra || wd->extra->staticContentsSize.isEmpty()
402 || !w->isVisible() || (parent && !parent->isAncestorOf(w))) {
406 QRect rect(0, 0, wd->extra->staticContentsSize.width(), wd->extra->staticContentsSize.height());
407 const QPoint offset = w->mapTo(parent ? parent : tlw, QPoint());
409 rect &= withinClipRect.translated(-offset);
413 rect &= wd->clipRect();
417 QRegion visible(rect);
418 wd->clipToEffectiveMask(visible);
419 if (visible.isEmpty())
421 wd->subtractOpaqueSiblings(visible, 0, /*alsoNonOpaque=*/true);
423 visible.translate(offset);
430 static inline void sendUpdateRequest(QWidget *widget, bool updateImmediately)
435 if (updateImmediately) {
436 QEvent event(QEvent::UpdateRequest);
437 QApplication::sendEvent(widget, &event);
439 QApplication::postEvent(widget, new QEvent(QEvent::UpdateRequest), Qt::LowEventPriority);
444 Marks the region of the widget as dirty (if not already marked as dirty) and
445 posts an UpdateRequest event to the top-level widget (if not already posted).
447 If updateImmediately is true, the event is sent immediately instead of posted.
449 If invalidateBuffer is true, all widgets intersecting with the region will be dirty.
451 If the widget paints directly on screen, the event is sent to the widget
452 instead of the top-level widget, and invalidateBuffer is completely ignored.
454 ### Qt 4.6: Merge into a template function (after MSVC isn't supported anymore).
456 void QWidgetBackingStore::markDirty(const QRegion &rgn, QWidget *widget, bool updateImmediately,
457 bool invalidateBuffer)
459 Q_ASSERT(tlw->d_func()->extra);
460 Q_ASSERT(tlw->d_func()->extra->topextra);
461 Q_ASSERT(!tlw->d_func()->extra->topextra->inTopLevelResize);
462 Q_ASSERT(widget->isVisible() && widget->updatesEnabled());
463 Q_ASSERT(widget->window() == tlw);
464 Q_ASSERT(!rgn.isEmpty());
466 #ifndef QT_NO_GRAPHICSEFFECT
467 widget->d_func()->invalidateGraphicsEffectsRecursively();
468 #endif //QT_NO_GRAPHICSEFFECT
470 if (widget->d_func()->paintOnScreen()) {
471 if (widget->d_func()->dirty.isEmpty()) {
472 widget->d_func()->dirty = rgn;
473 sendUpdateRequest(widget, updateImmediately);
475 } else if (qt_region_strictContains(widget->d_func()->dirty, widget->rect())) {
476 if (updateImmediately)
477 sendUpdateRequest(widget, updateImmediately);
478 return; // Already dirty.
481 const bool eventAlreadyPosted = !widget->d_func()->dirty.isEmpty();
482 widget->d_func()->dirty += rgn;
483 if (!eventAlreadyPosted || updateImmediately)
484 sendUpdateRequest(widget, updateImmediately);
488 if (fullUpdatePending) {
489 if (updateImmediately)
490 sendUpdateRequest(tlw, updateImmediately);
494 const QPoint offset = widget->mapTo(tlw, QPoint());
495 const QRect widgetRect = widget->d_func()->effectiveRectFor(widget->rect());
496 if (qt_region_strictContains(dirty, widgetRect.translated(offset))) {
497 if (updateImmediately)
498 sendUpdateRequest(tlw, updateImmediately);
499 return; // Already dirty.
502 if (invalidateBuffer) {
503 const bool eventAlreadyPosted = !dirty.isEmpty();
504 #ifndef QT_NO_GRAPHICSEFFECT
505 if (widget->d_func()->graphicsEffect)
506 dirty += widget->d_func()->effectiveRectFor(rgn.boundingRect()).translated(offset);
508 #endif //QT_NO_GRAPHICSEFFECT
509 dirty += rgn.translated(offset);
510 if (!eventAlreadyPosted || updateImmediately)
511 sendUpdateRequest(tlw, updateImmediately);
515 if (dirtyWidgets.isEmpty()) {
516 addDirtyWidget(widget, rgn);
517 sendUpdateRequest(tlw, updateImmediately);
521 if (widget->d_func()->inDirtyList) {
522 if (!qt_region_strictContains(widget->d_func()->dirty, widgetRect)) {
523 #ifndef QT_NO_GRAPHICSEFFECT
524 if (widget->d_func()->graphicsEffect)
525 widget->d_func()->dirty += widget->d_func()->effectiveRectFor(rgn.boundingRect());
527 #endif //QT_NO_GRAPHICSEFFECT
528 widget->d_func()->dirty += rgn;
531 addDirtyWidget(widget, rgn);
534 if (updateImmediately)
535 sendUpdateRequest(tlw, updateImmediately);
539 This function is equivalent to calling markDirty(QRegion(rect), ...), but
540 is more efficient as it eliminates QRegion operations/allocations and can
541 use the rect more precisely for additional cut-offs.
543 ### Qt 4.6: Merge into a template function (after MSVC isn't supported anymore).
545 void QWidgetBackingStore::markDirty(const QRect &rect, QWidget *widget, bool updateImmediately,
546 bool invalidateBuffer)
548 Q_ASSERT(tlw->d_func()->extra);
549 Q_ASSERT(tlw->d_func()->extra->topextra);
550 Q_ASSERT(!tlw->d_func()->extra->topextra->inTopLevelResize);
551 Q_ASSERT(widget->isVisible() && widget->updatesEnabled());
552 Q_ASSERT(widget->window() == tlw);
553 Q_ASSERT(!rect.isEmpty());
555 #ifndef QT_NO_GRAPHICSEFFECT
556 widget->d_func()->invalidateGraphicsEffectsRecursively();
557 #endif //QT_NO_GRAPHICSEFFECT
559 if (widget->d_func()->paintOnScreen()) {
560 if (widget->d_func()->dirty.isEmpty()) {
561 widget->d_func()->dirty = QRegion(rect);
562 sendUpdateRequest(widget, updateImmediately);
564 } else if (qt_region_strictContains(widget->d_func()->dirty, rect)) {
565 if (updateImmediately)
566 sendUpdateRequest(widget, updateImmediately);
567 return; // Already dirty.
570 const bool eventAlreadyPosted = !widget->d_func()->dirty.isEmpty();
571 widget->d_func()->dirty += rect;
572 if (!eventAlreadyPosted || updateImmediately)
573 sendUpdateRequest(widget, updateImmediately);
577 if (fullUpdatePending) {
578 if (updateImmediately)
579 sendUpdateRequest(tlw, updateImmediately);
583 const QRect widgetRect = widget->d_func()->effectiveRectFor(rect);
584 const QRect translatedRect(widgetRect.translated(widget->mapTo(tlw, QPoint())));
585 if (qt_region_strictContains(dirty, translatedRect)) {
586 if (updateImmediately)
587 sendUpdateRequest(tlw, updateImmediately);
588 return; // Already dirty
591 if (invalidateBuffer) {
592 const bool eventAlreadyPosted = !dirty.isEmpty();
593 dirty += translatedRect;
594 if (!eventAlreadyPosted || updateImmediately)
595 sendUpdateRequest(tlw, updateImmediately);
599 if (dirtyWidgets.isEmpty()) {
600 addDirtyWidget(widget, rect);
601 sendUpdateRequest(tlw, updateImmediately);
605 if (widget->d_func()->inDirtyList) {
606 if (!qt_region_strictContains(widget->d_func()->dirty, widgetRect))
607 widget->d_func()->dirty += widgetRect;
609 addDirtyWidget(widget, rect);
612 if (updateImmediately)
613 sendUpdateRequest(tlw, updateImmediately);
617 Marks the \a region of the \a widget as dirty on screen. The \a region will be copied from
618 the backing store to the \a widget's native parent next time flush() is called.
620 Paint on screen widgets are ignored.
622 void QWidgetBackingStore::markDirtyOnScreen(const QRegion ®ion, QWidget *widget, const QPoint &topLevelOffset)
624 if (!widget || widget->d_func()->paintOnScreen() || region.isEmpty())
627 #if defined(Q_WS_MAC)
628 if (!widget->testAttribute(Qt::WA_WState_InPaintEvent))
629 dirtyOnScreen += region.translated(topLevelOffset);
635 if (!widget->testAttribute(Qt::WA_WState_InPaintEvent))
636 dirtyOnScreen += region;
641 if (!widget->internalWinId() && !widget->isWindow()) {
642 QWidget *nativeParent = widget->nativeParentWidget(); // Alien widgets with the top-level as the native parent (common case).
643 if (nativeParent == tlw) {
644 if (!widget->testAttribute(Qt::WA_WState_InPaintEvent))
645 dirtyOnScreen += region.translated(topLevelOffset);
649 // Alien widgets with native parent != tlw.
650 QWidgetPrivate *nativeParentPrivate = nativeParent->d_func();
651 if (!nativeParentPrivate->needsFlush)
652 nativeParentPrivate->needsFlush = new QRegion;
653 const QPoint nativeParentOffset = widget->mapTo(nativeParent, QPoint());
654 *nativeParentPrivate->needsFlush += region.translated(nativeParentOffset);
655 appendDirtyOnScreenWidget(nativeParent);
659 // Native child widgets.
660 QWidgetPrivate *widgetPrivate = widget->d_func();
661 if (!widgetPrivate->needsFlush)
662 widgetPrivate->needsFlush = new QRegion;
663 *widgetPrivate->needsFlush += region;
664 appendDirtyOnScreenWidget(widget);
667 void QWidgetBackingStore::removeDirtyWidget(QWidget *w)
672 dirtyWidgetsRemoveAll(w);
673 dirtyOnScreenWidgetsRemoveAll(w);
676 QWidgetPrivate *wd = w->d_func();
677 const int n = wd->children.count();
678 for (int i = 0; i < n; ++i) {
679 if (QWidget *child = qobject_cast<QWidget*>(wd->children.at(i)))
680 removeDirtyWidget(child);
684 void QWidgetBackingStore::updateLists(QWidget *cur)
689 QList<QObject*> children = cur->children();
690 for (int i = 0; i < children.size(); ++i) {
691 QWidget *child = qobject_cast<QWidget*>(children.at(i));
698 if (cur->testAttribute(Qt::WA_StaticContents))
699 addStaticWidget(cur);
702 QWidgetBackingStore::QWidgetBackingStore(QWidget *topLevel)
703 : tlw(topLevel), dirtyOnScreenWidgets(0), fullUpdatePending(0)
705 store = tlw->backingStore();
708 // Ensure all existing subsurfaces and static widgets are added to their respective lists.
709 updateLists(topLevel);
712 QWidgetBackingStore::~QWidgetBackingStore()
714 for (int c = 0; c < dirtyWidgets.size(); ++c) {
715 resetWidget(dirtyWidgets.at(c));
718 delete dirtyOnScreenWidgets;
719 dirtyOnScreenWidgets = 0;
722 //parent's coordinates; move whole rect; update parent and widget
723 //assume the screen blt has already been done, so we don't need to refresh that part
724 void QWidgetPrivate::moveRect(const QRect &rect, int dx, int dy)
727 if (!q->isVisible() || (dx == 0 && dy == 0))
730 QWidget *tlw = q->window();
731 QTLWExtra* x = tlw->d_func()->topData();
732 if (x->inTopLevelResize)
735 static int accelEnv = -1;
736 if (accelEnv == -1) {
737 accelEnv = qgetenv("QT_NO_FAST_MOVE").toInt() == 0;
740 QWidget *pw = q->parentWidget();
741 QPoint toplevelOffset = pw->mapTo(tlw, QPoint());
742 QWidgetPrivate *pd = pw->d_func();
743 QRect clipR(pd->clipRect());
744 const QRect newRect(rect.translated(dx, dy));
745 QRect destRect = rect.intersected(clipR);
746 if (destRect.isValid())
747 destRect = destRect.translated(dx, dy).intersected(clipR);
748 const QRect sourceRect(destRect.translated(-dx, -dy));
749 const QRect parentRect(rect & clipR);
751 bool accelerateMove = accelEnv && isOpaque
752 #ifndef QT_NO_GRAPHICSVIEW
753 // No accelerate move for proxy widgets.
754 && !tlw->d_func()->extra->proxyWidget
756 && !isOverlapped(sourceRect) && !isOverlapped(destRect);
758 if (!accelerateMove) {
759 QRegion parentR(effectiveRectFor(parentRect));
760 if (!extra || !extra->hasMask) {
763 // invalidateBuffer() excludes anything outside the mask
764 parentR += newRect & clipR;
766 pd->invalidateBuffer(parentR);
767 invalidateBuffer((newRect & clipR).translated(-data.crect.topLeft()));
770 QWidgetBackingStore *wbs = x->backingStoreTracker.data();
771 QRegion childExpose(newRect & clipR);
773 if (sourceRect.isValid() && wbs->bltRect(sourceRect, dx, dy, pw))
774 childExpose -= destRect;
776 if (!pw->updatesEnabled())
779 const bool childUpdatesEnabled = q->updatesEnabled();
780 if (childUpdatesEnabled && !childExpose.isEmpty()) {
781 childExpose.translate(-data.crect.topLeft());
782 wbs->markDirty(childExpose, q);
786 QRegion parentExpose(parentRect);
787 parentExpose -= newRect;
788 if (extra && extra->hasMask)
789 parentExpose += QRegion(newRect) - extra->mask.translated(data.crect.topLeft());
791 if (!parentExpose.isEmpty()) {
792 wbs->markDirty(parentExpose, pw);
796 if (childUpdatesEnabled) {
797 QRegion needsFlush(sourceRect);
798 needsFlush += destRect;
799 wbs->markDirtyOnScreen(needsFlush, pw, toplevelOffset);
804 //widget's coordinates; scroll within rect; only update widget
805 void QWidgetPrivate::scrollRect(const QRect &rect, int dx, int dy)
808 QWidget *tlw = q->window();
809 QTLWExtra* x = tlw->d_func()->topData();
810 if (x->inTopLevelResize)
813 QWidgetBackingStore *wbs = x->backingStoreTracker.data();
817 static int accelEnv = -1;
818 if (accelEnv == -1) {
819 accelEnv = qgetenv("QT_NO_FAST_SCROLL").toInt() == 0;
822 QRect scrollRect = rect & clipRect();
823 bool overlapped = false;
824 bool accelerateScroll = accelEnv && isOpaque
825 && !(overlapped = isOverlapped(scrollRect.translated(data.crect.topLeft())));
827 if (!accelerateScroll) {
829 QRegion region(scrollRect);
830 subtractOpaqueSiblings(region);
831 invalidateBuffer(region);
833 invalidateBuffer(scrollRect);
836 const QPoint toplevelOffset = q->mapTo(tlw, QPoint());
837 const QRect destRect = scrollRect.translated(dx, dy) & scrollRect;
838 const QRect sourceRect = destRect.translated(-dx, -dy);
840 QRegion childExpose(scrollRect);
841 if (sourceRect.isValid()) {
842 if (wbs->bltRect(sourceRect, dx, dy, q))
843 childExpose -= destRect;
847 if (rect == q->rect()) {
848 dirty.translate(dx, dy);
850 QRegion dirtyScrollRegion = dirty.intersected(scrollRect);
851 if (!dirtyScrollRegion.isEmpty()) {
852 dirty -= dirtyScrollRegion;
853 dirtyScrollRegion.translate(dx, dy);
854 dirty += dirtyScrollRegion;
859 if (!q->updatesEnabled())
862 if (!childExpose.isEmpty()) {
863 wbs->markDirty(childExpose, q);
867 // Instead of using native scroll-on-screen, we copy from
868 // backingstore, giving only one screen update for each
869 // scroll, and a solid appearance
870 wbs->markDirtyOnScreen(destRect, q, toplevelOffset);
874 static inline bool discardSyncRequest(QWidget *tlw, QTLWExtra *tlwExtra)
876 if (!tlw || !tlwExtra || !tlw->testAttribute(Qt::WA_Mapped) || !tlw->isVisible())
883 Synchronizes the \a exposedRegion of the \a exposedWidget with the backing store.
885 If there's nothing to repaint, the area is flushed and painting does not occur;
886 otherwise the area is marked as dirty on screen and will be flushed right after
887 we are done with all painting.
889 void QWidgetBackingStore::sync(QWidget *exposedWidget, const QRegion &exposedRegion)
891 QTLWExtra *tlwExtra = tlw->d_func()->maybeTopData();
892 if (discardSyncRequest(tlw, tlwExtra) || tlwExtra->inTopLevelResize)
895 if (!exposedWidget || !exposedWidget->internalWinId() || !exposedWidget->isVisible()
896 || !exposedWidget->updatesEnabled() || exposedRegion.isEmpty()) {
900 // Nothing to repaint.
902 qt_flush(exposedWidget, exposedRegion, store, tlw, tlwOffset);
906 if (exposedWidget != tlw)
907 markDirtyOnScreen(exposedRegion, exposedWidget, exposedWidget->mapTo(tlw, QPoint()));
909 markDirtyOnScreen(exposedRegion, exposedWidget, QPoint());
914 Synchronizes the backing store, i.e. dirty areas are repainted and flushed.
916 void QWidgetBackingStore::sync()
918 QTLWExtra *tlwExtra = tlw->d_func()->maybeTopData();
919 if (discardSyncRequest(tlw, tlwExtra)) {
920 // If the top-level is minimized, it's not visible on the screen so we can delay the
921 // update until it's shown again. In order to do that we must keep the dirty states.
922 // These will be cleared when we receive the first expose after showNormal().
923 // However, if the widget is not visible (isVisible() returns false), everything will
924 // be invalidated once the widget is shown again, so clear all dirty states.
925 if (!tlw->isVisible()) {
927 for (int i = 0; i < dirtyWidgets.size(); ++i)
928 resetWidget(dirtyWidgets.at(i));
929 dirtyWidgets.clear();
930 fullUpdatePending = false;
935 const bool updatesDisabled = !tlw->updatesEnabled();
936 bool repaintAllWidgets = false;
938 const bool inTopLevelResize = tlwExtra->inTopLevelResize;
939 const QRect tlwRect(topLevelRect());
940 const QRect surfaceGeometry(tlwRect.topLeft(), store->size());
941 if ((fullUpdatePending || inTopLevelResize || surfaceGeometry.size() != tlwRect.size()) && !updatesDisabled) {
942 if (hasStaticContents()) {
943 // Repaint existing dirty area and newly visible area.
944 const QRect clipRect(0, 0, surfaceGeometry.width(), surfaceGeometry.height());
945 const QRegion staticRegion(staticContents(0, clipRect));
946 QRegion newVisible(0, 0, tlwRect.width(), tlwRect.height());
947 newVisible -= staticRegion;
949 store->setStaticContents(staticRegion);
951 // Repaint everything.
952 dirty = QRegion(0, 0, tlwRect.width(), tlwRect.height());
953 for (int i = 0; i < dirtyWidgets.size(); ++i)
954 resetWidget(dirtyWidgets.at(i));
955 dirtyWidgets.clear();
956 repaintAllWidgets = true;
960 if (inTopLevelResize || surfaceGeometry.size() != tlwRect.size())
961 store->resize(tlwRect.size());
966 // Contains everything that needs repaint.
967 QRegion toClean(dirty);
969 // Loop through all update() widgets and remove them from the list before they are
970 // painted (in case someone calls update() in paintEvent). If the widget is opaque
971 // and does not have transparent overlapping siblings, append it to the
972 // opaqueNonOverlappedWidgets list and paint it directly without composition.
973 QVarLengthArray<QWidget *, 32> opaqueNonOverlappedWidgets;
974 for (int i = 0; i < dirtyWidgets.size(); ++i) {
975 QWidget *w = dirtyWidgets.at(i);
976 QWidgetPrivate *wd = w->d_func();
977 if (wd->data.in_destructor)
980 // Clip with mask() and clipRect().
981 wd->dirty &= wd->clipRect();
982 wd->clipToEffectiveMask(wd->dirty);
984 // Subtract opaque siblings and children.
985 bool hasDirtySiblingsAbove = false;
986 // We know for sure that the widget isn't overlapped if 'isMoved' is true.
988 wd->subtractOpaqueSiblings(wd->dirty, &hasDirtySiblingsAbove);
989 // Scrolled and moved widgets must draw all children.
990 if (!wd->isScrolled && !wd->isMoved)
991 wd->subtractOpaqueChildren(wd->dirty, w->rect());
993 if (wd->dirty.isEmpty()) {
998 const QRegion widgetDirty(w != tlw ? wd->dirty.translated(w->mapTo(tlw, QPoint()))
1000 toClean += widgetDirty;
1002 #ifndef QT_NO_GRAPHICSVIEW
1003 if (tlw->d_func()->extra->proxyWidget) {
1009 if (!hasDirtySiblingsAbove && wd->isOpaque && !dirty.intersects(widgetDirty.boundingRect())) {
1010 opaqueNonOverlappedWidgets.append(w);
1013 dirty += widgetDirty;
1016 dirtyWidgets.clear();
1018 fullUpdatePending = false;
1020 if (toClean.isEmpty()) {
1021 // Nothing to repaint. However, we might have newly exposed areas on the
1022 // screen if this function was called from sync(QWidget *, QRegion)), so
1023 // we have to make sure those are flushed.
1028 #ifndef QT_NO_GRAPHICSVIEW
1029 if (tlw->d_func()->extra->proxyWidget) {
1030 updateStaticContentsSize();
1032 const QVector<QRect> rects(toClean.rects());
1033 for (int i = 0; i < rects.size(); ++i)
1034 tlw->d_func()->extra->proxyWidget->update(rects.at(i));
1039 BeginPaintInfo beginPaintInfo;
1040 beginPaint(toClean, tlw, store, &beginPaintInfo);
1041 if (beginPaintInfo.nothingToPaint) {
1042 for (int i = 0; i < opaqueNonOverlappedWidgets.size(); ++i)
1043 resetWidget(opaqueNonOverlappedWidgets[i]);
1048 // Must do this before sending any paint events because
1049 // the size may change in the paint event.
1050 updateStaticContentsSize();
1051 const QRegion dirtyCopy(dirty);
1054 // Paint opaque non overlapped widgets.
1055 for (int i = 0; i < opaqueNonOverlappedWidgets.size(); ++i) {
1056 QWidget *w = opaqueNonOverlappedWidgets[i];
1057 QWidgetPrivate *wd = w->d_func();
1059 int flags = QWidgetPrivate::DrawRecursive;
1060 // Scrolled and moved widgets must draw all children.
1061 if (!wd->isScrolled && !wd->isMoved)
1062 flags |= QWidgetPrivate::DontDrawOpaqueChildren;
1064 flags |= QWidgetPrivate::DrawAsRoot;
1066 QRegion toBePainted(wd->dirty);
1069 QPoint offset(tlwOffset);
1071 offset += w->mapTo(tlw, QPoint());
1072 wd->drawWidget(store->paintDevice(), toBePainted, offset, flags, 0, this);
1075 // Paint the rest with composition.
1076 if (repaintAllWidgets || !dirtyCopy.isEmpty()) {
1077 const int flags = QWidgetPrivate::DrawAsRoot | QWidgetPrivate::DrawRecursive;
1078 tlw->d_func()->drawWidget(store->paintDevice(), dirtyCopy, tlwOffset, flags, 0, this);
1081 endPaint(toClean, store, &beginPaintInfo);
1085 Flushes the contents of the backing store into the top-level widget.
1086 If the \a widget is non-zero, the content is flushed to the \a widget.
1087 If the \a surface is non-zero, the content of the \a surface is flushed.
1089 void QWidgetBackingStore::flush(QWidget *widget)
1091 if (!dirtyOnScreen.isEmpty()) {
1092 QWidget *target = widget ? widget : tlw;
1093 qt_flush(target, dirtyOnScreen, store, tlw, tlwOffset);
1094 dirtyOnScreen = QRegion();
1097 if (!dirtyOnScreenWidgets || dirtyOnScreenWidgets->isEmpty())
1100 for (int i = 0; i < dirtyOnScreenWidgets->size(); ++i) {
1101 QWidget *w = dirtyOnScreenWidgets->at(i);
1102 QWidgetPrivate *wd = w->d_func();
1103 Q_ASSERT(wd->needsFlush);
1104 qt_flush(w, *wd->needsFlush, store, tlw, tlwOffset);
1105 *wd->needsFlush = QRegion();
1107 dirtyOnScreenWidgets->clear();
1110 static inline bool discardInvalidateBufferRequest(QWidget *widget, QTLWExtra *tlwExtra)
1113 if (QApplication::closingDown())
1116 if (!tlwExtra || tlwExtra->inTopLevelResize || !tlwExtra->backingStore)
1119 if (!widget->isVisible() || !widget->updatesEnabled())
1126 Invalidates the buffer when the widget is resized.
1127 Static areas are never invalidated unless absolutely needed.
1129 void QWidgetPrivate::invalidateBuffer_resizeHelper(const QPoint &oldPos, const QSize &oldSize)
1132 Q_ASSERT(!q->isWindow());
1133 Q_ASSERT(q->parentWidget());
1135 const bool staticContents = q->testAttribute(Qt::WA_StaticContents);
1136 const bool sizeDecreased = (data.crect.width() < oldSize.width())
1137 || (data.crect.height() < oldSize.height());
1139 const QPoint offset(data.crect.x() - oldPos.x(), data.crect.y() - oldPos.y());
1140 const bool parentAreaExposed = !offset.isNull() || sizeDecreased;
1141 const QRect newWidgetRect(q->rect());
1142 const QRect oldWidgetRect(0, 0, oldSize.width(), oldSize.height());
1144 if (!staticContents || graphicsEffect) {
1145 QRegion staticChildren;
1146 QWidgetBackingStore *bs = 0;
1147 if (offset.isNull() && (bs = maybeBackingStore()))
1148 staticChildren = bs->staticContents(q, oldWidgetRect);
1149 const bool hasStaticChildren = !staticChildren.isEmpty();
1151 if (hasStaticChildren) {
1152 QRegion dirty(newWidgetRect);
1153 dirty -= staticChildren;
1154 invalidateBuffer(dirty);
1156 // Entire widget needs repaint.
1157 invalidateBuffer(newWidgetRect);
1160 if (!parentAreaExposed)
1163 // Invalidate newly exposed area of the parent.
1164 if (!graphicsEffect && extra && extra->hasMask) {
1165 QRegion parentExpose(extra->mask.translated(oldPos));
1166 parentExpose &= QRect(oldPos, oldSize);
1167 if (hasStaticChildren)
1168 parentExpose -= data.crect; // Offset is unchanged, safe to do this.
1169 q->parentWidget()->d_func()->invalidateBuffer(parentExpose);
1171 if (hasStaticChildren && !graphicsEffect) {
1172 QRegion parentExpose(QRect(oldPos, oldSize));
1173 parentExpose -= data.crect; // Offset is unchanged, safe to do this.
1174 q->parentWidget()->d_func()->invalidateBuffer(parentExpose);
1176 q->parentWidget()->d_func()->invalidateBuffer(effectiveRectFor(QRect(oldPos, oldSize)));
1182 // Move static content to its new position.
1183 if (!offset.isNull()) {
1184 if (sizeDecreased) {
1185 const QSize minSize(qMin(oldSize.width(), data.crect.width()),
1186 qMin(oldSize.height(), data.crect.height()));
1187 moveRect(QRect(oldPos, minSize), offset.x(), offset.y());
1189 moveRect(QRect(oldPos, oldSize), offset.x(), offset.y());
1193 // Invalidate newly visible area of the widget.
1194 if (!sizeDecreased || !oldWidgetRect.contains(newWidgetRect)) {
1195 QRegion newVisible(newWidgetRect);
1196 newVisible -= oldWidgetRect;
1197 invalidateBuffer(newVisible);
1200 if (!parentAreaExposed)
1203 // Invalidate newly exposed area of the parent.
1204 const QRect oldRect(oldPos, oldSize);
1205 if (extra && extra->hasMask) {
1206 QRegion parentExpose(oldRect);
1207 parentExpose &= extra->mask.translated(oldPos);
1208 parentExpose -= (extra->mask.translated(data.crect.topLeft()) & data.crect);
1209 q->parentWidget()->d_func()->invalidateBuffer(parentExpose);
1211 QRegion parentExpose(oldRect);
1212 parentExpose -= data.crect;
1213 q->parentWidget()->d_func()->invalidateBuffer(parentExpose);
1218 Invalidates the \a rgn (in widget's coordinates) of the backing store, i.e.
1219 all widgets intersecting with the region will be repainted when the backing store
1222 ### Qt 4.6: Merge into a template function (after MSVC isn't supported anymore).
1224 void QWidgetPrivate::invalidateBuffer(const QRegion &rgn)
1228 QTLWExtra *tlwExtra = q->window()->d_func()->maybeTopData();
1229 if (discardInvalidateBufferRequest(q, tlwExtra) || rgn.isEmpty())
1234 if (!graphicsEffect && extra && extra->hasMask)
1235 wrgn &= extra->mask;
1239 tlwExtra->backingStoreTracker->markDirty(wrgn, q, false, true);
1243 This function is equivalent to calling invalidateBuffer(QRegion(rect), ...), but
1244 is more efficient as it eliminates QRegion operations/allocations and can
1245 use the rect more precisely for additional cut-offs.
1247 ### Qt 4.6: Merge into a template function (after MSVC isn't supported anymore).
1249 void QWidgetPrivate::invalidateBuffer(const QRect &rect)
1253 QTLWExtra *tlwExtra = q->window()->d_func()->maybeTopData();
1254 if (discardInvalidateBufferRequest(q, tlwExtra) || rect.isEmpty())
1258 wRect &= clipRect();
1259 if (wRect.isEmpty())
1262 if (graphicsEffect || !extra || !extra->hasMask) {
1263 tlwExtra->backingStoreTracker->markDirty(wRect, q, false, true);
1267 QRegion wRgn(extra->mask);
1272 tlwExtra->backingStoreTracker->markDirty(wRgn, q, false, true);
1275 void QWidgetPrivate::repaint_sys(const QRegion &rgn)
1277 if (data.in_destructor)
1281 if (discardSyncRequest(q, maybeTopData()))
1284 if (q->testAttribute(Qt::WA_StaticContents)) {
1287 extra->staticContentsSize = data.crect.size();
1290 QPaintEngine *engine = q->paintEngine();
1292 // QGLWidget does not support partial updates if:
1293 // 1) The context is double buffered
1294 // 2) The context is single buffered and auto-fill background is enabled.
1295 const bool noPartialUpdateSupport = (engine && (engine->type() == QPaintEngine::OpenGL
1296 || engine->type() == QPaintEngine::OpenGL2))
1297 && (usesDoubleBufferedGLContext || q->autoFillBackground());
1298 QRegion toBePainted(noPartialUpdateSupport ? q->rect() : rgn);
1301 // No difference between update() and repaint() on the Mac.
1302 update_sys(toBePainted);
1306 toBePainted &= clipRect();
1307 clipToEffectiveMask(toBePainted);
1308 if (toBePainted.isEmpty())
1309 return; // Nothing to repaint.
1311 #ifndef QT_NO_PAINT_DEBUG
1312 bool flushed = QWidgetBackingStore::flushPaint(q, toBePainted);
1315 drawWidget(q, toBePainted, QPoint(), QWidgetPrivate::DrawAsRoot | QWidgetPrivate::DrawPaintOnScreen, 0);
1317 #ifndef QT_NO_PAINT_DEBUG
1319 QWidgetBackingStore::unflushPaint(q, toBePainted);
1322 if (q->paintingActive())
1323 qWarning("QWidget::repaint: It is dangerous to leave painters active on a widget outside of the PaintEvent");