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