diff options
Diffstat (limited to 'src/plugins/platforms/cocoa')
-rw-r--r-- | src/plugins/platforms/cocoa/qcocoabackingstore.h | 16 | ||||
-rw-r--r-- | src/plugins/platforms/cocoa/qcocoabackingstore.mm | 128 | ||||
-rw-r--r-- | src/plugins/platforms/cocoa/qcocoaglcontext.mm | 21 | ||||
-rw-r--r-- | src/plugins/platforms/cocoa/qcocoahelpers.h | 41 | ||||
-rw-r--r-- | src/plugins/platforms/cocoa/qcocoahelpers.mm | 99 | ||||
-rw-r--r-- | src/plugins/platforms/cocoa/qcocoasystemtrayicon.mm | 1 | ||||
-rw-r--r-- | src/plugins/platforms/cocoa/qcocoawindow.mm | 25 | ||||
-rw-r--r-- | src/plugins/platforms/cocoa/qiosurfacegraphicsbuffer.h | 4 | ||||
-rw-r--r-- | src/plugins/platforms/cocoa/qiosurfacegraphicsbuffer.mm | 21 | ||||
-rw-r--r-- | src/plugins/platforms/cocoa/qnsview_drawing.mm | 11 | ||||
-rw-r--r-- | src/plugins/platforms/cocoa/qnswindow.mm | 20 |
11 files changed, 191 insertions, 196 deletions
diff --git a/src/plugins/platforms/cocoa/qcocoabackingstore.h b/src/plugins/platforms/cocoa/qcocoabackingstore.h index a874936ce6..3d9dfd8359 100644 --- a/src/plugins/platforms/cocoa/qcocoabackingstore.h +++ b/src/plugins/platforms/cocoa/qcocoabackingstore.h @@ -47,6 +47,8 @@ #include <QScopedPointer> #include "qiosurfacegraphicsbuffer.h" +#include <unordered_map> + QT_BEGIN_NAMESPACE class QCocoaBackingStore : public QRasterBackingStore @@ -54,8 +56,6 @@ class QCocoaBackingStore : public QRasterBackingStore protected: QCocoaBackingStore(QWindow *window); QCFType<CGColorSpaceRef> colorSpace() const; - QMacNotificationObserver m_backingPropertiesObserver; - virtual void backingPropertiesChanged() = 0; }; class QNSWindowBackingStore : public QCocoaBackingStore @@ -71,11 +71,11 @@ private: bool windowHasUnifiedToolbar() const; QImage::Format format() const override; void redrawRoundedBottomCorners(CGRect) const; - void backingPropertiesChanged() override; }; -class QCALayerBackingStore : public QCocoaBackingStore +class QCALayerBackingStore : public QObject, public QCocoaBackingStore { + Q_OBJECT public: QCALayerBackingStore(QWindow *window); ~QCALayerBackingStore(); @@ -118,9 +118,15 @@ private: bool recreateBackBufferIfNeeded(); bool prepareForFlush(); - void backingPropertiesChanged() override; + void backingPropertiesChanged(); + QMacNotificationObserver m_backingPropertiesObserver; std::list<std::unique_ptr<GraphicsBuffer>> m_buffers; + + void flushSubWindow(QWindow *window); + std::unordered_map<QWindow*, std::unique_ptr<QCALayerBackingStore>> m_subWindowBackingstores; + void windowDestroyed(QObject *object); + bool m_clearSurfaceOnPaint = true; }; QT_END_NAMESPACE diff --git a/src/plugins/platforms/cocoa/qcocoabackingstore.mm b/src/plugins/platforms/cocoa/qcocoabackingstore.mm index 2343f16b9f..6ea6714649 100644 --- a/src/plugins/platforms/cocoa/qcocoabackingstore.mm +++ b/src/plugins/platforms/cocoa/qcocoabackingstore.mm @@ -51,17 +51,6 @@ 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<QCocoaWindow *>(window->handle())->view(); - m_backingPropertiesObserver = QMacNotificationObserver(view.window, - NSWindowDidChangeBackingPropertiesNotification, [this]() { - qCDebug(lcQpaBackingStore) << "Backing properties for" - << this->window() << "did change"; - backingPropertiesChanged(); - }); } QCFType<CGColorSpaceRef> QCocoaBackingStore::colorSpace() const @@ -341,11 +330,6 @@ void QNSWindowBackingStore::redrawRoundedBottomCorners(CGRect windowRect) const #endif } -void QNSWindowBackingStore::backingPropertiesChanged() -{ - m_image = QImage(); -} - // ---------------------------------------------------------------------------- // https://stackoverflow.com/a/52722575/2761869 @@ -365,6 +349,26 @@ QCALayerBackingStore::QCALayerBackingStore(QWindow *window) { qCDebug(lcQpaBackingStore) << "Creating QCALayerBackingStore for" << window; m_buffers.resize(1); + + // 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 update our backingstore when the backing + // properties (color space e.g.) are changed. + NSView *view = static_cast<QCocoaWindow *>(window->handle())->view(); + m_backingPropertiesObserver = QMacNotificationObserver(view.window, + NSWindowDidChangeBackingPropertiesNotification, [this]() { + if (!this->window()->handle()) { + // The platform window has been destroyed, but the backingstore + // is still alive, as that's tied to a QWindow. The original + // NSWindow we were observing is also likely gone. FIXME: + // We should listen for surface events from the QWindow and + // remove and re-attach our observer based on those. + return; + } + qCDebug(lcQpaBackingStore) << "Backing properties for" + << this->window() << "did change"; + backingPropertiesChanged(); + }); } QCALayerBackingStore::~QCALayerBackingStore() @@ -398,7 +402,7 @@ void QCALayerBackingStore::beginPaint(const QRegion ®ion) // 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()) { + if (m_clearSurfaceOnPaint && !bufferWasRecreated && window()->format().hasAlpha()) { qCDebug(lcQpaBackingStore) << "Clearing" << region << "before use"; QPainter painter(m_buffers.back()->asImage()); painter.setCompositionMode(QPainter::CompositionMode_Source); @@ -527,9 +531,13 @@ void QCALayerBackingStore::flush(QWindow *flushedWindow, const QRegion ®ion, if (!prepareForFlush()) return; + if (flushedWindow != window()) { + flushSubWindow(flushedWindow); + return; + } + QMacAutoReleasePool pool; - NSView *backingStoreView = static_cast<QCocoaWindow *>(window()->handle())->view(); NSView *flushedView = static_cast<QCocoaWindow *>(flushedWindow->handle())->view(); // If the backingstore is just flushed, without being painted to first, then we may @@ -564,7 +572,7 @@ void QCALayerBackingStore::flush(QWindow *flushedWindow, const QRegion ®ion, // 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; + flushedView.window.viewsNeedDisplay = YES; if (window()->format().swapBehavior() == QSurfaceFormat::SingleBuffer) { // The private API [CALayer reloadValueForKeyPath:@"contents"] would be preferable, @@ -572,27 +580,10 @@ void QCALayerBackingStore::flush(QWindow *flushedWindow, const QRegion ®ion, flushedView.layer.contents = nil; } - if (flushedView == backingStoreView) { - qCInfo(lcQpaBackingStore) << "Flushing" << backBufferSurface - << "to" << flushedView.layer << "of" << flushedView; - flushedView.layer.contents = backBufferSurface; - } else { - auto subviewRect = [flushedView convertRect:flushedView.bounds toView:backingStoreView]; - auto scale = flushedView.layer.contentsScale; - subviewRect = CGRectApplyAffineTransform(subviewRect, CGAffineTransformMakeScale(scale, scale)); - - // We make a copy of the image data up front, which means we don't - // need to mark the IOSurface as being in use. FIXME: Investigate - // if there's a cheaper way to get sub-image data to a layer. - m_buffers.back()->lock(QPlatformGraphicsBuffer::SWReadAccess); - QImage subImage = m_buffers.back()->asImage()->copy(QRectF::fromCGRect(subviewRect).toRect()); - m_buffers.back()->unlock(); + qCInfo(lcQpaBackingStore) << "Flushing" << backBufferSurface + << "to" << flushedView.layer << "of" << flushedView; - qCInfo(lcQpaBackingStore) << "Flushing" << subImage - << "to" << flushedView.layer << "of subview" << flushedView; - QCFType<CGImageRef> cgImage = subImage.toCGImage(); - flushedView.layer.contents = (__bridge id)static_cast<CGImageRef>(cgImage); - } + flushedView.layer.contents = backBufferSurface; // 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 @@ -604,6 +595,53 @@ void QCALayerBackingStore::flush(QWindow *flushedWindow, const QRegion ®ion, // the window server. } +void QCALayerBackingStore::flushSubWindow(QWindow *subWindow) +{ + qCInfo(lcQpaBackingStore) << "Flushing sub-window" << subWindow + << "via its own backingstore"; + + auto &subWindowBackingStore = m_subWindowBackingstores[subWindow]; + if (!subWindowBackingStore) { + subWindowBackingStore.reset(new QCALayerBackingStore(subWindow)); + QObject::connect(subWindow, &QObject::destroyed, this, &QCALayerBackingStore::windowDestroyed); + subWindowBackingStore->m_clearSurfaceOnPaint = false; + } + + auto subWindowSize = subWindow->size(); + static const auto kNoStaticContents = QRegion(); + subWindowBackingStore->resize(subWindowSize, kNoStaticContents); + + auto subWindowLocalRect = QRect(QPoint(), subWindowSize); + subWindowBackingStore->beginPaint(subWindowLocalRect); + + QPainter painter(subWindowBackingStore->m_buffers.back()->asImage()); + painter.setCompositionMode(QPainter::CompositionMode_Source); + + NSView *backingStoreView = static_cast<QCocoaWindow *>(window()->handle())->view(); + NSView *flushedView = static_cast<QCocoaWindow *>(subWindow->handle())->view(); + auto subviewRect = [flushedView convertRect:flushedView.bounds toView:backingStoreView]; + auto scale = flushedView.layer.contentsScale; + subviewRect = CGRectApplyAffineTransform(subviewRect, CGAffineTransformMakeScale(scale, scale)); + + m_buffers.back()->lock(QPlatformGraphicsBuffer::SWReadAccess); + const QImage *backingStoreImage = m_buffers.back()->asImage(); + painter.drawImage(subWindowLocalRect, *backingStoreImage, QRectF::fromCGRect(subviewRect)); + m_buffers.back()->unlock(); + + painter.end(); + subWindowBackingStore->endPaint(); + subWindowBackingStore->flush(subWindow, subWindowLocalRect, QPoint()); + + qCInfo(lcQpaBackingStore) << "Done flushing sub-window" << subWindow; +} + +void QCALayerBackingStore::windowDestroyed(QObject *object) +{ + auto *window = static_cast<QWindow*>(object); + qCInfo(lcQpaBackingStore) << "Removing backingstore for sub-window" << window; + m_subWindowBackingstores.erase(window); +} + #ifndef QT_NO_OPENGL void QCALayerBackingStore::composeAndFlush(QWindow *window, const QRegion ®ion, const QPoint &offset, QPlatformTextureList *textures, bool translucentBackground) @@ -632,8 +670,11 @@ QImage QCALayerBackingStore::toImage() const void QCALayerBackingStore::backingPropertiesChanged() { - m_buffers.clear(); - m_buffers.resize(1); + qCDebug(lcQpaBackingStore) << "Updating color space of existing buffers"; + for (auto &buffer : m_buffers) { + if (buffer) + buffer->setColorSpace(colorSpace()); + } } QPlatformGraphicsBuffer *QCALayerBackingStore::graphicsBuffer() const @@ -710,10 +751,11 @@ bool QCALayerBackingStore::prepareForFlush() QCALayerBackingStore::GraphicsBuffer::GraphicsBuffer(const QSize &size, qreal devicePixelRatio, const QPixelFormat &format, QCFType<CGColorSpaceRef> colorSpace) - : QIOSurfaceGraphicsBuffer(size, format, colorSpace) + : QIOSurfaceGraphicsBuffer(size, format) , dirtyRegion(0, 0, size.width() / devicePixelRatio, size.height() / devicePixelRatio) , m_devicePixelRatio(devicePixelRatio) { + setColorSpace(colorSpace); } QImage *QCALayerBackingStore::GraphicsBuffer::asImage() @@ -733,4 +775,6 @@ QImage *QCALayerBackingStore::GraphicsBuffer::asImage() return &m_image; } +#include "moc_qcocoabackingstore.cpp" + QT_END_NAMESPACE diff --git a/src/plugins/platforms/cocoa/qcocoaglcontext.mm b/src/plugins/platforms/cocoa/qcocoaglcontext.mm index d91b2cabaf..c8b5365a75 100644 --- a/src/plugins/platforms/cocoa/qcocoaglcontext.mm +++ b/src/plugins/platforms/cocoa/qcocoaglcontext.mm @@ -357,6 +357,8 @@ QCocoaGLContext::~QCocoaGLContext() bool QCocoaGLContext::makeCurrent(QPlatformSurface *surface) { + QMacAutoReleasePool pool; + qCDebug(lcQpaOpenGLContext) << "Making" << m_context << "current" << "in" << QThread::currentThread() << "for" << surface; @@ -479,6 +481,8 @@ void QCocoaGLContext::update() void QCocoaGLContext::swapBuffers(QPlatformSurface *surface) { + QMacAutoReleasePool pool; + qCDebug(lcQpaOpenGLContext) << "Swapping" << m_context << "in" << QThread::currentThread() << "to" << surface; @@ -491,12 +495,29 @@ void QCocoaGLContext::swapBuffers(QPlatformSurface *surface) return; } + if (m_context.view.layer) { + // Flushing an NSOpenGLContext will hit the screen immediately, ignoring + // any Core Animation transactions in place. This may result in major + // visual artifacts if the flush happens out of sync with the size + // of the layer, view, and window reflected by other parts of the UI, + // e.g. if the application flushes in the resize event or a timer during + // window resizing, instead of in the expose event. + auto *cocoaWindow = static_cast<QCocoaWindow *>(surface); + if (cocoaWindow->geometry().size() != cocoaWindow->m_exposedRect.size()) { + qCInfo(lcQpaOpenGLContext) << "Window exposed size does not match geometry (yet)." + << "Skipping flush to avoid visual artifacts."; + return; + } + } + QMutexLocker locker(&s_reentrancyMutex); [m_context flushBuffer]; } void QCocoaGLContext::doneCurrent() { + QMacAutoReleasePool pool; + qCDebug(lcQpaOpenGLContext) << "Clearing current context" << [NSOpenGLContext currentContext] << "in" << QThread::currentThread(); diff --git a/src/plugins/platforms/cocoa/qcocoahelpers.h b/src/plugins/platforms/cocoa/qcocoahelpers.h index 69aa7937b6..514d3ac550 100644 --- a/src/plugins/platforms/cocoa/qcocoahelpers.h +++ b/src/plugins/platforms/cocoa/qcocoahelpers.h @@ -55,6 +55,7 @@ #include <QtCore/qoperatingsystemversion.h> #include <QtGui/qpalette.h> #include <QtGui/qscreen.h> +#include <qpa/qplatformdialoghelper.h> #include <objc/runtime.h> #include <objc/message.h> @@ -178,32 +179,6 @@ T qt_mac_resolveOption(const T &fallback, QWindow *window, const QByteArray &pro // ------------------------------------------------------------------------- -#if !defined(Q_PROCESSOR_X86_64) -#error "32-bit builds are not supported" -#endif - -class QMacVersion -{ -public: - enum VersionTarget { - ApplicationBinary, - QtLibraries - }; - - static QOperatingSystemVersion buildSDK(VersionTarget target = ApplicationBinary); - static QOperatingSystemVersion deploymentTarget(VersionTarget target = ApplicationBinary); - static QOperatingSystemVersion currentRuntime(); - -private: - QMacVersion() = default; - using VersionTuple = QPair<QOperatingSystemVersion, QOperatingSystemVersion>; - static VersionTuple versionsForImage(const mach_header *machHeader); - static VersionTuple applicationVersion(); - static VersionTuple libraryVersion(); -}; - -// ------------------------------------------------------------------------- - QT_END_NAMESPACE // @compatibility_alias doesn't work with protocols @@ -225,7 +200,7 @@ QT_END_NAMESPACE - (instancetype)initWithPanelDelegate:(id<QNSPanelDelegate>)panelDelegate; - (void)dealloc; -- (NSButton *)createButtonWithTitle:(const char *)title; +- (NSButton *)createButtonWithTitle:(QPlatformDialogHelper::StandardButton)type; - (void)layout; @end @@ -254,14 +229,16 @@ template <typename T> struct objc_msgsend_requires_stret { static const bool value = #if defined(Q_PROCESSOR_X86) + #define PLATFORM_USES_SEND_SUPER_STRET 1 // Any return value larger than two registers on i386/x86_64 sizeof(T) > sizeof(void*) * 2; #elif defined(Q_PROCESSOR_ARM_32) + #define PLATFORM_USES_SEND_SUPER_STRET 1 // Any return value larger than a single register on arm - sizeof(T) > sizeof(void*); + sizeof(T) > sizeof(void*); #elif defined(Q_PROCESSOR_ARM_64) - // Stret not used on arm64 - false; + #define PLATFORM_USES_SEND_SUPER_STRET 0 + false; // Stret not used on arm64 #endif }; @@ -281,6 +258,7 @@ ReturnType qt_msgSendSuper(id receiver, SEL selector, Args... args) return superFn(&sup, selector, args...); } +#if PLATFORM_USES_SEND_SUPER_STRET template <typename ReturnType, typename... Args> ReturnType qt_msgSendSuper_stret(id receiver, SEL selector, Args... args) { @@ -295,6 +273,7 @@ ReturnType qt_msgSendSuper_stret(id receiver, SEL selector, Args... args) superStretFn(&ret, &sup, selector, args...); return ret; } +#endif template<typename... Args> class QSendSuperHelper { @@ -335,11 +314,13 @@ private: return qt_msgSendSuper<ReturnType>(m_receiver, m_selector, std::get<Is>(args)...); } +#if PLATFORM_USES_SEND_SUPER_STRET template <typename ReturnType, int... Is> if_requires_stret<ReturnType, true> msgSendSuper(std::tuple<Args...>& args, QtPrivate::IndexesList<Is...>) { return qt_msgSendSuper_stret<ReturnType>(m_receiver, m_selector, std::get<Is>(args)...); } +#endif template <typename ReturnType> ReturnType msgSendSuper(std::tuple<Args...>& args) diff --git a/src/plugins/platforms/cocoa/qcocoahelpers.mm b/src/plugins/platforms/cocoa/qcocoahelpers.mm index d36a7f6d09..2b1e512cf3 100644 --- a/src/plugins/platforms/cocoa/qcocoahelpers.mm +++ b/src/plugins/platforms/cocoa/qcocoahelpers.mm @@ -55,9 +55,6 @@ #include <algorithm> -#include <mach-o/dyld.h> -#include <dlfcn.h> - QT_BEGIN_NAMESPACE Q_LOGGING_CATEGORY(lcQpaWindow, "qt.qpa.window"); @@ -370,92 +367,6 @@ QString qt_mac_removeAmpersandEscapes(QString s) return QPlatformTheme::removeMnemonics(s).trimmed(); } -// ------------------------------------------------------------------------- - -#if !defined(Q_PROCESSOR_X86_64) -#error "32-bit builds are not supported" -#endif - -QOperatingSystemVersion QMacVersion::buildSDK(VersionTarget target) -{ - switch (target) { - case ApplicationBinary: return applicationVersion().second; - case QtLibraries: return libraryVersion().second; - } - Q_UNREACHABLE(); -} - -QOperatingSystemVersion QMacVersion::deploymentTarget(VersionTarget target) -{ - switch (target) { - case ApplicationBinary: return applicationVersion().first; - case QtLibraries: return libraryVersion().first; - } - Q_UNREACHABLE(); -} - -QOperatingSystemVersion QMacVersion::currentRuntime() -{ - return QOperatingSystemVersion::current(); -} - -QMacVersion::VersionTuple QMacVersion::versionsForImage(const mach_header *machHeader) -{ - static auto makeVersionTuple = [](uint32_t dt, uint32_t sdk) { - return qMakePair( - QOperatingSystemVersion(QOperatingSystemVersion::MacOS, - dt >> 16 & 0xffff, dt >> 8 & 0xff, dt & 0xff), - QOperatingSystemVersion(QOperatingSystemVersion::MacOS, - sdk >> 16 & 0xffff, sdk >> 8 & 0xff, sdk & 0xff) - ); - }; - - auto commandCursor = uintptr_t(machHeader) + sizeof(mach_header_64); - for (uint32_t i = 0; i < machHeader->ncmds; ++i) { - load_command *loadCommand = reinterpret_cast<load_command *>(commandCursor); - if (loadCommand->cmd == LC_VERSION_MIN_MACOSX) { - auto versionCommand = reinterpret_cast<version_min_command *>(loadCommand); - return makeVersionTuple(versionCommand->version, versionCommand->sdk); -#if QT_MACOS_PLATFORM_SDK_EQUAL_OR_ABOVE(__MAC_10_13) - } else if (loadCommand->cmd == LC_BUILD_VERSION) { - auto versionCommand = reinterpret_cast<build_version_command *>(loadCommand); - return makeVersionTuple(versionCommand->minos, versionCommand->sdk); -#endif - } - commandCursor += loadCommand->cmdsize; - } - Q_ASSERT_X(false, "QCocoaIntegration", "Could not find any version load command"); - Q_UNREACHABLE(); -} - -QMacVersion::VersionTuple QMacVersion::applicationVersion() -{ - static VersionTuple version = []() { - const mach_header *executableHeader = nullptr; - for (uint32_t i = 0; i < _dyld_image_count(); ++i) { - auto header = _dyld_get_image_header(i); - if (header->filetype == MH_EXECUTE) { - executableHeader = header; - break; - } - } - Q_ASSERT_X(executableHeader, "QCocoaIntegration", "Failed to resolve Mach-O header of executable"); - return versionsForImage(executableHeader); - }(); - return version; -} - -QMacVersion::VersionTuple QMacVersion::libraryVersion() -{ - static VersionTuple version = []() { - Dl_info cocoaPluginImage; - dladdr((const void *)&QMacVersion::libraryVersion, &cocoaPluginImage); - Q_ASSERT_X(cocoaPluginImage.dli_fbase, "QCocoaIntegration", "Failed to resolve Mach-O header of Cocoa plugin"); - return versionsForImage(static_cast<mach_header*>(cocoaPluginImage.dli_fbase)); - }(); - return version; -} - QT_END_NAMESPACE /*! \internal @@ -483,11 +394,10 @@ QT_END_NAMESPACE { if ((self = [super initWithFrame:NSZeroRect])) { // create OK and Cancel buttons and add these as subviews - _okButton = [self createButtonWithTitle:"&OK"]; + _okButton = [self createButtonWithTitle:QPlatformDialogHelper::Ok]; _okButton.action = @selector(onOkClicked); _okButton.target = panelDelegate; - - _cancelButton = [self createButtonWithTitle:"Cancel"]; + _cancelButton = [self createButtonWithTitle:QPlatformDialogHelper::Cancel]; _cancelButton.action = @selector(onCancelClicked); _cancelButton.target = panelDelegate; @@ -511,12 +421,13 @@ QT_END_NAMESPACE [super dealloc]; } -- (NSButton *)createButtonWithTitle:(const char *)title +- (NSButton *)createButtonWithTitle:(QPlatformDialogHelper::StandardButton)type { NSButton *button = [[NSButton alloc] initWithFrame:NSZeroRect]; button.buttonType = NSMomentaryLightButton; button.bezelStyle = NSRoundedBezelStyle; - const QString &cleanTitle = QPlatformTheme::removeMnemonics(QCoreApplication::translate("QDialogButtonBox", title)); + const QString &cleanTitle = + QPlatformTheme::removeMnemonics(QGuiApplicationPrivate::platformTheme()->standardButtonText(type)); // FIXME: Not obvious, from Cocoa's documentation, that QString::toNSString() makes a deep copy button.title = (NSString *)cleanTitle.toCFString(); ((NSButtonCell *)button.cell).font = diff --git a/src/plugins/platforms/cocoa/qcocoasystemtrayicon.mm b/src/plugins/platforms/cocoa/qcocoasystemtrayicon.mm index 597cfa8318..ed26d3afea 100644 --- a/src/plugins/platforms/cocoa/qcocoasystemtrayicon.mm +++ b/src/plugins/platforms/cocoa/qcocoasystemtrayicon.mm @@ -226,6 +226,7 @@ void QCocoaSystemTrayIcon::updateIcon(const QIcon &icon) r.moveCenter(fullHeightPixmap.rect().center()); p.drawPixmap(r, pixmap); } + fullHeightPixmap.setDevicePixelRatio(devicePixelRatio); NSImage *nsimage = static_cast<NSImage *>(qt_mac_create_nsimage(fullHeightPixmap)); [nsimage setTemplate:icon.isMask()]; diff --git a/src/plugins/platforms/cocoa/qcocoawindow.mm b/src/plugins/platforms/cocoa/qcocoawindow.mm index a3120f4ccc..2a23bf286f 100644 --- a/src/plugins/platforms/cocoa/qcocoawindow.mm +++ b/src/plugins/platforms/cocoa/qcocoawindow.mm @@ -500,7 +500,10 @@ NSUInteger QCocoaWindow::windowStyleMask(Qt::WindowFlags flags) NSUInteger styleMask = (frameless || !resizable) ? NSWindowStyleMaskBorderless : NSWindowStyleMaskResizable; if (frameless) { - // No further customizations for frameless since there are no window decorations. + // Frameless windows do not display the traffic lights buttons for + // e.g. minimize, however StyleMaskMiniaturizable is required to allow + // programatic minimize. + styleMask |= NSWindowStyleMaskMiniaturizable; } else if (flags & Qt::CustomizeWindowHint) { if (flags & Qt::WindowTitleHint) styleMask |= NSWindowStyleMaskTitled; @@ -1197,15 +1200,17 @@ void QCocoaWindow::windowDidResignKey() if (isForeignWindow()) return; - // Key window will be non-nil if another window became key, so do not - // set the active window to zero here -- the new key window's - // NSWindowDidBecomeKeyNotification hander will change the active window. - NSWindow *keyWindow = [NSApp keyWindow]; - if (!keyWindow || keyWindow == m_view.window) { - // No new key window, go ahead and set the active window to zero - if (!windowIsPopupType()) - QWindowSystemInterface::handleWindowActivated<QWindowSystemInterface::SynchronousDelivery>(0); - } + // The current key window will be non-nil if another window became key. If that + // window is a Qt window, we delay the window activation event until the didBecomeKey + // notification is delivered to the active window, to ensure an atomic update. + NSWindow *newKeyWindow = [NSApp keyWindow]; + if (newKeyWindow && newKeyWindow != m_view.window + && [newKeyWindow conformsToProtocol:@protocol(QNSWindowProtocol)]) + return; + + // Lost key window, go ahead and set the active window to zero + if (!windowIsPopupType()) + QWindowSystemInterface::handleWindowActivated<QWindowSystemInterface::SynchronousDelivery>(nullptr); } void QCocoaWindow::windowDidOrderOnScreen() diff --git a/src/plugins/platforms/cocoa/qiosurfacegraphicsbuffer.h b/src/plugins/platforms/cocoa/qiosurfacegraphicsbuffer.h index 872773cb7a..e070ba977d 100644 --- a/src/plugins/platforms/cocoa/qiosurfacegraphicsbuffer.h +++ b/src/plugins/platforms/cocoa/qiosurfacegraphicsbuffer.h @@ -48,9 +48,11 @@ QT_BEGIN_NAMESPACE class QIOSurfaceGraphicsBuffer : public QPlatformGraphicsBuffer { public: - QIOSurfaceGraphicsBuffer(const QSize &size, const QPixelFormat &format, QCFType<CGColorSpaceRef> colorSpace); + QIOSurfaceGraphicsBuffer(const QSize &size, const QPixelFormat &format); ~QIOSurfaceGraphicsBuffer(); + void setColorSpace(QCFType<CGColorSpaceRef> colorSpace); + const uchar *data() const override; uchar *data() override; int bytesPerLine() const override; diff --git a/src/plugins/platforms/cocoa/qiosurfacegraphicsbuffer.mm b/src/plugins/platforms/cocoa/qiosurfacegraphicsbuffer.mm index a367487e85..fc187e0f51 100644 --- a/src/plugins/platforms/cocoa/qiosurfacegraphicsbuffer.mm +++ b/src/plugins/platforms/cocoa/qiosurfacegraphicsbuffer.mm @@ -53,7 +53,7 @@ QT_BEGIN_NAMESPACE Q_LOGGING_CATEGORY(lcQpaIOSurface, "qt.qpa.backingstore.iosurface"); -QIOSurfaceGraphicsBuffer::QIOSurfaceGraphicsBuffer(const QSize &size, const QPixelFormat &format, QCFType<CGColorSpaceRef> colorSpace) +QIOSurfaceGraphicsBuffer::QIOSurfaceGraphicsBuffer(const QSize &size, const QPixelFormat &format) : QPlatformGraphicsBuffer(size, format) { const size_t width = size.width(); @@ -81,17 +81,26 @@ QIOSurfaceGraphicsBuffer::QIOSurfaceGraphicsBuffer(const QSize &size, const QPix Q_ASSERT(size_t(bytesPerLine()) == bytesPerRow); Q_ASSERT(size_t(byteCount()) == totalBytes); - - if (colorSpace) { - IOSurfaceSetValue(m_surface, CFSTR("IOSurfaceColorSpace"), - QCFType<CFPropertyListRef>(CGColorSpaceCopyPropertyList(colorSpace))); - } } QIOSurfaceGraphicsBuffer::~QIOSurfaceGraphicsBuffer() { } +void QIOSurfaceGraphicsBuffer::setColorSpace(QCFType<CGColorSpaceRef> colorSpace) +{ + static const auto kIOSurfaceColorSpace = CFSTR("IOSurfaceColorSpace"); + + qCDebug(lcQpaIOSurface) << "Tagging" << this << "with color space" << colorSpace; + + if (colorSpace) { + IOSurfaceSetValue(m_surface, kIOSurfaceColorSpace, + QCFType<CFPropertyListRef>(CGColorSpaceCopyPropertyList(colorSpace))); + } else { + IOSurfaceRemoveValue(m_surface, kIOSurfaceColorSpace); + } +} + const uchar *QIOSurfaceGraphicsBuffer::data() const { return (const uchar *)IOSurfaceGetBaseAddress(m_surface); diff --git a/src/plugins/platforms/cocoa/qnsview_drawing.mm b/src/plugins/platforms/cocoa/qnsview_drawing.mm index 116a9a73df..14e45a038e 100644 --- a/src/plugins/platforms/cocoa/qnsview_drawing.mm +++ b/src/plugins/platforms/cocoa/qnsview_drawing.mm @@ -95,8 +95,15 @@ // by AppKit at a point where we've already set up other parts of the platform plugin // based on the presence of layers or not. Once we've rewritten these parts to support // dynamically picking up layer enablement we can let AppKit do its thing. - return QMacVersion::buildSDK() >= QOperatingSystemVersion::MacOSMojave - && QMacVersion::currentRuntime() >= QOperatingSystemVersion::MacOSMojave; + + if (QMacVersion::currentRuntime() >= QOperatingSystemVersion::MacOSBigSur) + return true; // Big Sur always enables layer-backing, regardless of SDK + + if (QMacVersion::currentRuntime() >= QOperatingSystemVersion::MacOSMojave + && QMacVersion::buildSDK() >= QOperatingSystemVersion::MacOSMojave) + return true; // Mojave and Catalina enable layers based on the app's SDK + + return false; // Prior versions needed explicitly enabled layer backing } - (BOOL)layerExplicitlyRequested diff --git a/src/plugins/platforms/cocoa/qnswindow.mm b/src/plugins/platforms/cocoa/qnswindow.mm index 6b4e110af2..8cff444359 100644 --- a/src/plugins/platforms/cocoa/qnswindow.mm +++ b/src/plugins/platforms/cocoa/qnswindow.mm @@ -227,14 +227,12 @@ static bool isMouseEvent(NSEvent *ev) - (BOOL)canBecomeMainWindow { - BOOL canBecomeMain = YES; // By default, windows can become the main window - // Windows with a transient parent (such as combobox popup windows) // cannot become the main window: if (!m_platformWindow || m_platformWindow->window()->transientParent()) - canBecomeMain = NO; + return NO; - return canBecomeMain; + return [super canBecomeMainWindow]; } - (BOOL)worksWhenModal @@ -255,8 +253,18 @@ static bool isMouseEvent(NSEvent *ev) - (NSColor *)backgroundColor { - return self.styleMask == NSWindowStyleMaskBorderless ? - [NSColor clearColor] : [super backgroundColor]; + // FIXME: Plumb to a WA_NoSystemBackground-like window flag, + // or a QWindow::backgroundColor() property. In the meantime + // we assume that if you have translucent content, without a + // frame then you intend to do all background drawing yourself. + const QWindow *window = m_platformWindow ? m_platformWindow->window() : nullptr; + if (!self.opaque && window && window->flags().testFlag(Qt::FramelessWindowHint)) + return [NSColor clearColor]; + + // This still allows you to have translucent content with a frame, + // where the system background (or color set via NSWindow) will + // shine through. + return [super backgroundColor]; } - (void)sendEvent:(NSEvent*)theEvent |