summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorQt Forward Merge Bot <qt_forward_merge_bot@qt-project.org>2019-12-13 12:46:12 +0100
committerQt Forward Merge Bot <qt_forward_merge_bot@qt-project.org>2019-12-13 12:46:12 +0100
commit56b0dabf8056c3af06d7e1e8e138742565904520 (patch)
tree05954d1b5c81e611c82804fc600c692d45c44f3b /src
parent072ca960f517f0cdfcb768dcbeebba2212a6c21e (diff)
parent2a887a517eaaa2c5324aecf3b919899b7a86ff4a (diff)
Merge remote-tracking branch 'origin/5.14.0' into 5.14
Diffstat (limited to 'src')
-rw-r--r--src/3rdparty/VulkanMemoryAllocator/qt_attribution.json2
-rw-r--r--src/corelib/global/qlibraryinfo.cpp36
-rw-r--r--src/corelib/time/qcalendar.cpp4
-rw-r--r--src/plugins/platforms/android/qandroidassetsfileenginehandler.cpp75
-rw-r--r--src/plugins/platforms/cocoa/qcocoascreen.h21
-rw-r--r--src/plugins/platforms/cocoa/qcocoascreen.mm272
6 files changed, 320 insertions, 90 deletions
diff --git a/src/3rdparty/VulkanMemoryAllocator/qt_attribution.json b/src/3rdparty/VulkanMemoryAllocator/qt_attribution.json
index 2548856ca7..0a5df738a8 100644
--- a/src/3rdparty/VulkanMemoryAllocator/qt_attribution.json
+++ b/src/3rdparty/VulkanMemoryAllocator/qt_attribution.json
@@ -2,7 +2,7 @@
{
"Id": "VulkanMemoryAllocator",
"Name": "Vulkan Memory Allocator",
- "QDocModule": "qtrhi",
+ "QDocModule": "qtgui",
"Description": "Vulkan Memory Allocator",
"QtUsage": "Memory management for the Vulkan backend of QRhi.",
diff --git a/src/corelib/global/qlibraryinfo.cpp b/src/corelib/global/qlibraryinfo.cpp
index 276741c9fb..f0f77fe68e 100644
--- a/src/corelib/global/qlibraryinfo.cpp
+++ b/src/corelib/global/qlibraryinfo.cpp
@@ -562,9 +562,39 @@ static QString getRelocatablePrefix()
HMODULE hModule = getWindowsModuleHandle();
const int kBufferSize = 4096;
wchar_t buffer[kBufferSize];
- const int pathSize = GetModuleFileName(hModule, buffer, kBufferSize);
- if (pathSize > 0)
- prefixPath = prefixFromQtCoreLibraryHelper(QString::fromWCharArray(buffer, pathSize));
+ DWORD pathSize = GetModuleFileName(hModule, buffer, kBufferSize);
+ const QString qtCoreFilePath = QString::fromWCharArray(buffer, int(pathSize));
+ const QString qtCoreDirPath = QFileInfo(qtCoreFilePath).absolutePath();
+ pathSize = GetModuleFileName(NULL, buffer, kBufferSize);
+ const QString exeDirPath = QFileInfo(QString::fromWCharArray(buffer, int(pathSize))).absolutePath();
+ if (QFileInfo(exeDirPath) == QFileInfo(qtCoreDirPath)) {
+ // QtCore DLL is next to the executable. This is either a windeployqt'ed executable or an
+ // executable within the QT_HOST_BIN directory. We're detecting the latter case by checking
+ // whether there's an import library corresponding to our QtCore DLL in PREFIX/lib.
+ const QString libdir = QString::fromLatin1(
+ qt_configure_strs + qt_configure_str_offsets[QLibraryInfo::LibrariesPath - 1]);
+ const QLatin1Char slash('/');
+#if defined(Q_CC_MINGW)
+ const QString implibPrefix = QStringLiteral("lib");
+ const QString implibSuffix = QStringLiteral(".a");
+#else
+ const QString implibPrefix;
+ const QString implibSuffix = QStringLiteral(".lib");
+#endif
+ const QString qtCoreImpLibFileName = implibPrefix
+ + QFileInfo(qtCoreFilePath).completeBaseName() + implibSuffix;
+ const QString qtCoreImpLibPath = qtCoreDirPath
+ + slash + QLatin1String(QT_CONFIGURE_LIBLOCATION_TO_PREFIX_PATH)
+ + slash + libdir
+ + slash + qtCoreImpLibFileName;
+ if (!QFileInfo::exists(qtCoreImpLibPath)) {
+ // We did not find a corresponding import library and conclude that this is a
+ // windeployqt'ed executable.
+ return exeDirPath;
+ }
+ }
+ if (!qtCoreFilePath.isEmpty())
+ prefixPath = prefixFromQtCoreLibraryHelper(qtCoreFilePath);
#else
#error "The chosen platform / config does not support querying for a dynamic prefix."
#endif
diff --git a/src/corelib/time/qcalendar.cpp b/src/corelib/time/qcalendar.cpp
index d308aeba2b..6a4623ce92 100644
--- a/src/corelib/time/qcalendar.cpp
+++ b/src/corelib/time/qcalendar.cpp
@@ -100,7 +100,7 @@ struct Registry {
if (id == QCalendar::System::User) {
byId.push_back(calendar);
} else {
- Q_ASSERT(byId.at(size_t(id)) == nullptr);
+ Q_ASSERT(byId[size_t(id)] == nullptr);
byId[size_t(id)] = calendar;
}
if (id == QCalendar::System::Gregorian) {
@@ -618,7 +618,7 @@ const QCalendarBackend *QCalendarBackend::fromEnum(QCalendar::System system)
if (calendarRegistry.isDestroyed() || system == QCalendar::System::User)
return nullptr;
Q_ASSERT(calendarRegistry->byId.size() >= size_t(system));
- if (auto *c = calendarRegistry->byId.at(size_t(system)))
+ if (auto *c = calendarRegistry->byId[size_t(system)])
return c;
switch (system) {
case QCalendar::System::Gregorian:
diff --git a/src/plugins/platforms/android/qandroidassetsfileenginehandler.cpp b/src/plugins/platforms/android/qandroidassetsfileenginehandler.cpp
index 26e72a480f..fcc08ea00d 100644
--- a/src/plugins/platforms/android/qandroidassetsfileenginehandler.cpp
+++ b/src/plugins/platforms/android/qandroidassetsfileenginehandler.cpp
@@ -72,9 +72,10 @@ static inline QString prefixedPath(QString path)
struct AssetItem {
enum class Type {
File,
- Folder
+ Folder,
+ Invalid
};
-
+ AssetItem() = default;
AssetItem (const QString &rawName)
: name(rawName)
{
@@ -92,21 +93,47 @@ using AssetItemList = QVector<AssetItem>;
class FolderIterator : public AssetItemList
{
public:
- static QSharedPointer<FolderIterator> fromCache(const QString &path)
+ static QSharedPointer<FolderIterator> fromCache(const QString &path, bool clone)
{
QMutexLocker lock(&m_assetsCacheMutex);
QSharedPointer<FolderIterator> *folder = m_assetsCache.object(path);
if (!folder) {
folder = new QSharedPointer<FolderIterator>{new FolderIterator{path}};
- if (!m_assetsCache.insert(path, folder)) {
+ if ((*folder)->empty() || !m_assetsCache.insert(path, folder)) {
QSharedPointer<FolderIterator> res = *folder;
delete folder;
return res;
}
}
- return *folder;
+ return clone ? QSharedPointer<FolderIterator>{new FolderIterator{*(*folder)}} : *folder;
+ }
+
+ static AssetItem::Type fileType(const QString &filePath)
+ {
+ const QStringList paths = filePath.split(QLatin1Char('/'));
+ QString fullPath;
+ AssetItem::Type res = AssetItem::Type::Invalid;
+ for (const auto &path: paths) {
+ auto folder = fromCache(fullPath, false);
+ auto it = std::lower_bound(folder->begin(), folder->end(), AssetItem{path}, [](const AssetItem &val, const AssetItem &assetItem) {
+ return val.name < assetItem.name;
+ });
+ if (it == folder->end() || it->name != path)
+ return AssetItem::Type::Invalid;
+ if (!fullPath.isEmpty())
+ fullPath.append(QLatin1Char('/'));
+ fullPath += path;
+ res = it->type;
+ }
+ return res;
}
+ FolderIterator(const FolderIterator &other)
+ : AssetItemList(other)
+ , m_index(-1)
+ , m_path(other.m_path)
+ {}
+
FolderIterator(const QString &path)
: m_path(path)
{
@@ -118,8 +145,12 @@ public:
QJNIEnvironmentPrivate env;
jobjectArray jFiles = static_cast<jobjectArray>(files.object());
const jint nFiles = env->GetArrayLength(jFiles);
- for (int i = 0; i < nFiles; ++i)
- push_back({QJNIObjectPrivate(env->GetObjectArrayElement(jFiles, i)).toString()});
+ for (int i = 0; i < nFiles; ++i) {
+ AssetItem item{QJNIObjectPrivate(env->GetObjectArrayElement(jFiles, i)).toString()};
+ insert(std::upper_bound(begin(), end(), item, [](const auto &a, const auto &b){
+ return a.name < b.name;
+ }), item);
+ }
}
m_path = assetsPrefix + QLatin1Char('/') + m_path + QLatin1Char('/');
m_path.replace(QLatin1String("//"), QLatin1String("/"));
@@ -169,7 +200,7 @@ public:
const QString &path)
: QAbstractFileEngineIterator(filters, nameFilters)
{
- m_stack.push_back(FolderIterator::fromCache(cleanedAssetPath(path)));
+ m_stack.push_back(FolderIterator::fromCache(cleanedAssetPath(path), true));
if (m_stack.last()->empty())
m_stack.pop_back();
}
@@ -215,7 +246,7 @@ public:
if (!res)
return {};
if (res->second.type == AssetItem::Type::Folder) {
- m_stack.push_back(FolderIterator::fromCache(cleanedAssetPath(currentFilePath())));
+ m_stack.push_back(FolderIterator::fromCache(cleanedAssetPath(currentFilePath()), true));
if (m_stack.last()->empty())
m_stack.pop_back();
}
@@ -257,6 +288,7 @@ public:
m_assetFile = 0;
return true;
}
+ m_isFolder = false;
return false;
}
@@ -305,12 +337,12 @@ public:
FileFlags fileFlags(FileFlags type = FileInfoAll) const override
{
- FileFlags flags(ReadOwnerPerm|ReadUserPerm|ReadGroupPerm|ReadOtherPerm|ExistsFlag);
+ FileFlags commonFlags(ReadOwnerPerm|ReadUserPerm|ReadGroupPerm|ReadOtherPerm|ExistsFlag);
+ FileFlags flags;
if (m_assetFile)
- flags |= FileType;
+ flags = FileType | commonFlags;
else if (m_isFolder)
- flags |= DirectoryType;
-
+ flags = DirectoryType | commonFlags;
return type & flags;
}
@@ -341,9 +373,20 @@ public:
void setFileName(const QString &file) override
{
+ if (m_fileName == cleanedAssetPath(file))
+ return;
close();
m_fileName = cleanedAssetPath(file);
- m_isFolder = !open(QIODevice::ReadOnly) && !FolderIterator::fromCache(m_fileName)->empty();
+ switch (FolderIterator::fileType(m_fileName)) {
+ case AssetItem::Type::File:
+ open(QIODevice::ReadOnly);
+ break;
+ case AssetItem::Type::Folder:
+ m_isFolder = true;
+ break;
+ case AssetItem::Type::Invalid:
+ break;
+ }
}
Iterator *beginEntryList(QDir::Filters filters, const QStringList &filterNames) override
@@ -355,9 +398,9 @@ public:
private:
AAsset *m_assetFile = nullptr;
- AAssetManager *m_assetManager;
+ AAssetManager *m_assetManager = nullptr;
QString m_fileName;
- bool m_isFolder;
+ bool m_isFolder = false;
};
diff --git a/src/plugins/platforms/cocoa/qcocoascreen.h b/src/plugins/platforms/cocoa/qcocoascreen.h
index 7ec9a2b5af..dcf6f1c753 100644
--- a/src/plugins/platforms/cocoa/qcocoascreen.h
+++ b/src/plugins/platforms/cocoa/qcocoascreen.h
@@ -53,9 +53,6 @@ class QCocoaIntegration;
class QCocoaScreen : public QPlatformScreen
{
public:
- static void initializeScreens();
- static void cleanupScreens();
-
~QCocoaScreen();
// ----------------------------------------------------
@@ -79,7 +76,6 @@ public:
// ----------------------------------------------------
NSScreen *nativeScreen() const;
- void updateProperties();
void requestUpdate();
void deliverUpdateRequests();
@@ -88,6 +84,7 @@ public:
static QCocoaScreen *primaryScreen();
static QCocoaScreen *get(NSScreen *nsScreen);
static QCocoaScreen *get(CGDirectDisplayID displayId);
+ static QCocoaScreen *get(CFUUIDRef uuid);
static CGPoint mapToNative(const QPointF &pos, QCocoaScreen *screen = QCocoaScreen::primaryScreen());
static CGRect mapToNative(const QRectF &rect, QCocoaScreen *screen = QCocoaScreen::primaryScreen());
@@ -95,11 +92,23 @@ public:
static QRectF mapFromNative(CGRect rect, QCocoaScreen *screen = QCocoaScreen::primaryScreen());
private:
- QCocoaScreen(CGDirectDisplayID displayId);
+ static void initializeScreens();
+ static void updateScreens();
+ static void cleanupScreens();
+
+ static bool updateScreensIfNeeded();
+ static NSArray *s_screenConfigurationBeforeUpdate;
+
static void add(CGDirectDisplayID displayId);
+ QCocoaScreen(CGDirectDisplayID displayId);
+ void update(CGDirectDisplayID displayId);
void remove();
+ bool isOnline() const;
+ bool isMirroring() const;
+
CGDirectDisplayID m_displayId = kCGNullDirectDisplay;
+ CGDirectDisplayID displayId() const { return m_displayId; }
QRect m_geometry;
QRect m_availableGeometry;
@@ -116,6 +125,8 @@ private:
dispatch_source_t m_displayLinkSource = nullptr;
QAtomicInt m_pendingUpdates;
+ friend class QCocoaIntegration;
+ friend class QCocoaWindow;
friend QDebug operator<<(QDebug debug, const QCocoaScreen *screen);
};
diff --git a/src/plugins/platforms/cocoa/qcocoascreen.mm b/src/plugins/platforms/cocoa/qcocoascreen.mm
index bd5c95b9d0..e4dd4cf6c6 100644
--- a/src/plugins/platforms/cocoa/qcocoascreen.mm
+++ b/src/plugins/platforms/cocoa/qcocoascreen.mm
@@ -57,6 +57,7 @@ QT_BEGIN_NAMESPACE
namespace CoreGraphics {
Q_NAMESPACE
enum DisplayChange {
+ ReconfiguredWithFlagsMissing = 0,
Moved = kCGDisplayMovedFlag,
SetMain = kCGDisplaySetMainFlag,
SetMode = kCGDisplaySetModeFlag,
@@ -71,73 +72,165 @@ namespace CoreGraphics {
Q_ENUM_NS(DisplayChange)
}
+NSArray *QCocoaScreen::s_screenConfigurationBeforeUpdate = nil;
+
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);
+ updateScreens();
CGDisplayRegisterReconfigurationCallback([](CGDirectDisplayID displayId, CGDisplayChangeSummaryFlags flags, void *userInfo) {
- if (flags & kCGDisplayBeginConfigurationFlag)
- return; // Wait for changes to apply
-
Q_UNUSED(userInfo);
- qCDebug(lcQpaScreen).verbosity(0).nospace() << "Display reconfiguration"
- << " (" << QFlags<CoreGraphics::DisplayChange>(flags) << ")"
- << " for displayId=" << displayId;
-
- QCocoaScreen *cocoaScreen = QCocoaScreen::get(displayId);
+ // Displays are reconfigured in batches, and we want to update our screens
+ // once a batch ends, so that all the states of the displays are up to date.
+ static int displayReconfigurationsInProgress = 0;
+
+ const bool beforeReconfigure = flags & kCGDisplayBeginConfigurationFlag;
+ qCDebug(lcQpaScreen).verbosity(0).nospace() << "Display " << displayId
+ << (beforeReconfigure ? " about to reconfigure" : " was ")
+ << QFlags<CoreGraphics::DisplayChange>(flags)
+ << " with " << displayReconfigurationsInProgress
+ << " display configuration(s) in progress";
+
+ if (!flags) {
+ // CGDisplayRegisterReconfigurationCallback has been observed to be called
+ // with flags unset. This seems like a bug. The callback is not paired with
+ // a matching "completion" callback either, so we don't know whether to treat
+ // it as a begin or end of reconfigure.
+ return;
+ }
- if ((flags & kCGDisplayAddFlag) || !cocoaScreen) {
- if (!CGDisplayIsActive(displayId)) {
- qCDebug(lcQpaScreen) << "Not adding inactive display" << displayId;
- return; // Will be added when activated
+ if (beforeReconfigure) {
+ if (!displayReconfigurationsInProgress++) {
+ // There might have been a screen reconfigure before this that
+ // we didn't process yet, so do that now if that's the case.
+ updateScreensIfNeeded();
+
+ Q_ASSERT(!s_screenConfigurationBeforeUpdate);
+ s_screenConfigurationBeforeUpdate = NSScreen.screens;
+ qCDebug(lcQpaScreen, "Display reconfigure transaction started"
+ " with screen configuration %p", s_screenConfigurationBeforeUpdate);
+
+ static void (^tryScreenUpdate)();
+ tryScreenUpdate = ^void () {
+ qCDebug(lcQpaScreen) << "Attempting screen update from runloop block";
+ if (!updateScreensIfNeeded())
+ CFRunLoopPerformBlock(CFRunLoopGetMain(), kCFRunLoopCommonModes, tryScreenUpdate);
+ };
+ CFRunLoopPerformBlock(CFRunLoopGetMain(), kCFRunLoopCommonModes, tryScreenUpdate);
}
- 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);
- if (cocoaScreen == mainDisplay)
- return; // Already reconfigured
- }
+ Q_ASSERT_X(displayReconfigurationsInProgress, "QCococaScreen",
+ "Display configuration transactions are expected to be balanced");
- cocoaScreen->updateProperties();
- qCInfo(lcQpaScreen).nospace() << "Reconfigured " <<
- (primaryScreen() == cocoaScreen ? "primary " : "")
- << cocoaScreen;
+ if (!--displayReconfigurationsInProgress) {
+ qCDebug(lcQpaScreen) << "Display reconfigure transaction completed";
+ // We optimistically update now, in case the NSScreens have changed
+ updateScreensIfNeeded();
+ }
}
}, nullptr);
+
+ static QMacNotificationObserver screenParameterObserver(NSApplication.sharedApplication,
+ NSApplicationDidChangeScreenParametersNotification, [&]() {
+ qCDebug(lcQpaScreen) << "Received screen parameter change notification";
+ updateScreensIfNeeded(); // As a last resort we update screens here
+ });
+}
+
+bool QCocoaScreen::updateScreensIfNeeded()
+{
+ if (!s_screenConfigurationBeforeUpdate) {
+ qCDebug(lcQpaScreen) << "QScreens have already been updated, all good";
+ return true;
+ }
+
+ if (s_screenConfigurationBeforeUpdate == NSScreen.screens) {
+ qCDebug(lcQpaScreen) << "Still waiting for NSScreen configuration change";
+ return false;
+ }
+
+ qCDebug(lcQpaScreen, "NSScreen configuration changed to %p", NSScreen.screens);
+ updateScreens();
+
+ s_screenConfigurationBeforeUpdate = nil;
+ return true;
+}
+
+/*
+ Update the list of available QScreens, and the properties of existing screens.
+
+ At this point we rely on the NSScreen.screens to be up to date.
+*/
+void QCocoaScreen::updateScreens()
+{
+ uint32_t displayCount = 0;
+ if (CGGetOnlineDisplayList(0, nullptr, &displayCount) != kCGErrorSuccess)
+ qFatal("Failed to get number of online displays");
+
+ QVector<CGDirectDisplayID> onlineDisplays(displayCount);
+ if (CGGetOnlineDisplayList(displayCount, onlineDisplays.data(), &displayCount) != kCGErrorSuccess)
+ qFatal("Failed to get online displays");
+
+ qCInfo(lcQpaScreen) << "Updating screens with" << displayCount
+ << "online displays:" << onlineDisplays;
+
+ // TODO: Verify whether we can always assume the main display is first
+ int mainDisplayIndex = onlineDisplays.indexOf(CGMainDisplayID());
+ if (mainDisplayIndex < 0) {
+ qCWarning(lcQpaScreen) << "Main display not in list of online displays!";
+ } else if (mainDisplayIndex > 0) {
+ qCWarning(lcQpaScreen) << "Main display not first display, making sure it is";
+ onlineDisplays.move(mainDisplayIndex, 0);
+ }
+
+ for (CGDirectDisplayID displayId : onlineDisplays) {
+ Q_ASSERT(CGDisplayIsOnline(displayId));
+
+ if (CGDisplayMirrorsDisplay(displayId))
+ continue;
+
+ // A single physical screen can map to multiple displays IDs,
+ // depending on which GPU is in use or which physical port the
+ // screen is connected to. By mapping the display ID to a UUID,
+ // which are shared between displays that target the same screen,
+ // we can pick an existing QScreen to update instead of needlessly
+ // adding and removing QScreens.
+ QCFType<CFUUIDRef> uuid = CGDisplayCreateUUIDFromDisplayID(displayId);
+ Q_ASSERT(uuid);
+
+ if (QCocoaScreen *existingScreen = QCocoaScreen::get(uuid)) {
+ existingScreen->update(displayId);
+ qCInfo(lcQpaScreen) << "Updated" << existingScreen;
+ if (CGDisplayIsMain(displayId) && existingScreen != qGuiApp->primaryScreen()->handle()) {
+ qCInfo(lcQpaScreen) << "Primary screen changed to" << existingScreen;
+ QWindowSystemInterface::handlePrimaryScreenChanged(existingScreen);
+ }
+ } else {
+ QCocoaScreen::add(displayId);
+ }
+ }
+
+ for (QScreen *screen : QGuiApplication::screens()) {
+ QCocoaScreen *platformScreen = static_cast<QCocoaScreen*>(screen->handle());
+ if (!platformScreen->isOnline() || platformScreen->isMirroring())
+ platformScreen->remove();
+ }
}
void QCocoaScreen::add(CGDirectDisplayID displayId)
{
const bool isPrimary = CGDisplayIsMain(displayId);
QCocoaScreen *cocoaScreen = new QCocoaScreen(displayId);
- qCInfo(lcQpaScreen).nospace() << "Adding " << (isPrimary ? "new primary " : "") << cocoaScreen;
+ qCInfo(lcQpaScreen) << "Adding" << cocoaScreen
+ << (isPrimary ? "as new primary screen" : "");
QWindowSystemInterface::handleScreenAdded(cocoaScreen, isPrimary);
}
QCocoaScreen::QCocoaScreen(CGDirectDisplayID displayId)
: QPlatformScreen(), m_displayId(displayId)
{
- updateProperties();
+ update(m_displayId);
m_cursor = new QCocoaCursor;
}
@@ -150,8 +243,6 @@ void QCocoaScreen::cleanupScreens()
void QCocoaScreen::remove()
{
- m_displayId = kCGNullDirectDisplay; // 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
@@ -163,7 +254,7 @@ void QCocoaScreen::remove()
// 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.
- qCInfo(lcQpaScreen).nospace() << "Removing " << (primaryScreen() == this ? "current primary " : "") << this;
+ qCInfo(lcQpaScreen) << "Removing " << this;
QWindowSystemInterface::handleScreenRemoved(this);
}
@@ -210,9 +301,14 @@ static QString displayName(CGDirectDisplayID displayID)
return QString();
}
-void QCocoaScreen::updateProperties()
+void QCocoaScreen::update(CGDirectDisplayID displayId)
{
- Q_ASSERT(m_displayId);
+ if (displayId != m_displayId) {
+ qCDebug(lcQpaScreen) << "Reconnecting" << this << "as display" << displayId;
+ m_displayId = displayId;
+ }
+
+ Q_ASSERT(isOnline());
const QRect previousGeometry = m_geometry;
const QRect previousAvailableGeometry = m_availableGeometry;
@@ -350,8 +446,8 @@ struct DeferredDebugHelper
void QCocoaScreen::deliverUpdateRequests()
{
- if (!m_displayId)
- return; // Screen removed
+ if (!isOnline())
+ return;
QMacAutoReleasePool pool;
@@ -562,6 +658,29 @@ QPixmap QCocoaScreen::grabWindow(WId view, int x, int y, int width, int height)
return windowPixmap;
}
+bool QCocoaScreen::isOnline() const
+{
+ // When a display is disconnected CGDisplayIsOnline and other CGDisplay
+ // functions that take a displayId will not return false, but will start
+ // returning -1 to signal that the displayId is invalid. Some functions
+ // will also assert or even crash in this case, so it's important that
+ // we double check if a display is online before calling other functions.
+ auto isOnline = CGDisplayIsOnline(m_displayId);
+ static const uint32_t kCGDisplayIsDisconnected = int32_t(-1);
+ return isOnline != kCGDisplayIsDisconnected && isOnline;
+}
+
+/*
+ Returns true if a screen is mirroring another screen
+*/
+bool QCocoaScreen::isMirroring() const
+{
+ if (!isOnline())
+ return false;
+
+ return CGDisplayMirrorsDisplay(m_displayId);
+}
+
/*!
The screen used as a reference for global window geometry
*/
@@ -586,6 +705,12 @@ QList<QPlatformScreen*> QCocoaScreen::virtualSiblings() const
QCocoaScreen *QCocoaScreen::get(NSScreen *nsScreen)
{
+ if (s_screenConfigurationBeforeUpdate) {
+ qCWarning(lcQpaScreen) << "Trying to resolve screen while waiting for screen reconfigure!";
+ if (!updateScreensIfNeeded())
+ qCWarning(lcQpaScreen) << "Failed to do last minute screen update. Expect crashes.";
+ }
+
return get(nsScreen.qt_displayId);
}
@@ -600,23 +725,34 @@ QCocoaScreen *QCocoaScreen::get(CGDirectDisplayID displayId)
return nullptr;
}
+QCocoaScreen *QCocoaScreen::get(CFUUIDRef uuid)
+{
+ for (QScreen *screen : QGuiApplication::screens()) {
+ auto *platformScreen = static_cast<QCocoaScreen*>(screen->handle());
+ if (!platformScreen->isOnline())
+ continue;
+
+ auto displayId = platformScreen->displayId();
+ QCFType<CFUUIDRef> candidateUuid(CGDisplayCreateUUIDFromDisplayID(displayId));
+ Q_ASSERT(candidateUuid);
+
+ if (candidateUuid == uuid)
+ return platformScreen;
+ }
+
+ 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 (QCFType<CFUUIDRef>(CGDisplayCreateUUIDFromDisplayID(screen.qt_displayId)) == uuid)
+ for (NSScreen *screen in NSScreen.screens) {
+ if (screen.qt_displayId == m_displayId)
return screen;
}
- qCWarning(lcQpaScreen) << "Could not find NSScreen for display ID" << m_displayId;
return nil;
}
@@ -651,11 +787,21 @@ QDebug operator<<(QDebug debug, const QCocoaScreen *screen)
debug.nospace();
debug << "QCocoaScreen(" << (const void *)screen;
if (screen) {
- debug << ", geometry=" << screen->geometry();
+ debug << ", " << screen->name();
+ if (screen->isOnline()) {
+ if (CGDisplayIsAsleep(screen->displayId()))
+ debug << ", Sleeping";
+ if (auto mirroring = CGDisplayMirrorsDisplay(screen->displayId()))
+ debug << ", mirroring=" << mirroring;
+ } else {
+ debug << ", Offline";
+ }
+ debug << ", " << screen->geometry();
debug << ", dpr=" << screen->devicePixelRatio();
- debug << ", name=" << screen->name();
- debug << ", displayId=" << screen->m_displayId;
- debug << ", native=" << screen->nativeScreen();
+ debug << ", displayId=" << screen->displayId();
+
+ if (auto nativeScreen = screen->nativeScreen())
+ debug << ", " << nativeScreen;
}
debug << ')';
return debug;