diff options
author | Tor Arne Vestbø <tor.arne.vestbo@qt.io> | 2020-08-05 11:07:45 +0200 |
---|---|---|
committer | Tor Arne Vestbø <tor.arne.vestbo@qt.io> | 2020-08-18 00:35:21 +0200 |
commit | 1fc7ca091b3fdda52381a383318a3a752ec21132 (patch) | |
tree | bc794ba56de848a543c9167856fe9cf60b938de2 /src/plugins/platforms/cocoa/qcocoabackingstore.mm | |
parent | d1111632e29124531d5b4512e0492314caaae396 (diff) |
macOS: Remove support for surface-backed views
Our deployment target is 10.14, which enables layer-backing by default,
and our layer-backing support nowadays is stable enough that we don't
need to maintain any of the old code paths for compatibility.
The wantsBestResolutionOpenGLSurface property on NSView is only relevant
for surface-backed views, so we no longer need to deal with it.
Change-Id: I8aef4ac99371113d463ac35eee648a8a2fd1ea72
Reviewed-by: Timur Pocheptsov <timur.pocheptsov@qt.io>
Reviewed-by: Morten Johan Sørvig <morten.sorvig@qt.io>
Diffstat (limited to 'src/plugins/platforms/cocoa/qcocoabackingstore.mm')
-rw-r--r-- | src/plugins/platforms/cocoa/qcocoabackingstore.mm | 270 |
1 files changed, 0 insertions, 270 deletions
diff --git a/src/plugins/platforms/cocoa/qcocoabackingstore.mm b/src/plugins/platforms/cocoa/qcocoabackingstore.mm index 9ee6ab5b4f..01787da1af 100644 --- a/src/plugins/platforms/cocoa/qcocoabackingstore.mm +++ b/src/plugins/platforms/cocoa/qcocoabackingstore.mm @@ -64,276 +64,6 @@ QCFType<CGColorSpaceRef> QCocoaBackingStore::colorSpace() const // ---------------------------------------------------------------------------- -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<QCocoaWindow *>(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<QCocoaWindow *>(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<QCocoaWindow *>(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(); - - Q_ASSERT(topLevelWindow->handle() && window->handle()); - Q_ASSERT(!topLevelWindow->handle()->isForeignWindow() && !window->handle()->isForeignWindow()); - - QNSView *topLevelView = qnsview_cast(static_cast<QCocoaWindow *>(topLevelWindow->handle())->view()); - QNSView *view = qnsview_cast(static_cast<QCocoaWindow *>(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 QWidgetRepaintManager 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 && !QT_IGNORE_DEPRECATIONS([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<QCocoaWindow *>(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<CGImageRef> cgImage = CGImageCreateCopyWithColorSpace( - QCFType<CGImageRef>(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) - QT_IGNORE_DEPRECATIONS([view unlockFocus]); - - if (drawingOutsideOfDisplayCycle) { - redrawRoundedBottomCorners([view convertRect:region.boundingRect().toCGRect() toView:nil]); - QT_IGNORE_DEPRECATIONS([view.window flushWindow]); - } - - // Done flushing to NSWindow backingstore - - QCocoaWindow *topLevelCocoaWindow = static_cast<QCocoaWindow *>(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<QCocoaWindow *>(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 -} - -// ---------------------------------------------------------------------------- - QCALayerBackingStore::QCALayerBackingStore(QWindow *window) : QCocoaBackingStore(window) { |