1/****************************************************************************
2**
3** Copyright (C) 2016 The Qt Company Ltd.
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the QtWidgets 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 https://www.qt.io/terms-conditions. For further
15** information use the contact form at https://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 3 as published by the Free Software
20** Foundation and appearing in the file LICENSE.LGPL3 included in the
21** packaging of this file. Please review the following information to
22** ensure the GNU Lesser General Public License version 3 requirements
23** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24**
25** GNU General Public License Usage
26** Alternatively, this file may be used under the terms of the GNU
27** General Public License version 2.0 or (at your option) the GNU General
28** Public license version 3 or any later version approved by the KDE Free
29** Qt Foundation. The licenses are as published by the Free Software
30** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31** included in the packaging of this file. Please review the following
32** information to ensure the GNU General Public License requirements will
33** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34** https://www.gnu.org/licenses/gpl-3.0.html.
35**
36** $QT_END_LICENSE$
37**
38****************************************************************************/
39
40
41#include "qplatformdefs.h"
42
43#include "qwidgetbackingstore_p.h"
44
45#include <QtCore/qglobal.h>
46#include <QtCore/qdebug.h>
47#include <QtCore/qvarlengtharray.h>
48#include <QtGui/qevent.h>
49#include <QtWidgets/qapplication.h>
50#include <QtGui/qpaintengine.h>
51#if QT_CONFIG(graphicsview)
52#include <QtWidgets/qgraphicsproxywidget.h>
53#endif
54
55#include <private/qwidget_p.h>
56#include <private/qapplication_p.h>
57#include <private/qpaintengine_raster_p.h>
58#if QT_CONFIG(graphicseffect)
59#include <private/qgraphicseffect_p.h>
60#endif
61#include <QtGui/private/qwindow_p.h>
62#include <QtGui/private/qhighdpiscaling_p.h>
63
64#include <qpa/qplatformbackingstore.h>
65
66#if defined(Q_OS_WIN) && !defined(QT_NO_PAINT_DEBUG)
67# include <QtCore/qt_windows.h>
68# include <qpa/qplatformnativeinterface.h>
69#endif
70
71QT_BEGIN_NAMESPACE
72
73extern QRegion qt_dirtyRegion(QWidget *);
74
75#ifndef QT_NO_OPENGL
76Q_GLOBAL_STATIC(QPlatformTextureList, qt_dummy_platformTextureList)
77#endif
78
79/**
80 * Flushes the contents of the \a backingStore into the screen area of \a widget.
81 * \a region is the region to be updated in \a widget coordinates.
82 */
83void QWidgetBackingStore::qt_flush(QWidget *widget, const QRegion &region, QBackingStore *backingStore,
84 QWidget *tlw, QPlatformTextureList *widgetTextures,
85 QWidgetBackingStore *widgetBackingStore)
86{
87#ifdef QT_NO_OPENGL
88 Q_UNUSED(widgetTextures);
89 Q_ASSERT(!region.isEmpty());
90#else
91 Q_ASSERT(!region.isEmpty() || widgetTextures);
92#endif
93 Q_ASSERT(widget);
94 Q_ASSERT(backingStore);
95 Q_ASSERT(tlw);
96#if !defined(QT_NO_PAINT_DEBUG)
97 static int flushUpdate = qEnvironmentVariableIntValue("QT_FLUSH_UPDATE");
98 if (flushUpdate > 0)
99 QWidgetBackingStore::showYellowThing(widget, region, flushUpdate * 10, false);
100#endif
101
102 if (tlw->testAttribute(Qt::WA_DontShowOnScreen) || widget->testAttribute(Qt::WA_DontShowOnScreen))
103 return;
104
105 // Foreign Windows do not have backing store content and must not be flushed
106 if (QWindow *widgetWindow = widget->windowHandle()) {
107 if (widgetWindow->type() == Qt::ForeignWindow)
108 return;
109 }
110
111 static bool fpsDebug = qEnvironmentVariableIntValue("QT_DEBUG_FPS");
112 if (fpsDebug) {
113 if (!widgetBackingStore->perfFrames++)
114 widgetBackingStore->perfTime.start();
115 if (widgetBackingStore->perfTime.elapsed() > 5000) {
116 double fps = double(widgetBackingStore->perfFrames * 1000) / widgetBackingStore->perfTime.restart();
117 qDebug("FPS: %.1f\n", fps);
118 widgetBackingStore->perfFrames = 0;
119 }
120 }
121
122 QPoint offset;
123 if (widget != tlw)
124 offset += widget->mapTo(tlw, QPoint());
125
126 QRegion effectiveRegion = region;
127#ifndef QT_NO_OPENGL
128 const bool compositionWasActive = widget->d_func()->renderToTextureComposeActive;
129 if (!widgetTextures) {
130 widget->d_func()->renderToTextureComposeActive = false;
131 // Detect the case of falling back to the normal flush path when no
132 // render-to-texture widgets are visible anymore. We will force one
133 // last flush to go through the OpenGL-based composition to prevent
134 // artifacts. The next flush after this one will use the normal path.
135 if (compositionWasActive)
136 widgetTextures = qt_dummy_platformTextureList;
137 } else {
138 widget->d_func()->renderToTextureComposeActive = true;
139 }
140 // When changing the composition status, make sure the dirty region covers
141 // the entire widget. Just having e.g. the shown/hidden render-to-texture
142 // widget's area marked as dirty is incorrect when changing flush paths.
143 if (compositionWasActive != widget->d_func()->renderToTextureComposeActive)
144 effectiveRegion = widget->rect();
145
146 // re-test since we may have been forced to this path via the dummy texture list above
147 if (widgetTextures) {
148 qt_window_private(tlw->windowHandle())->compositing = true;
149 widget->window()->d_func()->sendComposeStatus(widget->window(), false);
150 // A window may have alpha even when the app did not request
151 // WA_TranslucentBackground. Therefore the compositor needs to know whether the app intends
152 // to rely on translucency, in order to decide if it should clear to transparent or opaque.
153 const bool translucentBackground = widget->testAttribute(Qt::WA_TranslucentBackground);
154 backingStore->handle()->composeAndFlush(widget->windowHandle(), effectiveRegion, offset,
155 widgetTextures, translucentBackground);
156 widget->window()->d_func()->sendComposeStatus(widget->window(), true);
157 } else
158#endif
159 backingStore->flush(effectiveRegion, widget->windowHandle(), offset);
160}
161
162#ifndef QT_NO_PAINT_DEBUG
163#if defined(Q_OS_WIN) && !defined(Q_OS_WINRT)
164
165static void showYellowThing_win(QWidget *widget, const QRegion &region, int msec)
166{
167 // We expect to be passed a native parent.
168 QWindow *nativeWindow = widget->windowHandle();
169 if (!nativeWindow)
170 return;
171 void *hdcV = QGuiApplication::platformNativeInterface()->nativeResourceForWindow(QByteArrayLiteral("getDC"), nativeWindow);
172 if (!hdcV)
173 return;
174 const HDC hdc = reinterpret_cast<HDC>(hdcV);
175
176 static const COLORREF colors[] = {RGB(255, 255, 0), RGB(255, 200, 55), RGB(200, 255, 55), RGB(200, 200, 0)};
177
178 static size_t i = 0;
179 const HBRUSH brush = CreateSolidBrush(colors[i]);
180 i = (i + 1) % (sizeof(colors) / sizeof(colors[0]));
181
182 for (const QRect &rect : region) {
183 RECT winRect;
184 SetRect(&winRect, rect.left(), rect.top(), rect.right(), rect.bottom());
185 FillRect(hdc, &winRect, brush);
186 }
187 DeleteObject(brush);
188 QGuiApplication::platformNativeInterface()->nativeResourceForWindow(QByteArrayLiteral("releaseDC"), nativeWindow);
189 ::Sleep(msec);
190}
191#endif // defined(Q_OS_WIN) && !defined(Q_OS_WINRT)
192
193void QWidgetBackingStore::showYellowThing(QWidget *widget, const QRegion &toBePainted, int msec, bool unclipped)
194{
195#ifdef Q_OS_WINRT
196 Q_UNUSED(msec)
197#endif
198 QRegion paintRegion = toBePainted;
199 QRect widgetRect = widget->rect();
200
201 if (!widget->internalWinId()) {
202 QWidget *nativeParent = widget->nativeParentWidget();
203 const QPoint offset = widget->mapTo(nativeParent, QPoint(0, 0));
204 paintRegion.translate(offset);
205 widgetRect.translate(offset);
206 widget = nativeParent;
207 }
208
209#if defined(Q_OS_WIN) && !defined(Q_OS_WINRT)
210 Q_UNUSED(unclipped);
211 showYellowThing_win(widget, paintRegion, msec);
212#else
213 //flags to fool painter
214 bool paintUnclipped = widget->testAttribute(Qt::WA_PaintUnclipped);
215 if (unclipped && !widget->d_func()->paintOnScreen())
216 widget->setAttribute(Qt::WA_PaintUnclipped);
217
218 const bool setFlag = !widget->testAttribute(Qt::WA_WState_InPaintEvent);
219 if (setFlag)
220 widget->setAttribute(Qt::WA_WState_InPaintEvent);
221
222 //setup the engine
223 QPaintEngine *pe = widget->paintEngine();
224 if (pe) {
225 pe->setSystemClip(paintRegion);
226 {
227 QPainter p(widget);
228 p.setClipRegion(paintRegion);
229 static int i = 0;
230 switch (i) {
231 case 0:
232 p.fillRect(widgetRect, QColor(255,255,0));
233 break;
234 case 1:
235 p.fillRect(widgetRect, QColor(255,200,55));
236 break;
237 case 2:
238 p.fillRect(widgetRect, QColor(200,255,55));
239 break;
240 case 3:
241 p.fillRect(widgetRect, QColor(200,200,0));
242 break;
243 }
244 i = (i+1) & 3;
245 p.end();
246 }
247 }
248
249 if (setFlag)
250 widget->setAttribute(Qt::WA_WState_InPaintEvent, false);
251
252 //restore
253 widget->setAttribute(Qt::WA_PaintUnclipped, paintUnclipped);
254
255 if (pe)
256 pe->setSystemClip(QRegion());
257
258#if defined(Q_OS_UNIX)
259 ::usleep(1000 * msec);
260#endif
261#endif // !Q_OS_WIN
262}
263
264bool QWidgetBackingStore::flushPaint(QWidget *widget, const QRegion &rgn)
265{
266 if (!widget)
267 return false;
268
269 int delay = 0;
270 if (widget->testAttribute(Qt::WA_WState_InPaintEvent)) {
271 static int flushPaintEvent = qEnvironmentVariableIntValue("QT_FLUSH_PAINT_EVENT");
272 if (!flushPaintEvent)
273 return false;
274 delay = flushPaintEvent;
275 } else {
276 static int flushPaint = qEnvironmentVariableIntValue("QT_FLUSH_PAINT");
277 if (!flushPaint)
278 return false;
279 delay = flushPaint;
280 }
281
282 QWidgetBackingStore::showYellowThing(widget, rgn, delay * 10, true);
283 return true;
284}
285
286void QWidgetBackingStore::unflushPaint(QWidget *widget, const QRegion &rgn)
287{
288 if (widget->d_func()->paintOnScreen() || rgn.isEmpty())
289 return;
290
291 QWidget *tlw = widget->window();
292 QTLWExtra *tlwExtra = tlw->d_func()->maybeTopData();
293 if (!tlwExtra)
294 return;
295
296 qt_flush(widget, rgn, tlwExtra->backingStoreTracker->store, tlw, 0, tlw->d_func()->maybeBackingStore());
297}
298#endif // QT_NO_PAINT_DEBUG
299
300/*
301 Moves the whole rect by (dx, dy) in widget's coordinate system.
302 Doesn't generate any updates.
303*/
304bool QWidgetBackingStore::bltRect(const QRect &rect, int dx, int dy, QWidget *widget)
305{
306 const QPoint pos(widget->mapTo(tlw, rect.topLeft()));
307 const QRect tlwRect(QRect(pos, rect.size()));
308 if (dirty.intersects(tlwRect))
309 return false; // We don't want to scroll junk.
310 return store->scroll(tlwRect, dx, dy);
311}
312
313/*!
314 Prepares the window surface to paint a\ toClean region of the \a widget and
315 updates the BeginPaintInfo struct accordingly.
316
317 The \a toClean region might be clipped by the window surface.
318*/
319void QWidgetBackingStore::beginPaint(QRegion &toClean, QWidget *widget, QBackingStore *backingStore,
320 BeginPaintInfo *returnInfo, bool toCleanIsInTopLevelCoordinates)
321{
322 Q_UNUSED(widget);
323 Q_UNUSED(toCleanIsInTopLevelCoordinates);
324
325 // Always flush repainted areas.
326 dirtyOnScreen += toClean;
327
328#ifdef QT_NO_PAINT_DEBUG
329 backingStore->beginPaint(toClean);
330#else
331 returnInfo->wasFlushed = QWidgetBackingStore::flushPaint(tlw, toClean);
332 // Avoid deadlock with QT_FLUSH_PAINT: the server will wait for
333 // the BackingStore lock, so if we hold that, the server will
334 // never release the Communication lock that we are waiting for in
335 // sendSynchronousCommand
336 if (!returnInfo->wasFlushed)
337 backingStore->beginPaint(toClean);
338#endif
339
340 Q_UNUSED(returnInfo);
341}
342
343void QWidgetBackingStore::endPaint(const QRegion &cleaned, QBackingStore *backingStore,
344 BeginPaintInfo *beginPaintInfo)
345{
346#ifndef QT_NO_PAINT_DEBUG
347 if (!beginPaintInfo->wasFlushed)
348 backingStore->endPaint();
349 else
350 QWidgetBackingStore::unflushPaint(tlw, cleaned);
351#else
352 Q_UNUSED(beginPaintInfo);
353 Q_UNUSED(cleaned);
354 backingStore->endPaint();
355#endif
356
357 flush();
358}
359
360/*!
361 Returns the region (in top-level coordinates) that needs repaint and/or flush.
362
363 If the widget is non-zero, only the dirty region for the widget is returned
364 and the region will be in widget coordinates.
365*/
366QRegion QWidgetBackingStore::dirtyRegion(QWidget *widget) const
367{
368 const bool widgetDirty = widget && widget != tlw;
369 const QRect tlwRect(topLevelRect());
370 const QRect surfaceGeometry(tlwRect.topLeft(), store->size());
371 if (surfaceGeometry != tlwRect && surfaceGeometry.size() != tlwRect.size()) {
372 if (widgetDirty) {
373 const QRect dirtyTlwRect = QRect(QPoint(), tlwRect.size());
374 const QPoint offset(widget->mapTo(tlw, QPoint()));
375 const QRect dirtyWidgetRect(dirtyTlwRect & widget->rect().translated(offset));
376 return dirtyWidgetRect.translated(-offset);
377 }
378 return QRect(QPoint(), tlwRect.size());
379 }
380
381 // Calculate the region that needs repaint.
382 QRegion r(dirty);
383 for (int i = 0; i < dirtyWidgets.size(); ++i) {
384 QWidget *w = dirtyWidgets.at(i);
385 if (widgetDirty && w != widget && !widget->isAncestorOf(w))
386 continue;
387 r += w->d_func()->dirty.translated(w->mapTo(tlw, QPoint()));
388 }
389
390 // Append the region that needs flush.
391 r += dirtyOnScreen;
392
393 if (dirtyOnScreenWidgets) { // Only in use with native child widgets.
394 for (int i = 0; i < dirtyOnScreenWidgets->size(); ++i) {
395 QWidget *w = dirtyOnScreenWidgets->at(i);
396 if (widgetDirty && w != widget && !widget->isAncestorOf(w))
397 continue;
398 QWidgetPrivate *wd = w->d_func();
399 Q_ASSERT(wd->needsFlush);
400 r += wd->needsFlush->translated(w->mapTo(tlw, QPoint()));
401 }
402 }
403
404 if (widgetDirty) {
405 // Intersect with the widget geometry and translate to its coordinates.
406 const QPoint offset(widget->mapTo(tlw, QPoint()));
407 r &= widget->rect().translated(offset);
408 r.translate(-offset);
409 }
410 return r;
411}
412
413/*!
414 Returns the static content inside the \a parent if non-zero; otherwise the static content
415 for the entire backing store is returned. The content will be clipped to \a withinClipRect
416 if non-empty.
417*/
418QRegion QWidgetBackingStore::staticContents(QWidget *parent, const QRect &withinClipRect) const
419{
420 if (!parent && tlw->testAttribute(Qt::WA_StaticContents)) {
421 const QSize surfaceGeometry(store->size());
422 QRect surfaceRect(0, 0, surfaceGeometry.width(), surfaceGeometry.height());
423 if (!withinClipRect.isEmpty())
424 surfaceRect &= withinClipRect;
425 return QRegion(surfaceRect);
426 }
427
428 QRegion region;
429 if (parent && parent->d_func()->children.isEmpty())
430 return region;
431
432 const bool clipToRect = !withinClipRect.isEmpty();
433 const int count = staticWidgets.count();
434 for (int i = 0; i < count; ++i) {
435 QWidget *w = staticWidgets.at(i);
436 QWidgetPrivate *wd = w->d_func();
437 if (!wd->isOpaque || !wd->extra || wd->extra->staticContentsSize.isEmpty()
438 || !w->isVisible() || (parent && !parent->isAncestorOf(w))) {
439 continue;
440 }
441
442 QRect rect(0, 0, wd->extra->staticContentsSize.width(), wd->extra->staticContentsSize.height());
443 const QPoint offset = w->mapTo(parent ? parent : tlw, QPoint());
444 if (clipToRect)
445 rect &= withinClipRect.translated(-offset);
446 if (rect.isEmpty())
447 continue;
448
449 rect &= wd->clipRect();
450 if (rect.isEmpty())
451 continue;
452
453 QRegion visible(rect);
454 wd->clipToEffectiveMask(visible);
455 if (visible.isEmpty())
456 continue;
457 wd->subtractOpaqueSiblings(visible, 0, /*alsoNonOpaque=*/true);
458
459 visible.translate(offset);
460 region += visible;
461 }
462
463 return region;
464}
465
466void QWidgetBackingStore::sendUpdateRequest(QWidget *widget, UpdateTime updateTime)
467{
468 if (!widget)
469 return;
470
471#ifndef QT_NO_OPENGL
472 // Having every repaint() leading to a sync/flush is bad as it causes
473 // compositing and waiting for vsync each and every time. Change to
474 // UpdateLater, except for approx. once per frame to prevent starvation in
475 // case the control does not get back to the event loop.
476 QWidget *w = widget->window();
477 if (updateTime == UpdateNow && w && w->windowHandle() && QWindowPrivate::get(w->windowHandle())->compositing) {
478 int refresh = 60;
479 QScreen *ws = w->windowHandle()->screen();
480 if (ws)
481 refresh = ws->refreshRate();
482 QWindowPrivate *wd = QWindowPrivate::get(w->windowHandle());
483 if (wd->lastComposeTime.isValid()) {
484 const qint64 elapsed = wd->lastComposeTime.elapsed();
485 if (elapsed <= qint64(1000.0f / refresh))
486 updateTime = UpdateLater;
487 }
488 }
489#endif
490
491 switch (updateTime) {
492 case UpdateLater:
493 updateRequestSent = true;
494 QCoreApplication::postEvent(widget, new QEvent(QEvent::UpdateRequest), Qt::LowEventPriority);
495 break;
496 case UpdateNow: {
497 QEvent event(QEvent::UpdateRequest);
498 QCoreApplication::sendEvent(widget, &event);
499 break;
500 }
501 }
502}
503
504static inline QRect widgetRectFor(QWidget *, const QRect &r) { return r; }
505static inline QRect widgetRectFor(QWidget *widget, const QRegion &) { return widget->rect(); }
506
507/*!
508 Marks the region of the widget as dirty (if not already marked as dirty) and
509 posts an UpdateRequest event to the top-level widget (if not already posted).
510
511 If updateTime is UpdateNow, the event is sent immediately instead of posted.
512
513 If bufferState is BufferInvalid, all widgets intersecting with the region will be dirty.
514
515 If the widget paints directly on screen, the event is sent to the widget
516 instead of the top-level widget, and bufferState is completely ignored.
517*/
518template <class T>
519void QWidgetBackingStore::markDirty(const T &r, QWidget *widget, UpdateTime updateTime, BufferState bufferState)
520{
521 Q_ASSERT(tlw->d_func()->extra);
522 Q_ASSERT(tlw->d_func()->extra->topextra);
523 Q_ASSERT(!tlw->d_func()->extra->topextra->inTopLevelResize);
524 Q_ASSERT(widget->isVisible() && widget->updatesEnabled());
525 Q_ASSERT(widget->window() == tlw);
526 Q_ASSERT(!r.isEmpty());
527
528#if QT_CONFIG(graphicseffect)
529 widget->d_func()->invalidateGraphicsEffectsRecursively();
530#endif
531
532 QRect widgetRect = widgetRectFor(widget, r);
533
534 // ---------------------------------------------------------------------------
535
536 if (widget->d_func()->paintOnScreen()) {
537 if (widget->d_func()->dirty.isEmpty()) {
538 widget->d_func()->dirty = r;
539 sendUpdateRequest(widget, updateTime);
540 return;
541 } else if (qt_region_strictContains(widget->d_func()->dirty, widgetRect)) {
542 if (updateTime == UpdateNow)
543 sendUpdateRequest(widget, updateTime);
544 return; // Already dirty
545 }
546
547 const bool eventAlreadyPosted = !widget->d_func()->dirty.isEmpty();
548 widget->d_func()->dirty += r;
549 if (!eventAlreadyPosted || updateTime == UpdateNow)
550 sendUpdateRequest(widget, updateTime);
551 return;
552 }
553
554 // ---------------------------------------------------------------------------
555
556 if (QWidgetPrivate::get(widget)->renderToTexture) {
557 if (!widget->d_func()->inDirtyList)
558 addDirtyRenderToTextureWidget(widget);
559 if (!updateRequestSent || updateTime == UpdateNow)
560 sendUpdateRequest(tlw, updateTime);
561 return;
562 }
563
564 // ---------------------------------------------------------------------------
565
566 QRect effectiveWidgetRect = widget->d_func()->effectiveRectFor(widgetRect);
567 const QPoint offset = widget->mapTo(tlw, QPoint());
568 QRect translatedRect = effectiveWidgetRect.translated(offset);
569#if QT_CONFIG(graphicseffect)
570 // Graphics effects may exceed window size, clamp
571 translatedRect = translatedRect.intersected(QRect(QPoint(), tlw->size()));
572#endif
573 if (qt_region_strictContains(dirty, translatedRect)) {
574 if (updateTime == UpdateNow)
575 sendUpdateRequest(tlw, updateTime);
576 return; // Already dirty
577 }
578
579 // ---------------------------------------------------------------------------
580
581 if (bufferState == BufferInvalid) {
582 const bool eventAlreadyPosted = !dirty.isEmpty() || updateRequestSent;
583#if QT_CONFIG(graphicseffect)
584 if (widget->d_func()->graphicsEffect)
585 dirty += widget->d_func()->effectiveRectFor(r).translated(offset);
586 else
587#endif
588 dirty += r.translated(offset);
589
590 if (!eventAlreadyPosted || updateTime == UpdateNow)
591 sendUpdateRequest(tlw, updateTime);
592 return;
593 }
594
595 // ---------------------------------------------------------------------------
596
597 if (dirtyWidgets.isEmpty()) {
598 addDirtyWidget(widget, r);
599 sendUpdateRequest(tlw, updateTime);
600 return;
601 }
602
603 // ---------------------------------------------------------------------------
604
605 if (widget->d_func()->inDirtyList) {
606 if (!qt_region_strictContains(widget->d_func()->dirty, effectiveWidgetRect)) {
607#if QT_CONFIG(graphicseffect)
608 if (widget->d_func()->graphicsEffect)
609 widget->d_func()->dirty += widget->d_func()->effectiveRectFor(r);
610 else
611#endif
612 widget->d_func()->dirty += r;
613 }
614 } else {
615 addDirtyWidget(widget, r);
616 }
617
618 // ---------------------------------------------------------------------------
619
620 if (updateTime == UpdateNow)
621 sendUpdateRequest(tlw, updateTime);
622}
623template void QWidgetBackingStore::markDirty<QRect>(const QRect &, QWidget *, UpdateTime, BufferState);
624template void QWidgetBackingStore::markDirty<QRegion>(const QRegion &, QWidget *, UpdateTime, BufferState);
625
626/*!
627 Marks the \a region of the \a widget as dirty on screen. The \a region will be copied from
628 the backing store to the \a widget's native parent next time flush() is called.
629
630 Paint on screen widgets are ignored.
631*/
632void QWidgetBackingStore::markDirtyOnScreen(const QRegion &region, QWidget *widget, const QPoint &topLevelOffset)
633{
634 if (!widget || widget->d_func()->paintOnScreen() || region.isEmpty())
635 return;
636
637#if 0 // Used to be included in Qt4 for Q_WS_MAC
638 if (!widget->testAttribute(Qt::WA_WState_InPaintEvent))
639 dirtyOnScreen += region.translated(topLevelOffset);
640 return;
641#endif
642
643 // Top-level.
644 if (widget == tlw) {
645 if (!widget->testAttribute(Qt::WA_WState_InPaintEvent))
646 dirtyOnScreen += region;
647 return;
648 }
649
650 // Alien widgets.
651 if (!widget->internalWinId() && !widget->isWindow()) {
652 QWidget *nativeParent = widget->nativeParentWidget(); // Alien widgets with the top-level as the native parent (common case).
653 if (nativeParent == tlw) {
654 if (!widget->testAttribute(Qt::WA_WState_InPaintEvent))
655 dirtyOnScreen += region.translated(topLevelOffset);
656 return;
657 }
658
659 // Alien widgets with native parent != tlw.
660 QWidgetPrivate *nativeParentPrivate = nativeParent->d_func();
661 if (!nativeParentPrivate->needsFlush)
662 nativeParentPrivate->needsFlush = new QRegion;
663 const QPoint nativeParentOffset = widget->mapTo(nativeParent, QPoint());
664 *nativeParentPrivate->needsFlush += region.translated(nativeParentOffset);
665 appendDirtyOnScreenWidget(nativeParent);
666 return;
667 }
668
669 // Native child widgets.
670 QWidgetPrivate *widgetPrivate = widget->d_func();
671 if (!widgetPrivate->needsFlush)
672 widgetPrivate->needsFlush = new QRegion;
673 *widgetPrivate->needsFlush += region;
674 appendDirtyOnScreenWidget(widget);
675}
676
677void QWidgetBackingStore::removeDirtyWidget(QWidget *w)
678{
679 if (!w)
680 return;
681
682 dirtyWidgetsRemoveAll(w);
683 dirtyOnScreenWidgetsRemoveAll(w);
684 dirtyRenderToTextureWidgets.removeAll(w);
685 resetWidget(w);
686
687 QWidgetPrivate *wd = w->d_func();
688 const int n = wd->children.count();
689 for (int i = 0; i < n; ++i) {
690 if (QWidget *child = qobject_cast<QWidget*>(wd->children.at(i)))
691 removeDirtyWidget(child);
692 }
693}
694
695void QWidgetBackingStore::updateLists(QWidget *cur)
696{
697 if (!cur)
698 return;
699
700 QList<QObject*> children = cur->children();
701 for (int i = 0; i < children.size(); ++i) {
702 QWidget *child = qobject_cast<QWidget*>(children.at(i));
703 if (!child || child->isWindow())
704 continue;
705
706 updateLists(child);
707 }
708
709 if (cur->testAttribute(Qt::WA_StaticContents))
710 addStaticWidget(cur);
711}
712
713QWidgetBackingStore::QWidgetBackingStore(QWidget *topLevel)
714 : tlw(topLevel),
715 dirtyOnScreenWidgets(0),
716 updateRequestSent(0),
717 textureListWatcher(0),
718 perfFrames(0)
719{
720 store = tlw->backingStore();
721 Q_ASSERT(store);
722
723 // Ensure all existing subsurfaces and static widgets are added to their respective lists.
724 updateLists(topLevel);
725}
726
727QWidgetBackingStore::~QWidgetBackingStore()
728{
729 for (int c = 0; c < dirtyWidgets.size(); ++c)
730 resetWidget(dirtyWidgets.at(c));
731 for (int c = 0; c < dirtyRenderToTextureWidgets.size(); ++c)
732 resetWidget(dirtyRenderToTextureWidgets.at(c));
733
734 delete dirtyOnScreenWidgets;
735}
736
737static QVector<QRect> getSortedRectsToScroll(const QRegion &region, int dx, int dy)
738{
739 QVector<QRect> rects;
740 std::copy(region.begin(), region.end(), std::back_inserter(rects));
741 if (rects.count() > 1) {
742 std::sort(rects.begin(), rects.end(), [=](const QRect &r1, const QRect &r2) {
743 if (r1.y() == r2.y()) {
744 if (dx > 0)
745 return r1.x() > r2.x();
746 return r1.x() < r2.x();
747 }
748 if (dy > 0)
749 return r1.y() > r2.y();
750 return r1.y() < r2.y();
751 });
752 }
753 return rects;
754}
755
756//parent's coordinates; move whole rect; update parent and widget
757//assume the screen blt has already been done, so we don't need to refresh that part
758void QWidgetPrivate::moveRect(const QRect &rect, int dx, int dy)
759{
760 Q_Q(QWidget);
761 if (!q->isVisible() || (dx == 0 && dy == 0))
762 return;
763
764 QWidget *tlw = q->window();
765 QTLWExtra* x = tlw->d_func()->topData();
766 if (x->inTopLevelResize)
767 return;
768
769 static const bool accelEnv = qEnvironmentVariableIntValue("QT_NO_FAST_MOVE") == 0;
770
771 QWidget *pw = q->parentWidget();
772 QPoint toplevelOffset = pw->mapTo(tlw, QPoint());
773 QWidgetPrivate *pd = pw->d_func();
774 QRect clipR(pd->clipRect());
775 const QRect newRect(rect.translated(dx, dy));
776 QRect destRect = rect.intersected(clipR);
777 if (destRect.isValid())
778 destRect = destRect.translated(dx, dy).intersected(clipR);
779 const QRect sourceRect(destRect.translated(-dx, -dy));
780 const QRect parentRect(rect & clipR);
781 const bool nativeWithTextureChild = textureChildSeen && q->internalWinId();
782
783 const bool accelerateMove = accelEnv && isOpaque && !nativeWithTextureChild
784#if QT_CONFIG(graphicsview)
785 // No accelerate move for proxy widgets.
786 && !tlw->d_func()->extra->proxyWidget
787#endif
788 ;
789
790 if (!accelerateMove) {
791 QRegion parentR(effectiveRectFor(parentRect));
792 if (!extra || !extra->hasMask) {
793 parentR -= newRect;
794 } else {
795 // invalidateBackingStore() excludes anything outside the mask
796 parentR += newRect & clipR;
797 }
798 pd->invalidateBackingStore(parentR);
799 invalidateBackingStore((newRect & clipR).translated(-data.crect.topLeft()));
800 } else {
801
802 QWidgetBackingStore *wbs = x->backingStoreTracker.data();
803 QRegion childExpose(newRect & clipR);
804 QRegion overlappedExpose;
805
806 if (sourceRect.isValid()) {
807 overlappedExpose = (overlappedRegion(sourceRect) | overlappedRegion(destRect)) & clipR;
808
809 const qreal factor = QHighDpiScaling::factor(q->windowHandle());
810 if (overlappedExpose.isEmpty() || qFloor(factor) == factor) {
811 const QVector<QRect> rectsToScroll
812 = getSortedRectsToScroll(QRegion(sourceRect) - overlappedExpose, dx, dy);
813 for (QRect rect : rectsToScroll) {
814 if (wbs->bltRect(rect, dx, dy, pw)) {
815 childExpose -= rect.translated(dx, dy);
816 }
817 }
818 }
819
820 childExpose -= overlappedExpose;
821 }
822
823 if (!pw->updatesEnabled())
824 return;
825
826 const bool childUpdatesEnabled = q->updatesEnabled();
827 if (childUpdatesEnabled) {
828 if (!overlappedExpose.isEmpty()) {
829 overlappedExpose.translate(-data.crect.topLeft());
830 invalidateBackingStore(overlappedExpose);
831 }
832 if (!childExpose.isEmpty()) {
833 childExpose.translate(-data.crect.topLeft());
834 wbs->markDirty(childExpose, q);
835 isMoved = true;
836 }
837 }
838
839 QRegion parentExpose(parentRect);
840 parentExpose -= newRect;
841 if (extra && extra->hasMask)
842 parentExpose += QRegion(newRect) - extra->mask.translated(data.crect.topLeft());
843
844 if (!parentExpose.isEmpty()) {
845 wbs->markDirty(parentExpose, pw);
846 pd->isMoved = true;
847 }
848
849 if (childUpdatesEnabled) {
850 QRegion needsFlush(sourceRect);
851 needsFlush += destRect;
852 wbs->markDirtyOnScreen(needsFlush, pw, toplevelOffset);
853 }
854 }
855}
856
857//widget's coordinates; scroll within rect; only update widget
858void QWidgetPrivate::scrollRect(const QRect &rect, int dx, int dy)
859{
860 Q_Q(QWidget);
861 QWidget *tlw = q->window();
862 QTLWExtra* x = tlw->d_func()->topData();
863 if (x->inTopLevelResize)
864 return;
865
866 QWidgetBackingStore *wbs = x->backingStoreTracker.data();
867 if (!wbs)
868 return;
869
870 static const bool accelEnv = qEnvironmentVariableIntValue("QT_NO_FAST_SCROLL") == 0;
871
872 const QRect clipR = clipRect();
873 const QRect scrollRect = rect & clipR;
874 const bool accelerateScroll = accelEnv && isOpaque && !q_func()->testAttribute(Qt::WA_WState_InPaintEvent);
875
876 if (!accelerateScroll) {
877 if (!overlappedRegion(scrollRect.translated(data.crect.topLeft()), true).isEmpty()) {
878 QRegion region(scrollRect);
879 subtractOpaqueSiblings(region);
880 invalidateBackingStore(region);
881 }else {
882 invalidateBackingStore(scrollRect);
883 }
884 } else {
885 const QPoint toplevelOffset = q->mapTo(tlw, QPoint());
886 const QRect destRect = scrollRect.translated(dx, dy) & scrollRect;
887 const QRect sourceRect = destRect.translated(-dx, -dy);
888
889 const QRegion overlappedExpose = (overlappedRegion(scrollRect.translated(data.crect.topLeft())))
890 .translated(-data.crect.topLeft()) & clipR;
891 QRegion childExpose(scrollRect);
892
893 const qreal factor = QHighDpiScaling::factor(q->windowHandle());
894 if (overlappedExpose.isEmpty() || qFloor(factor) == factor) {
895 const QVector<QRect> rectsToScroll
896 = getSortedRectsToScroll(QRegion(sourceRect) - overlappedExpose, dx, dy);
897 for (const QRect &rect : rectsToScroll) {
898 if (wbs->bltRect(rect, dx, dy, q)) {
899 childExpose -= rect.translated(dx, dy);
900 }
901 }
902 }
903
904 childExpose -= overlappedExpose;
905
906 if (inDirtyList) {
907 if (rect == q->rect()) {
908 dirty.translate(dx, dy);
909 } else {
910 QRegion dirtyScrollRegion = dirty.intersected(scrollRect);
911 if (!dirtyScrollRegion.isEmpty()) {
912 dirty -= dirtyScrollRegion;
913 dirtyScrollRegion.translate(dx, dy);
914 dirty += dirtyScrollRegion;
915 }
916 }
917 }
918
919 if (!q->updatesEnabled())
920 return;
921
922 if (!overlappedExpose.isEmpty())
923 invalidateBackingStore(overlappedExpose);
924 if (!childExpose.isEmpty()) {
925 wbs->markDirty(childExpose, q);
926 isScrolled = true;
927 }
928
929 // Instead of using native scroll-on-screen, we copy from
930 // backingstore, giving only one screen update for each
931 // scroll, and a solid appearance
932 wbs->markDirtyOnScreen(destRect, q, toplevelOffset);
933 }
934}
935
936#ifndef QT_NO_OPENGL
937static void findTextureWidgetsRecursively(QWidget *tlw, QWidget *widget, QPlatformTextureList *widgetTextures, QVector<QWidget *> *nativeChildren)
938{
939 QWidgetPrivate *wd = QWidgetPrivate::get(widget);
940 if (wd->renderToTexture) {
941 QPlatformTextureList::Flags flags = wd->textureListFlags();
942 const QRect rect(widget->mapTo(tlw, QPoint()), widget->size());
943 widgetTextures->appendTexture(widget, wd->textureId(), rect, wd->clipRect(), flags);
944 }
945
946 for (int i = 0; i < wd->children.size(); ++i) {
947 QWidget *w = qobject_cast<QWidget *>(wd->children.at(i));
948 // Stop at native widgets but store them. Stop at hidden widgets too.
949 if (w && !w->isWindow() && w->internalWinId())
950 nativeChildren->append(w);
951 if (w && !w->isWindow() && !w->internalWinId() && !w->isHidden() && QWidgetPrivate::get(w)->textureChildSeen)
952 findTextureWidgetsRecursively(tlw, w, widgetTextures, nativeChildren);
953 }
954}
955
956static void findAllTextureWidgetsRecursively(QWidget *tlw, QWidget *widget)
957{
958 // textureChildSeen does not take native child widgets into account and that's good.
959 if (QWidgetPrivate::get(widget)->textureChildSeen) {
960 QVector<QWidget *> nativeChildren;
961 QScopedPointer<QPlatformTextureList> tl(new QPlatformTextureList);
962 // Look for texture widgets (incl. widget itself) from 'widget' down,
963 // but skip subtrees with a parent of a native child widget.
964 findTextureWidgetsRecursively(tlw, widget, tl.data(), &nativeChildren);
965 // tl may be empty regardless of textureChildSeen if we have native or hidden children.
966 if (!tl->isEmpty())
967 QWidgetPrivate::get(tlw)->topData()->widgetTextures.append(tl.take());
968 // Native child widgets, if there was any, get their own separate QPlatformTextureList.
969 foreach (QWidget *ncw, nativeChildren) {
970 if (QWidgetPrivate::get(ncw)->textureChildSeen)
971 findAllTextureWidgetsRecursively(tlw, ncw);
972 }
973 }
974}
975
976static QPlatformTextureList *widgetTexturesFor(QWidget *tlw, QWidget *widget)
977{
978 foreach (QPlatformTextureList *tl, QWidgetPrivate::get(tlw)->topData()->widgetTextures) {
979 Q_ASSERT(!tl->isEmpty());
980 for (int i = 0; i < tl->count(); ++i) {
981 QWidget *w = static_cast<QWidget *>(tl->source(i));
982 if ((w->internalWinId() && w == widget) || (!w->internalWinId() && w->nativeParentWidget() == widget))
983 return tl;
984 }
985 }
986
987 if (QWidgetPrivate::get(widget)->textureChildSeen) {
988 // No render-to-texture widgets in the (sub-)tree due to hidden or native
989 // children. Returning null results in using the normal backingstore flush path
990 // without OpenGL-based compositing. This is very desirable normally. However,
991 // some platforms cannot handle switching between the non-GL and GL paths for
992 // their windows so it has to be opt-in.
993 static bool switchableWidgetComposition =
994 QGuiApplicationPrivate::instance()->platformIntegration()
995 ->hasCapability(QPlatformIntegration::SwitchableWidgetComposition);
996 if (!switchableWidgetComposition)
997 return qt_dummy_platformTextureList();
998 }
999
1000 return 0;
1001}
1002
1003// Watches one or more QPlatformTextureLists for changes in the lock state and
1004// triggers a backingstore sync when all the registered lists turn into
1005// unlocked state. This is essential when a custom composeAndFlush()
1006// implementation in a platform plugin is not synchronous and keeps
1007// holding on to the textures for some time even after returning from there.
1008QPlatformTextureListWatcher::QPlatformTextureListWatcher(QWidgetBackingStore *backingStore)
1009 : m_backingStore(backingStore)
1010{
1011}
1012
1013void QPlatformTextureListWatcher::watch(QPlatformTextureList *textureList)
1014{
1015 connect(textureList, SIGNAL(locked(bool)), SLOT(onLockStatusChanged(bool)));
1016 m_locked[textureList] = textureList->isLocked();
1017}
1018
1019bool QPlatformTextureListWatcher::isLocked() const
1020{
1021 foreach (bool v, m_locked) {
1022 if (v)
1023 return true;
1024 }
1025 return false;
1026}
1027
1028void QPlatformTextureListWatcher::onLockStatusChanged(bool locked)
1029{
1030 QPlatformTextureList *tl = static_cast<QPlatformTextureList *>(sender());
1031 m_locked[tl] = locked;
1032 if (!isLocked())
1033 m_backingStore->sync();
1034}
1035
1036#else
1037
1038static QPlatformTextureList *widgetTexturesFor(QWidget *tlw, QWidget *widget)
1039{
1040 Q_UNUSED(tlw);
1041 Q_UNUSED(widget);
1042 return nullptr;
1043}
1044
1045#endif // QT_NO_OPENGL
1046
1047static inline bool discardSyncRequest(QWidget *tlw, QTLWExtra *tlwExtra)
1048{
1049 if (!tlw || !tlwExtra || !tlw->testAttribute(Qt::WA_Mapped) || !tlw->isVisible())
1050 return true;
1051
1052 return false;
1053}
1054
1055bool QWidgetBackingStore::syncAllowed()
1056{
1057#ifndef QT_NO_OPENGL
1058 QTLWExtra *tlwExtra = tlw->d_func()->maybeTopData();
1059 if (textureListWatcher && !textureListWatcher->isLocked()) {
1060 textureListWatcher->deleteLater();
1061 textureListWatcher = 0;
1062 } else if (!tlwExtra->widgetTextures.isEmpty()) {
1063 bool skipSync = false;
1064 foreach (QPlatformTextureList *tl, tlwExtra->widgetTextures) {
1065 if (tl->isLocked()) {
1066 if (!textureListWatcher)
1067 textureListWatcher = new QPlatformTextureListWatcher(this);
1068 if (!textureListWatcher->isLocked())
1069 textureListWatcher->watch(tl);
1070 skipSync = true;
1071 }
1072 }
1073 if (skipSync) // cannot compose due to widget textures being in use
1074 return false;
1075 }
1076#endif
1077 return true;
1078}
1079
1080/*!
1081 Synchronizes the \a exposedRegion of the \a exposedWidget with the backing store.
1082
1083 If there's nothing to repaint, the area is flushed and painting does not occur;
1084 otherwise the area is marked as dirty on screen and will be flushed right after
1085 we are done with all painting.
1086*/
1087void QWidgetBackingStore::sync(QWidget *exposedWidget, const QRegion &exposedRegion)
1088{
1089 QTLWExtra *tlwExtra = tlw->d_func()->maybeTopData();
1090 if (!tlw->isVisible() || !tlwExtra || tlwExtra->inTopLevelResize)
1091 return;
1092
1093 if (!exposedWidget || !exposedWidget->internalWinId() || !exposedWidget->isVisible() || !exposedWidget->testAttribute(Qt::WA_Mapped)
1094 || !exposedWidget->updatesEnabled() || exposedRegion.isEmpty()) {
1095 return;
1096 }
1097
1098 // Nothing to repaint.
1099 if (!isDirty() && store->size().isValid()) {
1100 QPlatformTextureList *tl = widgetTexturesFor(tlw, exposedWidget);
1101 qt_flush(exposedWidget, tl ? QRegion() : exposedRegion, store, tlw, tl, this);
1102 return;
1103 }
1104
1105 if (exposedWidget != tlw)
1106 markDirtyOnScreen(exposedRegion, exposedWidget, exposedWidget->mapTo(tlw, QPoint()));
1107 else
1108 markDirtyOnScreen(exposedRegion, exposedWidget, QPoint());
1109
1110 if (syncAllowed())
1111 doSync();
1112}
1113
1114/*!
1115 Synchronizes the backing store, i.e. dirty areas are repainted and flushed.
1116*/
1117void QWidgetBackingStore::sync()
1118{
1119 updateRequestSent = false;
1120 QTLWExtra *tlwExtra = tlw->d_func()->maybeTopData();
1121 if (discardSyncRequest(tlw, tlwExtra)) {
1122 // If the top-level is minimized, it's not visible on the screen so we can delay the
1123 // update until it's shown again. In order to do that we must keep the dirty states.
1124 // These will be cleared when we receive the first expose after showNormal().
1125 // However, if the widget is not visible (isVisible() returns false), everything will
1126 // be invalidated once the widget is shown again, so clear all dirty states.
1127 if (!tlw->isVisible()) {
1128 dirty = QRegion();
1129 for (int i = 0; i < dirtyWidgets.size(); ++i)
1130 resetWidget(dirtyWidgets.at(i));
1131 dirtyWidgets.clear();
1132 }
1133 return;
1134 }
1135
1136 if (syncAllowed())
1137 doSync();
1138}
1139
1140void QWidgetBackingStore::doSync()
1141{
1142 const bool updatesDisabled = !tlw->updatesEnabled();
1143 bool repaintAllWidgets = false;
1144
1145 const bool inTopLevelResize = tlw->d_func()->maybeTopData()->inTopLevelResize;
1146 const QRect tlwRect(topLevelRect());
1147 const QRect surfaceGeometry(tlwRect.topLeft(), store->size());
1148 if ((inTopLevelResize || surfaceGeometry.size() != tlwRect.size()) && !updatesDisabled) {
1149 if (hasStaticContents() && !store->size().isEmpty() ) {
1150 // Repaint existing dirty area and newly visible area.
1151 const QRect clipRect(0, 0, surfaceGeometry.width(), surfaceGeometry.height());
1152 const QRegion staticRegion(staticContents(0, clipRect));
1153 QRegion newVisible(0, 0, tlwRect.width(), tlwRect.height());
1154 newVisible -= staticRegion;
1155 dirty += newVisible;
1156 store->setStaticContents(staticRegion);
1157 } else {
1158 // Repaint everything.
1159 dirty = QRegion(0, 0, tlwRect.width(), tlwRect.height());
1160 for (int i = 0; i < dirtyWidgets.size(); ++i)
1161 resetWidget(dirtyWidgets.at(i));
1162 dirtyWidgets.clear();
1163 repaintAllWidgets = true;
1164 }
1165 }
1166
1167 if (inTopLevelResize || surfaceGeometry.size() != tlwRect.size())
1168 store->resize(tlwRect.size());
1169
1170 if (updatesDisabled)
1171 return;
1172
1173 // Contains everything that needs repaint.
1174 QRegion toClean(dirty);
1175
1176 // Loop through all update() widgets and remove them from the list before they are
1177 // painted (in case someone calls update() in paintEvent). If the widget is opaque
1178 // and does not have transparent overlapping siblings, append it to the
1179 // opaqueNonOverlappedWidgets list and paint it directly without composition.
1180 QVarLengthArray<QWidget *, 32> opaqueNonOverlappedWidgets;
1181 for (int i = 0; i < dirtyWidgets.size(); ++i) {
1182 QWidget *w = dirtyWidgets.at(i);
1183 QWidgetPrivate *wd = w->d_func();
1184 if (wd->data.in_destructor)
1185 continue;
1186
1187 // Clip with mask() and clipRect().
1188 wd->dirty &= wd->clipRect();
1189 wd->clipToEffectiveMask(wd->dirty);
1190
1191 // Subtract opaque siblings and children.
1192 bool hasDirtySiblingsAbove = false;
1193 // We know for sure that the widget isn't overlapped if 'isMoved' is true.
1194 if (!wd->isMoved)
1195 wd->subtractOpaqueSiblings(wd->dirty, &hasDirtySiblingsAbove);
1196
1197 // Make a copy of the widget's dirty region, to restore it in case there is an opaque
1198 // render-to-texture child that completely covers the widget, because otherwise the
1199 // render-to-texture child won't be visible, due to its parent widget not being redrawn
1200 // with a proper blending mask.
1201 const QRegion dirtyBeforeSubtractedOpaqueChildren = wd->dirty;
1202
1203 // Scrolled and moved widgets must draw all children.
1204 if (!wd->isScrolled && !wd->isMoved)
1205 wd->subtractOpaqueChildren(wd->dirty, w->rect());
1206
1207 if (wd->dirty.isEmpty() && wd->textureChildSeen)
1208 wd->dirty = dirtyBeforeSubtractedOpaqueChildren;
1209
1210 if (wd->dirty.isEmpty()) {
1211 resetWidget(w);
1212 continue;
1213 }
1214
1215 const QRegion widgetDirty(w != tlw ? wd->dirty.translated(w->mapTo(tlw, QPoint()))
1216 : wd->dirty);
1217 toClean += widgetDirty;
1218
1219#if QT_CONFIG(graphicsview)
1220 if (tlw->d_func()->extra->proxyWidget) {
1221 resetWidget(w);
1222 continue;
1223 }
1224#endif
1225
1226 if (!hasDirtySiblingsAbove && wd->isOpaque && !dirty.intersects(widgetDirty.boundingRect())) {
1227 opaqueNonOverlappedWidgets.append(w);
1228 } else {
1229 resetWidget(w);
1230 dirty += widgetDirty;
1231 }
1232 }
1233 dirtyWidgets.clear();
1234
1235#ifndef QT_NO_OPENGL
1236 // Find all render-to-texture child widgets (including self).
1237 // The search is cut at native widget boundaries, meaning that each native child widget
1238 // has its own list for the subtree below it.
1239 QTLWExtra *tlwExtra = tlw->d_func()->topData();
1240 qDeleteAll(tlwExtra->widgetTextures);
1241 tlwExtra->widgetTextures.clear();
1242 findAllTextureWidgetsRecursively(tlw, tlw);
1243 qt_window_private(tlw->windowHandle())->compositing = false; // will get updated in qt_flush()
1244#endif
1245
1246 if (toClean.isEmpty()) {
1247 // Nothing to repaint. However renderToTexture widgets are handled
1248 // specially, they are not in the regular dirty list, in order to
1249 // prevent triggering unnecessary backingstore painting when only the
1250 // OpenGL content changes. Check if we have such widgets in the special
1251 // dirty list.
1252 QVarLengthArray<QWidget *, 16> paintPending;
1253 const int numPaintPending = dirtyRenderToTextureWidgets.count();
1254 paintPending.reserve(numPaintPending);
1255 for (int i = 0; i < numPaintPending; ++i) {
1256 QWidget *w = dirtyRenderToTextureWidgets.at(i);
1257 paintPending << w;
1258 resetWidget(w);
1259 }
1260 dirtyRenderToTextureWidgets.clear();
1261 for (int i = 0; i < numPaintPending; ++i) {
1262 QWidget *w = paintPending[i];
1263 w->d_func()->sendPaintEvent(w->rect());
1264 if (w != tlw) {
1265 QWidget *npw = w->nativeParentWidget();
1266 if (w->internalWinId() || (npw && npw != tlw)) {
1267 if (!w->internalWinId())
1268 w = npw;
1269 QWidgetPrivate *wPrivate = w->d_func();
1270 if (!wPrivate->needsFlush)
1271 wPrivate->needsFlush = new QRegion;
1272 appendDirtyOnScreenWidget(w);
1273 }
1274 }
1275 }
1276
1277 // We might have newly exposed areas on the screen if this function was
1278 // called from sync(QWidget *, QRegion)), so we have to make sure those
1279 // are flushed. We also need to composite the renderToTexture widgets.
1280 flush();
1281
1282 return;
1283 }
1284
1285#ifndef QT_NO_OPENGL
1286 foreach (QPlatformTextureList *tl, tlwExtra->widgetTextures) {
1287 for (int i = 0; i < tl->count(); ++i) {
1288 QWidget *w = static_cast<QWidget *>(tl->source(i));
1289 if (dirtyRenderToTextureWidgets.contains(w)) {
1290 const QRect rect = tl->geometry(i); // mapped to the tlw already
1291 // Set a flag to indicate that the paint event for this
1292 // render-to-texture widget must not to be optimized away.
1293 w->d_func()->renderToTextureReallyDirty = 1;
1294 dirty += rect;
1295 toClean += rect;
1296 }
1297 }
1298 }
1299 for (int i = 0; i < dirtyRenderToTextureWidgets.count(); ++i)
1300 resetWidget(dirtyRenderToTextureWidgets.at(i));
1301 dirtyRenderToTextureWidgets.clear();
1302#endif
1303
1304#if QT_CONFIG(graphicsview)
1305 if (tlw->d_func()->extra->proxyWidget) {
1306 updateStaticContentsSize();
1307 dirty = QRegion();
1308 updateRequestSent = false;
1309 for (const QRect &rect : toClean)
1310 tlw->d_func()->extra->proxyWidget->update(rect);
1311 return;
1312 }
1313#endif
1314
1315 BeginPaintInfo beginPaintInfo;
1316 beginPaint(toClean, tlw, store, &beginPaintInfo);
1317 if (beginPaintInfo.nothingToPaint) {
1318 for (int i = 0; i < opaqueNonOverlappedWidgets.size(); ++i)
1319 resetWidget(opaqueNonOverlappedWidgets[i]);
1320 dirty = QRegion();
1321 updateRequestSent = false;
1322 return;
1323 }
1324
1325 // Must do this before sending any paint events because
1326 // the size may change in the paint event.
1327 updateStaticContentsSize();
1328 const QRegion dirtyCopy(dirty);
1329 dirty = QRegion();
1330 updateRequestSent = false;
1331
1332 // Paint opaque non overlapped widgets.
1333 for (int i = 0; i < opaqueNonOverlappedWidgets.size(); ++i) {
1334 QWidget *w = opaqueNonOverlappedWidgets[i];
1335 QWidgetPrivate *wd = w->d_func();
1336
1337 int flags = QWidgetPrivate::DrawRecursive;
1338 // Scrolled and moved widgets must draw all children.
1339 if (!wd->isScrolled && !wd->isMoved)
1340 flags |= QWidgetPrivate::DontDrawOpaqueChildren;
1341 if (w == tlw)
1342 flags |= QWidgetPrivate::DrawAsRoot;
1343
1344 QRegion toBePainted(wd->dirty);
1345 resetWidget(w);
1346
1347 QPoint offset;
1348 if (w != tlw)
1349 offset += w->mapTo(tlw, QPoint());
1350 wd->drawWidget(store->paintDevice(), toBePainted, offset, flags, 0, this);
1351 }
1352
1353 // Paint the rest with composition.
1354 if (repaintAllWidgets || !dirtyCopy.isEmpty()) {
1355 const int flags = QWidgetPrivate::DrawAsRoot | QWidgetPrivate::DrawRecursive;
1356 tlw->d_func()->drawWidget(store->paintDevice(), dirtyCopy, QPoint(), flags, 0, this);
1357 }
1358
1359 endPaint(toClean, store, &beginPaintInfo);
1360}
1361
1362/*!
1363 Flushes the contents of the backing store into the top-level widget.
1364 If the \a widget is non-zero, the content is flushed to the \a widget.
1365 If the \a surface is non-zero, the content of the \a surface is flushed.
1366*/
1367void QWidgetBackingStore::flush(QWidget *widget)
1368{
1369 const bool hasDirtyOnScreenWidgets = dirtyOnScreenWidgets && !dirtyOnScreenWidgets->isEmpty();
1370 bool flushed = false;
1371
1372 // Flush the region in dirtyOnScreen.
1373 if (!dirtyOnScreen.isEmpty()) {
1374 QWidget *target = widget ? widget : tlw;
1375 qt_flush(target, dirtyOnScreen, store, tlw, widgetTexturesFor(tlw, tlw), this);
1376 dirtyOnScreen = QRegion();
1377 flushed = true;
1378 }
1379
1380 // Render-to-texture widgets are not in dirtyOnScreen so flush if we have not done it above.
1381 if (!flushed && !hasDirtyOnScreenWidgets) {
1382#ifndef QT_NO_OPENGL
1383 if (!tlw->d_func()->topData()->widgetTextures.isEmpty()) {
1384 QPlatformTextureList *tl = widgetTexturesFor(tlw, tlw);
1385 if (tl) {
1386 QWidget *target = widget ? widget : tlw;
1387 qt_flush(target, QRegion(), store, tlw, tl, this);
1388 }
1389 }
1390#endif
1391 }
1392
1393 if (!hasDirtyOnScreenWidgets)
1394 return;
1395
1396 for (int i = 0; i < dirtyOnScreenWidgets->size(); ++i) {
1397 QWidget *w = dirtyOnScreenWidgets->at(i);
1398 QWidgetPrivate *wd = w->d_func();
1399 Q_ASSERT(wd->needsFlush);
1400 QPlatformTextureList *widgetTexturesForNative = wd->textureChildSeen ? widgetTexturesFor(tlw, w) : 0;
1401 qt_flush(w, *wd->needsFlush, store, tlw, widgetTexturesForNative, this);
1402 *wd->needsFlush = QRegion();
1403 }
1404 dirtyOnScreenWidgets->clear();
1405}
1406
1407/*!
1408 Invalidates the backing store when the widget is resized.
1409 Static areas are never invalidated unless absolutely needed.
1410*/
1411void QWidgetPrivate::invalidateBackingStore_resizeHelper(const QPoint &oldPos, const QSize &oldSize)
1412{
1413 Q_Q(QWidget);
1414 Q_ASSERT(!q->isWindow());
1415 Q_ASSERT(q->parentWidget());
1416
1417 const bool staticContents = q->testAttribute(Qt::WA_StaticContents);
1418 const bool sizeDecreased = (data.crect.width() < oldSize.width())
1419 || (data.crect.height() < oldSize.height());
1420
1421 const QPoint offset(data.crect.x() - oldPos.x(), data.crect.y() - oldPos.y());
1422 const bool parentAreaExposed = !offset.isNull() || sizeDecreased;
1423 const QRect newWidgetRect(q->rect());
1424 const QRect oldWidgetRect(0, 0, oldSize.width(), oldSize.height());
1425
1426 if (!staticContents || graphicsEffect) {
1427 QRegion staticChildren;
1428 QWidgetBackingStore *bs = 0;
1429 if (offset.isNull() && (bs = maybeBackingStore()))
1430 staticChildren = bs->staticContents(q, oldWidgetRect);
1431 const bool hasStaticChildren = !staticChildren.isEmpty();
1432
1433 if (hasStaticChildren) {
1434 QRegion dirty(newWidgetRect);
1435 dirty -= staticChildren;
1436 invalidateBackingStore(dirty);
1437 } else {
1438 // Entire widget needs repaint.
1439 invalidateBackingStore(newWidgetRect);
1440 }
1441
1442 if (!parentAreaExposed)
1443 return;
1444
1445 // Invalidate newly exposed area of the parent.
1446 if (!graphicsEffect && extra && extra->hasMask) {
1447 QRegion parentExpose(extra->mask.translated(oldPos));
1448 parentExpose &= QRect(oldPos, oldSize);
1449 if (hasStaticChildren)
1450 parentExpose -= data.crect; // Offset is unchanged, safe to do this.
1451 q->parentWidget()->d_func()->invalidateBackingStore(parentExpose);
1452 } else {
1453 if (hasStaticChildren && !graphicsEffect) {
1454 QRegion parentExpose(QRect(oldPos, oldSize));
1455 parentExpose -= data.crect; // Offset is unchanged, safe to do this.
1456 q->parentWidget()->d_func()->invalidateBackingStore(parentExpose);
1457 } else {
1458 q->parentWidget()->d_func()->invalidateBackingStore(effectiveRectFor(QRect(oldPos, oldSize)));
1459 }
1460 }
1461 return;
1462 }
1463
1464 // Move static content to its new position.
1465 if (!offset.isNull()) {
1466 if (sizeDecreased) {
1467 const QSize minSize(qMin(oldSize.width(), data.crect.width()),
1468 qMin(oldSize.height(), data.crect.height()));
1469 moveRect(QRect(oldPos, minSize), offset.x(), offset.y());
1470 } else {
1471 moveRect(QRect(oldPos, oldSize), offset.x(), offset.y());
1472 }
1473 }
1474
1475 // Invalidate newly visible area of the widget.
1476 if (!sizeDecreased || !oldWidgetRect.contains(newWidgetRect)) {
1477 QRegion newVisible(newWidgetRect);
1478 newVisible -= oldWidgetRect;
1479 invalidateBackingStore(newVisible);
1480 }
1481
1482 if (!parentAreaExposed)
1483 return;
1484
1485 // Invalidate newly exposed area of the parent.
1486 const QRect oldRect(oldPos, oldSize);
1487 if (extra && extra->hasMask) {
1488 QRegion parentExpose(oldRect);
1489 parentExpose &= extra->mask.translated(oldPos);
1490 parentExpose -= (extra->mask.translated(data.crect.topLeft()) & data.crect);
1491 q->parentWidget()->d_func()->invalidateBackingStore(parentExpose);
1492 } else {
1493 QRegion parentExpose(oldRect);
1494 parentExpose -= data.crect;
1495 q->parentWidget()->d_func()->invalidateBackingStore(parentExpose);
1496 }
1497}
1498
1499/*!
1500 Invalidates the \a r (in widget's coordinates) of the backing store, i.e.
1501 all widgets intersecting with the region will be repainted when the backing
1502 store is synced.
1503*/
1504template <class T>
1505void QWidgetPrivate::invalidateBackingStore(const T &r)
1506{
1507 if (r.isEmpty())
1508 return;
1509
1510 if (QCoreApplication::closingDown())
1511 return;
1512
1513 Q_Q(QWidget);
1514 if (!q->isVisible() || !q->updatesEnabled())
1515 return;
1516
1517 QTLWExtra *tlwExtra = q->window()->d_func()->maybeTopData();
1518 if (!tlwExtra || tlwExtra->inTopLevelResize || !tlwExtra->backingStore)
1519 return;
1520
1521 T clipped(r);
1522 clipped &= clipRect();
1523 if (clipped.isEmpty())
1524 return;
1525
1526 if (!graphicsEffect && extra && extra->hasMask) {
1527 QRegion masked(extra->mask);
1528 masked &= clipped;
1529 if (masked.isEmpty())
1530 return;
1531
1532 tlwExtra->backingStoreTracker->markDirty(masked, q,
1533 QWidgetBackingStore::UpdateLater, QWidgetBackingStore::BufferInvalid);
1534 } else {
1535 tlwExtra->backingStoreTracker->markDirty(clipped, q,
1536 QWidgetBackingStore::UpdateLater, QWidgetBackingStore::BufferInvalid);
1537 }
1538}
1539// Needed by tst_QWidget
1540template Q_AUTOTEST_EXPORT void QWidgetPrivate::invalidateBackingStore<QRect>(const QRect &r);
1541
1542void QWidgetPrivate::repaint_sys(const QRegion &rgn)
1543{
1544 if (data.in_destructor)
1545 return;
1546
1547 Q_Q(QWidget);
1548 if (discardSyncRequest(q, maybeTopData()))
1549 return;
1550
1551 if (q->testAttribute(Qt::WA_StaticContents)) {
1552 if (!extra)
1553 createExtra();
1554 extra->staticContentsSize = data.crect.size();
1555 }
1556
1557 QPaintEngine *engine = q->paintEngine();
1558
1559 // QGLWidget does not support partial updates if:
1560 // 1) The context is double buffered
1561 // 2) The context is single buffered and auto-fill background is enabled.
1562 const bool noPartialUpdateSupport = (engine && (engine->type() == QPaintEngine::OpenGL
1563 || engine->type() == QPaintEngine::OpenGL2))
1564 && (usesDoubleBufferedGLContext || q->autoFillBackground());
1565 QRegion toBePainted(noPartialUpdateSupport ? q->rect() : rgn);
1566
1567#if 0 // Used to be included in Qt4 for Q_WS_MAC
1568 // No difference between update() and repaint() on the Mac.
1569 update_sys(toBePainted);
1570 return;
1571#endif
1572
1573 toBePainted &= clipRect();
1574 clipToEffectiveMask(toBePainted);
1575 if (toBePainted.isEmpty())
1576 return; // Nothing to repaint.
1577
1578#ifndef QT_NO_PAINT_DEBUG
1579 bool flushed = QWidgetBackingStore::flushPaint(q, toBePainted);
1580#endif
1581
1582 drawWidget(q, toBePainted, QPoint(), QWidgetPrivate::DrawAsRoot | QWidgetPrivate::DrawPaintOnScreen, 0);
1583
1584#ifndef QT_NO_PAINT_DEBUG
1585 if (flushed)
1586 QWidgetBackingStore::unflushPaint(q, toBePainted);
1587#endif
1588
1589 if (Q_UNLIKELY(q->paintingActive()))
1590 qWarning("QWidget::repaint: It is dangerous to leave painters active on a widget outside of the PaintEvent");
1591}
1592
1593
1594QT_END_NAMESPACE
1595
1596#include "moc_qwidgetbackingstore_p.cpp"
1597