source: trunk/src/gui/painting/qbackingstore.cpp@ 776

Last change on this file since 776 was 769, checked in by Dmitry A. Kuminov, 15 years ago

trunk: Merged in qt 4.6.3 sources from branches/vendor/nokia/qt.

File size: 54.0 KB
Line 
1/****************************************************************************
2**
3** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
4** All rights reserved.
5** Contact: Nokia Corporation ([email protected])
6**
7** This file is part of the QtGui module of the Qt Toolkit.
8**
9** $QT_BEGIN_LICENSE:LGPL$
10** Commercial Usage
11** Licensees holding valid Qt Commercial licenses may use this file in
12** accordance with the Qt Commercial License Agreement provided with the
13** Software or, alternatively, in accordance with the terms contained in
14** a written agreement between you and Nokia.
15**
16** GNU Lesser General Public License Usage
17** Alternatively, this file may be used under the terms of the GNU Lesser
18** General Public License version 2.1 as published by the Free Software
19** Foundation and appearing in the file LICENSE.LGPL included in the
20** packaging of this file. Please review the following information to
21** ensure the GNU Lesser General Public License version 2.1 requirements
22** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
23**
24** In addition, as a special exception, Nokia gives you certain additional
25** rights. These rights are described in the Nokia Qt LGPL Exception
26** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
27**
28** GNU General Public License Usage
29** Alternatively, this file may be used under the terms of the GNU
30** General Public License version 3.0 as published by the Free Software
31** Foundation and appearing in the file LICENSE.GPL included in the
32** packaging of this file. Please review the following information to
33** ensure the GNU General Public License version 3.0 requirements will be
34** met: http://www.gnu.org/copyleft/gpl.html.
35**
36** If you have questions regarding the use of this file, please contact
37** Nokia at [email protected].
38** $QT_END_LICENSE$
39**
40****************************************************************************/
41
42
43#include "qplatformdefs.h"
44
45#include "qbackingstore_p.h"
46
47#include <QtCore/qglobal.h>
48#include <QtCore/qdebug.h>
49#include <QtCore/qvarlengtharray.h>
50#include <QtGui/qevent.h>
51#include <QtGui/qapplication.h>
52#include <QtGui/qpaintengine.h>
53#include <QtGui/qgraphicsproxywidget.h>
54
55#include <private/qwidget_p.h>
56#include <private/qwindowsurface_raster_p.h>
57#include <private/qapplication_p.h>
58#include <private/qpaintengine_raster_p.h>
59#include <private/qgraphicseffect_p.h>
60
61#include "qgraphicssystem_p.h"
62
63#ifdef Q_WS_QWS
64#include <QtGui/qwsmanager_qws.h>
65#include <private/qwsmanager_p.h>
66#endif
67
68QT_BEGIN_NAMESPACE
69
70extern QRegion qt_dirtyRegion(QWidget *);
71
72/*
73 A version of QRect::intersects() that does not normalize the rects.
74*/
75static inline bool qRectIntersects(const QRect &r1, const QRect &r2)
76{
77 return (qMax(r1.left(), r2.left()) <= qMin(r1.right(), r2.right())
78 && qMax(r1.top(), r2.top()) <= qMin(r1.bottom(), r2.bottom()));
79}
80
81/**
82 * Flushes the contents of the \a windowSurface into the screen area of \a widget.
83 * \a tlwOffset is the position of the top level widget relative to the window surface.
84 * \a region is the region to be updated in \a widget coordinates.
85 */
86static inline void qt_flush(QWidget *widget, const QRegion &region, QWindowSurface *windowSurface,
87 QWidget *tlw, const QPoint &tlwOffset)
88{
89 Q_ASSERT(widget);
90 Q_ASSERT(!region.isEmpty());
91 Q_ASSERT(windowSurface);
92 Q_ASSERT(tlw);
93
94#if !defined(QT_NO_PAINT_DEBUG) && !defined(Q_WS_QWS)
95 // QWS does flush update in QWindowSurface::flush (because it needs to lock the surface etc).
96 static int flushUpdate = qgetenv("QT_FLUSH_UPDATE").toInt();
97 if (flushUpdate > 0)
98 QWidgetBackingStore::showYellowThing(widget, region, flushUpdate * 10, false);
99#endif
100
101 if (widget != tlw)
102 windowSurface->flush(widget, region, tlwOffset + widget->mapTo(tlw, QPoint()));
103 else
104 windowSurface->flush(widget, region, tlwOffset);
105}
106
107#ifndef QT_NO_PAINT_DEBUG
108#ifdef Q_WS_WIN
109static void showYellowThing_win(QWidget *widget, const QRegion &region, int msec)
110{
111 HBRUSH brush;
112 static int i = 0;
113 switch (i) {
114 case 0:
115 brush = CreateSolidBrush(RGB(255, 255, 0));
116 break;
117 case 1:
118 brush = CreateSolidBrush(RGB(255, 200, 55));
119 break;
120 case 2:
121 brush = CreateSolidBrush(RGB(200, 255, 55));
122 break;
123 case 3:
124 brush = CreateSolidBrush(RGB(200, 200, 0));
125 break;
126 }
127 i = (i + 1) & 3;
128
129 HDC hdc = widget->getDC();
130
131 const QVector<QRect> &rects = region.rects();
132 foreach (QRect rect, rects) {
133 RECT winRect;
134 SetRect(&winRect, rect.left(), rect.top(), rect.right(), rect.bottom());
135 FillRect(hdc, &winRect, brush);
136 }
137
138 widget->releaseDC(hdc);
139 ::Sleep(msec);
140}
141#endif
142
143void QWidgetBackingStore::showYellowThing(QWidget *widget, const QRegion &toBePainted, int msec, bool unclipped)
144{
145#ifdef Q_WS_QWS
146 Q_UNUSED(widget);
147 Q_UNUSED(unclipped);
148 static QWSYellowSurface surface(true);
149 surface.setDelay(msec);
150 surface.flush(widget, toBePainted, QPoint());
151#else
152 QRegion paintRegion = toBePainted;
153 QRect widgetRect = widget->rect();
154
155 if (!widget->internalWinId()) {
156 QWidget *nativeParent = widget->nativeParentWidget();
157 const QPoint offset = widget->mapTo(nativeParent, QPoint(0, 0));
158 paintRegion.translate(offset);
159 widgetRect.translate(offset);
160 widget = nativeParent;
161 }
162
163#ifdef Q_WS_WIN
164 Q_UNUSED(unclipped);
165 showYellowThing_win(widget, paintRegion, msec);
166#else
167 //flags to fool painter
168 bool paintUnclipped = widget->testAttribute(Qt::WA_PaintUnclipped);
169 if (unclipped && !widget->d_func()->paintOnScreen())
170 widget->setAttribute(Qt::WA_PaintUnclipped);
171
172 const bool setFlag = !widget->testAttribute(Qt::WA_WState_InPaintEvent);
173 if (setFlag)
174 widget->setAttribute(Qt::WA_WState_InPaintEvent);
175
176 //setup the engine
177 QPaintEngine *pe = widget->paintEngine();
178 if (pe) {
179 pe->setSystemClip(paintRegion);
180 {
181 QPainter p(widget);
182 p.setClipRegion(paintRegion);
183 static int i = 0;
184 switch (i) {
185 case 0:
186 p.fillRect(widgetRect, QColor(255,255,0));
187 break;
188 case 1:
189 p.fillRect(widgetRect, QColor(255,200,55));
190 break;
191 case 2:
192 p.fillRect(widgetRect, QColor(200,255,55));
193 break;
194 case 3:
195 p.fillRect(widgetRect, QColor(200,200,0));
196 break;
197 }
198 i = (i+1) & 3;
199 p.end();
200 }
201 }
202
203 if (setFlag)
204 widget->setAttribute(Qt::WA_WState_InPaintEvent, false);
205
206 //restore
207 widget->setAttribute(Qt::WA_PaintUnclipped, paintUnclipped);
208
209 if (pe)
210 pe->setSystemClip(QRegion());
211
212 QApplication::syncX();
213
214#if defined(Q_OS_UNIX)
215 ::usleep(1000 * msec);
216#endif
217#endif // Q_WS_WIN
218#endif // Q_WS_QWS
219}
220
221bool QWidgetBackingStore::flushPaint(QWidget *widget, const QRegion &rgn)
222{
223 if (!widget)
224 return false;
225
226 int delay = 0;
227 if (widget->testAttribute(Qt::WA_WState_InPaintEvent)) {
228 static int flushPaintEvent = qgetenv("QT_FLUSH_PAINT_EVENT").toInt();
229 if (!flushPaintEvent)
230 return false;
231 delay = flushPaintEvent;
232 } else {
233 static int flushPaint = qgetenv("QT_FLUSH_PAINT").toInt();
234 if (!flushPaint)
235 return false;
236 delay = flushPaint;
237 }
238
239 QWidgetBackingStore::showYellowThing(widget, rgn, delay * 10, true);
240 return true;
241}
242
243void QWidgetBackingStore::unflushPaint(QWidget *widget, const QRegion &rgn)
244{
245 if (widget->d_func()->paintOnScreen() || rgn.isEmpty())
246 return;
247
248 QWidget *tlw = widget->window();
249 QTLWExtra *tlwExtra = tlw->d_func()->maybeTopData();
250 if (!tlwExtra)
251 return;
252
253 const QPoint offset = widget->mapTo(tlw, QPoint());
254 qt_flush(widget, rgn, tlwExtra->backingStore->windowSurface, tlw, offset);
255}
256#endif // QT_NO_PAINT_DEBUG
257
258/*
259 Moves the whole rect by (dx, dy) in widget's coordinate system.
260 Doesn't generate any updates.
261*/
262bool QWidgetBackingStore::bltRect(const QRect &rect, int dx, int dy, QWidget *widget)
263{
264 const QPoint pos(tlwOffset + widget->mapTo(tlw, rect.topLeft()));
265 const QRect tlwRect(QRect(pos, rect.size()));
266 if (dirty.intersects(tlwRect))
267 return false; // We don't want to scroll junk.
268 return windowSurface->scroll(tlwRect, dx, dy);
269}
270
271void QWidgetBackingStore::releaseBuffer()
272{
273 if (windowSurface)
274 windowSurface->setGeometry(QRect());
275#ifdef Q_BACKINGSTORE_SUBSURFACES
276 for (int i = 0; i < subSurfaces.size(); ++i)
277 subSurfaces.at(i)->setGeometry(QRect());
278#endif
279}
280
281/*!
282 Prepares the window surface to paint a\ toClean region of the \a widget and
283 updates the BeginPaintInfo struct accordingly.
284
285 The \a toClean region might be clipped by the window surface.
286*/
287void QWidgetBackingStore::beginPaint(QRegion &toClean, QWidget *widget, QWindowSurface *windowSurface,
288 BeginPaintInfo *returnInfo, bool toCleanIsInTopLevelCoordinates)
289{
290#ifdef Q_WS_QWS
291 QWSWindowSurface *surface = static_cast<QWSWindowSurface *>(windowSurface);
292 QWidget *surfaceWidget = surface->window();
293
294 if (!surface->isValid()) {
295 // this looks strange but it really just releases the surface
296 surface->releaseSurface();
297 // the old window surface is deleted in setWindowSurface, which is
298 // called from QWindowSurface constructor.
299 windowSurface = tlw->d_func()->createDefaultWindowSurface();
300 surface = static_cast<QWSWindowSurface *>(windowSurface);
301 // createDefaultWindowSurface() will set topdata->windowSurface on the
302 // widget to zero. However, if this is a sub-surface, it should point
303 // to the widget's sub windowSurface, so we set that here:
304 if (!surfaceWidget->isWindow())
305 surfaceWidget->d_func()->topData()->windowSurface = windowSurface;
306 surface->setGeometry(topLevelRect());
307 returnInfo->windowSurfaceRecreated = true;
308 }
309
310 const QRegion toCleanUnclipped(toClean);
311
312 if (surfaceWidget->isWindow())
313 tlwOffset = surface->painterOffset();
314#ifdef Q_BACKINGSTORE_SUBSURFACES
315 else if (toCleanIsInTopLevelCoordinates)
316 toClean &= surface->clipRegion().translated(surfaceWidget->mapTo(tlw, QPoint()));
317 if (!toCleanIsInTopLevelCoordinates && windowSurface == this->windowSurface)
318 toClean &= surface->clipRegion().translated(-widget->mapTo(surfaceWidget, QPoint()));
319#else
320 toClean &= surface->clipRegion();
321#endif
322
323 if (toClean.isEmpty()) {
324 if (surfaceWidget->isWindow()) {
325 dirtyFromPreviousSync += toCleanUnclipped;
326 hasDirtyFromPreviousSync = true;
327 }
328
329 returnInfo->nothingToPaint = true;
330 // Nothing to repaint. However, we might have newly exposed areas on the
331 // screen, so we have to make sure those are flushed.
332 flush();
333 return;
334 }
335
336 if (surfaceWidget->isWindow()) {
337 if (toCleanUnclipped != toClean) {
338 dirtyFromPreviousSync += (toCleanUnclipped - surface->clipRegion());
339 hasDirtyFromPreviousSync = true;
340 }
341 if (hasDirtyFromPreviousSync) {
342 dirtyFromPreviousSync -= toClean;
343 hasDirtyFromPreviousSync = !dirtyFromPreviousSync.isEmpty();
344 }
345 }
346
347#endif // Q_WS_QWS
348
349 Q_UNUSED(widget);
350 Q_UNUSED(toCleanIsInTopLevelCoordinates);
351
352 // Always flush repainted areas.
353 dirtyOnScreen += toClean;
354
355#ifdef Q_WS_QWS
356 toClean.translate(tlwOffset);
357#endif
358
359#ifdef QT_NO_PAINT_DEBUG
360 windowSurface->beginPaint(toClean);
361#else
362 returnInfo->wasFlushed = QWidgetBackingStore::flushPaint(tlw, toClean);
363 // Avoid deadlock with QT_FLUSH_PAINT: the server will wait for
364 // the BackingStore lock, so if we hold that, the server will
365 // never release the Communication lock that we are waiting for in
366 // sendSynchronousCommand
367 if (!returnInfo->wasFlushed)
368 windowSurface->beginPaint(toClean);
369#endif
370
371 Q_UNUSED(returnInfo);
372}
373
374void QWidgetBackingStore::endPaint(const QRegion &cleaned, QWindowSurface *windowSurface,
375 BeginPaintInfo *beginPaintInfo)
376{
377#ifndef QT_NO_PAINT_DEBUG
378 if (!beginPaintInfo->wasFlushed)
379 windowSurface->endPaint(cleaned);
380 else
381 QWidgetBackingStore::unflushPaint(tlw, cleaned);
382#else
383 Q_UNUSED(beginPaintInfo);
384 windowSurface->endPaint(cleaned);
385#endif
386
387#ifdef Q_BACKINGSTORE_SUBSURFACES
388 flush(static_cast<QWSWindowSurface *>(windowSurface)->window(), windowSurface);
389#else
390 flush();
391#endif
392}
393
394/*!
395 Returns the region (in top-level coordinates) that needs repaint and/or flush.
396
397 If the widget is non-zero, only the dirty region for the widget is returned
398 and the region will be in widget coordinates.
399*/
400QRegion QWidgetBackingStore::dirtyRegion(QWidget *widget) const
401{
402 const bool widgetDirty = widget && widget != tlw;
403 const QRect tlwRect(topLevelRect());
404 const QRect surfaceGeometry(windowSurface->geometry());
405 if (surfaceGeometry != tlwRect && surfaceGeometry.size() != tlwRect.size()) {
406 if (widgetDirty) {
407 const QRect dirtyTlwRect = QRect(QPoint(), tlwRect.size());
408 const QPoint offset(widget->mapTo(tlw, QPoint()));
409 const QRect dirtyWidgetRect(dirtyTlwRect & widget->rect().translated(offset));
410 return dirtyWidgetRect.translated(-offset);
411 }
412 return QRect(QPoint(), tlwRect.size());
413 }
414
415 // Calculate the region that needs repaint.
416 QRegion r(dirty);
417 for (int i = 0; i < dirtyWidgets.size(); ++i) {
418 QWidget *w = dirtyWidgets.at(i);
419 if (widgetDirty && w != widget && !widget->isAncestorOf(w))
420 continue;
421 r += w->d_func()->dirty.translated(w->mapTo(tlw, QPoint()));
422 }
423
424 // Append the region that needs flush.
425 r += dirtyOnScreen;
426
427 if (dirtyOnScreenWidgets) { // Only in use with native child widgets.
428 for (int i = 0; i < dirtyOnScreenWidgets->size(); ++i) {
429 QWidget *w = dirtyOnScreenWidgets->at(i);
430 if (widgetDirty && w != widget && !widget->isAncestorOf(w))
431 continue;
432 QWidgetPrivate *wd = w->d_func();
433 Q_ASSERT(wd->needsFlush);
434 r += wd->needsFlush->translated(w->mapTo(tlw, QPoint()));
435 }
436 }
437
438 if (widgetDirty) {
439 // Intersect with the widget geometry and translate to its coordinates.
440 const QPoint offset(widget->mapTo(tlw, QPoint()));
441 r &= widget->rect().translated(offset);
442 r.translate(-offset);
443 }
444 return r;
445}
446
447/*!
448 Returns the static content inside the \a parent if non-zero; otherwise the static content
449 for the entire backing store is returned. The content will be clipped to \a withingClipRect
450 if non-empty.
451*/
452QRegion QWidgetBackingStore::staticContents(QWidget *parent, const QRect &withinClipRect) const
453{
454 if (!parent && tlw->testAttribute(Qt::WA_StaticContents)) {
455 const QRect surfaceGeometry(windowSurface->geometry());
456 QRect surfaceRect(0, 0, surfaceGeometry.width(), surfaceGeometry.height());
457 if (!withinClipRect.isEmpty())
458 surfaceRect &= withinClipRect;
459 return QRegion(surfaceRect);
460 }
461
462 QRegion region;
463 if (parent && parent->d_func()->children.isEmpty())
464 return region;
465
466 const bool clipToRect = !withinClipRect.isEmpty();
467 const int count = staticWidgets.count();
468 for (int i = 0; i < count; ++i) {
469 QWidget *w = staticWidgets.at(i);
470 QWidgetPrivate *wd = w->d_func();
471 if (!wd->isOpaque || !wd->extra || wd->extra->staticContentsSize.isEmpty()
472 || !w->isVisible() || (parent && !parent->isAncestorOf(w))) {
473 continue;
474 }
475
476 QRect rect(0, 0, wd->extra->staticContentsSize.width(), wd->extra->staticContentsSize.height());
477 const QPoint offset = w->mapTo(parent ? parent : tlw, QPoint());
478 if (clipToRect)
479 rect &= withinClipRect.translated(-offset);
480 if (rect.isEmpty())
481 continue;
482
483 rect &= wd->clipRect();
484 if (rect.isEmpty())
485 continue;
486
487 QRegion visible(rect);
488 wd->clipToEffectiveMask(visible);
489 if (visible.isEmpty())
490 continue;
491 wd->subtractOpaqueSiblings(visible, 0, /*alsoNonOpaque=*/true);
492
493 visible.translate(offset);
494 region += visible;
495 }
496
497 return region;
498}
499
500static inline void sendUpdateRequest(QWidget *widget, bool updateImmediately)
501{
502 if (!widget)
503 return;
504
505 if (updateImmediately) {
506 QEvent event(QEvent::UpdateRequest);
507 QApplication::sendEvent(widget, &event);
508 } else {
509 QApplication::postEvent(widget, new QEvent(QEvent::UpdateRequest), Qt::LowEventPriority);
510 }
511}
512
513/*!
514 Marks the region of the widget as dirty (if not already marked as dirty) and
515 posts an UpdateRequest event to the top-level widget (if not already posted).
516
517 If updateImmediately is true, the event is sent immediately instead of posted.
518
519 If invalidateBuffer is true, all widgets intersecting with the region will be dirty.
520
521 If the widget paints directly on screen, the event is sent to the widget
522 instead of the top-level widget, and invalidateBuffer is completely ignored.
523
524 ### Qt 4.6: Merge into a template function (after MSVC isn't supported anymore).
525*/
526void QWidgetBackingStore::markDirty(const QRegion &rgn, QWidget *widget, bool updateImmediately,
527 bool invalidateBuffer)
528{
529 Q_ASSERT(tlw->d_func()->extra);
530 Q_ASSERT(tlw->d_func()->extra->topextra);
531 Q_ASSERT(!tlw->d_func()->extra->topextra->inTopLevelResize);
532 Q_ASSERT(widget->isVisible() && widget->updatesEnabled());
533 Q_ASSERT(widget->window() == tlw);
534 Q_ASSERT(!rgn.isEmpty());
535
536#ifndef QT_NO_GRAPHICSEFFECT
537 widget->d_func()->invalidateGraphicsEffectsRecursively();
538#endif //QT_NO_GRAPHICSEFFECT
539
540 if (widget->d_func()->paintOnScreen()) {
541 if (widget->d_func()->dirty.isEmpty()) {
542 widget->d_func()->dirty = rgn;
543 sendUpdateRequest(widget, updateImmediately);
544 return;
545 } else if (qt_region_strictContains(widget->d_func()->dirty, widget->rect())) {
546 if (updateImmediately)
547 sendUpdateRequest(widget, updateImmediately);
548 return; // Already dirty.
549 }
550
551 const bool eventAlreadyPosted = !widget->d_func()->dirty.isEmpty();
552 widget->d_func()->dirty += rgn;
553 if (!eventAlreadyPosted || updateImmediately)
554 sendUpdateRequest(widget, updateImmediately);
555 return;
556 }
557
558 const QPoint offset = widget->mapTo(tlw, QPoint());
559 const QRect widgetRect = widget->d_func()->effectiveRectFor(widget->rect());
560 if (qt_region_strictContains(dirty, widgetRect.translated(offset))) {
561 if (updateImmediately)
562 sendUpdateRequest(tlw, updateImmediately);
563 return; // Already dirty.
564 }
565
566 if (invalidateBuffer) {
567 const bool eventAlreadyPosted = !dirty.isEmpty();
568#ifndef QT_NO_GRAPHICSEFFECT
569 if (widget->d_func()->graphicsEffect)
570 dirty += widget->d_func()->effectiveRectFor(rgn.boundingRect()).translated(offset);
571 else
572#endif //QT_NO_GRAPHICSEFFECT
573 dirty += rgn.translated(offset);
574 if (!eventAlreadyPosted || updateImmediately)
575 sendUpdateRequest(tlw, updateImmediately);
576 return;
577 }
578
579 if (dirtyWidgets.isEmpty()) {
580 addDirtyWidget(widget, rgn);
581 sendUpdateRequest(tlw, updateImmediately);
582 return;
583 }
584
585 if (widget->d_func()->inDirtyList) {
586 if (!qt_region_strictContains(widget->d_func()->dirty, widgetRect)) {
587#ifndef QT_NO_GRAPHICSEFFECT
588 if (widget->d_func()->graphicsEffect)
589 widget->d_func()->dirty += widget->d_func()->effectiveRectFor(rgn.boundingRect());
590 else
591#endif //QT_NO_GRAPHICSEFFECT
592 widget->d_func()->dirty += rgn;
593 }
594 } else {
595 addDirtyWidget(widget, rgn);
596 }
597
598 if (updateImmediately)
599 sendUpdateRequest(tlw, updateImmediately);
600}
601
602/*!
603 This function is equivalent to calling markDirty(QRegion(rect), ...), but
604 is more efficient as it eliminates QRegion operations/allocations and can
605 use the rect more precisely for additional cut-offs.
606
607 ### Qt 4.6: Merge into a template function (after MSVC isn't supported anymore).
608*/
609void QWidgetBackingStore::markDirty(const QRect &rect, QWidget *widget, bool updateImmediately,
610 bool invalidateBuffer)
611{
612 Q_ASSERT(tlw->d_func()->extra);
613 Q_ASSERT(tlw->d_func()->extra->topextra);
614 Q_ASSERT(!tlw->d_func()->extra->topextra->inTopLevelResize);
615 Q_ASSERT(widget->isVisible() && widget->updatesEnabled());
616 Q_ASSERT(widget->window() == tlw);
617 Q_ASSERT(!rect.isEmpty());
618
619#ifndef QT_NO_GRAPHICSEFFECT
620 widget->d_func()->invalidateGraphicsEffectsRecursively();
621#endif //QT_NO_GRAPHICSEFFECT
622
623 if (widget->d_func()->paintOnScreen()) {
624 if (widget->d_func()->dirty.isEmpty()) {
625 widget->d_func()->dirty = QRegion(rect);
626 sendUpdateRequest(widget, updateImmediately);
627 return;
628 } else if (qt_region_strictContains(widget->d_func()->dirty, rect)) {
629 if (updateImmediately)
630 sendUpdateRequest(widget, updateImmediately);
631 return; // Already dirty.
632 }
633
634 const bool eventAlreadyPosted = !widget->d_func()->dirty.isEmpty();
635 widget->d_func()->dirty += rect;
636 if (!eventAlreadyPosted || updateImmediately)
637 sendUpdateRequest(widget, updateImmediately);
638 return;
639 }
640
641 const QRect widgetRect = widget->d_func()->effectiveRectFor(rect);
642 const QRect translatedRect(widgetRect.translated(widget->mapTo(tlw, QPoint())));
643 if (qt_region_strictContains(dirty, translatedRect)) {
644 if (updateImmediately)
645 sendUpdateRequest(tlw, updateImmediately);
646 return; // Already dirty
647 }
648
649 if (invalidateBuffer) {
650 const bool eventAlreadyPosted = !dirty.isEmpty();
651 dirty += translatedRect;
652 if (!eventAlreadyPosted || updateImmediately)
653 sendUpdateRequest(tlw, updateImmediately);
654 return;
655 }
656
657 if (dirtyWidgets.isEmpty()) {
658 addDirtyWidget(widget, rect);
659 sendUpdateRequest(tlw, updateImmediately);
660 return;
661 }
662
663 if (widget->d_func()->inDirtyList) {
664 if (!qt_region_strictContains(widget->d_func()->dirty, widgetRect))
665 widget->d_func()->dirty += widgetRect;
666 } else {
667 addDirtyWidget(widget, rect);
668 }
669
670 if (updateImmediately)
671 sendUpdateRequest(tlw, updateImmediately);
672}
673
674/*!
675 Marks the \a region of the \a widget as dirty on screen. The \a region will be copied from
676 the backing store to the \a widget's native parent next time flush() is called.
677
678 Paint on screen widgets are ignored.
679*/
680void QWidgetBackingStore::markDirtyOnScreen(const QRegion &region, QWidget *widget, const QPoint &topLevelOffset)
681{
682 if (!widget || widget->d_func()->paintOnScreen() || region.isEmpty())
683 return;
684
685#if defined(Q_WS_QWS) || defined(Q_WS_MAC)
686 if (!widget->testAttribute(Qt::WA_WState_InPaintEvent))
687 dirtyOnScreen += region.translated(topLevelOffset);
688 return;
689#endif
690
691 // Top-level.
692 if (widget == tlw) {
693 if (!widget->testAttribute(Qt::WA_WState_InPaintEvent))
694 dirtyOnScreen += region;
695 return;
696 }
697
698 // Alien widgets.
699 if (!widget->internalWinId()) {
700 QWidget *nativeParent = widget->nativeParentWidget();
701 // Alien widgets with the top-level as the native parent (common case).
702 if (nativeParent == tlw) {
703 if (!widget->testAttribute(Qt::WA_WState_InPaintEvent))
704 dirtyOnScreen += region.translated(topLevelOffset);
705 return;
706 }
707
708 // Alien widgets with native parent != tlw.
709 QWidgetPrivate *nativeParentPrivate = nativeParent->d_func();
710 if (!nativeParentPrivate->needsFlush)
711 nativeParentPrivate->needsFlush = new QRegion;
712 const QPoint nativeParentOffset = widget->mapTo(nativeParent, QPoint());
713 *nativeParentPrivate->needsFlush += region.translated(nativeParentOffset);
714 appendDirtyOnScreenWidget(nativeParent);
715 return;
716 }
717
718 // Native child widgets.
719 QWidgetPrivate *widgetPrivate = widget->d_func();
720 if (!widgetPrivate->needsFlush)
721 widgetPrivate->needsFlush = new QRegion;
722 *widgetPrivate->needsFlush += region;
723 appendDirtyOnScreenWidget(widget);
724}
725
726void QWidgetBackingStore::removeDirtyWidget(QWidget *w)
727{
728 if (!w)
729 return;
730
731 dirtyWidgetsRemoveAll(w);
732 dirtyOnScreenWidgetsRemoveAll(w);
733 resetWidget(w);
734
735 QWidgetPrivate *wd = w->d_func();
736 const int n = wd->children.count();
737 for (int i = 0; i < n; ++i) {
738 if (QWidget *child = qobject_cast<QWidget*>(wd->children.at(i)))
739 removeDirtyWidget(child);
740 }
741}
742
743#if defined(Q_WS_QWS) && !defined(QT_NO_QWS_MANAGER)
744bool QWidgetBackingStore::hasDirtyWindowDecoration() const
745{
746 QTLWExtra *tlwExtra = tlw->d_func()->maybeTopData();
747 if (tlwExtra && tlwExtra->qwsManager)
748 return !tlwExtra->qwsManager->d_func()->dirtyRegions.isEmpty();
749 return false;
750}
751
752void QWidgetBackingStore::paintWindowDecoration()
753{
754 if (!hasDirtyWindowDecoration())
755 return;
756
757 QDecoration &decoration = QApplication::qwsDecoration();
758 const QRect decorationRect = tlw->rect();
759 QRegion decorationRegion = decoration.region(tlw, decorationRect);
760
761 QWSManagerPrivate *managerPrivate = tlw->d_func()->topData()->qwsManager->d_func();
762 const bool doClipping = !managerPrivate->entireDecorationNeedsRepaint
763 && !managerPrivate->dirtyClip.isEmpty();
764
765 if (doClipping) {
766 decorationRegion &= static_cast<QWSWindowSurface *>(windowSurface)->clipRegion();
767 decorationRegion &= managerPrivate->dirtyClip;
768 }
769
770 if (decorationRegion.isEmpty())
771 return;
772
773 //### The QWS decorations do not always paint the pixels they promise to paint.
774 // This causes painting problems with QWSMemorySurface. Since none of the other
775 // window surfaces actually use the region, passing an empty region is a safe
776 // workaround.
777
778 windowSurface->beginPaint(QRegion());
779
780 QPaintEngine *engine = windowSurface->paintDevice()->paintEngine();
781 Q_ASSERT(engine);
782 const QRegion oldSystemClip(engine->systemClip());
783 engine->setSystemClip(decorationRegion.translated(tlwOffset));
784
785 QPainter painter(windowSurface->paintDevice());
786 painter.setFont(QApplication::font());
787 painter.translate(tlwOffset);
788
789 const int numDirty = managerPrivate->dirtyRegions.size();
790 for (int i = 0; i < numDirty; ++i) {
791 const int area = managerPrivate->dirtyRegions.at(i);
792
793 QRegion clipRegion = decoration.region(tlw, decorationRect, area);
794 if (!clipRegion.isEmpty()) {
795 // Decoration styles changes the clip and assumes the old clip is non-empty,
796 // so we have to set it, but in theory it shouldn't be required.
797 painter.setClipRegion(clipRegion);
798 decoration.paint(&painter, tlw, area, managerPrivate->dirtyStates.at(i));
799 }
800 }
801 markDirtyOnScreen(decorationRegion, tlw, QPoint());
802
803 painter.end();
804 windowSurface->endPaint(decorationRegion);
805 managerPrivate->clearDirtyRegions();
806 engine->setSystemClip(oldSystemClip);
807}
808#endif
809
810void QWidgetBackingStore::updateLists(QWidget *cur)
811{
812 if (!cur)
813 return;
814
815 QList<QObject*> children = cur->children();
816 for (int i = 0; i < children.size(); ++i) {
817 QWidget *child = qobject_cast<QWidget*>(children.at(i));
818 if (!child)
819 continue;
820
821 updateLists(child);
822 }
823
824 if (cur->testAttribute(Qt::WA_StaticContents))
825 addStaticWidget(cur);
826
827#ifdef Q_BACKINGSTORE_SUBSURFACES
828 QTLWExtra *extra = cur->d_func()->maybeTopData();
829 if (extra && extra->windowSurface && cur != tlw)
830 subSurfaces.append(extra->windowSurface);
831#endif
832}
833
834QWidgetBackingStore::QWidgetBackingStore(QWidget *topLevel)
835 : tlw(topLevel), dirtyOnScreenWidgets(0), hasDirtyFromPreviousSync(false)
836{
837 windowSurface = tlw->windowSurface();
838 if (!windowSurface)
839 windowSurface = topLevel->d_func()->createDefaultWindowSurface();
840
841 // The QWindowSurface constructor will call QWidget::setWindowSurface(),
842 // but automatically created surfaces should not be added to the topdata.
843#ifdef Q_BACKINGSTORE_SUBSURFACES
844 Q_ASSERT(topLevel->d_func()->topData()->windowSurface == windowSurface);
845#endif
846 topLevel->d_func()->topData()->windowSurface = 0;
847
848 // Ensure all existing subsurfaces and static widgets are added to their respective lists.
849 updateLists(topLevel);
850}
851
852QWidgetBackingStore::~QWidgetBackingStore()
853{
854 for (int c = 0; c < dirtyWidgets.size(); ++c) {
855 resetWidget(dirtyWidgets.at(c));
856 }
857
858 delete windowSurface;
859 windowSurface = 0;
860 delete dirtyOnScreenWidgets;
861 dirtyOnScreenWidgets = 0;
862}
863
864//parent's coordinates; move whole rect; update parent and widget
865//assume the screen blt has already been done, so we don't need to refresh that part
866void QWidgetPrivate::moveRect(const QRect &rect, int dx, int dy)
867{
868 Q_Q(QWidget);
869 if (!q->isVisible() || (dx == 0 && dy == 0))
870 return;
871
872 QWidget *tlw = q->window();
873 QTLWExtra* x = tlw->d_func()->topData();
874 if (x->inTopLevelResize)
875 return;
876
877 static int accelEnv = -1;
878 if (accelEnv == -1) {
879 accelEnv = qgetenv("QT_NO_FAST_MOVE").toInt() == 0;
880 }
881
882 QWidget *pw = q->parentWidget();
883 QPoint toplevelOffset = pw->mapTo(tlw, QPoint());
884 QWidgetPrivate *pd = pw->d_func();
885 QRect clipR(pd->clipRect());
886#ifdef Q_WS_QWS
887 QWidgetBackingStore *wbs = x->backingStore;
888 QWSWindowSurface *surface = static_cast<QWSWindowSurface*>(wbs->windowSurface);
889 clipR = clipR.intersected(surface->clipRegion().translated(-toplevelOffset).boundingRect());
890#endif
891 const QRect newRect(rect.translated(dx, dy));
892 QRect destRect = rect.intersected(clipR);
893 if (destRect.isValid())
894 destRect = destRect.translated(dx, dy).intersected(clipR);
895 const QRect sourceRect(destRect.translated(-dx, -dy));
896 const QRect parentRect(rect & clipR);
897
898 bool accelerateMove = accelEnv && isOpaque
899#ifndef QT_NO_GRAPHICSVIEW
900 // No accelerate move for proxy widgets.
901 && !tlw->d_func()->extra->proxyWidget
902#endif
903 && !isOverlapped(sourceRect) && !isOverlapped(destRect);
904
905 if (!accelerateMove) {
906 QRegion parentR(effectiveRectFor(parentRect));
907 if (!extra || !extra->hasMask) {
908 parentR -= newRect;
909 } else {
910 // invalidateBuffer() excludes anything outside the mask
911 parentR += newRect & clipR;
912 }
913 pd->invalidateBuffer(parentR);
914 invalidateBuffer((newRect & clipR).translated(-data.crect.topLeft()));
915 } else {
916
917 QWidgetBackingStore *wbs = x->backingStore;
918 QRegion childExpose(newRect & clipR);
919
920 if (sourceRect.isValid() && wbs->bltRect(sourceRect, dx, dy, pw))
921 childExpose -= destRect;
922
923 if (!pw->updatesEnabled())
924 return;
925
926 const bool childUpdatesEnabled = q->updatesEnabled();
927 if (childUpdatesEnabled && !childExpose.isEmpty()) {
928 childExpose.translate(-data.crect.topLeft());
929 wbs->markDirty(childExpose, q);
930 isMoved = true;
931 }
932
933 QRegion parentExpose(parentRect);
934 parentExpose -= newRect;
935 if (extra && extra->hasMask)
936 parentExpose += QRegion(newRect) - extra->mask.translated(data.crect.topLeft());
937
938 if (!parentExpose.isEmpty()) {
939 wbs->markDirty(parentExpose, pw);
940 pd->isMoved = true;
941 }
942
943 if (childUpdatesEnabled) {
944 QRegion needsFlush(sourceRect);
945 needsFlush += destRect;
946 wbs->markDirtyOnScreen(needsFlush, pw, toplevelOffset);
947 }
948 }
949}
950
951//widget's coordinates; scroll within rect; only update widget
952void QWidgetPrivate::scrollRect(const QRect &rect, int dx, int dy)
953{
954 Q_Q(QWidget);
955 QWidget *tlw = q->window();
956 QTLWExtra* x = tlw->d_func()->topData();
957 if (x->inTopLevelResize)
958 return;
959
960 QWidgetBackingStore *wbs = x->backingStore;
961 if (!wbs)
962 return;
963
964 static int accelEnv = -1;
965 if (accelEnv == -1) {
966 accelEnv = qgetenv("QT_NO_FAST_SCROLL").toInt() == 0;
967 }
968
969 QRect scrollRect = rect & clipRect();
970 bool overlapped = false;
971 bool accelerateScroll = accelEnv && isOpaque
972 && !(overlapped = isOverlapped(scrollRect.translated(data.crect.topLeft())));
973
974#if defined(Q_WS_QWS)
975 QWSWindowSurface *surface;
976 surface = static_cast<QWSWindowSurface*>(wbs->windowSurface);
977
978 if (accelerateScroll && !surface->isBuffered()) {
979 const QRegion surfaceClip = surface->clipRegion();
980 const QRegion outsideClip = QRegion(rect) - surfaceClip;
981 if (!outsideClip.isEmpty()) {
982 const QVector<QRect> clipped = (surfaceClip & rect).rects();
983 if (clipped.size() < 8) {
984 for (int i = 0; i < clipped.size(); ++i)
985 this->scrollRect(clipped.at(i), dx, dy);
986 return;
987 } else {
988 accelerateScroll = false;
989 }
990 }
991 }
992#endif // Q_WS_QWS
993
994 if (!accelerateScroll) {
995 if (overlapped) {
996 QRegion region(scrollRect);
997 subtractOpaqueSiblings(region);
998 invalidateBuffer(region);
999 }else {
1000 invalidateBuffer(scrollRect);
1001 }
1002 } else {
1003 const QPoint toplevelOffset = q->mapTo(tlw, QPoint());
1004#ifdef Q_WS_QWS
1005 QWSWindowSurface *surface = static_cast<QWSWindowSurface*>(wbs->windowSurface);
1006 const QRegion clip = surface->clipRegion().translated(-toplevelOffset) & scrollRect;
1007 const QRect clipBoundingRect = clip.boundingRect();
1008 scrollRect &= clipBoundingRect;
1009#endif
1010 const QRect destRect = scrollRect.translated(dx, dy) & scrollRect;
1011 const QRect sourceRect = destRect.translated(-dx, -dy);
1012
1013 QRegion childExpose(scrollRect);
1014 if (sourceRect.isValid()) {
1015 if (wbs->bltRect(sourceRect, dx, dy, q))
1016 childExpose -= destRect;
1017 }
1018
1019 if (inDirtyList) {
1020 if (rect == q->rect()) {
1021 dirty.translate(dx, dy);
1022 } else {
1023 QRegion dirtyScrollRegion = dirty.intersected(scrollRect);
1024 if (!dirtyScrollRegion.isEmpty()) {
1025 dirty -= dirtyScrollRegion;
1026 dirtyScrollRegion.translate(dx, dy);
1027 dirty += dirtyScrollRegion;
1028 }
1029 }
1030 }
1031
1032 if (!q->updatesEnabled())
1033 return;
1034
1035 if (!childExpose.isEmpty()) {
1036 wbs->markDirty(childExpose, q);
1037 isScrolled = true;
1038 }
1039
1040 // Instead of using native scroll-on-screen, we copy from
1041 // backingstore, giving only one screen update for each
1042 // scroll, and a solid appearance
1043 wbs->markDirtyOnScreen(destRect, q, toplevelOffset);
1044 }
1045}
1046
1047static inline bool discardSyncRequest(QWidget *tlw, QTLWExtra *tlwExtra)
1048{
1049 if (!tlw || !tlwExtra)
1050 return true;
1051
1052#ifdef Q_WS_X11
1053 // Delay the sync until we get an Expose event from X11 (initial show).
1054 // Qt::WA_Mapped is set to true, but the actual mapping has not yet occurred.
1055 // However, we must repaint immediately regardless of the state if someone calls repaint().
1056 if (tlwExtra->waitingForMapNotify && !tlwExtra->inRepaint)
1057 return true;
1058#endif
1059
1060 if (!tlw->testAttribute(Qt::WA_Mapped))
1061 return true;
1062
1063 if (!tlw->isVisible()
1064#ifndef Q_WS_X11
1065 // If we're minimized on X11, WA_Mapped will be false and we
1066 // will return in the case above. Some window managers on X11
1067 // sends us the PropertyNotify to change the minimized state
1068 // *AFTER* we've received the expose event, which is baaad.
1069 || tlw->isMinimized()
1070#endif
1071 )
1072 return true;
1073
1074 return false;
1075}
1076
1077/*!
1078 Synchronizes the \a exposedRegion of the \a exposedWidget with the backing store.
1079
1080 If there's nothing to repaint, the area is flushed and painting does not occur;
1081 otherwise the area is marked as dirty on screen and will be flushed right after
1082 we are done with all painting.
1083*/
1084void QWidgetBackingStore::sync(QWidget *exposedWidget, const QRegion &exposedRegion)
1085{
1086 QTLWExtra *tlwExtra = tlw->d_func()->maybeTopData();
1087 if (discardSyncRequest(tlw, tlwExtra) || tlwExtra->inTopLevelResize)
1088 return;
1089
1090 if (!exposedWidget || !exposedWidget->internalWinId() || !exposedWidget->isVisible()
1091 || !exposedWidget->updatesEnabled() || exposedRegion.isEmpty()) {
1092 return;
1093 }
1094
1095 // Nothing to repaint.
1096 if (!isDirty()) {
1097 qt_flush(exposedWidget, exposedRegion, windowSurface, tlw, tlwOffset);
1098 return;
1099 }
1100
1101 if (exposedWidget != tlw)
1102 markDirtyOnScreen(exposedRegion, exposedWidget, exposedWidget->mapTo(tlw, QPoint()));
1103 else
1104 markDirtyOnScreen(exposedRegion, exposedWidget, QPoint());
1105 sync();
1106}
1107
1108/*!
1109 Synchronizes the backing store, i.e. dirty areas are repainted and flushed.
1110*/
1111void QWidgetBackingStore::sync()
1112{
1113 QTLWExtra *tlwExtra = tlw->d_func()->maybeTopData();
1114 if (discardSyncRequest(tlw, tlwExtra)) {
1115 // If the top-level is minimized, it's not visible on the screen so we can delay the
1116 // update until it's shown again. In order to do that we must keep the dirty states.
1117 // These will be cleared when we receive the first expose after showNormal().
1118 // However, if the widget is not visible (isVisible() returns false), everything will
1119 // be invalidated once the widget is shown again, so clear all dirty states.
1120 if (!tlw->isVisible()) {
1121 dirty = QRegion();
1122 for (int i = 0; i < dirtyWidgets.size(); ++i)
1123 resetWidget(dirtyWidgets.at(i));
1124 dirtyWidgets.clear();
1125 }
1126 return;
1127 }
1128
1129 const bool inTopLevelResize = tlwExtra->inTopLevelResize;
1130 const bool updatesDisabled = !tlw->updatesEnabled();
1131 const QRect tlwRect(topLevelRect());
1132 const QRect surfaceGeometry(windowSurface->geometry());
1133 bool repaintAllWidgets = false;
1134
1135 if (inTopLevelResize || surfaceGeometry != tlwRect) {
1136 if ((inTopLevelResize || surfaceGeometry.size() != tlwRect.size()) && !updatesDisabled) {
1137 if (hasStaticContents()) {
1138 // Repaint existing dirty area and newly visible area.
1139 const QRect clipRect(0, 0, surfaceGeometry.width(), surfaceGeometry.height());
1140 const QRegion staticRegion(staticContents(0, clipRect));
1141 QRegion newVisible(0, 0, tlwRect.width(), tlwRect.height());
1142 newVisible -= staticRegion;
1143 dirty += newVisible;
1144 windowSurface->setStaticContents(staticRegion);
1145 } else {
1146 // Repaint everything.
1147 dirty = QRegion(0, 0, tlwRect.width(), tlwRect.height());
1148 for (int i = 0; i < dirtyWidgets.size(); ++i)
1149 resetWidget(dirtyWidgets.at(i));
1150 dirtyWidgets.clear();
1151 repaintAllWidgets = true;
1152 }
1153 }
1154 windowSurface->setGeometry(tlwRect);
1155 }
1156
1157 if (updatesDisabled)
1158 return;
1159
1160 if (hasDirtyFromPreviousSync)
1161 dirty += dirtyFromPreviousSync;
1162
1163 // Contains everything that needs repaint.
1164 QRegion toClean(dirty);
1165
1166 // Loop through all update() widgets and remove them from the list before they are
1167 // painted (in case someone calls update() in paintEvent). If the widget is opaque
1168 // and does not have transparent overlapping siblings, append it to the
1169 // opaqueNonOverlappedWidgets list and paint it directly without composition.
1170 QVarLengthArray<QWidget *, 32> opaqueNonOverlappedWidgets;
1171 for (int i = 0; i < dirtyWidgets.size(); ++i) {
1172 QWidget *w = dirtyWidgets.at(i);
1173 QWidgetPrivate *wd = w->d_func();
1174 if (wd->data.in_destructor)
1175 continue;
1176
1177 // Clip with mask() and clipRect().
1178 wd->dirty &= wd->clipRect();
1179 wd->clipToEffectiveMask(wd->dirty);
1180
1181 // Subtract opaque siblings and children.
1182 bool hasDirtySiblingsAbove = false;
1183 // We know for sure that the widget isn't overlapped if 'isMoved' is true.
1184 if (!wd->isMoved)
1185 wd->subtractOpaqueSiblings(wd->dirty, &hasDirtySiblingsAbove);
1186 // Scrolled and moved widgets must draw all children.
1187 if (!wd->isScrolled && !wd->isMoved)
1188 wd->subtractOpaqueChildren(wd->dirty, w->rect());
1189
1190 if (wd->dirty.isEmpty()) {
1191 resetWidget(w);
1192 continue;
1193 }
1194
1195 const QRegion widgetDirty(w != tlw ? wd->dirty.translated(w->mapTo(tlw, QPoint()))
1196 : wd->dirty);
1197 toClean += widgetDirty;
1198
1199#ifndef QT_NO_GRAPHICSVIEW
1200 if (tlw->d_func()->extra->proxyWidget) {
1201 resetWidget(w);
1202 continue;
1203 }
1204#endif
1205
1206 if (!hasDirtySiblingsAbove && wd->isOpaque && !dirty.intersects(widgetDirty.boundingRect())) {
1207 opaqueNonOverlappedWidgets.append(w);
1208 } else {
1209 resetWidget(w);
1210 dirty += widgetDirty;
1211 }
1212 }
1213 dirtyWidgets.clear();
1214
1215 if (toClean.isEmpty()) {
1216 // Nothing to repaint. However, we might have newly exposed areas on the
1217 // screen if this function was called from sync(QWidget *, QRegion)), so
1218 // we have to make sure those are flushed.
1219 flush();
1220 return;
1221 }
1222
1223#ifndef QT_NO_GRAPHICSVIEW
1224 if (tlw->d_func()->extra->proxyWidget) {
1225 updateStaticContentsSize();
1226 dirty = QRegion();
1227 const QVector<QRect> rects(toClean.rects());
1228 for (int i = 0; i < rects.size(); ++i)
1229 tlw->d_func()->extra->proxyWidget->update(rects.at(i));
1230 return;
1231 }
1232#endif
1233
1234#ifndef Q_BACKINGSTORE_SUBSURFACES
1235 BeginPaintInfo beginPaintInfo;
1236 beginPaint(toClean, tlw, windowSurface, &beginPaintInfo);
1237 if (beginPaintInfo.nothingToPaint) {
1238 for (int i = 0; i < opaqueNonOverlappedWidgets.size(); ++i)
1239 resetWidget(opaqueNonOverlappedWidgets[i]);
1240 dirty = QRegion();
1241 return;
1242 }
1243#endif
1244
1245 // Must do this before sending any paint events because
1246 // the size may change in the paint event.
1247 updateStaticContentsSize();
1248 const QRegion dirtyCopy(dirty);
1249 dirty = QRegion();
1250
1251 // Paint opaque non overlapped widgets.
1252 for (int i = 0; i < opaqueNonOverlappedWidgets.size(); ++i) {
1253 QWidget *w = opaqueNonOverlappedWidgets[i];
1254 QWidgetPrivate *wd = w->d_func();
1255
1256 int flags = QWidgetPrivate::DrawRecursive;
1257 // Scrolled and moved widgets must draw all children.
1258 if (!wd->isScrolled && !wd->isMoved)
1259 flags |= QWidgetPrivate::DontDrawOpaqueChildren;
1260 if (w == tlw)
1261 flags |= QWidgetPrivate::DrawAsRoot;
1262
1263 QRegion toBePainted(wd->dirty);
1264 resetWidget(w);
1265
1266#ifdef Q_BACKINGSTORE_SUBSURFACES
1267 QWindowSurface *subSurface = w->windowSurface();
1268 BeginPaintInfo beginPaintInfo;
1269 beginPaint(toBePainted, w, subSurface, &beginPaintInfo, false);
1270 if (beginPaintInfo.nothingToPaint)
1271 continue;
1272
1273 if (beginPaintInfo.windowSurfaceRecreated) {
1274 // Eep the window surface has changed. The old one may have been
1275 // deleted, in which case we will segfault on the call to
1276 // painterOffset() below. Use the new window surface instead.
1277 subSurface = w->windowSurface();
1278 }
1279
1280 QPoint offset(tlwOffset);
1281 if (subSurface == windowSurface)
1282 offset += w->mapTo(tlw, QPoint());
1283 else
1284 offset = static_cast<QWSWindowSurface*>(subSurface)->painterOffset();
1285 wd->drawWidget(subSurface->paintDevice(), toBePainted, offset, flags, 0, this);
1286
1287 endPaint(toBePainted, subSurface, &beginPaintInfo);
1288#else
1289 QPoint offset(tlwOffset);
1290 if (w != tlw)
1291 offset += w->mapTo(tlw, QPoint());
1292 wd->drawWidget(windowSurface->paintDevice(), toBePainted, offset, flags, 0, this);
1293#endif
1294 }
1295
1296 // Paint the rest with composition.
1297#ifndef Q_BACKINGSTORE_SUBSURFACES
1298 if (repaintAllWidgets || !dirtyCopy.isEmpty()) {
1299 const int flags = QWidgetPrivate::DrawAsRoot | QWidgetPrivate::DrawRecursive;
1300 tlw->d_func()->drawWidget(windowSurface->paintDevice(), dirtyCopy, tlwOffset, flags, 0, this);
1301 }
1302
1303 endPaint(toClean, windowSurface, &beginPaintInfo);
1304#else
1305 if (!repaintAllWidgets && dirtyCopy.isEmpty())
1306 return; // Nothing more to paint.
1307
1308 QList<QWindowSurface *> surfaceList(subSurfaces);
1309 surfaceList.prepend(windowSurface);
1310 const QRect dirtyBoundingRect(dirtyCopy.boundingRect());
1311
1312 // Loop through all window surfaces (incl. the top-level surface) and
1313 // repaint those intersecting with the bounding rect of the dirty region.
1314 for (int i = 0; i < surfaceList.size(); ++i) {
1315 QWindowSurface *subSurface = surfaceList.at(i);
1316 QWidget *w = subSurface->window();
1317 QWidgetPrivate *wd = w->d_func();
1318
1319 const QRect clipRect = wd->clipRect().translated(w->mapTo(tlw, QPoint()));
1320 if (!qRectIntersects(dirtyBoundingRect, clipRect))
1321 continue;
1322
1323 toClean = dirtyCopy;
1324 BeginPaintInfo beginPaintInfo;
1325 beginPaint(toClean, w, subSurface, &beginPaintInfo);
1326 if (beginPaintInfo.nothingToPaint)
1327 continue;
1328
1329 if (beginPaintInfo.windowSurfaceRecreated) {
1330 // Eep the window surface has changed. The old one may have been
1331 // deleted, in which case we will segfault on the call to
1332 // painterOffset() below. Use the new window surface instead.
1333 subSurface = w->windowSurface();
1334 }
1335
1336 int flags = QWidgetPrivate::DrawRecursive;
1337 if (w == tlw)
1338 flags |= QWidgetPrivate::DrawAsRoot;
1339 const QPoint painterOffset = static_cast<QWSWindowSurface*>(subSurface)->painterOffset();
1340 wd->drawWidget(subSurface->paintDevice(), toClean, painterOffset, flags, 0, this);
1341
1342 endPaint(toClean, subSurface, &beginPaintInfo);
1343 }
1344#endif
1345}
1346
1347/*!
1348 Flushes the contents of the backing store into the top-level widget.
1349 If the \a widget is non-zero, the content is flushed to the \a widget.
1350 If the \a surface is non-zero, the content of the \a surface is flushed.
1351*/
1352void QWidgetBackingStore::flush(QWidget *widget, QWindowSurface *surface)
1353{
1354#if defined(Q_WS_QWS) && !defined(QT_NO_QWS_MANAGER)
1355 paintWindowDecoration();
1356#endif
1357
1358 if (!dirtyOnScreen.isEmpty()) {
1359 QWidget *target = widget ? widget : tlw;
1360 QWindowSurface *source = surface ? surface : windowSurface;
1361 qt_flush(target, dirtyOnScreen, source, tlw, tlwOffset);
1362 dirtyOnScreen = QRegion();
1363 }
1364
1365 if (!dirtyOnScreenWidgets || dirtyOnScreenWidgets->isEmpty())
1366 return;
1367
1368 for (int i = 0; i < dirtyOnScreenWidgets->size(); ++i) {
1369 QWidget *w = dirtyOnScreenWidgets->at(i);
1370 QWidgetPrivate *wd = w->d_func();
1371 Q_ASSERT(wd->needsFlush);
1372 qt_flush(w, *wd->needsFlush, windowSurface, tlw, tlwOffset);
1373 *wd->needsFlush = QRegion();
1374 }
1375 dirtyOnScreenWidgets->clear();
1376}
1377
1378static inline bool discardInvalidateBufferRequest(QWidget *widget, QTLWExtra *tlwExtra)
1379{
1380 Q_ASSERT(widget);
1381 if (QApplication::closingDown())
1382 return true;
1383
1384 if (!tlwExtra || tlwExtra->inTopLevelResize || !tlwExtra->backingStore)
1385 return true;
1386
1387 if (!widget->isVisible() || !widget->updatesEnabled())
1388 return true;
1389
1390 return false;
1391}
1392
1393/*!
1394 Invalidates the buffer when the widget is resized.
1395 Static areas are never invalidated unless absolutely needed.
1396*/
1397void QWidgetPrivate::invalidateBuffer_resizeHelper(const QPoint &oldPos, const QSize &oldSize)
1398{
1399 Q_Q(QWidget);
1400 Q_ASSERT(!q->isWindow());
1401 Q_ASSERT(q->parentWidget());
1402
1403 const bool staticContents = q->testAttribute(Qt::WA_StaticContents);
1404 const bool sizeDecreased = (data.crect.width() < oldSize.width())
1405 || (data.crect.height() < oldSize.height());
1406
1407 const QPoint offset(data.crect.x() - oldPos.x(), data.crect.y() - oldPos.y());
1408 const bool parentAreaExposed = !offset.isNull() || sizeDecreased;
1409 const QRect newWidgetRect(q->rect());
1410 const QRect oldWidgetRect(0, 0, oldSize.width(), oldSize.height());
1411
1412 if (!staticContents || graphicsEffect) {
1413 QRegion staticChildren;
1414 QWidgetBackingStore *bs = 0;
1415 if (offset.isNull() && (bs = maybeBackingStore()))
1416 staticChildren = bs->staticContents(q, oldWidgetRect);
1417 const bool hasStaticChildren = !staticChildren.isEmpty();
1418
1419 if (hasStaticChildren) {
1420 QRegion dirty(newWidgetRect);
1421 dirty -= staticChildren;
1422 invalidateBuffer(dirty);
1423 } else {
1424 // Entire widget needs repaint.
1425 invalidateBuffer(newWidgetRect);
1426 }
1427
1428 if (!parentAreaExposed)
1429 return;
1430
1431 // Invalidate newly exposed area of the parent.
1432 if (!graphicsEffect && extra && extra->hasMask) {
1433 QRegion parentExpose(extra->mask.translated(oldPos));
1434 parentExpose &= QRect(oldPos, oldSize);
1435 if (hasStaticChildren)
1436 parentExpose -= data.crect; // Offset is unchanged, safe to do this.
1437 q->parentWidget()->d_func()->invalidateBuffer(parentExpose);
1438 } else {
1439 if (hasStaticChildren && !graphicsEffect) {
1440 QRegion parentExpose(QRect(oldPos, oldSize));
1441 parentExpose -= data.crect; // Offset is unchanged, safe to do this.
1442 q->parentWidget()->d_func()->invalidateBuffer(parentExpose);
1443 } else {
1444 q->parentWidget()->d_func()->invalidateBuffer(effectiveRectFor(QRect(oldPos, oldSize)));
1445 }
1446 }
1447 return;
1448 }
1449
1450 // Move static content to its new position.
1451 if (!offset.isNull()) {
1452 if (sizeDecreased) {
1453 const QSize minSize(qMin(oldSize.width(), data.crect.width()),
1454 qMin(oldSize.height(), data.crect.height()));
1455 moveRect(QRect(oldPos, minSize), offset.x(), offset.y());
1456 } else {
1457 moveRect(QRect(oldPos, oldSize), offset.x(), offset.y());
1458 }
1459 }
1460
1461 // Invalidate newly visible area of the widget.
1462 if (!sizeDecreased || !oldWidgetRect.contains(newWidgetRect)) {
1463 QRegion newVisible(newWidgetRect);
1464 newVisible -= oldWidgetRect;
1465 invalidateBuffer(newVisible);
1466 }
1467
1468 if (!parentAreaExposed)
1469 return;
1470
1471 // Invalidate newly exposed area of the parent.
1472 const QRect oldRect(oldPos, oldSize);
1473 if (extra && extra->hasMask) {
1474 QRegion parentExpose(oldRect);
1475 parentExpose &= extra->mask.translated(oldPos);
1476 parentExpose -= (extra->mask.translated(data.crect.topLeft()) & data.crect);
1477 q->parentWidget()->d_func()->invalidateBuffer(parentExpose);
1478 } else {
1479 QRegion parentExpose(oldRect);
1480 parentExpose -= data.crect;
1481 q->parentWidget()->d_func()->invalidateBuffer(parentExpose);
1482 }
1483}
1484
1485/*!
1486 Invalidates the \a rgn (in widget's coordinates) of the backing store, i.e.
1487 all widgets intersecting with the region will be repainted when the backing store
1488 is synced.
1489
1490 ### Qt 4.6: Merge into a template function (after MSVC isn't supported anymore).
1491*/
1492void QWidgetPrivate::invalidateBuffer(const QRegion &rgn)
1493{
1494 Q_Q(QWidget);
1495
1496 QTLWExtra *tlwExtra = q->window()->d_func()->maybeTopData();
1497 if (discardInvalidateBufferRequest(q, tlwExtra) || rgn.isEmpty())
1498 return;
1499
1500 QRegion wrgn(rgn);
1501 wrgn &= clipRect();
1502 if (!graphicsEffect && extra && extra->hasMask)
1503 wrgn &= extra->mask;
1504 if (wrgn.isEmpty())
1505 return;
1506
1507 tlwExtra->backingStore->markDirty(wrgn, q, false, true);
1508}
1509
1510/*!
1511 This function is equivalent to calling invalidateBuffer(QRegion(rect), ...), but
1512 is more efficient as it eliminates QRegion operations/allocations and can
1513 use the rect more precisely for additional cut-offs.
1514
1515 ### Qt 4.6: Merge into a template function (after MSVC isn't supported anymore).
1516*/
1517void QWidgetPrivate::invalidateBuffer(const QRect &rect)
1518{
1519 Q_Q(QWidget);
1520
1521 QTLWExtra *tlwExtra = q->window()->d_func()->maybeTopData();
1522 if (discardInvalidateBufferRequest(q, tlwExtra) || rect.isEmpty())
1523 return;
1524
1525 QRect wRect(rect);
1526 wRect &= clipRect();
1527 if (wRect.isEmpty())
1528 return;
1529
1530 if (graphicsEffect || !extra || !extra->hasMask) {
1531 tlwExtra->backingStore->markDirty(wRect, q, false, true);
1532 return;
1533 }
1534
1535 QRegion wRgn(extra->mask);
1536 wRgn &= wRect;
1537 if (wRgn.isEmpty())
1538 return;
1539
1540 tlwExtra->backingStore->markDirty(wRgn, q, false, true);
1541}
1542
1543void QWidgetPrivate::repaint_sys(const QRegion &rgn)
1544{
1545 if (data.in_destructor)
1546 return;
1547
1548 Q_Q(QWidget);
1549 if (q->testAttribute(Qt::WA_StaticContents)) {
1550 if (!extra)
1551 createExtra();
1552 extra->staticContentsSize = data.crect.size();
1553 }
1554
1555 QPaintEngine *engine = q->paintEngine();
1556 // QGLWidget does not support partial updates if:
1557 // 1) The context is double buffered
1558 // 2) The context is single buffered and auto-fill background is enabled.
1559 const bool noPartialUpdateSupport = (engine && (engine->type() == QPaintEngine::OpenGL
1560 || engine->type() == QPaintEngine::OpenGL2))
1561 && (usesDoubleBufferedGLContext || q->autoFillBackground());
1562 QRegion toBePainted(noPartialUpdateSupport ? q->rect() : rgn);
1563
1564#ifdef Q_WS_MAC
1565 // No difference between update() and repaint() on the Mac.
1566 update_sys(toBePainted);
1567 return;
1568#endif
1569
1570 toBePainted &= clipRect();
1571 clipToEffectiveMask(toBePainted);
1572 if (toBePainted.isEmpty())
1573 return; // Nothing to repaint.
1574
1575#ifndef QT_NO_PAINT_DEBUG
1576 bool flushed = QWidgetBackingStore::flushPaint(q, toBePainted);
1577#endif
1578
1579 drawWidget(q, toBePainted, QPoint(), QWidgetPrivate::DrawAsRoot | QWidgetPrivate::DrawPaintOnScreen, 0);
1580
1581#ifndef QT_NO_PAINT_DEBUG
1582 if (flushed)
1583 QWidgetBackingStore::unflushPaint(q, toBePainted);
1584#endif
1585
1586 if (!q->testAttribute(Qt::WA_PaintOutsidePaintEvent) && q->paintingActive())
1587 qWarning("QWidget::repaint: It is dangerous to leave painters active on a widget outside of the PaintEvent");
1588}
1589
1590
1591QT_END_NAMESPACE
Note: See TracBrowser for help on using the repository browser.