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

Last change on this file since 113 was 2, checked in by Dmitry A. Kuminov, 17 years ago

Initially imported qt-all-opensource-src-4.5.1 from Trolltech.

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