From ea9ad9fff1b9b3ac3746e860a8aa00072aea5750 Mon Sep 17 00:00:00 2001 From: kleuter Date: Mon, 25 Nov 2019 20:54:47 +0100 Subject: [PATCH] revert commit from Alberto --- .../platforms/cocoa/qcocoabackingstore.mm | 721 -------- .../widgets/kernel/qwidgetbackingstore.cpp | 1602 ----------------- 2 files changed, 2323 deletions(-) delete mode 100644 5.13.2/qtbase/src/plugins/platforms/cocoa/qcocoabackingstore.mm delete mode 100644 5.13.2/qtbase/src/widgets/kernel/qwidgetbackingstore.cpp diff --git a/5.13.2/qtbase/src/plugins/platforms/cocoa/qcocoabackingstore.mm b/5.13.2/qtbase/src/plugins/platforms/cocoa/qcocoabackingstore.mm deleted file mode 100644 index b7a1fe9..0000000 --- a/5.13.2/qtbase/src/plugins/platforms/cocoa/qcocoabackingstore.mm +++ /dev/null @@ -1,721 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the plugins of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qcocoabackingstore.h" - -#include "qcocoawindow.h" -#include "qcocoahelpers.h" - -#include - -#include - -QT_BEGIN_NAMESPACE - -QCocoaBackingStore::QCocoaBackingStore(QWindow *window) - : QRasterBackingStore(window) -{ - // Ideally this would be plumbed from the platform layer to QtGui, and - // the QBackingStore would be recreated, but we don't have that code yet, - // so at least make sure we invalidate our backingstore when the backing - // properties (color space e.g.) are changed. - NSView *view = static_cast(window->handle())->view(); - m_backingPropertiesObserver = QMacNotificationObserver(view.window, - NSWindowDidChangeBackingPropertiesNotification, [this]() { - qCDebug(lcQpaBackingStore) << "Backing properties for" - << this->window() << "did change"; - backingPropertiesChanged(); - }); -} - -QCFType QCocoaBackingStore::colorSpace() const -{ - NSView *view = static_cast(window()->handle())->view(); - return QCFType::constructFromGet(view.window.colorSpace.CGColorSpace); -} - -// ---------------------------------------------------------------------------- - -QNSWindowBackingStore::QNSWindowBackingStore(QWindow *window) - : QCocoaBackingStore(window) -{ - // Choose an appropriate window depth based on the requested surface format. - // On deep color displays the default bit depth is 16-bit, so unless we need - // that level of precision we opt out of it (and the expensive RGB32 -> RGB64 - // conversions that come with it if our backingstore depth does not match). - - NSWindow *nsWindow = static_cast(window->handle())->view().window; - auto colorSpaceName = NSColorSpaceFromDepth(nsWindow.depthLimit); - - static const int kDefaultBitDepth = 8; - auto surfaceFormat = window->requestedFormat(); - auto bitsPerSample = qMax(kDefaultBitDepth, qMax(surfaceFormat.redBufferSize(), - qMax(surfaceFormat.greenBufferSize(), surfaceFormat.blueBufferSize()))); - - // NSBestDepth does not seem to guarantee a window depth deep enough for the - // given bits per sample, even if documented as such. For example, requesting - // 10 bits per sample will not give us a 16-bit format, even if that's what's - // available. Work around this by manually bumping the bit depth. - bitsPerSample = !(bitsPerSample & (bitsPerSample - 1)) - ? bitsPerSample : qNextPowerOfTwo(bitsPerSample); - - auto bestDepth = NSBestDepth(colorSpaceName, bitsPerSample, 0, NO, nullptr); - - // Disable dynamic depth limit, otherwise our depth limit will be overwritten - // by AppKit if the window moves to a screen with a different depth. We call - // this before setting the depth limit, as the call will reset the depth to 0. - [nsWindow setDynamicDepthLimit:NO]; - - qCDebug(lcQpaBackingStore) << "Using" << NSBitsPerSampleFromDepth(bestDepth) - << "bit window depth for" << nsWindow; - - nsWindow.depthLimit = bestDepth; -} - -QNSWindowBackingStore::~QNSWindowBackingStore() -{ -} - -bool QNSWindowBackingStore::windowHasUnifiedToolbar() const -{ - Q_ASSERT(window()->handle()); - return static_cast(window()->handle())->m_drawContentBorderGradient; -} - -QImage::Format QNSWindowBackingStore::format() const -{ - if (windowHasUnifiedToolbar()) - return QImage::Format_ARGB32_Premultiplied; - - return QRasterBackingStore::format(); -} - -void QNSWindowBackingStore::resize(const QSize &size, const QRegion &staticContents) -{ - qCDebug(lcQpaBackingStore) << "Resize requested to" << size; - QRasterBackingStore::resize(size, staticContents); - - // The window shadow rendered by AppKit is based on the shape/content of the - // NSWindow surface. Technically any flush of the backingstore can result in - // a potentially new shape of the window, and would need a shadow invalidation, - // but this is likely too expensive to do at every flush for the few cases where - // clients change the shape dynamically. One case where we do know that the shadow - // likely needs invalidation, if the window has partially transparent content, - // is after a resize, where AppKit's default shadow may be based on the previous - // window content. - QCocoaWindow *cocoaWindow = static_cast(window()->handle()); - if (cocoaWindow->isContentView() && !cocoaWindow->isOpaque()) - cocoaWindow->m_needsInvalidateShadow = true; -} - -/*! - Flushes the given \a region from the specified \a window onto the - screen. - - The \a window is the top level window represented by this backingstore, - or a non-transient child of that window. - - If the \a window is a child window, the \a region will be in child window - coordinates, and the \a offset will be the child window's offset in relation - to the backingstore's top level window. -*/ -void QNSWindowBackingStore::flush(QWindow *window, const QRegion ®ion, const QPoint &offset) -{ - if (m_image.isNull()) - return; - - // Use local pool so that any stale image references are cleaned up after flushing - QMacAutoReleasePool pool; - - const QWindow *topLevelWindow = this->window(); - - if (window->handle()->isForeignWindow()) { - NSView *view = static_cast(window->handle())->view(); - NSRect nsRect = NSRectFromCGRect(region.boundingRect().toCGRect()); - [view displayRect:nsRect]; - return; - } - Q_ASSERT(topLevelWindow->handle() && window->handle()); - Q_ASSERT(!topLevelWindow->handle()->isForeignWindow() && !window->handle()->isForeignWindow()); - - QNSView *topLevelView = qnsview_cast(static_cast(topLevelWindow->handle())->view()); - QNSView *view = qnsview_cast(static_cast(window->handle())->view()); - - if (lcQpaBackingStore().isDebugEnabled()) { - QString targetViewDescription; - if (view != topLevelView) { - QDebug targetDebug(&targetViewDescription); - targetDebug << "onto" << topLevelView << "at" << offset; - } - qCDebug(lcQpaBackingStore) << "Flushing" << region << "of" << view << qPrintable(targetViewDescription); - } - - // Normally a NSView is drawn via drawRect, as part of the display cycle in the - // main runloop, via setNeedsDisplay and friends. AppKit will lock focus on each - // individual view, starting with the top level and then traversing any subviews, - // calling drawRect for each of them. This pull model results in expose events - // sent to Qt, which result in drawing to the backingstore and flushing it. - // Qt may also decide to paint and flush the backingstore via e.g. timers, - // or other events such as mouse events, in which case we're in a push model. - // If there is no focused view, it means we're in the latter case, and need - // to manually flush the NSWindow after drawing to its graphic context. - const bool drawingOutsideOfDisplayCycle = ![NSView focusView]; - - // We also need to ensure the flushed view has focus, so that the graphics - // context is set up correctly (coordinate system, clipping, etc). Outside - // of the normal display cycle there is no focused view, as explained above, - // so we have to handle it manually. There's also a corner case inside the - // normal display cycle due to way QWidgetBackingStore composits native child - // widgets, where we'll get a flush of a native child during the drawRect of - // its parent/ancestor, and the parent/ancestor being the one locked by AppKit. - // In this case we also need to lock and unlock focus manually. - const bool shouldHandleViewLockManually = [NSView focusView] != view; - if (shouldHandleViewLockManually && ![view lockFocusIfCanDraw]) { - qWarning() << "failed to lock focus of" << view; - return; - } - - const qreal devicePixelRatio = m_image.devicePixelRatio(); - - // If the flushed window is a content view, and we're filling the drawn area - // completely, or it doesn't have a window background we need to preserve, - // we can get away with copying instead of blending the backing store. - QCocoaWindow *cocoaWindow = static_cast(window->handle()); - const NSCompositingOperation compositingOperation = cocoaWindow->isContentView() - && (cocoaWindow->isOpaque() || view.window.backgroundColor == NSColor.clearColor) - ? NSCompositingOperationCopy : NSCompositingOperationSourceOver; - -#ifdef QT_DEBUG - static bool debugBackingStoreFlush = [[NSUserDefaults standardUserDefaults] - boolForKey:@"QtCocoaDebugBackingStoreFlush"]; -#endif - - // ------------------------------------------------------------------------- - - // The current contexts is typically a NSWindowGraphicsContext, but can be - // NSBitmapGraphicsContext e.g. when debugging the view hierarchy in Xcode. - // If we need to distinguish things here in the future, we can use e.g. - // [NSGraphicsContext drawingToScreen], or the attributes of the context. - NSGraphicsContext *graphicsContext = [NSGraphicsContext currentContext]; - Q_ASSERT_X(graphicsContext, "QCocoaBackingStore", - "Focusing the view should give us a current graphics context"); - - // Tag backingstore image with color space based on the window. - // Note: This does not copy the underlying image data. - QCFType cgImage = CGImageCreateCopyWithColorSpace( - QCFType(m_image.toCGImage()), colorSpace()); - - // Create temporary image to use for blitting, without copying image data - NSImage *backingStoreImage = [[[NSImage alloc] initWithCGImage:cgImage size:NSZeroSize] autorelease]; - - QRegion clippedRegion = region; - for (QWindow *w = window; w; w = w->parent()) { - if (!w->mask().isEmpty()) { - clippedRegion &= w == window ? w->mask() - : w->mask().translated(window->mapFromGlobal(w->mapToGlobal(QPoint(0, 0)))); - } - } - - for (const QRect &viewLocalRect : clippedRegion) { - QPoint backingStoreOffset = viewLocalRect.topLeft() + offset; - QRect backingStoreRect(backingStoreOffset * devicePixelRatio, viewLocalRect.size() * devicePixelRatio); - if (graphicsContext.flipped) // Flip backingStoreRect to match graphics context - backingStoreRect.moveTop(m_image.height() - (backingStoreRect.y() + backingStoreRect.height())); - - CGRect viewRect = viewLocalRect.toCGRect(); - - [backingStoreImage drawInRect:viewRect fromRect:backingStoreRect.toCGRect() - operation:compositingOperation fraction:1.0 respectFlipped:YES hints:nil]; - -#ifdef QT_DEBUG - if (Q_UNLIKELY(debugBackingStoreFlush)) { - [[NSColor colorWithCalibratedRed:drand48() green:drand48() blue:drand48() alpha:0.3] set]; - [NSBezierPath fillRect:viewRect]; - - if (drawingOutsideOfDisplayCycle) { - [[[NSColor magentaColor] colorWithAlphaComponent:0.5] set]; - [NSBezierPath strokeLineFromPoint:viewLocalRect.topLeft().toCGPoint() - toPoint:viewLocalRect.bottomRight().toCGPoint()]; - } - } -#endif - } - - // ------------------------------------------------------------------------- - - if (shouldHandleViewLockManually) - [view unlockFocus]; - - if (drawingOutsideOfDisplayCycle) { - redrawRoundedBottomCorners([view convertRect:region.boundingRect().toCGRect() toView:nil]); - [view.window flushWindow]; - } - - - // Done flushing to NSWindow backingstore - - QCocoaWindow *topLevelCocoaWindow = static_cast(topLevelWindow->handle()); - if (Q_UNLIKELY(topLevelCocoaWindow->m_needsInvalidateShadow)) { - qCDebug(lcQpaBackingStore) << "Invalidating window shadow for" << topLevelCocoaWindow; - [topLevelView.window invalidateShadow]; - topLevelCocoaWindow->m_needsInvalidateShadow = false; - } -} - -/* - When drawing outside of the display cycle, which Qt Widget does a lot, - we end up drawing over the NSThemeFrame, losing the rounded corners of - windows in the process. - - To work around this, until we've enabled updates via setNeedsDisplay and/or - enabled layer-backed views, we ask the NSWindow to redraw the bottom corners - if they intersect with the flushed region. - - This is the same logic used internally by e.g [NSView displayIfNeeded], - [NSRulerView _scrollToMatchContentView], and [NSClipView _immediateScrollToPoint:], - as well as the workaround used by WebKit to fix a similar bug: - - https://trac.webkit.org/changeset/85376/webkit -*/ -void QNSWindowBackingStore::redrawRoundedBottomCorners(CGRect windowRect) const -{ -#if !defined(QT_APPLE_NO_PRIVATE_APIS) - Q_ASSERT(this->window()->handle()); - NSWindow *window = static_cast(this->window()->handle())->nativeWindow(); - - static SEL intersectBottomCornersWithRect = NSSelectorFromString( - [NSString stringWithFormat:@"_%s%s:", "intersectBottomCorners", "WithRect"]); - if (NSMethodSignature *signature = [window methodSignatureForSelector:intersectBottomCornersWithRect]) { - NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:signature]; - invocation.target = window; - invocation.selector = intersectBottomCornersWithRect; - [invocation setArgument:&windowRect atIndex:2]; - [invocation invoke]; - - NSRect cornerOverlap = NSZeroRect; - [invocation getReturnValue:&cornerOverlap]; - if (!NSIsEmptyRect(cornerOverlap)) { - static SEL maskRoundedBottomCorners = NSSelectorFromString( - [NSString stringWithFormat:@"_%s%s:", "maskRounded", "BottomCorners"]); - if ((signature = [window methodSignatureForSelector:maskRoundedBottomCorners])) { - invocation = [NSInvocation invocationWithMethodSignature:signature]; - invocation.target = window; - invocation.selector = maskRoundedBottomCorners; - [invocation setArgument:&cornerOverlap atIndex:2]; - [invocation invoke]; - } - } - } -#else - Q_UNUSED(windowRect); -#endif -} - -void QNSWindowBackingStore::backingPropertiesChanged() -{ - m_image = QImage(); -} - -// ---------------------------------------------------------------------------- - -QCALayerBackingStore::QCALayerBackingStore(QWindow *window) - : QCocoaBackingStore(window) -{ - qCDebug(lcQpaBackingStore) << "Creating QCALayerBackingStore for" << window; - m_buffers.resize(1); -} - -QCALayerBackingStore::~QCALayerBackingStore() -{ -} - -void QCALayerBackingStore::resize(const QSize &size, const QRegion &staticContents) -{ - qCDebug(lcQpaBackingStore) << "Resize requested to" << size; - - if (!staticContents.isNull()) - qCWarning(lcQpaBackingStore) << "QCALayerBackingStore does not support static contents"; - - m_requestedSize = size; -} - -void QCALayerBackingStore::beginPaint(const QRegion ®ion) -{ - Q_UNUSED(region); - - QMacAutoReleasePool pool; - - qCInfo(lcQpaBackingStore) << "Beginning paint of" << region << "into backingstore of" << m_requestedSize; - - ensureBackBuffer(); // Find an unused back buffer, or reserve space for a new one - - const bool bufferWasRecreated = recreateBackBufferIfNeeded(); - - m_buffers.back()->lock(QPlatformGraphicsBuffer::SWWriteAccess); - - // Although undocumented, QBackingStore::beginPaint expects the painted region - // to be cleared before use if the window has a surface format with an alpha. - // Fresh IOSurfaces are already cleared, so we don't need to clear those. - if (!bufferWasRecreated && window()->format().hasAlpha()) { - qCDebug(lcQpaBackingStore) << "Clearing" << region << "before use"; - QPainter painter(m_buffers.back()->asImage()); - painter.setCompositionMode(QPainter::CompositionMode_Source); - for (const QRect &rect : region) - painter.fillRect(rect, Qt::transparent); - } - - m_paintedRegion += region; -} - -void QCALayerBackingStore::ensureBackBuffer() -{ - if (window()->format().swapBehavior() == QSurfaceFormat::SingleBuffer) - return; - - // The current back buffer may have been assigned to a layer in a previous flush, - // but we deferred the swap. Do it now if the surface has been picked up by CA. - if (m_buffers.back() && m_buffers.back()->isInUse() && m_buffers.back() != m_buffers.front()) { - qCInfo(lcQpaBackingStore) << "Back buffer has been picked up by CA, swapping to front"; - std::swap(m_buffers.back(), m_buffers.front()); - } - - if (Q_UNLIKELY(lcQpaBackingStore().isDebugEnabled())) { - // ┌───────┬───────┬───────┬─────┬──────┐ - // │ front ┊ spare ┊ spare ┊ ... ┊ back │ - // └───────┴───────┴───────┴─────┴──────┘ - for (const auto &buffer : m_buffers) { - qCDebug(lcQpaBackingStore).nospace() << " " - << (buffer == m_buffers.front() ? "front" : - buffer == m_buffers.back() ? " back" : - "spare" - ) << ": " << buffer.get(); - } - } - - // Ensure our back buffer is ready to draw into. If not, find a buffer that - // is not in use, or reserve space for a new buffer if none can be found. - for (auto &buffer : backwards(m_buffers)) { - if (!buffer || !buffer->isInUse()) { - // Buffer is okey to use, swap if necessary - if (buffer != m_buffers.back()) - std::swap(buffer, m_buffers.back()); - qCDebug(lcQpaBackingStore) << "Using back buffer" << m_buffers.back().get(); - - static const int kMaxSwapChainDepth = 3; - if (m_buffers.size() > kMaxSwapChainDepth) { - qCDebug(lcQpaBackingStore) << "Reducing swap chain depth to" << kMaxSwapChainDepth; - m_buffers.erase(std::next(m_buffers.begin(), 1), std::prev(m_buffers.end(), 2)); - } - - break; - } else if (buffer == m_buffers.front()) { - // We've exhausted the available buffers, make room for a new one - const int swapChainDepth = m_buffers.size() + 1; - qCDebug(lcQpaBackingStore) << "Available buffers exhausted, increasing swap chain depth to" << swapChainDepth; - m_buffers.resize(swapChainDepth); - break; - } - } - - Q_ASSERT(!m_buffers.back() || !m_buffers.back()->isInUse()); -} - -// Disabled until performance issue on 5K iMac Pro has been investigated further, -// as rounding up during resize will typically result in full screen buffer sizes -// and low frame rate also for smaller window sizes. -#define USE_LAZY_BUFFER_ALLOCATION_DURING_LIVE_WINDOW_RESIZE 0 - -bool QCALayerBackingStore::recreateBackBufferIfNeeded() -{ - const QCocoaWindow *platformWindow = static_cast(window()->handle()); - const qreal devicePixelRatio = platformWindow->devicePixelRatio(); - QSize requestedBufferSize = m_requestedSize * devicePixelRatio; - - const NSView *backingStoreView = platformWindow->view(); - Q_UNUSED(backingStoreView); - - auto bufferSizeMismatch = [&](const QSize requested, const QSize actual) { -#if USE_LAZY_BUFFER_ALLOCATION_DURING_LIVE_WINDOW_RESIZE - if (backingStoreView.inLiveResize) { - // Prevent over-eager buffer allocation during window resize by reusing larger buffers - return requested.width() > actual.width() || requested.height() > actual.height(); - } -#endif - return requested != actual; - }; - - if (!m_buffers.back() || bufferSizeMismatch(requestedBufferSize, m_buffers.back()->size())) { -#if USE_LAZY_BUFFER_ALLOCATION_DURING_LIVE_WINDOW_RESIZE - if (backingStoreView.inLiveResize) { - // Prevent over-eager buffer allocation during window resize by rounding up - QSize nativeScreenSize = window()->screen()->geometry().size() * devicePixelRatio; - requestedBufferSize = QSize(qNextPowerOfTwo(requestedBufferSize.width()), - qNextPowerOfTwo(requestedBufferSize.height())).boundedTo(nativeScreenSize); - } -#endif - - qCInfo(lcQpaBackingStore) << "Creating surface of" << requestedBufferSize - << "based on requested" << m_requestedSize << "and dpr =" << devicePixelRatio; - - static auto pixelFormat = QImage::toPixelFormat(QImage::Format_ARGB32_Premultiplied); - m_buffers.back().reset(new GraphicsBuffer(requestedBufferSize, devicePixelRatio, pixelFormat, colorSpace())); - return true; - } - - return false; -} - -QPaintDevice *QCALayerBackingStore::paintDevice() -{ - Q_ASSERT(m_buffers.back()); - return m_buffers.back()->asImage(); -} - -void QCALayerBackingStore::endPaint() -{ - qCInfo(lcQpaBackingStore) << "Paint ended with painted region" << m_paintedRegion; - m_buffers.back()->unlock(); -} - -void QCALayerBackingStore::flush(QWindow *flushedWindow, const QRegion ®ion, const QPoint &offset) -{ - Q_UNUSED(region); - Q_UNUSED(offset); - - if (!prepareForFlush()) - return; - - QMacAutoReleasePool pool; - - NSView *backingStoreView = static_cast(window()->handle())->view(); - NSView *flushedView = static_cast(flushedWindow->handle())->view(); - - // If the backingstore is just flushed, without being painted to first, then we may - // end in a situation where the backingstore is flushed to a layer with a different - // scale factor than the one it was created for in beginPaint. This is the client's - // fault in not picking up the change in scale factor of the window and re-painting - // the backingstore accordingly. To smoothing things out, we warn about this situation, - // and change the layer's contentsScale to match the scale of the back buffer, so that - // we at least cover the whole layer. This is necessary since we set the view's - // contents placement policy to NSViewLayerContentsPlacementTopLeft, which means - // AppKit will not do any scaling on our behalf. - if (m_buffers.back()->devicePixelRatio() != flushedView.layer.contentsScale) { - qCWarning(lcQpaBackingStore) << "Back buffer dpr of" << m_buffers.back()->devicePixelRatio() - << "doesn't match" << flushedView.layer << "contents scale of" << flushedView.layer.contentsScale - << "- updating layer to match."; - flushedView.layer.contentsScale = m_buffers.back()->devicePixelRatio(); - } - - id backBufferSurface = (__bridge id)m_buffers.back()->surface(); - if (flushedView.layer.contents == backBufferSurface) { - // We've managed to paint to the back buffer again before Core Animation had time - // to flush the transaction and persist the layer changes to the window server, or - // we've been asked to flush without painting anything. The layer already knows about - // the back buffer, and we don't need to re-apply it to pick up any possible surface - // changes, so bail out early. - qCInfo(lcQpaBackingStore).nospace() << "Skipping flush of " << flushedView - << ", layer already reflects back buffer"; - return; - } - - // Trigger a new display cycle if there isn't one. This ensures that our layer updates - // are committed as part of a display-cycle instead of on the next runloop pass. This - // means CA won't try to throttle us if we flush too fast, and we'll coalesce our flush - // with other pending view and layer updates. - backingStoreView.window.viewsNeedDisplay = YES; - - if (window()->format().swapBehavior() == QSurfaceFormat::SingleBuffer) { - // The private API [CALayer reloadValueForKeyPath:@"contents"] would be preferable, - // but barring any side effects or performance issues we opt for the hammer for now. - flushedView.layer.contents = nil; - } - - qCInfo(lcQpaBackingStore) << "Flushing" << backBufferSurface - << "to" << flushedView.layer << "of" << flushedView; - - flushedView.layer.contents = backBufferSurface; - - if (flushedView != backingStoreView) { - const CGSize backingStoreSize = backingStoreView.bounds.size; - flushedView.layer.contentsRect = CGRectApplyAffineTransform( - [flushedView convertRect:flushedView.bounds toView:backingStoreView], - // The contentsRect is in unit coordinate system - CGAffineTransformMakeScale(1.0 / backingStoreSize.width, 1.0 / backingStoreSize.height)); - } - - // Since we may receive multiple flushes before a new frame is started, we do not - // swap any buffers just yet. Instead we check in the next beginPaint if the layer's - // surface is in use, and if so swap to an unused surface as the new back buffer. - - // Note: Ideally CoreAnimation would mark a surface as in use the moment we assign - // it to a layer, but as that's not the case we may end up painting to the same back - // buffer once more if we are painting faster than CA can ship the surfaces over to - // the window server. -} - -#ifndef QT_NO_OPENGL -void QCALayerBackingStore::composeAndFlush(QWindow *window, const QRegion ®ion, const QPoint &offset, - QPlatformTextureList *textures, bool translucentBackground) -{ - if (!prepareForFlush()) - return; - - QPlatformBackingStore::composeAndFlush(window, region, offset, textures, translucentBackground); -} -#endif - -QImage QCALayerBackingStore::toImage() const -{ - if (!const_cast(this)->prepareForFlush()) - return QImage(); - - // We need to make a copy here, as the returned image could be used just - // for reading, in which case it won't detach, and then the underlying - // image data might change under the feet of the client when we re-use - // the buffer at a later point. - m_buffers.back()->lock(QPlatformGraphicsBuffer::SWReadAccess); - QImage imageCopy = m_buffers.back()->asImage()->copy(); - m_buffers.back()->unlock(); - return imageCopy; -} - -void QCALayerBackingStore::backingPropertiesChanged() -{ - m_buffers.clear(); - m_buffers.resize(1); -} - -QPlatformGraphicsBuffer *QCALayerBackingStore::graphicsBuffer() const -{ - return m_buffers.back().get(); -} - -bool QCALayerBackingStore::prepareForFlush() -{ - if (!m_buffers.back()) { - qCWarning(lcQpaBackingStore) << "Tried to flush backingstore without painting to it first"; - return false; - } - - // Update dirty state of buffers based on what was painted. The back buffer will be - // less dirty, since we painted to it, while other buffers will become more dirty. - // This allows us to minimize copies between front and back buffers on swap in the - // cases where the painted region overlaps with the previous frame (front buffer). - for (const auto &buffer : m_buffers) { - if (buffer == m_buffers.back()) - buffer->dirtyRegion -= m_paintedRegion; - else - buffer->dirtyRegion += m_paintedRegion; - } - - // After painting, the back buffer is only guaranteed to have content for the painted - // region, and may still have dirty areas that need to be synced up with the front buffer, - // if we have one. We know that the front buffer is always up to date. - if (!m_buffers.back()->dirtyRegion.isEmpty() && m_buffers.front() != m_buffers.back()) { - QRegion preserveRegion = m_buffers.back()->dirtyRegion; - qCDebug(lcQpaBackingStore) << "Preserving" << preserveRegion << "from front to back buffer"; - - m_buffers.front()->lock(QPlatformGraphicsBuffer::SWReadAccess); - const QImage *frontBuffer = m_buffers.front()->asImage(); - - const QRect frontSurfaceBounds(QPoint(0, 0), m_buffers.front()->size()); - const qreal sourceDevicePixelRatio = frontBuffer->devicePixelRatio(); - - m_buffers.back()->lock(QPlatformGraphicsBuffer::SWWriteAccess); - QPainter painter(m_buffers.back()->asImage()); - painter.setCompositionMode(QPainter::CompositionMode_Source); - - // Let painter operate in device pixels, to make it easier to compare coordinates - const qreal targetDevicePixelRatio = painter.device()->devicePixelRatio(); - painter.scale(1.0 / targetDevicePixelRatio, 1.0 / targetDevicePixelRatio); - - for (const QRect &rect : preserveRegion) { - QRect sourceRect(rect.topLeft() * sourceDevicePixelRatio, rect.size() * sourceDevicePixelRatio); - QRect targetRect(rect.topLeft() * targetDevicePixelRatio, rect.size() * targetDevicePixelRatio); - -#ifdef QT_DEBUG - if (Q_UNLIKELY(!frontSurfaceBounds.contains(sourceRect.bottomRight()))) { - qCWarning(lcQpaBackingStore) << "Front buffer too small to preserve" - << QRegion(sourceRect).subtracted(frontSurfaceBounds); - } -#endif - painter.drawImage(targetRect, *frontBuffer, sourceRect); - } - - m_buffers.back()->unlock(); - m_buffers.front()->unlock(); - - // The back buffer is now completely in sync, ready to be presented - m_buffers.back()->dirtyRegion = QRegion(); - } - - // Prepare for another round of painting - m_paintedRegion = QRegion(); - - return true; -} - -// ---------------------------------------------------------------------------- - -QCALayerBackingStore::GraphicsBuffer::GraphicsBuffer(const QSize &size, qreal devicePixelRatio, - const QPixelFormat &format, QCFType colorSpace) - : QIOSurfaceGraphicsBuffer(size, format, colorSpace) - , dirtyRegion(0, 0, size.width() / devicePixelRatio, size.height() / devicePixelRatio) - , m_devicePixelRatio(devicePixelRatio) -{ -} - -QImage *QCALayerBackingStore::GraphicsBuffer::asImage() -{ - if (m_image.isNull()) { - qCDebug(lcQpaBackingStore) << "Setting up paint device for" << this; - CFRetain(surface()); - m_image = QImage(data(), size().width(), size().height(), - bytesPerLine(), QImage::toImageFormat(format()), - QImageCleanupFunction(CFRelease), surface()); - m_image.setDevicePixelRatio(m_devicePixelRatio); - } - - Q_ASSERT_X(m_image.constBits() == data(), "QCALayerBackingStore", - "IOSurfaces should have have a fixed location in memory once created"); - - return &m_image; -} - -QT_END_NAMESPACE diff --git a/5.13.2/qtbase/src/widgets/kernel/qwidgetbackingstore.cpp b/5.13.2/qtbase/src/widgets/kernel/qwidgetbackingstore.cpp deleted file mode 100644 index ee1107f..0000000 --- a/5.13.2/qtbase/src/widgets/kernel/qwidgetbackingstore.cpp +++ /dev/null @@ -1,1602 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtWidgets module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - - -#include "qplatformdefs.h" - -#include "qwidgetbackingstore_p.h" - -#include -#include -#include -#include -#include -#include -#if QT_CONFIG(graphicsview) -#include -#endif - -#include -#include -#include -#if QT_CONFIG(graphicseffect) -#include -#endif -#include -#include - -#include - -#if defined(Q_OS_WIN) && !defined(QT_NO_PAINT_DEBUG) -# include -# include -#endif - -QT_BEGIN_NAMESPACE - -extern QRegion qt_dirtyRegion(QWidget *); - -#ifndef QT_NO_OPENGL -Q_GLOBAL_STATIC(QPlatformTextureList, qt_dummy_platformTextureList) -#endif - -static bool hasPlatformWindow(QWidget *widget) -{ - return widget && widget->windowHandle() && widget->windowHandle()->handle(); -} - -/** - * Flushes the contents of the \a backingStore into the screen area of \a widget. - * \a region is the region to be updated in \a widget coordinates. - */ -void QWidgetBackingStore::qt_flush(QWidget *widget, const QRegion ®ion, QBackingStore *backingStore, - QWidget *tlw, QPlatformTextureList *widgetTextures, - QWidgetBackingStore *widgetBackingStore) -{ -#ifdef QT_NO_OPENGL - Q_UNUSED(widgetTextures); - Q_ASSERT(!region.isEmpty()); -#else - Q_ASSERT(!region.isEmpty() || widgetTextures); -#endif - Q_ASSERT(widget); - Q_ASSERT(backingStore); - Q_ASSERT(tlw); -#if !defined(QT_NO_PAINT_DEBUG) - static int flushUpdate = qEnvironmentVariableIntValue("QT_FLUSH_UPDATE"); - if (flushUpdate > 0) - QWidgetBackingStore::showYellowThing(widget, region, flushUpdate * 10, false); -#endif - - if (tlw->testAttribute(Qt::WA_DontShowOnScreen) || widget->testAttribute(Qt::WA_DontShowOnScreen)) - return; - - static bool fpsDebug = qEnvironmentVariableIntValue("QT_DEBUG_FPS"); - if (fpsDebug) { - if (!widgetBackingStore->perfFrames++) - widgetBackingStore->perfTime.start(); - if (widgetBackingStore->perfTime.elapsed() > 5000) { - double fps = double(widgetBackingStore->perfFrames * 1000) / widgetBackingStore->perfTime.restart(); - qDebug("FPS: %.1f\n", fps); - widgetBackingStore->perfFrames = 0; - } - } - - QPoint offset; - if (widget != tlw) - offset += widget->mapTo(tlw, QPoint()); - - QRegion effectiveRegion = region; -#ifndef QT_NO_OPENGL - const bool compositionWasActive = widget->d_func()->renderToTextureComposeActive; - if (!widgetTextures) { - widget->d_func()->renderToTextureComposeActive = false; - // Detect the case of falling back to the normal flush path when no - // render-to-texture widgets are visible anymore. We will force one - // last flush to go through the OpenGL-based composition to prevent - // artifacts. The next flush after this one will use the normal path. - if (compositionWasActive) - widgetTextures = qt_dummy_platformTextureList; - } else { - widget->d_func()->renderToTextureComposeActive = true; - } - // When changing the composition status, make sure the dirty region covers - // the entire widget. Just having e.g. the shown/hidden render-to-texture - // widget's area marked as dirty is incorrect when changing flush paths. - if (compositionWasActive != widget->d_func()->renderToTextureComposeActive) - effectiveRegion = widget->rect(); - - // re-test since we may have been forced to this path via the dummy texture list above - if (widgetTextures) { - qt_window_private(tlw->windowHandle())->compositing = true; - widget->window()->d_func()->sendComposeStatus(widget->window(), false); - // A window may have alpha even when the app did not request - // WA_TranslucentBackground. Therefore the compositor needs to know whether the app intends - // to rely on translucency, in order to decide if it should clear to transparent or opaque. - const bool translucentBackground = widget->testAttribute(Qt::WA_TranslucentBackground); - backingStore->handle()->composeAndFlush(widget->windowHandle(), effectiveRegion, offset, - widgetTextures, translucentBackground); - widget->window()->d_func()->sendComposeStatus(widget->window(), true); - } else -#endif - backingStore->flush(effectiveRegion, widget->windowHandle(), offset); -} - -#ifndef QT_NO_PAINT_DEBUG -#if defined(Q_OS_WIN) && !defined(Q_OS_WINRT) - -static void showYellowThing_win(QWidget *widget, const QRegion ®ion, int msec) -{ - // We expect to be passed a native parent. - QWindow *nativeWindow = widget->windowHandle(); - if (!nativeWindow) - return; - void *hdcV = QGuiApplication::platformNativeInterface()->nativeResourceForWindow(QByteArrayLiteral("getDC"), nativeWindow); - if (!hdcV) - return; - const HDC hdc = reinterpret_cast(hdcV); - - static const COLORREF colors[] = {RGB(255, 255, 0), RGB(255, 200, 55), RGB(200, 255, 55), RGB(200, 200, 0)}; - - static size_t i = 0; - const HBRUSH brush = CreateSolidBrush(colors[i]); - i = (i + 1) % (sizeof(colors) / sizeof(colors[0])); - - for (const QRect &rect : region) { - RECT winRect; - SetRect(&winRect, rect.left(), rect.top(), rect.right(), rect.bottom()); - FillRect(hdc, &winRect, brush); - } - DeleteObject(brush); - QGuiApplication::platformNativeInterface()->nativeResourceForWindow(QByteArrayLiteral("releaseDC"), nativeWindow); - ::Sleep(msec); -} -#endif // defined(Q_OS_WIN) && !defined(Q_OS_WINRT) - -void QWidgetBackingStore::showYellowThing(QWidget *widget, const QRegion &toBePainted, int msec, bool unclipped) -{ -#ifdef Q_OS_WINRT - Q_UNUSED(msec) -#endif - QRegion paintRegion = toBePainted; - QRect widgetRect = widget->rect(); - - if (!hasPlatformWindow(widget)) { - QWidget *nativeParent = widget->nativeParentWidget(); - const QPoint offset = widget->mapTo(nativeParent, QPoint(0, 0)); - paintRegion.translate(offset); - widgetRect.translate(offset); - widget = nativeParent; - } - -#if defined(Q_OS_WIN) && !defined(Q_OS_WINRT) - Q_UNUSED(unclipped); - showYellowThing_win(widget, paintRegion, msec); -#else - //flags to fool painter - bool paintUnclipped = widget->testAttribute(Qt::WA_PaintUnclipped); - if (unclipped && !widget->d_func()->paintOnScreen()) - widget->setAttribute(Qt::WA_PaintUnclipped); - - const bool setFlag = !widget->testAttribute(Qt::WA_WState_InPaintEvent); - if (setFlag) - widget->setAttribute(Qt::WA_WState_InPaintEvent); - - //setup the engine - QPaintEngine *pe = widget->paintEngine(); - if (pe) { - pe->setSystemClip(paintRegion); - { - QPainter p(widget); - p.setClipRegion(paintRegion); - static int i = 0; - switch (i) { - case 0: - p.fillRect(widgetRect, QColor(255,255,0)); - break; - case 1: - p.fillRect(widgetRect, QColor(255,200,55)); - break; - case 2: - p.fillRect(widgetRect, QColor(200,255,55)); - break; - case 3: - p.fillRect(widgetRect, QColor(200,200,0)); - break; - } - i = (i+1) & 3; - p.end(); - } - } - - if (setFlag) - widget->setAttribute(Qt::WA_WState_InPaintEvent, false); - - //restore - widget->setAttribute(Qt::WA_PaintUnclipped, paintUnclipped); - - if (pe) - pe->setSystemClip(QRegion()); - -#if defined(Q_OS_UNIX) - ::usleep(1000 * msec); -#endif -#endif // !Q_OS_WIN -} - -bool QWidgetBackingStore::flushPaint(QWidget *widget, const QRegion &rgn) -{ - if (!widget) - return false; - - int delay = 0; - if (widget->testAttribute(Qt::WA_WState_InPaintEvent)) { - static int flushPaintEvent = qEnvironmentVariableIntValue("QT_FLUSH_PAINT_EVENT"); - if (!flushPaintEvent) - return false; - delay = flushPaintEvent; - } else { - static int flushPaint = qEnvironmentVariableIntValue("QT_FLUSH_PAINT"); - if (!flushPaint) - return false; - delay = flushPaint; - } - - QWidgetBackingStore::showYellowThing(widget, rgn, delay * 10, true); - return true; -} - -void QWidgetBackingStore::unflushPaint(QWidget *widget, const QRegion &rgn) -{ - if (widget->d_func()->paintOnScreen() || rgn.isEmpty()) - return; - - QWidget *tlw = widget->window(); - QTLWExtra *tlwExtra = tlw->d_func()->maybeTopData(); - if (!tlwExtra) - return; - - qt_flush(widget, rgn, tlwExtra->backingStoreTracker->store, tlw, 0, tlw->d_func()->maybeBackingStore()); -} -#endif // QT_NO_PAINT_DEBUG - -/* - Moves the whole rect by (dx, dy) in widget's coordinate system. - Doesn't generate any updates. -*/ -bool QWidgetBackingStore::bltRect(const QRect &rect, int dx, int dy, QWidget *widget) -{ - const QPoint pos(widget->mapTo(tlw, rect.topLeft())); - const QRect tlwRect(QRect(pos, rect.size())); - if (dirty.intersects(tlwRect)) - return false; // We don't want to scroll junk. - return store->scroll(tlwRect, dx, dy); -} - -void QWidgetBackingStore::releaseBuffer() -{ - if (store) - store->resize(QSize()); -} - -/*! - Prepares the window surface to paint a\ toClean region of the \a widget and - updates the BeginPaintInfo struct accordingly. - - The \a toClean region might be clipped by the window surface. -*/ -void QWidgetBackingStore::beginPaint(QRegion &toClean, QWidget *widget, QBackingStore *backingStore, - BeginPaintInfo *returnInfo, bool toCleanIsInTopLevelCoordinates) -{ - Q_UNUSED(widget); - Q_UNUSED(toCleanIsInTopLevelCoordinates); - - // Always flush repainted areas. - dirtyOnScreen += toClean; - -#ifdef QT_NO_PAINT_DEBUG - backingStore->beginPaint(toClean); -#else - returnInfo->wasFlushed = QWidgetBackingStore::flushPaint(tlw, toClean); - // Avoid deadlock with QT_FLUSH_PAINT: the server will wait for - // the BackingStore lock, so if we hold that, the server will - // never release the Communication lock that we are waiting for in - // sendSynchronousCommand - if (!returnInfo->wasFlushed) - backingStore->beginPaint(toClean); -#endif - - Q_UNUSED(returnInfo); -} - -void QWidgetBackingStore::endPaint(const QRegion &cleaned, QBackingStore *backingStore, - BeginPaintInfo *beginPaintInfo) -{ -#ifndef QT_NO_PAINT_DEBUG - if (!beginPaintInfo->wasFlushed) - backingStore->endPaint(); - else - QWidgetBackingStore::unflushPaint(tlw, cleaned); -#else - Q_UNUSED(beginPaintInfo); - Q_UNUSED(cleaned); - backingStore->endPaint(); -#endif - - flush(); -} - -/*! - Returns the region (in top-level coordinates) that needs repaint and/or flush. - - If the widget is non-zero, only the dirty region for the widget is returned - and the region will be in widget coordinates. -*/ -QRegion QWidgetBackingStore::dirtyRegion(QWidget *widget) const -{ - const bool widgetDirty = widget && widget != tlw; - const QRect tlwRect(topLevelRect()); - const QRect surfaceGeometry(tlwRect.topLeft(), store->size()); - if (surfaceGeometry != tlwRect && surfaceGeometry.size() != tlwRect.size()) { - if (widgetDirty) { - const QRect dirtyTlwRect = QRect(QPoint(), tlwRect.size()); - const QPoint offset(widget->mapTo(tlw, QPoint())); - const QRect dirtyWidgetRect(dirtyTlwRect & widget->rect().translated(offset)); - return dirtyWidgetRect.translated(-offset); - } - return QRect(QPoint(), tlwRect.size()); - } - - // Calculate the region that needs repaint. - QRegion r(dirty); - for (int i = 0; i < dirtyWidgets.size(); ++i) { - QWidget *w = dirtyWidgets.at(i); - if (widgetDirty && w != widget && !widget->isAncestorOf(w)) - continue; - r += w->d_func()->dirty.translated(w->mapTo(tlw, QPoint())); - } - - // Append the region that needs flush. - r += dirtyOnScreen; - - if (dirtyOnScreenWidgets) { // Only in use with native child widgets. - for (int i = 0; i < dirtyOnScreenWidgets->size(); ++i) { - QWidget *w = dirtyOnScreenWidgets->at(i); - if (widgetDirty && w != widget && !widget->isAncestorOf(w)) - continue; - QWidgetPrivate *wd = w->d_func(); - Q_ASSERT(wd->needsFlush); - r += wd->needsFlush->translated(w->mapTo(tlw, QPoint())); - } - } - - if (widgetDirty) { - // Intersect with the widget geometry and translate to its coordinates. - const QPoint offset(widget->mapTo(tlw, QPoint())); - r &= widget->rect().translated(offset); - r.translate(-offset); - } - return r; -} - -/*! - Returns the static content inside the \a parent if non-zero; otherwise the static content - for the entire backing store is returned. The content will be clipped to \a withinClipRect - if non-empty. -*/ -QRegion QWidgetBackingStore::staticContents(QWidget *parent, const QRect &withinClipRect) const -{ - if (!parent && tlw->testAttribute(Qt::WA_StaticContents)) { - const QSize surfaceGeometry(store->size()); - QRect surfaceRect(0, 0, surfaceGeometry.width(), surfaceGeometry.height()); - if (!withinClipRect.isEmpty()) - surfaceRect &= withinClipRect; - return QRegion(surfaceRect); - } - - QRegion region; - if (parent && parent->d_func()->children.isEmpty()) - return region; - - const bool clipToRect = !withinClipRect.isEmpty(); - const int count = staticWidgets.count(); - for (int i = 0; i < count; ++i) { - QWidget *w = staticWidgets.at(i); - QWidgetPrivate *wd = w->d_func(); - if (!wd->isOpaque || !wd->extra || wd->extra->staticContentsSize.isEmpty() - || !w->isVisible() || (parent && !parent->isAncestorOf(w))) { - continue; - } - - QRect rect(0, 0, wd->extra->staticContentsSize.width(), wd->extra->staticContentsSize.height()); - const QPoint offset = w->mapTo(parent ? parent : tlw, QPoint()); - if (clipToRect) - rect &= withinClipRect.translated(-offset); - if (rect.isEmpty()) - continue; - - rect &= wd->clipRect(); - if (rect.isEmpty()) - continue; - - QRegion visible(rect); - wd->clipToEffectiveMask(visible); - if (visible.isEmpty()) - continue; - wd->subtractOpaqueSiblings(visible, 0, /*alsoNonOpaque=*/true); - - visible.translate(offset); - region += visible; - } - - return region; -} - -void QWidgetBackingStore::sendUpdateRequest(QWidget *widget, UpdateTime updateTime) -{ - if (!widget) - return; - -#ifndef QT_NO_OPENGL - // Having every repaint() leading to a sync/flush is bad as it causes - // compositing and waiting for vsync each and every time. Change to - // UpdateLater, except for approx. once per frame to prevent starvation in - // case the control does not get back to the event loop. - QWidget *w = widget->window(); - if (updateTime == UpdateNow && w && w->windowHandle() && QWindowPrivate::get(w->windowHandle())->compositing) { - int refresh = 60; - QScreen *ws = w->windowHandle()->screen(); - if (ws) - refresh = ws->refreshRate(); - QWindowPrivate *wd = QWindowPrivate::get(w->windowHandle()); - if (wd->lastComposeTime.isValid()) { - const qint64 elapsed = wd->lastComposeTime.elapsed(); - if (elapsed <= qint64(1000.0f / refresh)) - updateTime = UpdateLater; - } - } -#endif - - switch (updateTime) { - case UpdateLater: - updateRequestSent = true; - QApplication::postEvent(widget, new QEvent(QEvent::UpdateRequest), Qt::LowEventPriority); - break; - case UpdateNow: { - QEvent event(QEvent::UpdateRequest); - QApplication::sendEvent(widget, &event); - break; - } - } -} - -static inline QRect widgetRectFor(QWidget *, const QRect &r) { return r; } -static inline QRect widgetRectFor(QWidget *widget, const QRegion &) { return widget->rect(); } - -/*! - Marks the region of the widget as dirty (if not already marked as dirty) and - posts an UpdateRequest event to the top-level widget (if not already posted). - - If updateTime is UpdateNow, the event is sent immediately instead of posted. - - If bufferState is BufferInvalid, all widgets intersecting with the region will be dirty. - - If the widget paints directly on screen, the event is sent to the widget - instead of the top-level widget, and bufferState is completely ignored. -*/ -template -void QWidgetBackingStore::markDirty(const T &r, QWidget *widget, UpdateTime updateTime, BufferState bufferState) -{ - Q_ASSERT(tlw->d_func()->extra); - Q_ASSERT(tlw->d_func()->extra->topextra); - Q_ASSERT(!tlw->d_func()->extra->topextra->inTopLevelResize); - Q_ASSERT(widget->isVisible() && widget->updatesEnabled()); - Q_ASSERT(widget->window() == tlw); - Q_ASSERT(!r.isEmpty()); - -#if QT_CONFIG(graphicseffect) - widget->d_func()->invalidateGraphicsEffectsRecursively(); -#endif - - QRect widgetRect = widgetRectFor(widget, r); - - // --------------------------------------------------------------------------- - - if (widget->d_func()->paintOnScreen()) { - if (widget->d_func()->dirty.isEmpty()) { - widget->d_func()->dirty = r; - sendUpdateRequest(widget, updateTime); - return; - } else if (qt_region_strictContains(widget->d_func()->dirty, widgetRect)) { - if (updateTime == UpdateNow) - sendUpdateRequest(widget, updateTime); - return; // Already dirty - } - - const bool eventAlreadyPosted = !widget->d_func()->dirty.isEmpty(); - widget->d_func()->dirty += r; - if (!eventAlreadyPosted || updateTime == UpdateNow) - sendUpdateRequest(widget, updateTime); - return; - } - - // --------------------------------------------------------------------------- - - if (QWidgetPrivate::get(widget)->renderToTexture) { - if (!widget->d_func()->inDirtyList) - addDirtyRenderToTextureWidget(widget); - if (!updateRequestSent || updateTime == UpdateNow) - sendUpdateRequest(tlw, updateTime); - return; - } - - // --------------------------------------------------------------------------- - - QRect effectiveWidgetRect = widget->d_func()->effectiveRectFor(widgetRect); - const QPoint offset = widget->mapTo(tlw, QPoint()); - QRect translatedRect = effectiveWidgetRect.translated(offset); -#if QT_CONFIG(graphicseffect) - // Graphics effects may exceed window size, clamp - translatedRect = translatedRect.intersected(QRect(QPoint(), tlw->size())); -#endif - if (qt_region_strictContains(dirty, translatedRect)) { - if (updateTime == UpdateNow) - sendUpdateRequest(tlw, updateTime); - return; // Already dirty - } - - // --------------------------------------------------------------------------- - - if (bufferState == BufferInvalid) { - const bool eventAlreadyPosted = !dirty.isEmpty() || updateRequestSent; -#if QT_CONFIG(graphicseffect) - if (widget->d_func()->graphicsEffect) - dirty += widget->d_func()->effectiveRectFor(r).translated(offset); - else -#endif - dirty += r.translated(offset); - - if (!eventAlreadyPosted || updateTime == UpdateNow) - sendUpdateRequest(tlw, updateTime); - return; - } - - // --------------------------------------------------------------------------- - - if (dirtyWidgets.isEmpty()) { - addDirtyWidget(widget, r); - sendUpdateRequest(tlw, updateTime); - return; - } - - // --------------------------------------------------------------------------- - - if (widget->d_func()->inDirtyList) { - if (!qt_region_strictContains(widget->d_func()->dirty, effectiveWidgetRect)) { -#if QT_CONFIG(graphicseffect) - if (widget->d_func()->graphicsEffect) - widget->d_func()->dirty += widget->d_func()->effectiveRectFor(r); - else -#endif - widget->d_func()->dirty += r; - } - } else { - addDirtyWidget(widget, r); - } - - // --------------------------------------------------------------------------- - - if (updateTime == UpdateNow) - sendUpdateRequest(tlw, updateTime); -} -template void QWidgetBackingStore::markDirty(const QRect &, QWidget *, UpdateTime, BufferState); -template void QWidgetBackingStore::markDirty(const QRegion &, QWidget *, UpdateTime, BufferState); - -/*! - Marks the \a region of the \a widget as dirty on screen. The \a region will be copied from - the backing store to the \a widget's native parent next time flush() is called. - - Paint on screen widgets are ignored. -*/ -void QWidgetBackingStore::markDirtyOnScreen(const QRegion ®ion, QWidget *widget, const QPoint &topLevelOffset) -{ - if (!widget || widget->d_func()->paintOnScreen() || region.isEmpty()) - return; - -#if 0 // Used to be included in Qt4 for Q_WS_MAC - if (!widget->testAttribute(Qt::WA_WState_InPaintEvent)) - dirtyOnScreen += region.translated(topLevelOffset); - return; -#endif - - // Top-level. - if (widget == tlw) { - if (!widget->testAttribute(Qt::WA_WState_InPaintEvent)) - dirtyOnScreen += region; - return; - } - - // Alien widgets. - if (!hasPlatformWindow(widget) && !widget->isWindow()) { - QWidget *nativeParent = widget->nativeParentWidget(); // Alien widgets with the top-level as the native parent (common case). - if (nativeParent == tlw) { - if (!widget->testAttribute(Qt::WA_WState_InPaintEvent)) - dirtyOnScreen += region.translated(topLevelOffset); - return; - } - - // Alien widgets with native parent != tlw. - QWidgetPrivate *nativeParentPrivate = nativeParent->d_func(); - if (!nativeParentPrivate->needsFlush) - nativeParentPrivate->needsFlush = new QRegion; - const QPoint nativeParentOffset = widget->mapTo(nativeParent, QPoint()); - *nativeParentPrivate->needsFlush += region.translated(nativeParentOffset); - appendDirtyOnScreenWidget(nativeParent); - return; - } - - // Native child widgets. - QWidgetPrivate *widgetPrivate = widget->d_func(); - if (!widgetPrivate->needsFlush) - widgetPrivate->needsFlush = new QRegion; - *widgetPrivate->needsFlush += region; - appendDirtyOnScreenWidget(widget); -} - -void QWidgetBackingStore::removeDirtyWidget(QWidget *w) -{ - if (!w) - return; - - dirtyWidgetsRemoveAll(w); - dirtyOnScreenWidgetsRemoveAll(w); - dirtyRenderToTextureWidgets.removeAll(w); - resetWidget(w); - - QWidgetPrivate *wd = w->d_func(); - const int n = wd->children.count(); - for (int i = 0; i < n; ++i) { - if (QWidget *child = qobject_cast(wd->children.at(i))) - removeDirtyWidget(child); - } -} - -void QWidgetBackingStore::updateLists(QWidget *cur) -{ - if (!cur) - return; - - QList children = cur->children(); - for (int i = 0; i < children.size(); ++i) { - QWidget *child = qobject_cast(children.at(i)); - if (!child || child->isWindow()) - continue; - - updateLists(child); - } - - if (cur->testAttribute(Qt::WA_StaticContents)) - addStaticWidget(cur); -} - -QWidgetBackingStore::QWidgetBackingStore(QWidget *topLevel) - : tlw(topLevel), - dirtyOnScreenWidgets(0), - updateRequestSent(0), - textureListWatcher(0), - perfFrames(0) -{ - store = tlw->backingStore(); - Q_ASSERT(store); - - // Ensure all existing subsurfaces and static widgets are added to their respective lists. - updateLists(topLevel); -} - -QWidgetBackingStore::~QWidgetBackingStore() -{ - for (int c = 0; c < dirtyWidgets.size(); ++c) - resetWidget(dirtyWidgets.at(c)); - for (int c = 0; c < dirtyRenderToTextureWidgets.size(); ++c) - resetWidget(dirtyRenderToTextureWidgets.at(c)); - - delete dirtyOnScreenWidgets; -} - -static QVector getSortedRectsToScroll(const QRegion ®ion, int dx, int dy) -{ - QVector rects; - std::copy(region.begin(), region.end(), std::back_inserter(rects)); - if (rects.count() > 1) { - std::sort(rects.begin(), rects.end(), [=](const QRect &r1, const QRect &r2) { - if (r1.y() == r2.y()) { - if (dx > 0) - return r1.x() > r2.x(); - return r1.x() < r2.x(); - } - if (dy > 0) - return r1.y() > r2.y(); - return r1.y() < r2.y(); - }); - } - return rects; -} - -//parent's coordinates; move whole rect; update parent and widget -//assume the screen blt has already been done, so we don't need to refresh that part -void QWidgetPrivate::moveRect(const QRect &rect, int dx, int dy) -{ - Q_Q(QWidget); - if (!q->isVisible() || (dx == 0 && dy == 0)) - return; - - QWidget *tlw = q->window(); - QTLWExtra* x = tlw->d_func()->topData(); - if (x->inTopLevelResize) - return; - - static const bool accelEnv = qEnvironmentVariableIntValue("QT_NO_FAST_MOVE") == 0; - - QWidget *pw = q->parentWidget(); - QPoint toplevelOffset = pw->mapTo(tlw, QPoint()); - QWidgetPrivate *pd = pw->d_func(); - QRect clipR(pd->clipRect()); - const QRect newRect(rect.translated(dx, dy)); - QRect destRect = rect.intersected(clipR); - if (destRect.isValid()) - destRect = destRect.translated(dx, dy).intersected(clipR); - const QRect sourceRect(destRect.translated(-dx, -dy)); - const QRect parentRect(rect & clipR); - const bool nativeWithTextureChild = textureChildSeen && hasPlatformWindow(q); - - const bool accelerateMove = accelEnv && isOpaque && !nativeWithTextureChild -#if QT_CONFIG(graphicsview) - // No accelerate move for proxy widgets. - && !tlw->d_func()->extra->proxyWidget -#endif - ; - - if (!accelerateMove) { - QRegion parentR(effectiveRectFor(parentRect)); - if (!extra || !extra->hasMask) { - parentR -= newRect; - } else { - // invalidateBackingStore() excludes anything outside the mask - parentR += newRect & clipR; - } - pd->invalidateBackingStore(parentR); - invalidateBackingStore((newRect & clipR).translated(-data.crect.topLeft())); - } else { - - QWidgetBackingStore *wbs = x->backingStoreTracker.data(); - QRegion childExpose(newRect & clipR); - QRegion overlappedExpose; - - if (sourceRect.isValid()) { - overlappedExpose = (overlappedRegion(sourceRect) | overlappedRegion(destRect)) & clipR; - - const qreal factor = QHighDpiScaling::factor(q->windowHandle()); - if (overlappedExpose.isEmpty() || qFloor(factor) == factor) { - const QVector rectsToScroll - = getSortedRectsToScroll(QRegion(sourceRect) - overlappedExpose, dx, dy); - for (QRect rect : rectsToScroll) { - if (wbs->bltRect(rect, dx, dy, pw)) { - childExpose -= rect.translated(dx, dy); - } - } - } - - childExpose -= overlappedExpose; - } - - if (!pw->updatesEnabled()) - return; - - const bool childUpdatesEnabled = q->updatesEnabled(); - if (childUpdatesEnabled) { - if (!overlappedExpose.isEmpty()) { - overlappedExpose.translate(-data.crect.topLeft()); - invalidateBackingStore(overlappedExpose); - } - if (!childExpose.isEmpty()) { - childExpose.translate(-data.crect.topLeft()); - wbs->markDirty(childExpose, q); - isMoved = true; - } - } - - QRegion parentExpose(parentRect); - parentExpose -= newRect; - if (extra && extra->hasMask) - parentExpose += QRegion(newRect) - extra->mask.translated(data.crect.topLeft()); - - if (!parentExpose.isEmpty()) { - wbs->markDirty(parentExpose, pw); - pd->isMoved = true; - } - - if (childUpdatesEnabled) { - QRegion needsFlush(sourceRect); - needsFlush += destRect; - wbs->markDirtyOnScreen(needsFlush, pw, toplevelOffset); - } - } -} - -//widget's coordinates; scroll within rect; only update widget -void QWidgetPrivate::scrollRect(const QRect &rect, int dx, int dy) -{ - Q_Q(QWidget); - QWidget *tlw = q->window(); - QTLWExtra* x = tlw->d_func()->topData(); - if (x->inTopLevelResize) - return; - - QWidgetBackingStore *wbs = x->backingStoreTracker.data(); - if (!wbs) - return; - - static const bool accelEnv = qEnvironmentVariableIntValue("QT_NO_FAST_SCROLL") == 0; - - const QRect clipR = clipRect(); - const QRect scrollRect = rect & clipR; - const bool accelerateScroll = accelEnv && isOpaque && !q_func()->testAttribute(Qt::WA_WState_InPaintEvent); - - if (!accelerateScroll) { - if (!overlappedRegion(scrollRect.translated(data.crect.topLeft()), true).isEmpty()) { - QRegion region(scrollRect); - subtractOpaqueSiblings(region); - invalidateBackingStore(region); - }else { - invalidateBackingStore(scrollRect); - } - } else { - const QPoint toplevelOffset = q->mapTo(tlw, QPoint()); - const QRect destRect = scrollRect.translated(dx, dy) & scrollRect; - const QRect sourceRect = destRect.translated(-dx, -dy); - - const QRegion overlappedExpose = (overlappedRegion(scrollRect.translated(data.crect.topLeft()))) - .translated(-data.crect.topLeft()) & clipR; - QRegion childExpose(scrollRect); - - const qreal factor = QHighDpiScaling::factor(q->windowHandle()); - if (overlappedExpose.isEmpty() || qFloor(factor) == factor) { - const QVector rectsToScroll - = getSortedRectsToScroll(QRegion(sourceRect) - overlappedExpose, dx, dy); - for (const QRect &rect : rectsToScroll) { - if (wbs->bltRect(rect, dx, dy, q)) { - childExpose -= rect.translated(dx, dy); - } - } - } - - childExpose -= overlappedExpose; - - if (inDirtyList) { - if (rect == q->rect()) { - dirty.translate(dx, dy); - } else { - QRegion dirtyScrollRegion = dirty.intersected(scrollRect); - if (!dirtyScrollRegion.isEmpty()) { - dirty -= dirtyScrollRegion; - dirtyScrollRegion.translate(dx, dy); - dirty += dirtyScrollRegion; - } - } - } - - if (!q->updatesEnabled()) - return; - - if (!overlappedExpose.isEmpty()) - invalidateBackingStore(overlappedExpose); - if (!childExpose.isEmpty()) { - wbs->markDirty(childExpose, q); - isScrolled = true; - } - - // Instead of using native scroll-on-screen, we copy from - // backingstore, giving only one screen update for each - // scroll, and a solid appearance - wbs->markDirtyOnScreen(destRect, q, toplevelOffset); - } -} - -#ifndef QT_NO_OPENGL -static void findTextureWidgetsRecursively(QWidget *tlw, QWidget *widget, QPlatformTextureList *widgetTextures, QVector *nativeChildren) -{ - QWidgetPrivate *wd = QWidgetPrivate::get(widget); - if (wd->renderToTexture) { - QPlatformTextureList::Flags flags = wd->textureListFlags(); - const QRect rect(widget->mapTo(tlw, QPoint()), widget->size()); - widgetTextures->appendTexture(widget, wd->textureId(), rect, wd->clipRect(), flags); - } - - for (int i = 0; i < wd->children.size(); ++i) { - QWidget *w = qobject_cast(wd->children.at(i)); - // Stop at native widgets but store them. Stop at hidden widgets too. - if (w && !w->isWindow() && hasPlatformWindow(w)) - nativeChildren->append(w); - if (w && !w->isWindow() && !hasPlatformWindow(w) && !w->isHidden() && QWidgetPrivate::get(w)->textureChildSeen) - findTextureWidgetsRecursively(tlw, w, widgetTextures, nativeChildren); - } -} - -static void findAllTextureWidgetsRecursively(QWidget *tlw, QWidget *widget) -{ - // textureChildSeen does not take native child widgets into account and that's good. - if (QWidgetPrivate::get(widget)->textureChildSeen) { - QVector nativeChildren; - QScopedPointer tl(new QPlatformTextureList); - // Look for texture widgets (incl. widget itself) from 'widget' down, - // but skip subtrees with a parent of a native child widget. - findTextureWidgetsRecursively(tlw, widget, tl.data(), &nativeChildren); - // tl may be empty regardless of textureChildSeen if we have native or hidden children. - if (!tl->isEmpty()) - QWidgetPrivate::get(tlw)->topData()->widgetTextures.append(tl.take()); - // Native child widgets, if there was any, get their own separate QPlatformTextureList. - foreach (QWidget *ncw, nativeChildren) { - if (QWidgetPrivate::get(ncw)->textureChildSeen) - findAllTextureWidgetsRecursively(tlw, ncw); - } - } -} - -static QPlatformTextureList *widgetTexturesFor(QWidget *tlw, QWidget *widget) -{ - foreach (QPlatformTextureList *tl, QWidgetPrivate::get(tlw)->topData()->widgetTextures) { - Q_ASSERT(!tl->isEmpty()); - for (int i = 0; i < tl->count(); ++i) { - QWidget *w = static_cast(tl->source(i)); - if ((hasPlatformWindow(w) && w == widget) || (!hasPlatformWindow(w) && w->nativeParentWidget() == widget)) - return tl; - } - } - - if (QWidgetPrivate::get(widget)->textureChildSeen) { - // No render-to-texture widgets in the (sub-)tree due to hidden or native - // children. Returning null results in using the normal backingstore flush path - // without OpenGL-based compositing. This is very desirable normally. However, - // some platforms cannot handle switching between the non-GL and GL paths for - // their windows so it has to be opt-in. - static bool switchableWidgetComposition = - QGuiApplicationPrivate::instance()->platformIntegration() - ->hasCapability(QPlatformIntegration::SwitchableWidgetComposition); - if (!switchableWidgetComposition) - return qt_dummy_platformTextureList(); - } - - return 0; -} - -// Watches one or more QPlatformTextureLists for changes in the lock state and -// triggers a backingstore sync when all the registered lists turn into -// unlocked state. This is essential when a custom composeAndFlush() -// implementation in a platform plugin is not synchronous and keeps -// holding on to the textures for some time even after returning from there. -QPlatformTextureListWatcher::QPlatformTextureListWatcher(QWidgetBackingStore *backingStore) - : m_backingStore(backingStore) -{ -} - -void QPlatformTextureListWatcher::watch(QPlatformTextureList *textureList) -{ - connect(textureList, SIGNAL(locked(bool)), SLOT(onLockStatusChanged(bool))); - m_locked[textureList] = textureList->isLocked(); -} - -bool QPlatformTextureListWatcher::isLocked() const -{ - foreach (bool v, m_locked) { - if (v) - return true; - } - return false; -} - -void QPlatformTextureListWatcher::onLockStatusChanged(bool locked) -{ - QPlatformTextureList *tl = static_cast(sender()); - m_locked[tl] = locked; - if (!isLocked()) - m_backingStore->sync(); -} - -#else - -static QPlatformTextureList *widgetTexturesFor(QWidget *tlw, QWidget *widget) -{ - Q_UNUSED(tlw); - Q_UNUSED(widget); - return nullptr; -} - -#endif // QT_NO_OPENGL - -static inline bool discardSyncRequest(QWidget *tlw, QTLWExtra *tlwExtra) -{ - if (!tlw || !tlwExtra || !tlw->testAttribute(Qt::WA_Mapped) || !tlw->isVisible()) - return true; - - return false; -} - -bool QWidgetBackingStore::syncAllowed() -{ -#ifndef QT_NO_OPENGL - QTLWExtra *tlwExtra = tlw->d_func()->maybeTopData(); - if (textureListWatcher && !textureListWatcher->isLocked()) { - textureListWatcher->deleteLater(); - textureListWatcher = 0; - } else if (!tlwExtra->widgetTextures.isEmpty()) { - bool skipSync = false; - foreach (QPlatformTextureList *tl, tlwExtra->widgetTextures) { - if (tl->isLocked()) { - if (!textureListWatcher) - textureListWatcher = new QPlatformTextureListWatcher(this); - if (!textureListWatcher->isLocked()) - textureListWatcher->watch(tl); - skipSync = true; - } - } - if (skipSync) // cannot compose due to widget textures being in use - return false; - } -#endif - return true; -} - -/*! - Synchronizes the \a exposedRegion of the \a exposedWidget with the backing store. - - If there's nothing to repaint, the area is flushed and painting does not occur; - otherwise the area is marked as dirty on screen and will be flushed right after - we are done with all painting. -*/ -void QWidgetBackingStore::sync(QWidget *exposedWidget, const QRegion &exposedRegion) -{ - QTLWExtra *tlwExtra = tlw->d_func()->maybeTopData(); - if (!tlw->isVisible() || !tlwExtra || tlwExtra->inTopLevelResize) - return; - - if (!exposedWidget || !hasPlatformWindow(exposedWidget) - || !exposedWidget->isVisible() || !exposedWidget->testAttribute(Qt::WA_Mapped) - || !exposedWidget->updatesEnabled() || exposedRegion.isEmpty()) { - return; - } - - // Nothing to repaint. - if (!isDirty() && store->size().isValid()) { - QPlatformTextureList *tl = widgetTexturesFor(tlw, exposedWidget); - qt_flush(exposedWidget, tl ? QRegion() : exposedRegion, store, tlw, tl, this); - return; - } - - if (exposedWidget != tlw) - markDirtyOnScreen(exposedRegion, exposedWidget, exposedWidget->mapTo(tlw, QPoint())); - else - markDirtyOnScreen(exposedRegion, exposedWidget, QPoint()); - - if (syncAllowed()) - doSync(); -} - -/*! - Synchronizes the backing store, i.e. dirty areas are repainted and flushed. -*/ -void QWidgetBackingStore::sync() -{ - updateRequestSent = false; - QTLWExtra *tlwExtra = tlw->d_func()->maybeTopData(); - if (discardSyncRequest(tlw, tlwExtra)) { - // If the top-level is minimized, it's not visible on the screen so we can delay the - // update until it's shown again. In order to do that we must keep the dirty states. - // These will be cleared when we receive the first expose after showNormal(). - // However, if the widget is not visible (isVisible() returns false), everything will - // be invalidated once the widget is shown again, so clear all dirty states. - if (!tlw->isVisible()) { - dirty = QRegion(); - for (int i = 0; i < dirtyWidgets.size(); ++i) - resetWidget(dirtyWidgets.at(i)); - dirtyWidgets.clear(); - } - return; - } - - if (syncAllowed()) - doSync(); -} - -void QWidgetBackingStore::doSync() -{ - const bool updatesDisabled = !tlw->updatesEnabled(); - bool repaintAllWidgets = false; - - const bool inTopLevelResize = tlw->d_func()->maybeTopData()->inTopLevelResize; - const QRect tlwRect(topLevelRect()); - const QRect surfaceGeometry(tlwRect.topLeft(), store->size()); - if ((inTopLevelResize || surfaceGeometry.size() != tlwRect.size()) && !updatesDisabled) { - if (hasStaticContents() && !store->size().isEmpty() ) { - // Repaint existing dirty area and newly visible area. - const QRect clipRect(0, 0, surfaceGeometry.width(), surfaceGeometry.height()); - const QRegion staticRegion(staticContents(0, clipRect)); - QRegion newVisible(0, 0, tlwRect.width(), tlwRect.height()); - newVisible -= staticRegion; - dirty += newVisible; - store->setStaticContents(staticRegion); - } else { - // Repaint everything. - dirty = QRegion(0, 0, tlwRect.width(), tlwRect.height()); - for (int i = 0; i < dirtyWidgets.size(); ++i) - resetWidget(dirtyWidgets.at(i)); - dirtyWidgets.clear(); - repaintAllWidgets = true; - } - } - - if (inTopLevelResize || surfaceGeometry.size() != tlwRect.size()) - store->resize(tlwRect.size()); - - if (updatesDisabled) - return; - - // Contains everything that needs repaint. - QRegion toClean(dirty); - - // Loop through all update() widgets and remove them from the list before they are - // painted (in case someone calls update() in paintEvent). If the widget is opaque - // and does not have transparent overlapping siblings, append it to the - // opaqueNonOverlappedWidgets list and paint it directly without composition. - QVarLengthArray opaqueNonOverlappedWidgets; - for (int i = 0; i < dirtyWidgets.size(); ++i) { - QWidget *w = dirtyWidgets.at(i); - QWidgetPrivate *wd = w->d_func(); - if (wd->data.in_destructor) - continue; - - // Clip with mask() and clipRect(). - wd->dirty &= wd->clipRect(); - wd->clipToEffectiveMask(wd->dirty); - - // Subtract opaque siblings and children. - bool hasDirtySiblingsAbove = false; - // We know for sure that the widget isn't overlapped if 'isMoved' is true. - if (!wd->isMoved) - wd->subtractOpaqueSiblings(wd->dirty, &hasDirtySiblingsAbove); - - // Make a copy of the widget's dirty region, to restore it in case there is an opaque - // render-to-texture child that completely covers the widget, because otherwise the - // render-to-texture child won't be visible, due to its parent widget not being redrawn - // with a proper blending mask. - const QRegion dirtyBeforeSubtractedOpaqueChildren = wd->dirty; - - // Scrolled and moved widgets must draw all children. - if (!wd->isScrolled && !wd->isMoved) - wd->subtractOpaqueChildren(wd->dirty, w->rect()); - - if (wd->dirty.isEmpty() && wd->textureChildSeen) - wd->dirty = dirtyBeforeSubtractedOpaqueChildren; - - if (wd->dirty.isEmpty()) { - resetWidget(w); - continue; - } - - const QRegion widgetDirty(w != tlw ? wd->dirty.translated(w->mapTo(tlw, QPoint())) - : wd->dirty); - toClean += widgetDirty; - -#if QT_CONFIG(graphicsview) - if (tlw->d_func()->extra->proxyWidget) { - resetWidget(w); - continue; - } -#endif - - if (!hasDirtySiblingsAbove && wd->isOpaque && !dirty.intersects(widgetDirty.boundingRect())) { - opaqueNonOverlappedWidgets.append(w); - } else { - resetWidget(w); - dirty += widgetDirty; - } - } - dirtyWidgets.clear(); - -#ifndef QT_NO_OPENGL - // Find all render-to-texture child widgets (including self). - // The search is cut at native widget boundaries, meaning that each native child widget - // has its own list for the subtree below it. - QTLWExtra *tlwExtra = tlw->d_func()->topData(); - qDeleteAll(tlwExtra->widgetTextures); - tlwExtra->widgetTextures.clear(); - findAllTextureWidgetsRecursively(tlw, tlw); - qt_window_private(tlw->windowHandle())->compositing = false; // will get updated in qt_flush() -#endif - - if (toClean.isEmpty()) { - // Nothing to repaint. However renderToTexture widgets are handled - // specially, they are not in the regular dirty list, in order to - // prevent triggering unnecessary backingstore painting when only the - // OpenGL content changes. Check if we have such widgets in the special - // dirty list. - QVarLengthArray paintPending; - const int numPaintPending = dirtyRenderToTextureWidgets.count(); - paintPending.reserve(numPaintPending); - for (int i = 0; i < numPaintPending; ++i) { - QWidget *w = dirtyRenderToTextureWidgets.at(i); - paintPending << w; - resetWidget(w); - } - dirtyRenderToTextureWidgets.clear(); - for (int i = 0; i < numPaintPending; ++i) { - QWidget *w = paintPending[i]; - w->d_func()->sendPaintEvent(w->rect()); - if (w != tlw) { - QWidget *npw = w->nativeParentWidget(); - if (hasPlatformWindow(w) || (npw && npw != tlw)) { - if (!hasPlatformWindow(w)) - w = npw; - QWidgetPrivate *wPrivate = w->d_func(); - if (!wPrivate->needsFlush) - wPrivate->needsFlush = new QRegion; - appendDirtyOnScreenWidget(w); - } - } - } - - // We might have newly exposed areas on the screen if this function was - // called from sync(QWidget *, QRegion)), so we have to make sure those - // are flushed. We also need to composite the renderToTexture widgets. - flush(); - - return; - } - -#ifndef QT_NO_OPENGL - foreach (QPlatformTextureList *tl, tlwExtra->widgetTextures) { - for (int i = 0; i < tl->count(); ++i) { - QWidget *w = static_cast(tl->source(i)); - if (dirtyRenderToTextureWidgets.contains(w)) { - const QRect rect = tl->geometry(i); // mapped to the tlw already - // Set a flag to indicate that the paint event for this - // render-to-texture widget must not to be optimized away. - w->d_func()->renderToTextureReallyDirty = 1; - dirty += rect; - toClean += rect; - } - } - } - for (int i = 0; i < dirtyRenderToTextureWidgets.count(); ++i) - resetWidget(dirtyRenderToTextureWidgets.at(i)); - dirtyRenderToTextureWidgets.clear(); -#endif - -#if QT_CONFIG(graphicsview) - if (tlw->d_func()->extra->proxyWidget) { - updateStaticContentsSize(); - dirty = QRegion(); - updateRequestSent = false; - for (const QRect &rect : toClean) - tlw->d_func()->extra->proxyWidget->update(rect); - return; - } -#endif - - BeginPaintInfo beginPaintInfo; - beginPaint(toClean, tlw, store, &beginPaintInfo); - if (beginPaintInfo.nothingToPaint) { - for (int i = 0; i < opaqueNonOverlappedWidgets.size(); ++i) - resetWidget(opaqueNonOverlappedWidgets[i]); - dirty = QRegion(); - updateRequestSent = false; - return; - } - - // Must do this before sending any paint events because - // the size may change in the paint event. - updateStaticContentsSize(); - const QRegion dirtyCopy(dirty); - dirty = QRegion(); - updateRequestSent = false; - - // Paint opaque non overlapped widgets. - for (int i = 0; i < opaqueNonOverlappedWidgets.size(); ++i) { - QWidget *w = opaqueNonOverlappedWidgets[i]; - QWidgetPrivate *wd = w->d_func(); - - int flags = QWidgetPrivate::DrawRecursive; - // Scrolled and moved widgets must draw all children. - if (!wd->isScrolled && !wd->isMoved) - flags |= QWidgetPrivate::DontDrawOpaqueChildren; - if (w == tlw) - flags |= QWidgetPrivate::DrawAsRoot; - - QRegion toBePainted(wd->dirty); - resetWidget(w); - - QPoint offset; - if (w != tlw) - offset += w->mapTo(tlw, QPoint()); - wd->drawWidget(store->paintDevice(), toBePainted, offset, flags, 0, this); - } - - // Paint the rest with composition. - if (repaintAllWidgets || !dirtyCopy.isEmpty()) { - const int flags = QWidgetPrivate::DrawAsRoot | QWidgetPrivate::DrawRecursive; - tlw->d_func()->drawWidget(store->paintDevice(), dirtyCopy, QPoint(), flags, 0, this); - } - - endPaint(toClean, store, &beginPaintInfo); -} - -/*! - Flushes the contents of the backing store into the top-level widget. - If the \a widget is non-zero, the content is flushed to the \a widget. - If the \a surface is non-zero, the content of the \a surface is flushed. -*/ -void QWidgetBackingStore::flush(QWidget *widget) -{ - const bool hasDirtyOnScreenWidgets = dirtyOnScreenWidgets && !dirtyOnScreenWidgets->isEmpty(); - bool flushed = false; - - // Flush the region in dirtyOnScreen. - if (!dirtyOnScreen.isEmpty()) { - QWidget *target = widget ? widget : tlw; - qt_flush(target, dirtyOnScreen, store, tlw, widgetTexturesFor(tlw, tlw), this); - dirtyOnScreen = QRegion(); - flushed = true; - } - - // Render-to-texture widgets are not in dirtyOnScreen so flush if we have not done it above. - if (!flushed && !hasDirtyOnScreenWidgets) { -#ifndef QT_NO_OPENGL - if (!tlw->d_func()->topData()->widgetTextures.isEmpty()) { - QPlatformTextureList *tl = widgetTexturesFor(tlw, tlw); - if (tl) { - QWidget *target = widget ? widget : tlw; - qt_flush(target, QRegion(), store, tlw, tl, this); - } - } -#endif - } - - if (!hasDirtyOnScreenWidgets) - return; - - for (int i = 0; i < dirtyOnScreenWidgets->size(); ++i) { - QWidget *w = dirtyOnScreenWidgets->at(i); - QWidgetPrivate *wd = w->d_func(); - Q_ASSERT(wd->needsFlush); - QPlatformTextureList *widgetTexturesForNative = wd->textureChildSeen ? widgetTexturesFor(tlw, w) : 0; - qt_flush(w, *wd->needsFlush, store, tlw, widgetTexturesForNative, this); - *wd->needsFlush = QRegion(); - } - dirtyOnScreenWidgets->clear(); -} - -/*! - Invalidates the backing store when the widget is resized. - Static areas are never invalidated unless absolutely needed. -*/ -void QWidgetPrivate::invalidateBackingStore_resizeHelper(const QPoint &oldPos, const QSize &oldSize) -{ - Q_Q(QWidget); - Q_ASSERT(!q->isWindow()); - Q_ASSERT(q->parentWidget()); - - const bool staticContents = q->testAttribute(Qt::WA_StaticContents); - const bool sizeDecreased = (data.crect.width() < oldSize.width()) - || (data.crect.height() < oldSize.height()); - - const QPoint offset(data.crect.x() - oldPos.x(), data.crect.y() - oldPos.y()); - const bool parentAreaExposed = !offset.isNull() || sizeDecreased; - const QRect newWidgetRect(q->rect()); - const QRect oldWidgetRect(0, 0, oldSize.width(), oldSize.height()); - - if (!staticContents || graphicsEffect) { - QRegion staticChildren; - QWidgetBackingStore *bs = 0; - if (offset.isNull() && (bs = maybeBackingStore())) - staticChildren = bs->staticContents(q, oldWidgetRect); - const bool hasStaticChildren = !staticChildren.isEmpty(); - - if (hasStaticChildren) { - QRegion dirty(newWidgetRect); - dirty -= staticChildren; - invalidateBackingStore(dirty); - } else { - // Entire widget needs repaint. - invalidateBackingStore(newWidgetRect); - } - - if (!parentAreaExposed) - return; - - // Invalidate newly exposed area of the parent. - if (!graphicsEffect && extra && extra->hasMask) { - QRegion parentExpose(extra->mask.translated(oldPos)); - parentExpose &= QRect(oldPos, oldSize); - if (hasStaticChildren) - parentExpose -= data.crect; // Offset is unchanged, safe to do this. - q->parentWidget()->d_func()->invalidateBackingStore(parentExpose); - } else { - if (hasStaticChildren && !graphicsEffect) { - QRegion parentExpose(QRect(oldPos, oldSize)); - parentExpose -= data.crect; // Offset is unchanged, safe to do this. - q->parentWidget()->d_func()->invalidateBackingStore(parentExpose); - } else { - q->parentWidget()->d_func()->invalidateBackingStore(effectiveRectFor(QRect(oldPos, oldSize))); - } - } - return; - } - - // Move static content to its new position. - if (!offset.isNull()) { - if (sizeDecreased) { - const QSize minSize(qMin(oldSize.width(), data.crect.width()), - qMin(oldSize.height(), data.crect.height())); - moveRect(QRect(oldPos, minSize), offset.x(), offset.y()); - } else { - moveRect(QRect(oldPos, oldSize), offset.x(), offset.y()); - } - } - - // Invalidate newly visible area of the widget. - if (!sizeDecreased || !oldWidgetRect.contains(newWidgetRect)) { - QRegion newVisible(newWidgetRect); - newVisible -= oldWidgetRect; - invalidateBackingStore(newVisible); - } - - if (!parentAreaExposed) - return; - - // Invalidate newly exposed area of the parent. - const QRect oldRect(oldPos, oldSize); - if (extra && extra->hasMask) { - QRegion parentExpose(oldRect); - parentExpose &= extra->mask.translated(oldPos); - parentExpose -= (extra->mask.translated(data.crect.topLeft()) & data.crect); - q->parentWidget()->d_func()->invalidateBackingStore(parentExpose); - } else { - QRegion parentExpose(oldRect); - parentExpose -= data.crect; - q->parentWidget()->d_func()->invalidateBackingStore(parentExpose); - } -} - -/*! - Invalidates the \a r (in widget's coordinates) of the backing store, i.e. - all widgets intersecting with the region will be repainted when the backing - store is synced. -*/ -template -void QWidgetPrivate::invalidateBackingStore(const T &r) -{ - if (r.isEmpty()) - return; - - if (QApplication::closingDown()) - return; - - Q_Q(QWidget); - if (!q->isVisible() || !q->updatesEnabled()) - return; - - QTLWExtra *tlwExtra = q->window()->d_func()->maybeTopData(); - if (!tlwExtra || tlwExtra->inTopLevelResize || !tlwExtra->backingStore) - return; - - T clipped(r); - clipped &= clipRect(); - if (clipped.isEmpty()) - return; - - if (!graphicsEffect && extra && extra->hasMask) { - QRegion masked(extra->mask); - masked &= clipped; - if (masked.isEmpty()) - return; - - tlwExtra->backingStoreTracker->markDirty(masked, q, - QWidgetBackingStore::UpdateLater, QWidgetBackingStore::BufferInvalid); - } else { - tlwExtra->backingStoreTracker->markDirty(clipped, q, - QWidgetBackingStore::UpdateLater, QWidgetBackingStore::BufferInvalid); - } -} -// Needed by tst_QWidget -template Q_AUTOTEST_EXPORT void QWidgetPrivate::invalidateBackingStore(const QRect &r); - -void QWidgetPrivate::repaint_sys(const QRegion &rgn) -{ - if (data.in_destructor) - return; - - Q_Q(QWidget); - if (discardSyncRequest(q, maybeTopData())) - return; - - if (q->testAttribute(Qt::WA_StaticContents)) { - if (!extra) - createExtra(); - extra->staticContentsSize = data.crect.size(); - } - - QPaintEngine *engine = q->paintEngine(); - - // QGLWidget does not support partial updates if: - // 1) The context is double buffered - // 2) The context is single buffered and auto-fill background is enabled. - const bool noPartialUpdateSupport = (engine && (engine->type() == QPaintEngine::OpenGL - || engine->type() == QPaintEngine::OpenGL2)) - && (usesDoubleBufferedGLContext || q->autoFillBackground()); - QRegion toBePainted(noPartialUpdateSupport ? q->rect() : rgn); - -#if 0 // Used to be included in Qt4 for Q_WS_MAC - // No difference between update() and repaint() on the Mac. - update_sys(toBePainted); - return; -#endif - - toBePainted &= clipRect(); - clipToEffectiveMask(toBePainted); - if (toBePainted.isEmpty()) - return; // Nothing to repaint. - -#ifndef QT_NO_PAINT_DEBUG - bool flushed = QWidgetBackingStore::flushPaint(q, toBePainted); -#endif - - drawWidget(q, toBePainted, QPoint(), QWidgetPrivate::DrawAsRoot | QWidgetPrivate::DrawPaintOnScreen, 0); - -#ifndef QT_NO_PAINT_DEBUG - if (flushed) - QWidgetBackingStore::unflushPaint(q, toBePainted); -#endif - - if (Q_UNLIKELY(q->paintingActive())) - qWarning("QWidget::repaint: It is dangerous to leave painters active on a widget outside of the PaintEvent"); -} - - -QT_END_NAMESPACE - -#include "moc_qwidgetbackingstore_p.cpp"