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

Last change on this file was 846, checked in by Dmitry A. Kuminov, 14 years ago

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

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