summaryrefslogtreecommitdiffstats
path: root/src/plugins/platforms/cocoa/qcocoabackingstore.mm
diff options
context:
space:
mode:
authorTor Arne Vestbø <tor.arne.vestbo@qt.io>2020-08-05 11:07:45 +0200
committerTor Arne Vestbø <tor.arne.vestbo@qt.io>2020-08-18 00:35:21 +0200
commit1fc7ca091b3fdda52381a383318a3a752ec21132 (patch)
treebc794ba56de848a543c9167856fe9cf60b938de2 /src/plugins/platforms/cocoa/qcocoabackingstore.mm
parentd1111632e29124531d5b4512e0492314caaae396 (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.mm270
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 &region, 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)
{