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

Last change on this file since 742 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().