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

Last change on this file since 751 was 651, checked in by Dmitry A. Kuminov, 16 years ago

trunk: Merged in qt 4.6.2 sources.

File size: 53.7 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 QT_NO_PAINT_DEBUG
356 windowSurface->beginPaint(toClean);
357#else
358 returnInfo->wasFlushed = QWidgetBackingStore::flushPaint(tlw, toClean);
359 // Avoid deadlock with QT_FLUSH_PAINT: the server will wait for
360 // the BackingStore lock, so if we hold that, the server will
361 // never release the Communication lock that we are waiting for in
362 // sendSynchronousCommand
363 if (!returnInfo->wasFlushed)
364 windowSurface->beginPaint(toClean);
365#endif
366
367 Q_UNUSED(returnInfo);
368}
369
370void QWidgetBackingStore::endPaint(const QRegion &cleaned, QWindowSurface *windowSurface,
371 BeginPaintInfo *beginPaintInfo)
372{
373#ifndef QT_NO_PAINT_DEBUG
374 if (!beginPaintInfo->wasFlushed)
375 windowSurface->endPaint(cleaned);
376 else
377 QWidgetBackingStore::unflushPaint(tlw, cleaned);
378#else
379 Q_UNUSED(beginPaintInfo);
380 windowSurface->endPaint(cleaned);
381#endif
382
383#ifdef Q_BACKINGSTORE_SUBSURFACES
384 flush(static_cast<QWSWindowSurface *>(windowSurface)->window(), windowSurface);
385#else
386 flush();
387#endif
388}
389
390/*!
391 Returns the region (in top-level coordinates) that needs repaint and/or flush.
392
393 If the widget is non-zero, only the dirty region for the widget is returned
394 and the region will be in widget coordinates.
395*/
396QRegion QWidgetBackingStore::dirtyRegion(QWidget *widget) const
397{
398 const bool widgetDirty = widget && widget != tlw;
399 const QRect tlwRect(topLevelRect());
400 const QRect surfaceGeometry(windowSurface->geometry());
401 if (surfaceGeometry != tlwRect && surfaceGeometry.size() != tlwRect.size()) {
402 if (widgetDirty) {
403 const QRect dirtyTlwRect = QRect(QPoint(), tlwRect.size());
404 const QPoint offset(widget->mapTo(tlw, QPoint()));
405 const QRect dirtyWidgetRect(dirtyTlwRect & widget->rect().translated(offset));
406 return dirtyWidgetRect.translated(-offset);
407 }
408 return QRect(QPoint(), tlwRect.size());
409 }
410
411 // Calculate the region that needs repaint.
412 QRegion r(dirty);
413 for (int i = 0; i < dirtyWidgets.size(); ++i) {
414 QWidget *w = dirtyWidgets.at(i);
415 if (widgetDirty && w != widget && !widget->isAncestorOf(w))
416 continue;
417 r += w->d_func()->dirty.translated(w->mapTo(tlw, QPoint()));
418 }
419
420 // Append the region that needs flush.
421 r += dirtyOnScreen;
422
423 if (dirtyOnScreenWidgets) { // Only in use with native child widgets.
424 for (int i = 0; i < dirtyOnScreenWidgets->size(); ++i) {
425 QWidget *w = dirtyOnScreenWidgets->at(i);
426 if (widgetDirty && w != widget && !widget->isAncestorOf(w))
427 continue;
428 QWidgetPrivate *wd = w->d_func();
429 Q_ASSERT(wd->needsFlush);
430 r += wd->needsFlush->translated(w->mapTo(tlw, QPoint()));
431 }
432 }
433
434 if (widgetDirty) {
435 // Intersect with the widget geometry and translate to its coordinates.
436 const QPoint offset(widget->mapTo(tlw, QPoint()));
437 r &= widget->rect().translated(offset);
438 r.translate(-offset);
439 }
440 return r;
441}
442
443/*!
444 Returns the static content inside the \a parent if non-zero; otherwise the static content
445 for the entire backing store is returned. The content will be clipped to \a withingClipRect
446 if non-empty.
447*/
448QRegion QWidgetBackingStore::staticContents(QWidget *parent, const QRect &withinClipRect) const
449{
450 if (!parent && tlw->testAttribute(Qt::WA_StaticContents)) {
451 const QRect surfaceGeometry(windowSurface->geometry());
452 QRect surfaceRect(0, 0, surfaceGeometry.width(), surfaceGeometry.height());
453 if (!withinClipRect.isEmpty())
454 surfaceRect &= withinClipRect;
455 return QRegion(surfaceRect);
456 }
457
458 QRegion region;
459 if (parent && parent->d_func()->children.isEmpty())
460 return region;
461
462 const bool clipToRect = !withinClipRect.isEmpty();
463 const int count = staticWidgets.count();
464 for (int i = 0; i < count; ++i) {
465 QWidget *w = staticWidgets.at(i);
466 QWidgetPrivate *wd = w->d_func();
467 if (!wd->isOpaque || !wd->extra || wd->extra->staticContentsSize.isEmpty()
468 || !w->isVisible() || (parent && !parent->isAncestorOf(w))) {
469 continue;
470 }
471
472 QRect rect(0, 0, wd->extra->staticContentsSize.width(), wd->extra->staticContentsSize.height());
473 const QPoint offset = w->mapTo(parent ? parent : tlw, QPoint());
474 if (clipToRect)
475 rect &= withinClipRect.translated(-offset);
476 if (rect.isEmpty())
477 continue;
478
479 rect &= wd->clipRect();
480 if (rect.isEmpty())
481 continue;
482
483 QRegion visible(rect);
484 wd->clipToEffectiveMask(visible);
485 if (visible.isEmpty())
486 continue;
487 wd->subtractOpaqueSiblings(visible, 0, /*alsoNonOpaque=*/true);
488
489 visible.translate(offset);
490 region += visible;
491 }
492
493 return region;
494}
495
496static inline void sendUpdateRequest(QWidget *widget, bool updateImmediately)
497{
498 if (!widget)
499 return;
500
501 if (updateImmediately) {
502 QEvent event(QEvent::UpdateRequest);
503 QApplication::sendEvent(widget, &event);
504 } else {
505 QApplication::postEvent(widget, new QEvent(QEvent::UpdateRequest), Qt::LowEventPriority);
506 }
507}
508
509/*!
510 Marks the region of the widget as dirty (if not already marked as dirty) and
511 posts an UpdateRequest event to the top-level widget (if not already posted).
512
513 If updateImmediately is true, the event is sent immediately instead of posted.
514
515 If invalidateBuffer is true, all widgets intersecting with the region will be dirty.
516
517 If the widget paints directly on screen, the event is sent to the widget
518 instead of the top-level widget, and invalidateBuffer is completely ignored.
519
520 ### Qt 4.6: Merge into a template function (after MSVC isn't supported anymore).
521*/
522void QWidgetBackingStore::markDirty(const QRegion &rgn, QWidget *widget, bool updateImmediately,
523 bool invalidateBuffer)
524{
525 Q_ASSERT(tlw->d_func()->extra);
526 Q_ASSERT(tlw->d_func()->extra->topextra);
527 Q_ASSERT(!tlw->d_func()->extra->topextra->inTopLevelResize);
528 Q_ASSERT(widget->isVisible() && widget->updatesEnabled());
529 Q_ASSERT(widget->window() == tlw);
530 Q_ASSERT(!rgn.isEmpty());
531
532#ifndef QT_NO_GRAPHICSEFFECT
533 widget->d_func()->invalidateGraphicsEffectsRecursively();
534#endif //QT_NO_GRAPHICSEFFECT
535
536 if (widget->d_func()->paintOnScreen()) {
537 if (widget->d_func()->dirty.isEmpty()) {
538 widget->d_func()->dirty = rgn;
539 sendUpdateRequest(widget, updateImmediately);
540 return;
541 } else if (qt_region_strictContains(widget->d_func()->dirty, widget->rect())) {
542 if (updateImmediately)
543 sendUpdateRequest(widget, updateImmediately);
544 return; // Already dirty.
545 }
546
547 const bool eventAlreadyPosted = !widget->d_func()->dirty.isEmpty();
548 widget->d_func()->dirty += rgn;
549 if (!eventAlreadyPosted || updateImmediately)
550 sendUpdateRequest(widget, updateImmediately);
551 return;
552 }
553
554 const QPoint offset = widget->mapTo(tlw, QPoint());
555 const QRect widgetRect = widget->d_func()->effectiveRectFor(widget->rect());
556 if (qt_region_strictContains(dirty, widgetRect.translated(offset))) {
557 if (updateImmediately)
558 sendUpdateRequest(tlw, updateImmediately);
559 return; // Already dirty.
560 }
561
562 if (invalidateBuffer) {
563 const bool eventAlreadyPosted = !dirty.isEmpty();
564#ifndef QT_NO_GRAPHICSEFFECT
565 if (widget->d_func()->graphicsEffect)
566 dirty += widget->d_func()->effectiveRectFor(rgn.boundingRect()).translated(offset);
567 else
568#endif //QT_NO_GRAPHICSEFFECT
569 dirty += rgn.translated(offset);
570 if (!eventAlreadyPosted || updateImmediately)
571 sendUpdateRequest(tlw, updateImmediately);
572 return;
573 }
574
575 if (dirtyWidgets.isEmpty()) {
576 addDirtyWidget(widget, rgn);
577 sendUpdateRequest(tlw, updateImmediately);
578 return;
579 }
580
581 if (widget->d_func()->inDirtyList) {
582 if (!qt_region_strictContains(widget->d_func()->dirty, widgetRect)) {
583#ifndef QT_NO_GRAPHICSEFFECT
584 if (widget->d_func()->graphicsEffect)
585 widget->d_func()->dirty += widget->d_func()->effectiveRectFor(rgn.boundingRect());
586 else
587#endif //QT_NO_GRAPHICSEFFECT
588 widget->d_func()->dirty += rgn;
589 }
590 } else {
591 addDirtyWidget(widget, rgn);
592 }
593
594 if (updateImmediately)
595 sendUpdateRequest(tlw, updateImmediately);
596}
597
598/*!
599 This function is equivalent to calling markDirty(QRegion(rect), ...), but
600 is more efficient as it eliminates QRegion operations/allocations and can
601 use the rect more precisely for additional cut-offs.
602
603 ### Qt 4.6: Merge into a template function (after MSVC isn't supported anymore).
604*/
605void QWidgetBackingStore::markDirty(const QRect &rect, QWidget *widget, bool updateImmediately,
606 bool invalidateBuffer)
607{
608 Q_ASSERT(tlw->d_func()->extra);
609 Q_ASSERT(tlw->d_func()->extra->topextra);
610 Q_ASSERT(!tlw->d_func()->extra->topextra->inTopLevelResize);
611 Q_ASSERT(widget->isVisible() && widget->updatesEnabled());
612 Q_ASSERT(widget->window() == tlw);
613 Q_ASSERT(!rect.isEmpty());
614
615#ifndef QT_NO_GRAPHICSEFFECT
616 widget->d_func()->invalidateGraphicsEffectsRecursively();
617#endif //QT_NO_GRAPHICSEFFECT
618
619 if (widget->d_func()->paintOnScreen()) {
620 if (widget->d_func()->dirty.isEmpty()) {
621 widget->d_func()->dirty = QRegion(rect);
622 sendUpdateRequest(widget, updateImmediately);
623 return;
624 } else if (qt_region_strictContains(widget->d_func()->dirty, rect)) {
625 if (updateImmediately)
626 sendUpdateRequest(widget, updateImmediately);
627 return; // Already dirty.
628 }
629
630 const bool eventAlreadyPosted = !widget->d_func()->dirty.isEmpty();
631 widget->d_func()->dirty += rect;
632 if (!eventAlreadyPosted || updateImmediately)
633 sendUpdateRequest(widget, updateImmediately);
634 return;
635 }
636
637 const QRect widgetRect = widget->d_func()->effectiveRectFor(rect);
638 const QRect translatedRect(widgetRect.translated(widget->mapTo(tlw, QPoint())));
639 if (qt_region_strictContains(dirty, translatedRect)) {
640 if (updateImmediately)
641 sendUpdateRequest(tlw, updateImmediately);
642 return; // Already dirty
643 }
644
645 if (invalidateBuffer) {
646 const bool eventAlreadyPosted = !dirty.isEmpty();
647 dirty += translatedRect;
648 if (!eventAlreadyPosted || updateImmediately)
649 sendUpdateRequest(tlw, updateImmediately);
650 return;
651 }
652
653 if (dirtyWidgets.isEmpty()) {
654 addDirtyWidget(widget, rect);
655 sendUpdateRequest(tlw, updateImmediately);
656 return;
657 }
658
659 if (widget->d_func()->inDirtyList) {
660 if (!qt_region_strictContains(widget->d_func()->dirty, widgetRect))
661 widget->d_func()->dirty += widgetRect;
662 } else {
663 addDirtyWidget(widget, rect);
664 }
665
666 if (updateImmediately)
667 sendUpdateRequest(tlw, updateImmediately);
668}
669
670/*!
671 Marks the \a region of the \a widget as dirty on screen. The \a region will be copied from
672 the backing store to the \a widget's native parent next time flush() is called.
673
674 Paint on screen widgets are ignored.
675*/
676void QWidgetBackingStore::markDirtyOnScreen(const QRegion &region, QWidget *widget, const QPoint &topLevelOffset)
677{
678 if (!widget || widget->d_func()->paintOnScreen() || region.isEmpty())
679 return;
680
681#if defined(Q_WS_QWS) || defined(Q_WS_MAC)
682 if (!widget->testAttribute(Qt::WA_WState_InPaintEvent))
683 dirtyOnScreen += region.translated(topLevelOffset);
684 return;
685#endif
686
687 // Top-level.
688 if (widget == tlw) {
689 if (!widget->testAttribute(Qt::WA_WState_InPaintEvent))
690 dirtyOnScreen += region;
691 return;
692 }
693
694 // Alien widgets.
695 if (!widget->internalWinId()) {
696 QWidget *nativeParent = widget->nativeParentWidget();
697 // Alien widgets with the top-level as the native parent (common case).
698 if (nativeParent == tlw) {
699 if (!widget->testAttribute(Qt::WA_WState_InPaintEvent))
700 dirtyOnScreen += region.translated(topLevelOffset);
701 return;
702 }
703
704 // Alien widgets with native parent != tlw.
705 QWidgetPrivate *nativeParentPrivate = nativeParent->d_func();
706 if (!nativeParentPrivate->needsFlush)
707 nativeParentPrivate->needsFlush = new QRegion;
708 const QPoint nativeParentOffset = widget->mapTo(nativeParent, QPoint());
709 *nativeParentPrivate->needsFlush += region.translated(nativeParentOffset);
710 appendDirtyOnScreenWidget(nativeParent);
711 return;
712 }
713
714 // Native child widgets.
715 QWidgetPrivate *widgetPrivate = widget->d_func();
716 if (!widgetPrivate->needsFlush)
717 widgetPrivate->needsFlush = new QRegion;
718 *widgetPrivate->needsFlush += region;
719 appendDirtyOnScreenWidget(widget);
720}
721
722void QWidgetBackingStore::removeDirtyWidget(QWidget *w)
723{
724 if (!w)
725 return;
726
727 dirtyWidgetsRemoveAll(w);
728 dirtyOnScreenWidgetsRemoveAll(w);
729 resetWidget(w);
730
731 QWidgetPrivate *wd = w->d_func();
732 const int n = wd->children.count();
733 for (int i = 0; i < n; ++i) {
734 if (QWidget *child = qobject_cast<QWidget*>(wd->children.at(i)))
735 removeDirtyWidget(child);
736 }
737}
738
739#if defined(Q_WS_QWS) && !defined(QT_NO_QWS_MANAGER)
740bool QWidgetBackingStore::hasDirtyWindowDecoration() const
741{
742 QTLWExtra *tlwExtra = tlw->d_func()->maybeTopData();
743 if (tlwExtra && tlwExtra->qwsManager)
744 return !tlwExtra->qwsManager->d_func()->dirtyRegions.isEmpty();
745 return false;
746}
747
748void QWidgetBackingStore::paintWindowDecoration()
749{
750 if (!hasDirtyWindowDecoration())
751 return;
752
753 QDecoration &decoration = QApplication::qwsDecoration();
754 const QRect decorationRect = tlw->rect();
755 QRegion decorationRegion = decoration.region(tlw, decorationRect);
756
757 QWSManagerPrivate *managerPrivate = tlw->d_func()->topData()->qwsManager->d_func();
758 const bool doClipping = !managerPrivate->entireDecorationNeedsRepaint
759 && !managerPrivate->dirtyClip.isEmpty();
760
761 if (doClipping) {
762 decorationRegion &= static_cast<QWSWindowSurface *>(windowSurface)->clipRegion();
763 decorationRegion &= managerPrivate->dirtyClip;
764 }
765
766 if (decorationRegion.isEmpty())
767 return;
768
769 windowSurface->beginPaint(decorationRegion);
770
771 QPaintEngine *engine = windowSurface->paintDevice()->paintEngine();
772 Q_ASSERT(engine);
773 const QRegion oldSystemClip(engine->systemClip());
774 engine->setSystemClip(decorationRegion.translated(tlwOffset));
775
776 QPainter painter(windowSurface->paintDevice());
777 painter.setFont(QApplication::font());
778 painter.translate(tlwOffset);
779
780 const int numDirty = managerPrivate->dirtyRegions.size();
781 for (int i = 0; i < numDirty; ++i) {
782 const int area = managerPrivate->dirtyRegions.at(i);
783
784 QRegion clipRegion = decoration.region(tlw, decorationRect, area);
785 if (!clipRegion.isEmpty()) {
786 // Decoration styles changes the clip and assumes the old clip is non-empty,
787 // so we have to set it, but in theory it shouldn't be required.
788 painter.setClipRegion(clipRegion);
789 decoration.paint(&painter, tlw, area, managerPrivate->dirtyStates.at(i));
790 }
791 }
792 markDirtyOnScreen(decorationRegion, tlw, QPoint());
793
794 painter.end();
795 windowSurface->endPaint(decorationRegion);
796 managerPrivate->clearDirtyRegions();
797 engine->setSystemClip(oldSystemClip);
798}
799#endif
800
801void QWidgetBackingStore::updateLists(QWidget *cur)
802{
803 if (!cur)
804 return;
805
806 QList<QObject*> children = cur->children();
807 for (int i = 0; i < children.size(); ++i) {
808 QWidget *child = qobject_cast<QWidget*>(children.at(i));
809 if (!child)
810 continue;
811
812 updateLists(child);
813 }
814
815 if (cur->testAttribute(Qt::WA_StaticContents))
816 addStaticWidget(cur);
817
818#ifdef Q_BACKINGSTORE_SUBSURFACES
819 QTLWExtra *extra = cur->d_func()->maybeTopData();
820 if (extra && extra->windowSurface && cur != tlw)
821 subSurfaces.append(extra->windowSurface);
822#endif
823}
824
825QWidgetBackingStore::QWidgetBackingStore(QWidget *topLevel)
826 : tlw(topLevel), dirtyOnScreenWidgets(0), hasDirtyFromPreviousSync(false)
827{
828 windowSurface = tlw->windowSurface();
829 if (!windowSurface)
830 windowSurface = topLevel->d_func()->createDefaultWindowSurface();
831
832 // The QWindowSurface constructor will call QWidget::setWindowSurface(),
833 // but automatically created surfaces should not be added to the topdata.
834#ifdef Q_BACKINGSTORE_SUBSURFACES
835 Q_ASSERT(topLevel->d_func()->topData()->windowSurface == windowSurface);
836#endif
837 topLevel->d_func()->topData()->windowSurface = 0;
838
839 // Ensure all existing subsurfaces and static widgets are added to their respective lists.
840 updateLists(topLevel);
841}
842
843QWidgetBackingStore::~QWidgetBackingStore()
844{
845 for (int c = 0; c < dirtyWidgets.size(); ++c) {
846 resetWidget(dirtyWidgets.at(c));
847 }
848
849 delete windowSurface;
850 windowSurface = 0;
851 delete dirtyOnScreenWidgets;
852 dirtyOnScreenWidgets = 0;
853}
854
855//parent's coordinates; move whole rect; update parent and widget
856//assume the screen blt has already been done, so we don't need to refresh that part
857void QWidgetPrivate::moveRect(const QRect &rect, int dx, int dy)
858{
859 Q_Q(QWidget);
860 if (!q->isVisible() || (dx == 0 && dy == 0))
861 return;
862
863 QWidget *tlw = q->window();
864 QTLWExtra* x = tlw->d_func()->topData();
865 if (x->inTopLevelResize)
866 return;
867
868 static int accelEnv = -1;
869 if (accelEnv == -1) {
870 accelEnv = qgetenv("QT_NO_FAST_MOVE").toInt() == 0;
871 }
872
873 QWidget *pw = q->parentWidget();
874 QPoint toplevelOffset = pw->mapTo(tlw, QPoint());
875 QWidgetPrivate *pd = pw->d_func();
876 QRect clipR(pd->clipRect());
877#ifdef Q_WS_QWS
878 QWidgetBackingStore *wbs = x->backingStore;
879 QWSWindowSurface *surface = static_cast<QWSWindowSurface*>(wbs->windowSurface);
880 clipR = clipR.intersected(surface->clipRegion().translated(-toplevelOffset).boundingRect());
881#endif
882 const QRect newRect(rect.translated(dx, dy));
883 QRect destRect = rect.intersected(clipR);
884 if (destRect.isValid())
885 destRect = destRect.translated(dx, dy).intersected(clipR);
886 const QRect sourceRect(destRect.translated(-dx, -dy));
887 const QRect parentRect(rect & clipR);
888
889 bool accelerateMove = accelEnv && isOpaque
890#ifndef QT_NO_GRAPHICSVIEW
891 // No accelerate move for proxy widgets.
892 && !tlw->d_func()->extra->proxyWidget
893#endif
894 && !isOverlapped(sourceRect) && !isOverlapped(destRect);
895
896 if (!accelerateMove) {
897 QRegion parentR(effectiveRectFor(parentRect));
898 if (!extra || !extra->hasMask) {
899 parentR -= newRect;
900 } else {
901 // invalidateBuffer() excludes anything outside the mask
902 parentR += newRect & clipR;
903 }
904 pd->invalidateBuffer(parentR);
905 invalidateBuffer((newRect & clipR).translated(-data.crect.topLeft()));
906 } else {
907
908 QWidgetBackingStore *wbs = x->backingStore;
909 QRegion childExpose(newRect & clipR);
910
911 if (sourceRect.isValid() && wbs->bltRect(sourceRect, dx, dy, pw))
912 childExpose -= destRect;
913
914 if (!pw->updatesEnabled())
915 return;
916
917 const bool childUpdatesEnabled = q->updatesEnabled();
918 if (childUpdatesEnabled && !childExpose.isEmpty()) {
919 childExpose.translate(-data.crect.topLeft());
920 wbs->markDirty(childExpose, q);
921 isMoved = true;
922 }
923
924 QRegion parentExpose(parentRect);
925 parentExpose -= newRect;
926 if (extra && extra->hasMask)
927 parentExpose += QRegion(newRect) - extra->mask.translated(data.crect.topLeft());
928
929 if (!parentExpose.isEmpty()) {
930 wbs->markDirty(parentExpose, pw);
931 pd->isMoved = true;
932 }
933
934 if (childUpdatesEnabled) {
935 QRegion needsFlush(sourceRect);
936 needsFlush += destRect;
937 wbs->markDirtyOnScreen(needsFlush, pw, toplevelOffset);
938 }
939 }
940}
941
942//widget's coordinates; scroll within rect; only update widget
943void QWidgetPrivate::scrollRect(const QRect &rect, int dx, int dy)
944{
945 Q_Q(QWidget);
946 QWidget *tlw = q->window();
947 QTLWExtra* x = tlw->d_func()->topData();
948 if (x->inTopLevelResize)
949 return;
950
951 QWidgetBackingStore *wbs = x->backingStore;
952 if (!wbs)
953 return;
954
955 static int accelEnv = -1;
956 if (accelEnv == -1) {
957 accelEnv = qgetenv("QT_NO_FAST_SCROLL").toInt() == 0;
958 }
959
960 QRect scrollRect = rect & clipRect();
961 bool overlapped = false;
962 bool accelerateScroll = accelEnv && isOpaque
963 && !(overlapped = isOverlapped(scrollRect.translated(data.crect.topLeft())));
964
965#if defined(Q_WS_QWS)
966 QWSWindowSurface *surface;
967 surface = static_cast<QWSWindowSurface*>(wbs->windowSurface);
968
969 if (accelerateScroll && !surface->isBuffered()) {
970 const QRegion surfaceClip = surface->clipRegion();
971 const QRegion outsideClip = QRegion(rect) - surfaceClip;
972 if (!outsideClip.isEmpty()) {
973 const QVector<QRect> clipped = (surfaceClip & rect).rects();
974 if (clipped.size() < 8) {
975 for (int i = 0; i < clipped.size(); ++i)
976 this->scrollRect(clipped.at(i), dx, dy);
977 return;
978 } else {
979 accelerateScroll = false;
980 }
981 }
982 }
983#endif // Q_WS_QWS
984
985 if (!accelerateScroll) {
986 if (overlapped) {
987 QRegion region(scrollRect);
988 subtractOpaqueSiblings(region);
989 invalidateBuffer(region);
990 }else {
991 invalidateBuffer(scrollRect);
992 }
993 } else {
994 const QPoint toplevelOffset = q->mapTo(tlw, QPoint());
995#ifdef Q_WS_QWS
996 QWSWindowSurface *surface = static_cast<QWSWindowSurface*>(wbs->windowSurface);
997 const QRegion clip = surface->clipRegion().translated(-toplevelOffset) & scrollRect;
998 const QRect clipBoundingRect = clip.boundingRect();
999 scrollRect &= clipBoundingRect;
1000#endif
1001 const QRect destRect = scrollRect.translated(dx, dy) & scrollRect;
1002 const QRect sourceRect = destRect.translated(-dx, -dy);
1003
1004 QRegion childExpose(scrollRect);
1005 if (sourceRect.isValid()) {
1006 if (wbs->bltRect(sourceRect, dx, dy, q))
1007 childExpose -= destRect;
1008 }
1009
1010 if (inDirtyList) {
1011 if (rect == q->rect()) {
1012 dirty.translate(dx, dy);
1013 } else {
1014 QRegion dirtyScrollRegion = dirty.intersected(scrollRect);
1015 if (!dirtyScrollRegion.isEmpty()) {
1016 dirty -= dirtyScrollRegion;
1017 dirtyScrollRegion.translate(dx, dy);
1018 dirty += dirtyScrollRegion;
1019 }
1020 }
1021 }
1022
1023 if (!q->updatesEnabled())
1024 return;
1025
1026 if (!childExpose.isEmpty()) {
1027 wbs->markDirty(childExpose, q);
1028 isScrolled = true;
1029 }
1030
1031 // Instead of using native scroll-on-screen, we copy from
1032 // backingstore, giving only one screen update for each
1033 // scroll, and a solid appearance
1034 wbs->markDirtyOnScreen(destRect, q, toplevelOffset);
1035 }
1036}
1037
1038static inline bool discardSyncRequest(QWidget *tlw, QTLWExtra *tlwExtra)
1039{
1040 if (!tlw || !tlwExtra)
1041 return true;
1042
1043#ifdef Q_WS_X11
1044 // Delay the sync until we get an Expose event from X11 (initial show).
1045 // Qt::WA_Mapped is set to true, but the actual mapping has not yet occurred.
1046 // However, we must repaint immediately regardless of the state if someone calls repaint().
1047 if (tlwExtra->waitingForMapNotify && !tlwExtra->inRepaint)
1048 return true;
1049#endif
1050
1051 if (!tlw->testAttribute(Qt::WA_Mapped))
1052 return true;
1053
1054 if (!tlw->isVisible()
1055#ifndef Q_WS_X11
1056 // If we're minimized on X11, WA_Mapped will be false and we
1057 // will return in the case above. Some window managers on X11
1058 // sends us the PropertyNotify to change the minimized state
1059 // *AFTER* we've received the expose event, which is baaad.
1060 || tlw->isMinimized()
1061#endif
1062 )
1063 return true;
1064
1065 return false;
1066}
1067
1068/*!
1069 Synchronizes the \a exposedRegion of the \a exposedWidget with the backing store.
1070
1071 If there's nothing to repaint, the area is flushed and painting does not occur;
1072 otherwise the area is marked as dirty on screen and will be flushed right after
1073 we are done with all painting.
1074*/
1075void QWidgetBackingStore::sync(QWidget *exposedWidget, const QRegion &exposedRegion)
1076{
1077 QTLWExtra *tlwExtra = tlw->d_func()->maybeTopData();
1078 if (discardSyncRequest(tlw, tlwExtra) || tlwExtra->inTopLevelResize)
1079 return;
1080
1081 if (!exposedWidget || !exposedWidget->internalWinId() || !exposedWidget->isVisible()
1082 || !exposedWidget->updatesEnabled() || exposedRegion.isEmpty()) {
1083 return;
1084 }
1085
1086 // Nothing to repaint.
1087 if (!isDirty()) {
1088 qt_flush(exposedWidget, exposedRegion, windowSurface, tlw, tlwOffset);
1089 return;
1090 }
1091
1092 if (exposedWidget != tlw)
1093 markDirtyOnScreen(exposedRegion, exposedWidget, exposedWidget->mapTo(tlw, QPoint()));
1094 else
1095 markDirtyOnScreen(exposedRegion, exposedWidget, QPoint());
1096 sync();
1097}
1098
1099/*!
1100 Synchronizes the backing store, i.e. dirty areas are repainted and flushed.
1101*/
1102void QWidgetBackingStore::sync()
1103{
1104 QTLWExtra *tlwExtra = tlw->d_func()->maybeTopData();
1105 if (discardSyncRequest(tlw, tlwExtra)) {
1106 // If the top-level is minimized, it's not visible on the screen so we can delay the
1107 // update until it's shown again. In order to do that we must keep the dirty states.
1108 // These will be cleared when we receive the first expose after showNormal().
1109 // However, if the widget is not visible (isVisible() returns false), everything will
1110 // be invalidated once the widget is shown again, so clear all dirty states.
1111 if (!tlw->isVisible()) {
1112 dirty = QRegion();
1113 for (int i = 0; i < dirtyWidgets.size(); ++i)
1114 resetWidget(dirtyWidgets.at(i));
1115 dirtyWidgets.clear();
1116 }
1117 return;
1118 }
1119
1120 const bool inTopLevelResize = tlwExtra->inTopLevelResize;
1121 const bool updatesDisabled = !tlw->updatesEnabled();
1122 const QRect tlwRect(topLevelRect());
1123 const QRect surfaceGeometry(windowSurface->geometry());
1124 bool repaintAllWidgets = false;
1125
1126 if (inTopLevelResize || surfaceGeometry != tlwRect) {
1127 if ((inTopLevelResize || surfaceGeometry.size() != tlwRect.size()) && !updatesDisabled) {
1128 if (hasStaticContents()) {
1129 // Repaint existing dirty area and newly visible area.
1130 const QRect clipRect(0, 0, surfaceGeometry.width(), surfaceGeometry.height());
1131 const QRegion staticRegion(staticContents(0, clipRect));
1132 QRegion newVisible(0, 0, tlwRect.width(), tlwRect.height());
1133 newVisible -= staticRegion;
1134 dirty += newVisible;
1135 windowSurface->setStaticContents(staticRegion);
1136 } else {
1137 // Repaint everything.
1138 dirty = QRegion(0, 0, tlwRect.width(), tlwRect.height());
1139 for (int i = 0; i < dirtyWidgets.size(); ++i)
1140 resetWidget(dirtyWidgets.at(i));
1141 dirtyWidgets.clear();
1142 repaintAllWidgets = true;
1143 }
1144 }
1145 windowSurface->setGeometry(tlwRect);
1146 }
1147
1148 if (updatesDisabled)
1149 return;
1150
1151 if (hasDirtyFromPreviousSync)
1152 dirty += dirtyFromPreviousSync;
1153
1154 // Contains everything that needs repaint.
1155 QRegion toClean(dirty);
1156
1157 // Loop through all update() widgets and remove them from the list before they are
1158 // painted (in case someone calls update() in paintEvent). If the widget is opaque
1159 // and does not have transparent overlapping siblings, append it to the
1160 // opaqueNonOverlappedWidgets list and paint it directly without composition.
1161 QVarLengthArray<QWidget *, 32> opaqueNonOverlappedWidgets;
1162 for (int i = 0; i < dirtyWidgets.size(); ++i) {
1163 QWidget *w = dirtyWidgets.at(i);
1164 QWidgetPrivate *wd = w->d_func();
1165 if (wd->data.in_destructor)
1166 continue;
1167
1168 // Clip with mask() and clipRect().
1169 wd->dirty &= wd->clipRect();
1170 wd->clipToEffectiveMask(wd->dirty);
1171
1172 // Subtract opaque siblings and children.
1173 bool hasDirtySiblingsAbove = false;
1174 // We know for sure that the widget isn't overlapped if 'isMoved' is true.
1175 if (!wd->isMoved)
1176 wd->subtractOpaqueSiblings(wd->dirty, &hasDirtySiblingsAbove);
1177 // Scrolled and moved widgets must draw all children.
1178 if (!wd->isScrolled && !wd->isMoved)
1179 wd->subtractOpaqueChildren(wd->dirty, w->rect());
1180
1181 if (wd->dirty.isEmpty()) {
1182 resetWidget(w);
1183 continue;
1184 }
1185
1186 const QRegion widgetDirty(w != tlw ? wd->dirty.translated(w->mapTo(tlw, QPoint()))
1187 : wd->dirty);
1188 toClean += widgetDirty;
1189
1190#ifndef QT_NO_GRAPHICSVIEW
1191 if (tlw->d_func()->extra->proxyWidget) {
1192 resetWidget(w);
1193 continue;
1194 }
1195#endif
1196
1197 if (!hasDirtySiblingsAbove && wd->isOpaque && !dirty.intersects(widgetDirty.boundingRect())) {
1198 opaqueNonOverlappedWidgets.append(w);
1199 } else {
1200 resetWidget(w);
1201 dirty += widgetDirty;
1202 }
1203 }
1204 dirtyWidgets.clear();
1205
1206 if (toClean.isEmpty()) {
1207 // Nothing to repaint. However, we might have newly exposed areas on the
1208 // screen if this function was called from sync(QWidget *, QRegion)), so
1209 // we have to make sure those are flushed.
1210 flush();
1211 return;
1212 }
1213
1214#ifndef QT_NO_GRAPHICSVIEW
1215 if (tlw->d_func()->extra->proxyWidget) {
1216 updateStaticContentsSize();
1217 dirty = QRegion();
1218 const QVector<QRect> rects(toClean.rects());
1219 for (int i = 0; i < rects.size(); ++i)
1220 tlw->d_func()->extra->proxyWidget->update(rects.at(i));
1221 return;
1222 }
1223#endif
1224
1225#ifndef Q_BACKINGSTORE_SUBSURFACES
1226 BeginPaintInfo beginPaintInfo;
1227 beginPaint(toClean, tlw, windowSurface, &beginPaintInfo);
1228 if (beginPaintInfo.nothingToPaint) {
1229 for (int i = 0; i < opaqueNonOverlappedWidgets.size(); ++i)
1230 resetWidget(opaqueNonOverlappedWidgets[i]);
1231 dirty = QRegion();
1232 return;
1233 }
1234#endif
1235
1236 // Must do this before sending any paint events because
1237 // the size may change in the paint event.
1238 updateStaticContentsSize();
1239 const QRegion dirtyCopy(dirty);
1240 dirty = QRegion();
1241
1242 // Paint opaque non overlapped widgets.
1243 for (int i = 0; i < opaqueNonOverlappedWidgets.size(); ++i) {
1244 QWidget *w = opaqueNonOverlappedWidgets[i];
1245 QWidgetPrivate *wd = w->d_func();
1246
1247 int flags = QWidgetPrivate::DrawRecursive;
1248 // Scrolled and moved widgets must draw all children.
1249 if (!wd->isScrolled && !wd->isMoved)
1250 flags |= QWidgetPrivate::DontDrawOpaqueChildren;
1251 if (w == tlw)
1252 flags |= QWidgetPrivate::DrawAsRoot;
1253
1254 QRegion toBePainted(wd->dirty);
1255 resetWidget(w);
1256
1257#ifdef Q_BACKINGSTORE_SUBSURFACES
1258 QWindowSurface *subSurface = w->windowSurface();
1259 BeginPaintInfo beginPaintInfo;
1260 beginPaint(toBePainted, w, subSurface, &beginPaintInfo, false);
1261 if (beginPaintInfo.nothingToPaint)
1262 continue;
1263
1264 if (beginPaintInfo.windowSurfaceRecreated) {
1265 // Eep the window surface has changed. The old one may have been
1266 // deleted, in which case we will segfault on the call to
1267 // painterOffset() below. Use the new window surface instead.
1268 subSurface = w->windowSurface();
1269 }
1270
1271 QPoint offset(tlwOffset);
1272 if (subSurface == windowSurface)
1273 offset += w->mapTo(tlw, QPoint());
1274 else
1275 offset = static_cast<QWSWindowSurface*>(subSurface)->painterOffset();
1276 wd->drawWidget(subSurface->paintDevice(), toBePainted, offset, flags, 0, this);
1277
1278 endPaint(toBePainted, subSurface, &beginPaintInfo);
1279#else
1280 QPoint offset(tlwOffset);
1281 if (w != tlw)
1282 offset += w->mapTo(tlw, QPoint());
1283 wd->drawWidget(windowSurface->paintDevice(), toBePainted, offset, flags, 0, this);
1284#endif
1285 }
1286
1287 // Paint the rest with composition.
1288#ifndef Q_BACKINGSTORE_SUBSURFACES
1289 if (repaintAllWidgets || !dirtyCopy.isEmpty()) {
1290 const int flags = QWidgetPrivate::DrawAsRoot | QWidgetPrivate::DrawRecursive;
1291 tlw->d_func()->drawWidget(windowSurface->paintDevice(), dirtyCopy, tlwOffset, flags, 0, this);
1292 }
1293
1294 endPaint(toClean, windowSurface, &beginPaintInfo);
1295#else
1296 if (!repaintAllWidgets && dirtyCopy.isEmpty())
1297 return; // Nothing more to paint.
1298
1299 QList<QWindowSurface *> surfaceList(subSurfaces);
1300 surfaceList.prepend(windowSurface);
1301 const QRect dirtyBoundingRect(dirtyCopy.boundingRect());
1302
1303 // Loop through all window surfaces (incl. the top-level surface) and
1304 // repaint those intersecting with the bounding rect of the dirty region.
1305 for (int i = 0; i < surfaceList.size(); ++i) {
1306 QWindowSurface *subSurface = surfaceList.at(i);
1307 QWidget *w = subSurface->window();
1308 QWidgetPrivate *wd = w->d_func();
1309
1310 const QRect clipRect = wd->clipRect().translated(w->mapTo(tlw, QPoint()));
1311 if (!qRectIntersects(dirtyBoundingRect, clipRect))
1312 continue;
1313
1314 toClean = dirtyCopy;
1315 BeginPaintInfo beginPaintInfo;
1316 beginPaint(toClean, w, subSurface, &beginPaintInfo);
1317 if (beginPaintInfo.nothingToPaint)
1318 continue;
1319
1320 if (beginPaintInfo.windowSurfaceRecreated) {
1321 // Eep the window surface has changed. The old one may have been
1322 // deleted, in which case we will segfault on the call to
1323 // painterOffset() below. Use the new window surface instead.
1324 subSurface = w->windowSurface();
1325 }
1326
1327 int flags = QWidgetPrivate::DrawRecursive;
1328 if (w == tlw)
1329 flags |= QWidgetPrivate::DrawAsRoot;
1330 const QPoint painterOffset = static_cast<QWSWindowSurface*>(subSurface)->painterOffset();
1331 wd->drawWidget(subSurface->paintDevice(), toClean, painterOffset, flags, 0, this);
1332
1333 endPaint(toClean, subSurface, &beginPaintInfo);
1334 }
1335#endif
1336}
1337
1338/*!
1339 Flushes the contents of the backing store into the top-level widget.
1340 If the \a widget is non-zero, the content is flushed to the \a widget.
1341 If the \a surface is non-zero, the content of the \a surface is flushed.
1342*/
1343void QWidgetBackingStore::flush(QWidget *widget, QWindowSurface *surface)
1344{
1345#if defined(Q_WS_QWS) && !defined(QT_NO_QWS_MANAGER)
1346 paintWindowDecoration();
1347#endif
1348
1349 if (!dirtyOnScreen.isEmpty()) {
1350 QWidget *target = widget ? widget : tlw;
1351 QWindowSurface *source = surface ? surface : windowSurface;
1352 qt_flush(target, dirtyOnScreen, source, tlw, tlwOffset);
1353 dirtyOnScreen = QRegion();
1354 }
1355
1356 if (!dirtyOnScreenWidgets || dirtyOnScreenWidgets->isEmpty())
1357 return;
1358
1359 for (int i = 0; i < dirtyOnScreenWidgets->size(); ++i) {
1360 QWidget *w = dirtyOnScreenWidgets->at(i);
1361 QWidgetPrivate *wd = w->d_func();
1362 Q_ASSERT(wd->needsFlush);
1363 qt_flush(w, *wd->needsFlush, windowSurface, tlw, tlwOffset);
1364 *wd->needsFlush = QRegion();
1365 }
1366 dirtyOnScreenWidgets->clear();
1367}
1368
1369static inline bool discardInvalidateBufferRequest(QWidget *widget, QTLWExtra *tlwExtra)
1370{
1371 Q_ASSERT(widget);
1372 if (QApplication::closingDown())
1373 return true;
1374
1375 if (!tlwExtra || tlwExtra->inTopLevelResize || !tlwExtra->backingStore)
1376 return true;
1377
1378 if (!widget->isVisible() || !widget->updatesEnabled())
1379 return true;
1380
1381 return false;
1382}
1383
1384/*!
1385 Invalidates the buffer when the widget is resized.
1386 Static areas are never invalidated unless absolutely needed.
1387*/
1388void QWidgetPrivate::invalidateBuffer_resizeHelper(const QPoint &oldPos, const QSize &oldSize)
1389{
1390 Q_Q(QWidget);
1391 Q_ASSERT(!q->isWindow());
1392 Q_ASSERT(q->parentWidget());
1393
1394 const bool staticContents = q->testAttribute(Qt::WA_StaticContents);
1395 const bool sizeDecreased = (data.crect.width() < oldSize.width())
1396 || (data.crect.height() < oldSize.height());
1397
1398 const QPoint offset(data.crect.x() - oldPos.x(), data.crect.y() - oldPos.y());
1399 const bool parentAreaExposed = !offset.isNull() || sizeDecreased;
1400 const QRect newWidgetRect(q->rect());
1401 const QRect oldWidgetRect(0, 0, oldSize.width(), oldSize.height());
1402
1403 if (!staticContents || graphicsEffect) {
1404 QRegion staticChildren;
1405 QWidgetBackingStore *bs = 0;
1406 if (offset.isNull() && (bs = maybeBackingStore()))
1407 staticChildren = bs->staticContents(q, oldWidgetRect);
1408 const bool hasStaticChildren = !staticChildren.isEmpty();
1409
1410 if (hasStaticChildren) {
1411 QRegion dirty(newWidgetRect);
1412 dirty -= staticChildren;
1413 invalidateBuffer(dirty);
1414 } else {
1415 // Entire widget needs repaint.
1416 invalidateBuffer(newWidgetRect);
1417 }
1418
1419 if (!parentAreaExposed)
1420 return;
1421
1422 // Invalidate newly exposed area of the parent.
1423 if (!graphicsEffect && extra && extra->hasMask) {
1424 QRegion parentExpose(extra->mask.translated(oldPos));
1425 parentExpose &= QRect(oldPos, oldSize);
1426 if (hasStaticChildren)
1427 parentExpose -= data.crect; // Offset is unchanged, safe to do this.
1428 q->parentWidget()->d_func()->invalidateBuffer(parentExpose);
1429 } else {
1430 if (hasStaticChildren && !graphicsEffect) {
1431 QRegion parentExpose(QRect(oldPos, oldSize));
1432 parentExpose -= data.crect; // Offset is unchanged, safe to do this.
1433 q->parentWidget()->d_func()->invalidateBuffer(parentExpose);
1434 } else {
1435 q->parentWidget()->d_func()->invalidateBuffer(effectiveRectFor(QRect(oldPos, oldSize)));
1436 }
1437 }
1438 return;
1439 }
1440
1441 // Move static content to its new position.
1442 if (!offset.isNull()) {
1443 if (sizeDecreased) {
1444 const QSize minSize(qMin(oldSize.width(), data.crect.width()),
1445 qMin(oldSize.height(), data.crect.height()));
1446 moveRect(QRect(oldPos, minSize), offset.x(), offset.y());
1447 } else {
1448 moveRect(QRect(oldPos, oldSize), offset.x(), offset.y());
1449 }
1450 }
1451
1452 // Invalidate newly visible area of the widget.
1453 if (!sizeDecreased || !oldWidgetRect.contains(newWidgetRect)) {
1454 QRegion newVisible(newWidgetRect);
1455 newVisible -= oldWidgetRect;
1456 invalidateBuffer(newVisible);
1457 }
1458
1459 if (!parentAreaExposed)
1460 return;
1461
1462 // Invalidate newly exposed area of the parent.
1463 const QRect oldRect(oldPos, oldSize);
1464 if (extra && extra->hasMask) {
1465 QRegion parentExpose(oldRect);
1466 parentExpose &= extra->mask.translated(oldPos);
1467 parentExpose -= (extra->mask.translated(data.crect.topLeft()) & data.crect);
1468 q->parentWidget()->d_func()->invalidateBuffer(parentExpose);
1469 } else {
1470 QRegion parentExpose(oldRect);
1471 parentExpose -= data.crect;
1472 q->parentWidget()->d_func()->invalidateBuffer(parentExpose);
1473 }
1474}
1475
1476/*!
1477 Invalidates the \a rgn (in widget's coordinates) of the backing store, i.e.
1478 all widgets intersecting with the region will be repainted when the backing store
1479 is synced.
1480
1481 ### Qt 4.6: Merge into a template function (after MSVC isn't supported anymore).
1482*/
1483void QWidgetPrivate::invalidateBuffer(const QRegion &rgn)
1484{
1485 Q_Q(QWidget);
1486
1487 QTLWExtra *tlwExtra = q->window()->d_func()->maybeTopData();
1488 if (discardInvalidateBufferRequest(q, tlwExtra) || rgn.isEmpty())
1489 return;
1490
1491 QRegion wrgn(rgn);
1492 wrgn &= clipRect();
1493 if (!graphicsEffect && extra && extra->hasMask)
1494 wrgn &= extra->mask;
1495 if (wrgn.isEmpty())
1496 return;
1497
1498 tlwExtra->backingStore->markDirty(wrgn, q, false, true);
1499}
1500
1501/*!
1502 This function is equivalent to calling invalidateBuffer(QRegion(rect), ...), but
1503 is more efficient as it eliminates QRegion operations/allocations and can
1504 use the rect more precisely for additional cut-offs.
1505
1506 ### Qt 4.6: Merge into a template function (after MSVC isn't supported anymore).
1507*/
1508void QWidgetPrivate::invalidateBuffer(const QRect &rect)
1509{
1510 Q_Q(QWidget);
1511
1512 QTLWExtra *tlwExtra = q->window()->d_func()->maybeTopData();
1513 if (discardInvalidateBufferRequest(q, tlwExtra) || rect.isEmpty())
1514 return;
1515
1516 QRect wRect(rect);
1517 wRect &= clipRect();
1518 if (wRect.isEmpty())
1519 return;
1520
1521 if (graphicsEffect || !extra || !extra->hasMask) {
1522 tlwExtra->backingStore->markDirty(wRect, q, false, true);
1523 return;
1524 }
1525
1526 QRegion wRgn(extra->mask);
1527 wRgn &= wRect;
1528 if (wRgn.isEmpty())
1529 return;
1530
1531 tlwExtra->backingStore->markDirty(wRgn, q, false, true);
1532}
1533
1534void QWidgetPrivate::repaint_sys(const QRegion &rgn)
1535{
1536 if (data.in_destructor)
1537 return;
1538
1539 Q_Q(QWidget);
1540 if (q->testAttribute(Qt::WA_StaticContents)) {
1541 if (!extra)
1542 createExtra();
1543 extra->staticContentsSize = data.crect.size();
1544 }
1545
1546 QPaintEngine *engine = q->paintEngine();
1547 // QGLWidget does not support partial updates if:
1548 // 1) The context is double buffered
1549 // 2) The context is single buffered and auto-fill background is enabled.
1550 const bool noPartialUpdateSupport = (engine && (engine->type() == QPaintEngine::OpenGL
1551 || engine->type() == QPaintEngine::OpenGL2))
1552 && (usesDoubleBufferedGLContext || q->autoFillBackground());
1553 QRegion toBePainted(noPartialUpdateSupport ? q->rect() : rgn);
1554
1555#ifdef Q_WS_MAC
1556 // No difference between update() and repaint() on the Mac.
1557 update_sys(toBePainted);
1558 return;
1559#endif
1560
1561 toBePainted &= clipRect();
1562 clipToEffectiveMask(toBePainted);
1563 if (toBePainted.isEmpty())
1564 return; // Nothing to repaint.
1565
1566#ifndef QT_NO_PAINT_DEBUG
1567 bool flushed = QWidgetBackingStore::flushPaint(q, toBePainted);
1568#endif
1569
1570 drawWidget(q, toBePainted, QPoint(), QWidgetPrivate::DrawAsRoot | QWidgetPrivate::DrawPaintOnScreen, 0);
1571
1572#ifndef QT_NO_PAINT_DEBUG
1573 if (flushed)
1574 QWidgetBackingStore::unflushPaint(q, toBePainted);
1575#endif
1576
1577 if (!q->testAttribute(Qt::WA_PaintOutsidePaintEvent) && q->paintingActive())
1578 qWarning("QWidget::repaint: It is dangerous to leave painters active on a widget outside of the PaintEvent");
1579}
1580
1581
1582QT_END_NAMESPACE
Note: See TracBrowser for help on using the repository browser.