summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/plugins/platforms/cocoa/qcocoabackingstore.mm12
-rw-r--r--src/plugins/platforms/cocoa/qcocoahelpers.h12
-rw-r--r--src/plugins/platforms/cocoa/qcocoahelpers.mm2
-rw-r--r--src/plugins/platforms/cocoa/qcocoaintegration.h7
-rw-r--r--src/plugins/platforms/cocoa/qcocoaintegration.mm91
-rw-r--r--src/plugins/platforms/cocoa/qcocoascreen.h31
-rw-r--r--src/plugins/platforms/cocoa/qcocoascreen.mm220
-rw-r--r--src/plugins/platforms/cocoa/qcocoasystemtrayicon.mm6
-rw-r--r--src/plugins/platforms/cocoa/qcocoawindow.mm8
9 files changed, 204 insertions, 185 deletions
diff --git a/src/plugins/platforms/cocoa/qcocoabackingstore.mm b/src/plugins/platforms/cocoa/qcocoabackingstore.mm
index e786ecb5a5..a98fcfae92 100644
--- a/src/plugins/platforms/cocoa/qcocoabackingstore.mm
+++ b/src/plugins/platforms/cocoa/qcocoabackingstore.mm
@@ -273,18 +273,6 @@ void QNSWindowBackingStore::redrawRoundedBottomCorners(CGRect windowRect) const
// ----------------------------------------------------------------------------
-// https://stackoverflow.com/a/52722575/2761869
-template<class R>
-struct backwards_t {
- R r;
- constexpr auto begin() const { using std::rbegin; return rbegin(r); }
- constexpr auto begin() { using std::rbegin; return rbegin(r); }
- constexpr auto end() const { using std::rend; return rend(r); }
- constexpr auto end() { using std::rend; return rend(r); }
-};
-template<class R>
-constexpr backwards_t<R> backwards(R&& r) { return {std::forward<R>(r)}; }
-
QCALayerBackingStore::QCALayerBackingStore(QWindow *window)
: QPlatformBackingStore(window)
{
diff --git a/src/plugins/platforms/cocoa/qcocoahelpers.h b/src/plugins/platforms/cocoa/qcocoahelpers.h
index 69aa7937b6..69a1854598 100644
--- a/src/plugins/platforms/cocoa/qcocoahelpers.h
+++ b/src/plugins/platforms/cocoa/qcocoahelpers.h
@@ -176,6 +176,18 @@ T qt_mac_resolveOption(const T &fallback, QWindow *window, const QByteArray &pro
return fallback;
}
+// https://stackoverflow.com/a/52722575/2761869
+template<class R>
+struct backwards_t {
+ R r;
+ constexpr auto begin() const { using std::rbegin; return rbegin(r); }
+ constexpr auto begin() { using std::rbegin; return rbegin(r); }
+ constexpr auto end() const { using std::rend; return rend(r); }
+ constexpr auto end() { using std::rend; return rend(r); }
+};
+template<class R>
+constexpr backwards_t<R> backwards(R&& r) { return {std::forward<R>(r)}; }
+
// -------------------------------------------------------------------------
#if !defined(Q_PROCESSOR_X86_64)
diff --git a/src/plugins/platforms/cocoa/qcocoahelpers.mm b/src/plugins/platforms/cocoa/qcocoahelpers.mm
index 9c705616ba..1b184cd60f 100644
--- a/src/plugins/platforms/cocoa/qcocoahelpers.mm
+++ b/src/plugins/platforms/cocoa/qcocoahelpers.mm
@@ -63,7 +63,7 @@ QT_BEGIN_NAMESPACE
Q_LOGGING_CATEGORY(lcQpaWindow, "qt.qpa.window");
Q_LOGGING_CATEGORY(lcQpaDrawing, "qt.qpa.drawing");
Q_LOGGING_CATEGORY(lcQpaMouse, "qt.qpa.input.mouse", QtCriticalMsg);
-Q_LOGGING_CATEGORY(lcQpaScreen, "qt.qpa.screen");
+Q_LOGGING_CATEGORY(lcQpaScreen, "qt.qpa.screen", QtCriticalMsg);
//
// Conversion Functions
diff --git a/src/plugins/platforms/cocoa/qcocoaintegration.h b/src/plugins/platforms/cocoa/qcocoaintegration.h
index 04cb4e1226..bfc3bfe9de 100644
--- a/src/plugins/platforms/cocoa/qcocoaintegration.h
+++ b/src/plugins/platforms/cocoa/qcocoaintegration.h
@@ -61,8 +61,6 @@
QT_BEGIN_NAMESPACE
-class QCocoaScreen;
-
class QCocoaIntegration : public QObject, public QPlatformIntegration
{
Q_OBJECT
@@ -113,9 +111,6 @@ public:
Qt::KeyboardModifiers queryKeyboardModifiers() const override;
QList<int> possibleKeys(const QKeyEvent *event) const override;
- void updateScreens();
- QCocoaScreen *screenForNSScreen(NSScreen *nsScreen);
-
void setToolbar(QWindow *window, NSToolbar *toolbar);
NSToolbar *toolbar(QWindow *window) const;
void clearToolbars();
@@ -143,8 +138,6 @@ private:
QScopedPointer<QCocoaAccessibility> mAccessibility;
#endif
QScopedPointer<QPlatformTheme> mPlatformTheme;
- QList<QCocoaScreen *> mScreens;
- QMacScopedObserver m_screensObserver;
#ifndef QT_NO_CLIPBOARD
QCocoaClipboard *mCocoaClipboard;
#endif
diff --git a/src/plugins/platforms/cocoa/qcocoaintegration.mm b/src/plugins/platforms/cocoa/qcocoaintegration.mm
index fb3d05d3e4..1d35d9f440 100644
--- a/src/plugins/platforms/cocoa/qcocoaintegration.mm
+++ b/src/plugins/platforms/cocoa/qcocoaintegration.mm
@@ -207,9 +207,7 @@ QCocoaIntegration::QCocoaIntegration(const QStringList &paramList)
// which will resolve to an actual value and result in screen invalidation.
cocoaApplication.presentationOptions = NSApplicationPresentationDefault;
- m_screensObserver = QMacScopedObserver([NSApplication sharedApplication],
- NSApplicationDidChangeScreenParametersNotification, [&]() { updateScreens(); });
- updateScreens();
+ QCocoaScreen::initializeScreens();
QMacInternalPasteboardMime::initializeMimeTypes();
QCocoaMimeTypes::initializeMimeTypes();
@@ -242,10 +240,7 @@ QCocoaIntegration::~QCocoaIntegration()
QMacInternalPasteboardMime::destroyMimeTypes();
#endif
- // Delete screens in reverse order to avoid crash in case of multiple screens
- while (!mScreens.isEmpty()) {
- QWindowSystemInterface::handleScreenRemoved(mScreens.takeLast());
- }
+ QCocoaScreen::cleanupScreens();
clearToolbars();
}
@@ -260,88 +255,6 @@ QCocoaIntegration::Options QCocoaIntegration::options() const
return mOptions;
}
-/*!
- \brief Synchronizes the screen list, adds new screens, removes deleted ones
-*/
-void QCocoaIntegration::updateScreens()
-{
- NSArray<NSScreen *> *scrs = [NSScreen screens];
- NSMutableArray<NSScreen *> *screens = [NSMutableArray<NSScreen *> arrayWithArray:scrs];
- if ([screens count] == 0)
- if ([NSScreen mainScreen])
- [screens addObject:[NSScreen mainScreen]];
- if ([screens count] == 0)
- return;
- QSet<QCocoaScreen*> remainingScreens = QSet<QCocoaScreen*>::fromList(mScreens);
- QList<QPlatformScreen *> siblings;
- uint screenCount = [screens count];
- for (uint i = 0; i < screenCount; i++) {
- NSScreen* scr = [screens objectAtIndex:i];
- CGDirectDisplayID dpy = scr.qt_displayId;
- // If this screen is a mirror and is not the primary one of the mirror set, ignore it.
- // Exception: The NSScreen API has been observed to a return a screen list with one
- // mirrored, non-primary screen when Qt is running as a startup item. Always use the
- // screen if there's only one screen in the list.
- if (screenCount > 1 && CGDisplayIsInMirrorSet(dpy)) {
- CGDirectDisplayID primary = CGDisplayMirrorsDisplay(dpy);
- if (primary != kCGNullDirectDisplay && primary != dpy)
- continue;
- }
- QCocoaScreen* screen = nullptr;
- foreach (QCocoaScreen* existingScr, mScreens) {
- // NSScreen documentation says do not cache the array returned from [NSScreen screens].
- // However in practice, we can identify a screen by its pointer: if resolution changes,
- // the NSScreen object will be the same instance, just with different values.
- if (existingScr->nativeScreen() == scr) {
- screen = existingScr;
- break;
- }
- }
- if (screen) {
- remainingScreens.remove(screen);
- screen->updateProperties();
- } else {
- screen = new QCocoaScreen(i);
- mScreens.append(screen);
- qCDebug(lcQpaScreen) << "Adding" << screen;
- QWindowSystemInterface::handleScreenAdded(screen);
- }
- siblings << screen;
- }
-
- // Set virtual siblings list. All screens in mScreens are siblings, because we ignored the
- // mirrors. Note that some of the screens we update the siblings list for here may be deleted
- // below, but update anyway to keep the to-be-deleted screens out of the siblings list.
- foreach (QCocoaScreen* screen, mScreens)
- screen->setVirtualSiblings(siblings);
-
- // Now the leftovers in remainingScreens are no longer current, so we can delete them.
- foreach (QCocoaScreen* screen, remainingScreens) {
- mScreens.removeOne(screen);
- // Prevent stale references to NSScreen during destroy
- screen->m_screenIndex = -1;
- qCDebug(lcQpaScreen) << "Removing" << screen;
- QWindowSystemInterface::handleScreenRemoved(screen);
- }
-}
-
-QCocoaScreen *QCocoaIntegration::screenForNSScreen(NSScreen *nsScreen)
-{
- NSUInteger index = [[NSScreen screens] indexOfObject:nsScreen];
- if (index == NSNotFound)
- return nullptr;
-
- if (index >= unsigned(mScreens.count()))
- updateScreens();
-
- for (QCocoaScreen *screen : mScreens) {
- if (screen->nativeScreen() == nsScreen)
- return screen;
- }
-
- return nullptr;
-}
-
bool QCocoaIntegration::hasCapability(QPlatformIntegration::Capability cap) const
{
switch (cap) {
diff --git a/src/plugins/platforms/cocoa/qcocoascreen.h b/src/plugins/platforms/cocoa/qcocoascreen.h
index 9ded98df32..491af2fe9c 100644
--- a/src/plugins/platforms/cocoa/qcocoascreen.h
+++ b/src/plugins/platforms/cocoa/qcocoascreen.h
@@ -48,10 +48,14 @@
QT_BEGIN_NAMESPACE
+class QCocoaIntegration;
+
class QCocoaScreen : public QPlatformScreen
{
public:
- QCocoaScreen(int screenIndex);
+ static void initializeScreens();
+ static void cleanupScreens();
+
~QCocoaScreen();
// ----------------------------------------------------
@@ -61,19 +65,18 @@ public:
QRect availableGeometry() const override { return m_availableGeometry; }
int depth() const override { return m_depth; }
QImage::Format format() const override { return m_format; }
- qreal devicePixelRatio() const override;
+ qreal devicePixelRatio() const override { return m_devicePixelRatio; }
QSizeF physicalSize() const override { return m_physicalSize; }
QDpi logicalDpi() const override { return m_logicalDpi; }
qreal refreshRate() const override { return m_refreshRate; }
QString name() const override { return m_name; }
QPlatformCursor *cursor() const override { return m_cursor; }
QWindow *topLevelAt(const QPoint &point) const override;
- QList<QPlatformScreen *> virtualSiblings() const override { return m_siblings; }
+ QList<QPlatformScreen *> virtualSiblings() const override;
QPlatformScreen::SubpixelAntialiasingType subpixelAntialiasingTypeHint() const override;
// ----------------------------------------------------
- // Additional methods
- void setVirtualSiblings(const QList<QPlatformScreen *> &siblings) { m_siblings = siblings; }
+
NSScreen *nativeScreen() const;
void updateProperties();
@@ -82,14 +85,21 @@ public:
bool isRunningDisplayLink() const;
static QCocoaScreen *primaryScreen();
+ static QCocoaScreen *get(NSScreen *nsScreen);
+ static QCocoaScreen *get(CGDirectDisplayID displayId);
static CGPoint mapToNative(const QPointF &pos, QCocoaScreen *screen = QCocoaScreen::primaryScreen());
static CGRect mapToNative(const QRectF &rect, QCocoaScreen *screen = QCocoaScreen::primaryScreen());
static QPointF mapFromNative(CGPoint pos, QCocoaScreen *screen = QCocoaScreen::primaryScreen());
static QRectF mapFromNative(CGRect rect, QCocoaScreen *screen = QCocoaScreen::primaryScreen());
-public:
- int m_screenIndex;
+private:
+ QCocoaScreen(CGDirectDisplayID displayId);
+ static void add(CGDirectDisplayID displayId);
+ void remove();
+
+ CGDirectDisplayID m_displayId = 0;
+
QRect m_geometry;
QRect m_availableGeometry;
QDpi m_logicalDpi;
@@ -99,11 +109,13 @@ public:
QImage::Format m_format;
QSizeF m_physicalSize;
QCocoaCursor *m_cursor;
- QList<QPlatformScreen *> m_siblings;
+ qreal m_devicePixelRatio;
CVDisplayLinkRef m_displayLink = nullptr;
dispatch_source_t m_displayLinkSource = nullptr;
QAtomicInt m_pendingUpdates;
+
+ friend QDebug operator<<(QDebug debug, const QCocoaScreen *screen);
};
#ifndef QT_NO_DEBUG_STREAM
@@ -116,5 +128,4 @@ QT_END_NAMESPACE
@property(readonly) CGDirectDisplayID qt_displayId;
@end
-#endif
-
+#endif // QCOCOASCREEN_H
diff --git a/src/plugins/platforms/cocoa/qcocoascreen.mm b/src/plugins/platforms/cocoa/qcocoascreen.mm
index 6a5b0e6e3e..392099d083 100644
--- a/src/plugins/platforms/cocoa/qcocoascreen.mm
+++ b/src/plugins/platforms/cocoa/qcocoascreen.mm
@@ -41,6 +41,7 @@
#include "qcocoawindow.h"
#include "qcocoahelpers.h"
+#include "qcocoaintegration.h"
#include <QtCore/qcoreapplication.h>
#include <QtGui/private/qcoregraphics_p.h>
@@ -53,34 +54,104 @@
QT_BEGIN_NAMESPACE
-class QCoreTextFontEngine;
-class QFontEngineFT;
+void QCocoaScreen::initializeScreens()
+{
+ uint32_t displayCount = 0;
+ if (CGGetActiveDisplayList(0, nullptr, &displayCount) != kCGErrorSuccess)
+ qFatal("Failed to get number of active displays");
+
+ CGDirectDisplayID activeDisplays[displayCount];
+ if (CGGetActiveDisplayList(displayCount, &activeDisplays[0], &displayCount) != kCGErrorSuccess)
+ qFatal("Failed to get active displays");
+
+ for (CGDirectDisplayID displayId : activeDisplays)
+ QCocoaScreen::add(displayId);
+
+ CGDisplayRegisterReconfigurationCallback([](CGDirectDisplayID displayId, CGDisplayChangeSummaryFlags flags, void *userInfo) {
+ if (flags & kCGDisplayBeginConfigurationFlag)
+ return; // Wait for changes to apply
+
+ Q_UNUSED(userInfo);
+
+ QCocoaScreen *cocoaScreen = QCocoaScreen::get(displayId);
+
+ if ((flags & kCGDisplayAddFlag) || !cocoaScreen) {
+ if (!CGDisplayIsActive(displayId)) {
+ qCDebug(lcQpaScreen) << "Not adding inactive display" << displayId;
+ return; // Will be added when activated
+ }
+ QCocoaScreen::add(displayId);
+ } else if ((flags & kCGDisplayRemoveFlag) || !CGDisplayIsActive(displayId)) {
+ cocoaScreen->remove();
+ } else {
+ // Detect changes to the primary screen immediately, instead of
+ // waiting for a display reconfigure with kCGDisplaySetMainFlag.
+ // This ensures that any property updates to the other screens
+ // will be in reference to the correct primary screen.
+ QCocoaScreen *mainDisplay = QCocoaScreen::get(CGMainDisplayID());
+ if (QGuiApplication::primaryScreen()->handle() != mainDisplay) {
+ mainDisplay->updateProperties();
+ qCInfo(lcQpaScreen) << "Primary screen changed to" << mainDisplay;
+ QWindowSystemInterface::handlePrimaryScreenChanged(mainDisplay);
+ }
-QCocoaScreen::QCocoaScreen(int screenIndex)
- : QPlatformScreen(), m_screenIndex(screenIndex), m_refreshRate(60.0)
+ if (cocoaScreen == mainDisplay)
+ return; // Already reconfigured
+
+ cocoaScreen->updateProperties();
+ qCInfo(lcQpaScreen) << "Reconfigured" << cocoaScreen;
+ }
+ }, nullptr);
+}
+
+void QCocoaScreen::add(CGDirectDisplayID displayId)
+{
+ QCocoaScreen *cocoaScreen = new QCocoaScreen(displayId);
+ qCInfo(lcQpaScreen) << "Adding" << cocoaScreen;
+ QWindowSystemInterface::handleScreenAdded(cocoaScreen, CGDisplayIsMain(displayId));
+}
+
+QCocoaScreen::QCocoaScreen(CGDirectDisplayID displayId)
+ : QPlatformScreen(), m_displayId(displayId)
{
updateProperties();
m_cursor = new QCocoaCursor;
}
-QCocoaScreen::~QCocoaScreen()
+void QCocoaScreen::cleanupScreens()
{
- delete m_cursor;
+ // Remove screens in reverse order to avoid crash in case of multiple screens
+ for (QScreen *screen : backwards(QGuiApplication::screens()))
+ static_cast<QCocoaScreen*>(screen->handle())->remove();
+}
- CVDisplayLinkRelease(m_displayLink);
- if (m_displayLinkSource)
- dispatch_release(m_displayLinkSource);
+void QCocoaScreen::remove()
+{
+ m_displayId = 0; // Prevent stale references during removal
+
+ // This may result in the application responding to QGuiApplication::screenRemoved
+ // by moving the window to another screen, either by setGeometry, or by setScreen.
+ // If the window isn't moved by the application, Qt will as a fallback move it to
+ // the primary screen via setScreen. Due to the way setScreen works, this won't
+ // actually recreate the window on the new screen, it will just assign the new
+ // QScreen to the window. The associated NSWindow will have an NSScreen determined
+ // by AppKit. AppKit will then move the window to another screen by changing the
+ // geometry, and we will get a callback in QCocoaWindow::windowDidMove and then
+ // QCocoaWindow::windowDidChangeScreen. At that point the window will appear to have
+ // already changed its screen, but that's only true if comparing the Qt screens,
+ // not when comparing the NSScreens.
+ QWindowSystemInterface::handleScreenRemoved(this);
}
-NSScreen *QCocoaScreen::nativeScreen() const
+QCocoaScreen::~QCocoaScreen()
{
- NSArray<NSScreen *> *screens = [NSScreen screens];
+ Q_ASSERT_X(!screen(), "QCocoaScreen", "QScreen should be deleted first");
- // Stale reference, screen configuration has changed
- if (m_screenIndex < 0 || (NSUInteger)m_screenIndex >= [screens count])
- return nil;
+ delete m_cursor;
- return [screens objectAtIndex:m_screenIndex];
+ CVDisplayLinkRelease(m_displayLink);
+ if (m_displayLinkSource)
+ dispatch_release(m_displayLinkSource);
}
static QString displayName(CGDirectDisplayID displayID)
@@ -117,35 +188,37 @@ static QString displayName(CGDirectDisplayID displayID)
void QCocoaScreen::updateProperties()
{
- NSScreen *nsScreen = nativeScreen();
- if (!nsScreen)
- return;
+ Q_ASSERT(m_displayId);
const QRect previousGeometry = m_geometry;
const QRect previousAvailableGeometry = m_availableGeometry;
const QDpi previousLogicalDpi = m_logicalDpi;
const qreal previousRefreshRate = m_refreshRate;
+ // Some properties are only available via NSScreen
+ NSScreen *nsScreen = nativeScreen();
+ Q_ASSERT(nsScreen);
+
// The reference screen for the geometry is always the primary screen
- QRectF primaryScreenGeometry = QRectF::fromCGRect([[NSScreen screens] firstObject].frame);
+ QRectF primaryScreenGeometry = QRectF::fromCGRect(CGDisplayBounds(CGMainDisplayID()));
m_geometry = qt_mac_flip(QRectF::fromCGRect(nsScreen.frame), primaryScreenGeometry).toRect();
m_availableGeometry = qt_mac_flip(QRectF::fromCGRect(nsScreen.visibleFrame), primaryScreenGeometry).toRect();
+ m_devicePixelRatio = nsScreen.backingScaleFactor;
+
m_format = QImage::Format_RGB32;
- m_depth = NSBitsPerPixelFromDepth([nsScreen depth]);
+ m_depth = NSBitsPerPixelFromDepth(nsScreen.depth);
- CGDirectDisplayID dpy = nsScreen.qt_displayId;
- CGSize size = CGDisplayScreenSize(dpy);
+ CGSize size = CGDisplayScreenSize(m_displayId);
m_physicalSize = QSizeF(size.width, size.height);
m_logicalDpi.first = 72;
m_logicalDpi.second = 72;
- CGDisplayModeRef displayMode = CGDisplayCopyDisplayMode(dpy);
+
+ QCFType<CGDisplayModeRef> displayMode = CGDisplayCopyDisplayMode(m_displayId);
float refresh = CGDisplayModeGetRefreshRate(displayMode);
- CGDisplayModeRelease(displayMode);
- if (refresh > 0)
- m_refreshRate = refresh;
+ m_refreshRate = refresh > 0 ? refresh : 60.0;
- m_name = displayName(dpy);
+ m_name = displayName(m_displayId);
const bool didChangeGeometry = m_geometry != previousGeometry || m_availableGeometry != previousAvailableGeometry;
@@ -155,24 +228,6 @@ void QCocoaScreen::updateProperties()
QWindowSystemInterface::handleScreenLogicalDotsPerInchChange(screen(), m_logicalDpi.first, m_logicalDpi.second);
if (m_refreshRate != previousRefreshRate)
QWindowSystemInterface::handleScreenRefreshRateChange(screen(), m_refreshRate);
-
- qCDebug(lcQpaScreen) << "Updated properties for" << this;
-
- if (didChangeGeometry) {
- // When a screen changes its geometry, AppKit will send us a NSWindowDidMoveNotification
- // for each window, resulting in calls to handleGeometryChange(), but this happens before
- // the NSApplicationDidChangeScreenParametersNotification, so when we map the new geometry
- // (which is correct at that point) to the screen using QCocoaScreen::mapFromNative(), we
- // end up using the stale screen geometry, and the new window geometry we report is wrong.
- // To make sure we finally report the correct window geometry, we need to do another pass
- // of geometry reporting, now that the screen properties have been updates. FIXME: Ideally
- // this would be solved by not caching the screen properties in QCocoaScreen, but that
- // requires more research.
- for (QWindow *window : windows()) {
- if (QCocoaWindow *cocoaWindow = static_cast<QCocoaWindow*>(window->handle()))
- cocoaWindow->handleGeometryChange();
- }
- }
}
// ----------------------- Display link -----------------------
@@ -181,8 +236,10 @@ Q_LOGGING_CATEGORY(lcQpaScreenUpdates, "qt.qpa.screen.updates", QtCriticalMsg);
void QCocoaScreen::requestUpdate()
{
+ Q_ASSERT(m_displayId);
+
if (!m_displayLink) {
- CVDisplayLinkCreateWithCGDisplay(nativeScreen().qt_displayId, &m_displayLink);
+ CVDisplayLinkCreateWithCGDisplay(m_displayId, &m_displayLink);
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
@@ -269,6 +326,9 @@ struct DeferredDebugHelper
void QCocoaScreen::deliverUpdateRequests()
{
+ if (!m_displayId)
+ return; // Screen removed
+
QMacAutoReleasePool pool;
// The CVDisplayLink callback is a notification that it's a good time to produce a new frame.
@@ -283,7 +343,7 @@ void QCocoaScreen::deliverUpdateRequests()
const int pendingUpdates = ++m_pendingUpdates;
DeferredDebugHelper screenUpdates(lcQpaScreenUpdates());
- qDeferredDebug(screenUpdates) << "display link callback for screen " << m_screenIndex;
+ qDeferredDebug(screenUpdates) << "display link callback for screen " << m_displayId;
if (const int framesAheadOfDelivery = pendingUpdates - 1) {
// If we have more than one update pending it means that a previous display link callback
@@ -370,13 +430,6 @@ bool QCocoaScreen::isRunningDisplayLink() const
// -----------------------------------------------------------
-qreal QCocoaScreen::devicePixelRatio() const
-{
- QMacAutoReleasePool pool;
- NSScreen *nsScreen = nativeScreen();
- return qreal(nsScreen ? [nsScreen backingScaleFactor] : 1.0);
-}
-
QPlatformScreen::SubpixelAntialiasingType QCocoaScreen::subpixelAntialiasingTypeHint() const
{
QPlatformScreen::SubpixelAntialiasingType type = QPlatformScreen::subpixelAntialiasingTypeHint();
@@ -430,7 +483,7 @@ QPixmap QCocoaScreen::grabWindow(WId view, int x, int y, int width, int height)
{
// Determine the grab rect. FIXME: The rect should be bounded by the view's
// geometry, but note that for the pixeltool use case that window will be the
- // desktop widgets's view, which currently gets resized to fit one screen
+ // desktop widget's view, which currently gets resized to fit one screen
// only, since its NSWindow has the NSWindowStyleMaskTitled flag set.
Q_UNUSED(view);
QRect grabRect = QRect(x, y, width, height);
@@ -482,7 +535,7 @@ QPixmap QCocoaScreen::grabWindow(WId view, int x, int y, int width, int height)
for (uint i = 0; i < displayCount; ++i)
dpr = qMax(dpr, images.at(i).devicePixelRatio());
- // Alocate target pixmap and draw each screen's content
+ // Allocate target pixmap and draw each screen's content
qCDebug(lcQpaScreen) << "Create grap pixmap" << grabRect.size() << "at devicePixelRatio" << dpr;
QPixmap windowPixmap(grabRect.size() * dpr);
windowPixmap.setDevicePixelRatio(dpr);
@@ -499,7 +552,57 @@ QPixmap QCocoaScreen::grabWindow(WId view, int x, int y, int width, int height)
*/
QCocoaScreen *QCocoaScreen::primaryScreen()
{
- return static_cast<QCocoaScreen *>(QGuiApplication::primaryScreen()->handle());
+ auto screen = static_cast<QCocoaScreen *>(QGuiApplication::primaryScreen()->handle());
+ Q_ASSERT_X(screen == get(CGMainDisplayID()), "QCocoaScreen",
+ "The application's primary screen should always be in sync with the main display");
+ return screen;
+}
+
+QList<QPlatformScreen*> QCocoaScreen::virtualSiblings() const
+{
+ QList<QPlatformScreen*> siblings;
+
+ // Screens on macOS are always part of the same virtual desktop
+ for (QScreen *screen : QGuiApplication::screens())
+ siblings << screen->handle();
+
+ return siblings;
+}
+
+QCocoaScreen *QCocoaScreen::get(NSScreen *nsScreen)
+{
+ return get(nsScreen.qt_displayId);
+}
+
+QCocoaScreen *QCocoaScreen::get(CGDirectDisplayID displayId)
+{
+ for (QScreen *screen : QGuiApplication::screens()) {
+ QCocoaScreen *cocoaScreen = static_cast<QCocoaScreen*>(screen->handle());
+ if (cocoaScreen->m_displayId == displayId)
+ return cocoaScreen;
+ }
+
+ return nullptr;
+}
+
+NSScreen *QCocoaScreen::nativeScreen() const
+{
+ if (!m_displayId)
+ return nil; // The display has been disconnected
+
+ // A single display may have different displayIds depending on
+ // which GPU is in use or which physical port the display is
+ // connected to. By comparing UUIDs instead of display IDs we
+ // ensure that we always pick up the appropriate NSScreen.
+ QCFType<CFUUIDRef> uuid = CGDisplayCreateUUIDFromDisplayID(m_displayId);
+
+ for (NSScreen *screen in [NSScreen screens]) {
+ if (CGDisplayCreateUUIDFromDisplayID(screen.qt_displayId) == uuid)
+ return screen;
+ }
+
+ qCWarning(lcQpaScreen) << "Could not find NSScreen for display ID" << m_displayId;
+ return nil;
}
CGPoint QCocoaScreen::mapToNative(const QPointF &pos, QCocoaScreen *screen)
@@ -533,11 +636,10 @@ QDebug operator<<(QDebug debug, const QCocoaScreen *screen)
debug.nospace();
debug << "QCocoaScreen(" << (const void *)screen;
if (screen) {
- debug << ", index=" << screen->m_screenIndex;
- debug << ", native=" << screen->nativeScreen();
debug << ", geometry=" << screen->geometry();
debug << ", dpr=" << screen->devicePixelRatio();
debug << ", name=" << screen->name();
+ debug << ", native=" << screen->nativeScreen();
}
debug << ')';
return debug;
diff --git a/src/plugins/platforms/cocoa/qcocoasystemtrayicon.mm b/src/plugins/platforms/cocoa/qcocoasystemtrayicon.mm
index 4982f5ee05..de5cf85854 100644
--- a/src/plugins/platforms/cocoa/qcocoasystemtrayicon.mm
+++ b/src/plugins/platforms/cocoa/qcocoasystemtrayicon.mm
@@ -383,9 +383,9 @@ QT_END_NAMESPACE
}
- (QRectF)geometry {
- if (NSWindow *window = [[item view] window]) {
- if (QCocoaScreen *screen = QCocoaIntegration::instance()->screenForNSScreen([window screen]))
- return screen->mapFromNative([window frame]);
+ if (NSWindow *window = item.view.window) {
+ if (QCocoaScreen *screen = QCocoaScreen::get(window.screen))
+ return screen->mapFromNative(window.frame);
}
return QRectF();
}
diff --git a/src/plugins/platforms/cocoa/qcocoawindow.mm b/src/plugins/platforms/cocoa/qcocoawindow.mm
index 0d7eab9a94..c09ff12c2b 100644
--- a/src/plugins/platforms/cocoa/qcocoawindow.mm
+++ b/src/plugins/platforms/cocoa/qcocoawindow.mm
@@ -1209,17 +1209,17 @@ void QCocoaWindow::windowDidChangeScreen()
return;
// Note: When a window is resized to 0x0 Cocoa will report the window's screen as nil
- auto *currentScreen = QCocoaIntegration::instance()->screenForNSScreen(m_view.window.screen);
+ auto *currentScreen = QCocoaScreen::get(m_view.window.screen);
auto *previousScreen = static_cast<QCocoaScreen*>(screen());
Q_ASSERT_X(!m_view.window.screen || currentScreen,
"QCocoaWindow", "Failed to get QCocoaScreen for NSScreen");
// Note: The previous screen may be the same as the current screen, either because
- // the screen was just reconfigured, which still results in AppKit sending an
- // NSWindowDidChangeScreenNotification, because the previous screen was removed,
+ // a) the screen was just reconfigured, which still results in AppKit sending an
+ // NSWindowDidChangeScreenNotification, b) because the previous screen was removed,
// and we ended up calling QWindow::setScreen to move the window, which doesn't
- // actually move the window to the new screen, or because we've delivered the
+ // actually move the window to the new screen, or c) because we've delivered the
// screen change to the top level window, which will make all the child windows
// of that window report the new screen when requested via QWindow::screen().
// We still need to deliver the screen change in all these cases, as the