diff options
Diffstat (limited to 'src/plugins/platforms/cocoa/qcocoascreen.mm')
-rw-r--r-- | src/plugins/platforms/cocoa/qcocoascreen.mm | 130 |
1 files changed, 91 insertions, 39 deletions
diff --git a/src/plugins/platforms/cocoa/qcocoascreen.mm b/src/plugins/platforms/cocoa/qcocoascreen.mm index 08cc22cdf3..be562e5455 100644 --- a/src/plugins/platforms/cocoa/qcocoascreen.mm +++ b/src/plugins/platforms/cocoa/qcocoascreen.mm @@ -15,7 +15,9 @@ #include <IOKit/graphics/IOGraphicsLib.h> #include <QtGui/private/qwindow_p.h> +#include <QtGui/private/qhighdpiscaling_p.h> +#include <QtCore/private/qcore_mac_p.h> #include <QtCore/private/qeventdispatcher_cf_p.h> QT_BEGIN_NAMESPACE @@ -195,11 +197,10 @@ QCocoaScreen::~QCocoaScreen() dispatch_release(m_displayLinkSource); } -#if QT_MACOS_DEPLOYMENT_TARGET_BELOW(__MAC_10_15) static QString displayName(CGDirectDisplayID displayID) { QIOType<io_iterator_t> iterator; - if (IOServiceGetMatchingServices(kIOMasterPortDefault, + if (IOServiceGetMatchingServices(kIOMainPortDefault, IOServiceMatching("IODisplayConnect"), &iterator)) return QString(); @@ -227,7 +228,6 @@ static QString displayName(CGDirectDisplayID displayID) return QString(); } -#endif void QCocoaScreen::update(CGDirectDisplayID displayId) { @@ -248,6 +248,7 @@ void QCocoaScreen::update(CGDirectDisplayID displayId) const QRect previousGeometry = m_geometry; const QRect previousAvailableGeometry = m_availableGeometry; const qreal previousRefreshRate = m_refreshRate; + const double previousRotation = m_rotation; // The reference screen for the geometry is always the primary screen QRectF primaryScreenGeometry = QRectF::fromCGRect(CGDisplayBounds(CGMainDisplayID())); @@ -260,7 +261,9 @@ void QCocoaScreen::update(CGDirectDisplayID displayId) m_depth = NSBitsPerPixelFromDepth(nsScreen.depth); m_colorSpace = QColorSpace::fromIccProfile(QByteArray::fromNSData(nsScreen.colorSpace.ICCProfileData)); if (!m_colorSpace.isValid()) { - qWarning() << "macOS generated a color-profile Qt couldn't parse. This shouldn't happen."; + qCWarning(lcQpaScreen) << "Failed to parse ICC profile for" << nsScreen.colorSpace + << "with ICC data" << nsScreen.colorSpace.ICCProfileData + << "- Falling back to sRGB"; m_colorSpace = QColorSpace::SRgb; } @@ -270,6 +273,7 @@ void QCocoaScreen::update(CGDirectDisplayID displayId) QCFType<CGDisplayModeRef> displayMode = CGDisplayCopyDisplayMode(m_displayId); float refresh = CGDisplayModeGetRefreshRate(displayMode); m_refreshRate = refresh > 0 ? refresh : 60.0; + m_rotation = CGDisplayRotation(displayId); if (@available(macOS 10.15, *)) m_name = QString::fromNSString(nsScreen.localizedName); @@ -278,6 +282,9 @@ void QCocoaScreen::update(CGDirectDisplayID displayId) const bool didChangeGeometry = m_geometry != previousGeometry || m_availableGeometry != previousAvailableGeometry; + if (m_rotation != previousRotation) + QWindowSystemInterface::handleScreenOrientationChange(screen(), orientation()); + if (didChangeGeometry) QWindowSystemInterface::handleScreenGeometryChange(screen(), geometry(), availableGeometry()); if (m_refreshRate != previousRefreshRate) @@ -288,24 +295,33 @@ void QCocoaScreen::update(CGDirectDisplayID displayId) Q_LOGGING_CATEGORY(lcQpaScreenUpdates, "qt.qpa.screen.updates", QtCriticalMsg); -void QCocoaScreen::requestUpdate() +bool QCocoaScreen::requestUpdate() { Q_ASSERT(m_displayId); if (!isOnline()) { qCDebug(lcQpaScreenUpdates) << this << "is not online. Ignoring update request"; - return; + return false; } if (!m_displayLink) { - CVDisplayLinkCreateWithCGDisplay(m_displayId, &m_displayLink); + qCDebug(lcQpaScreenUpdates) << "Creating display link for" << this; + if (CVDisplayLinkCreateWithCGDisplay(m_displayId, &m_displayLink) != kCVReturnSuccess) { + qCWarning(lcQpaScreenUpdates) << "Failed to create display link for" << this; + return false; + } + if (auto displayId = CVDisplayLinkGetCurrentCGDisplay(m_displayLink); displayId != m_displayId) { + qCWarning(lcQpaScreenUpdates) << "Unexpected display" << displayId << "for display link"; + CVDisplayLinkRelease(m_displayLink); + m_displayLink = nullptr; + return false; + } CVDisplayLinkSetOutputCallback(m_displayLink, [](CVDisplayLinkRef, const CVTimeStamp*, const CVTimeStamp*, CVOptionFlags, CVOptionFlags*, void* displayLinkContext) -> int { // FIXME: It would be nice if update requests would include timing info static_cast<QCocoaScreen*>(displayLinkContext)->deliverUpdateRequests(); return kCVReturnSuccess; }, this); - qCDebug(lcQpaScreenUpdates) << "Display link created for" << this; // During live window resizing -[NSWindow _resizeWithEvent:] will spin a local event loop // in event-tracking mode, dequeuing only the mouse drag events needed to update the window's @@ -360,6 +376,8 @@ void QCocoaScreen::requestUpdate() qCDebug(lcQpaScreenUpdates) << "Starting display link for" << this; CVDisplayLinkStart(m_displayLink); } + + return true; } // Helper to allow building up debug output in multiple steps @@ -453,6 +471,25 @@ void QCocoaScreen::deliverUpdateRequests() if (!platformWindow->updatesWithDisplayLink()) continue; + // QTBUG-107198: Skip updates in a live resize for a better resize experience. + if (platformWindow->isContentView() && platformWindow->view().inLiveResize) { + const QSurface::SurfaceType surfaceType = window->surfaceType(); + const bool usesMetalLayer = surfaceType == QWindow::MetalSurface || surfaceType == QWindow::VulkanSurface; + const bool usesNonDefaultContentsPlacement = [platformWindow->view() layerContentsPlacement] + != NSViewLayerContentsPlacementScaleAxesIndependently; + if (usesMetalLayer && usesNonDefaultContentsPlacement) { + static bool deliverDisplayLinkUpdatesDuringLiveResize = + qEnvironmentVariableIsSet("QT_MAC_DISPLAY_LINK_UPDATE_IN_RESIZE"); + if (!deliverDisplayLinkUpdatesDuringLiveResize) { + // Must keep the link running, we do not know what the event + // handlers for UpdateRequest (which is not sent now) would do, + // would they trigger a new requestUpdate() or not. + pauseUpdates = false; + continue; + } + } + } + platformWindow->deliverUpdateRequest(); // Another update request was triggered, keep the display link running @@ -490,41 +527,56 @@ QPlatformScreen::SubpixelAntialiasingType QCocoaScreen::subpixelAntialiasingType return type; } +Qt::ScreenOrientation QCocoaScreen::orientation() const +{ + if (m_rotation == 0) + return Qt::LandscapeOrientation; + if (m_rotation == 90) + return Qt::PortraitOrientation; + if (m_rotation == 180) + return Qt::InvertedLandscapeOrientation; + if (m_rotation == 270) + return Qt::InvertedPortraitOrientation; + return QPlatformScreen::orientation(); +} + QWindow *QCocoaScreen::topLevelAt(const QPoint &point) const { - NSPoint screenPoint = mapToNative(point); - - // Search (hit test) for the top-level window. [NSWidow windowNumberAtPoint: - // belowWindowWithWindowNumber] may return windows that are not interesting - // to Qt. The search iterates until a suitable window or no window is found. - NSInteger topWindowNumber = 0; - QWindow *window = nullptr; - do { - // Get the top-most window, below any previously rejected window. - topWindowNumber = [NSWindow windowNumberAtPoint:screenPoint - belowWindowWithWindowNumber:topWindowNumber]; - - // Continue the search if the window does not belong to this process. - NSWindow *nsWindow = [NSApp windowWithWindowNumber:topWindowNumber]; - if (!nsWindow) - continue; + __block QWindow *window = nullptr; + [NSApp enumerateWindowsWithOptions:NSWindowListOrderedFrontToBack + usingBlock:^(NSWindow *nsWindow, BOOL *stop) { + if (!nsWindow) + return; - // Continue the search if the window does not belong to Qt. - if (![nsWindow conformsToProtocol:@protocol(QNSWindowProtocol)]) - continue; + // Continue the search if the window does not belong to Qt + if (![nsWindow conformsToProtocol:@protocol(QNSWindowProtocol)]) + return; - QCocoaWindow *cocoaWindow = qnsview_cast(nsWindow.contentView).platformWindow; - if (!cocoaWindow) - continue; - window = cocoaWindow->window(); + QCocoaWindow *cocoaWindow = qnsview_cast(nsWindow.contentView).platformWindow; + if (!cocoaWindow) + return; + + QWindow *w = cocoaWindow->window(); + if (!w->isVisible()) + return; - // Continue the search if the window is not a top-level window. - if (!window->isTopLevel()) - continue; + auto nativeGeometry = QHighDpi::toNativePixels(w->geometry(), w); + if (!nativeGeometry.contains(point)) + return; - // Stop searching. The current window is the correct window. - break; - } while (topWindowNumber > 0); + QRegion mask = QHighDpi::toNativeLocalPosition(w->mask(), w); + if (!mask.isEmpty() && !mask.contains(point - nativeGeometry.topLeft())) + return; + + window = w; + + // Continue the search if the window is not a top-level window + if (!window->isTopLevel()) + return; + + *stop = true; + } + ]; return window; } @@ -793,10 +845,10 @@ QDebug operator<<(QDebug debug, const QCocoaScreen *screen) } #endif // !QT_NO_DEBUG_STREAM -#include "qcocoascreen.moc" - QT_END_NAMESPACE +#include "qcocoascreen.moc" + @implementation NSScreen (QtExtras) - (CGDirectDisplayID)qt_displayId |