mirror of
https://github.com/crystalidea/qt-build-tools.git
synced 2024-11-22 19:00:04 +08:00
5.13.2: qcocoascreen patched from QTBUG-80193
This commit is contained in:
parent
ad520af861
commit
3085d86f9b
@ -53,9 +53,6 @@ class QCocoaIntegration;
|
|||||||
class QCocoaScreen : public QPlatformScreen
|
class QCocoaScreen : public QPlatformScreen
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
static void initializeScreens();
|
|
||||||
static void cleanupScreens();
|
|
||||||
|
|
||||||
~QCocoaScreen();
|
~QCocoaScreen();
|
||||||
|
|
||||||
// ----------------------------------------------------
|
// ----------------------------------------------------
|
||||||
@ -68,6 +65,7 @@ public:
|
|||||||
qreal devicePixelRatio() const override { return m_devicePixelRatio; }
|
qreal devicePixelRatio() const override { return m_devicePixelRatio; }
|
||||||
QSizeF physicalSize() const override { return m_physicalSize; }
|
QSizeF physicalSize() const override { return m_physicalSize; }
|
||||||
QDpi logicalDpi() const override { return m_logicalDpi; }
|
QDpi logicalDpi() const override { return m_logicalDpi; }
|
||||||
|
QDpi logicalBaseDpi() const override { return m_logicalDpi; }
|
||||||
qreal refreshRate() const override { return m_refreshRate; }
|
qreal refreshRate() const override { return m_refreshRate; }
|
||||||
QString name() const override { return m_name; }
|
QString name() const override { return m_name; }
|
||||||
QPlatformCursor *cursor() const override { return m_cursor; }
|
QPlatformCursor *cursor() const override { return m_cursor; }
|
||||||
@ -78,7 +76,6 @@ public:
|
|||||||
// ----------------------------------------------------
|
// ----------------------------------------------------
|
||||||
|
|
||||||
NSScreen *nativeScreen() const;
|
NSScreen *nativeScreen() const;
|
||||||
void updateProperties();
|
|
||||||
|
|
||||||
void requestUpdate();
|
void requestUpdate();
|
||||||
void deliverUpdateRequests();
|
void deliverUpdateRequests();
|
||||||
@ -87,6 +84,7 @@ public:
|
|||||||
static QCocoaScreen *primaryScreen();
|
static QCocoaScreen *primaryScreen();
|
||||||
static QCocoaScreen *get(NSScreen *nsScreen);
|
static QCocoaScreen *get(NSScreen *nsScreen);
|
||||||
static QCocoaScreen *get(CGDirectDisplayID displayId);
|
static QCocoaScreen *get(CGDirectDisplayID displayId);
|
||||||
|
static QCocoaScreen *get(CFUUIDRef uuid);
|
||||||
|
|
||||||
static CGPoint mapToNative(const QPointF &pos, QCocoaScreen *screen = QCocoaScreen::primaryScreen());
|
static CGPoint mapToNative(const QPointF &pos, QCocoaScreen *screen = QCocoaScreen::primaryScreen());
|
||||||
static CGRect mapToNative(const QRectF &rect, QCocoaScreen *screen = QCocoaScreen::primaryScreen());
|
static CGRect mapToNative(const QRectF &rect, QCocoaScreen *screen = QCocoaScreen::primaryScreen());
|
||||||
@ -94,27 +92,41 @@ public:
|
|||||||
static QRectF mapFromNative(CGRect rect, QCocoaScreen *screen = QCocoaScreen::primaryScreen());
|
static QRectF mapFromNative(CGRect rect, QCocoaScreen *screen = QCocoaScreen::primaryScreen());
|
||||||
|
|
||||||
private:
|
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);
|
static void add(CGDirectDisplayID displayId);
|
||||||
|
QCocoaScreen(CGDirectDisplayID displayId);
|
||||||
|
void update(CGDirectDisplayID displayId);
|
||||||
void remove();
|
void remove();
|
||||||
|
|
||||||
CGDirectDisplayID m_displayId = 0;
|
bool isOnline() const;
|
||||||
|
bool isMirroring() const;
|
||||||
|
|
||||||
|
CGDirectDisplayID m_displayId = kCGNullDirectDisplay;
|
||||||
|
CGDirectDisplayID displayId() const { return m_displayId; }
|
||||||
|
|
||||||
QRect m_geometry;
|
QRect m_geometry;
|
||||||
QRect m_availableGeometry;
|
QRect m_availableGeometry;
|
||||||
QDpi m_logicalDpi;
|
QDpi m_logicalDpi;
|
||||||
qreal m_refreshRate;
|
qreal m_refreshRate = 0;
|
||||||
int m_depth;
|
int m_depth = 0;
|
||||||
QString m_name;
|
QString m_name;
|
||||||
QImage::Format m_format;
|
QImage::Format m_format;
|
||||||
QSizeF m_physicalSize;
|
QSizeF m_physicalSize;
|
||||||
QCocoaCursor *m_cursor;
|
QCocoaCursor *m_cursor;
|
||||||
qreal m_devicePixelRatio;
|
qreal m_devicePixelRatio = 0;
|
||||||
|
|
||||||
CVDisplayLinkRef m_displayLink = nullptr;
|
CVDisplayLinkRef m_displayLink = nullptr;
|
||||||
dispatch_source_t m_displayLinkSource = nullptr;
|
dispatch_source_t m_displayLinkSource = nullptr;
|
||||||
QAtomicInt m_pendingUpdates;
|
QAtomicInt m_pendingUpdates;
|
||||||
|
|
||||||
|
friend class QCocoaIntegration;
|
||||||
|
friend class QCocoaWindow;
|
||||||
friend QDebug operator<<(QDebug debug, const QCocoaScreen *screen);
|
friend QDebug operator<<(QDebug debug, const QCocoaScreen *screen);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -54,67 +54,185 @@
|
|||||||
|
|
||||||
QT_BEGIN_NAMESPACE
|
QT_BEGIN_NAMESPACE
|
||||||
|
|
||||||
|
namespace CoreGraphics {
|
||||||
|
Q_NAMESPACE
|
||||||
|
enum DisplayChange {
|
||||||
|
ReconfiguredWithFlagsMissing = 0,
|
||||||
|
Moved = kCGDisplayMovedFlag,
|
||||||
|
SetMain = kCGDisplaySetMainFlag,
|
||||||
|
SetMode = kCGDisplaySetModeFlag,
|
||||||
|
Added = kCGDisplayAddFlag,
|
||||||
|
Removed = kCGDisplayRemoveFlag,
|
||||||
|
Enabled = kCGDisplayEnabledFlag,
|
||||||
|
Disabled = kCGDisplayDisabledFlag,
|
||||||
|
Mirrored = kCGDisplayMirrorFlag,
|
||||||
|
UnMirrored = kCGDisplayUnMirrorFlag,
|
||||||
|
DesktopShapeChanged = kCGDisplayDesktopShapeChangedFlag
|
||||||
|
};
|
||||||
|
Q_ENUM_NS(DisplayChange)
|
||||||
|
}
|
||||||
|
|
||||||
|
NSArray *QCocoaScreen::s_screenConfigurationBeforeUpdate = nil;
|
||||||
|
|
||||||
void QCocoaScreen::initializeScreens()
|
void QCocoaScreen::initializeScreens()
|
||||||
{
|
{
|
||||||
uint32_t displayCount = 0;
|
updateScreens();
|
||||||
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) {
|
CGDisplayRegisterReconfigurationCallback([](CGDirectDisplayID displayId, CGDisplayChangeSummaryFlags flags, void *userInfo) {
|
||||||
if (flags & kCGDisplayBeginConfigurationFlag)
|
|
||||||
return; // Wait for changes to apply
|
|
||||||
|
|
||||||
Q_UNUSED(userInfo);
|
Q_UNUSED(userInfo);
|
||||||
|
|
||||||
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;
|
||||||
|
|
||||||
if ((flags & kCGDisplayAddFlag) || !cocoaScreen) {
|
const bool beforeReconfigure = flags & kCGDisplayBeginConfigurationFlag;
|
||||||
if (!CGDisplayIsActive(displayId)) {
|
qCDebug(lcQpaScreen).verbosity(0).nospace() << "Display " << displayId
|
||||||
qCDebug(lcQpaScreen) << "Not adding inactive display" << displayId;
|
<< (beforeReconfigure ? " about to reconfigure" : " was ")
|
||||||
return; // Will be added when activated
|
<< 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 (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 {
|
} else {
|
||||||
// Detect changes to the primary screen immediately, instead of
|
Q_ASSERT_X(displayReconfigurationsInProgress, "QCococaScreen",
|
||||||
// waiting for a display reconfigure with kCGDisplaySetMainFlag.
|
"Display configuration transactions are expected to be balanced");
|
||||||
// This ensures that any property updates to the other screens
|
|
||||||
// will be in reference to the correct primary screen.
|
if (!--displayReconfigurationsInProgress) {
|
||||||
QCocoaScreen *mainDisplay = QCocoaScreen::get(CGMainDisplayID());
|
qCDebug(lcQpaScreen) << "Display reconfigure transaction completed";
|
||||||
if (QGuiApplication::primaryScreen()->handle() != mainDisplay) {
|
// We optimistically update now, in case the NSScreens have changed
|
||||||
mainDisplay->updateProperties();
|
updateScreensIfNeeded();
|
||||||
qCInfo(lcQpaScreen) << "Primary screen changed to" << mainDisplay;
|
|
||||||
QWindowSystemInterface::handlePrimaryScreenChanged(mainDisplay);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (cocoaScreen == mainDisplay)
|
|
||||||
return; // Already reconfigured
|
|
||||||
|
|
||||||
cocoaScreen->updateProperties();
|
|
||||||
qCInfo(lcQpaScreen) << "Reconfigured" << cocoaScreen;
|
|
||||||
}
|
}
|
||||||
}, nullptr);
|
}, nullptr);
|
||||||
|
|
||||||
|
static QMacNotificationObserver screenParamaterObserver(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 (CGDirectDisplayID mirroring = 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->remove();
|
||||||
|
else if (platformScreen->isMirroring())
|
||||||
|
platformScreen->remove();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void QCocoaScreen::add(CGDirectDisplayID displayId)
|
void QCocoaScreen::add(CGDirectDisplayID displayId)
|
||||||
{
|
{
|
||||||
|
const bool isPrimary = CGDisplayIsMain(displayId);
|
||||||
QCocoaScreen *cocoaScreen = new QCocoaScreen(displayId);
|
QCocoaScreen *cocoaScreen = new QCocoaScreen(displayId);
|
||||||
qCInfo(lcQpaScreen) << "Adding" << cocoaScreen;
|
qCInfo(lcQpaScreen) << "Adding" << cocoaScreen
|
||||||
QWindowSystemInterface::handleScreenAdded(cocoaScreen, CGDisplayIsMain(displayId));
|
<< (isPrimary ? "as new primary screen" : "");
|
||||||
|
QWindowSystemInterface::handleScreenAdded(cocoaScreen, isPrimary);
|
||||||
}
|
}
|
||||||
|
|
||||||
QCocoaScreen::QCocoaScreen(CGDirectDisplayID displayId)
|
QCocoaScreen::QCocoaScreen(CGDirectDisplayID displayId)
|
||||||
: QPlatformScreen(), m_displayId(displayId)
|
: QPlatformScreen(), m_displayId(displayId)
|
||||||
{
|
{
|
||||||
updateProperties();
|
update(m_displayId);
|
||||||
m_cursor = new QCocoaCursor;
|
m_cursor = new QCocoaCursor;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -127,8 +245,6 @@ void QCocoaScreen::cleanupScreens()
|
|||||||
|
|
||||||
void QCocoaScreen::remove()
|
void QCocoaScreen::remove()
|
||||||
{
|
{
|
||||||
m_displayId = 0; // Prevent stale references during removal
|
|
||||||
|
|
||||||
// This may result in the application responding to QGuiApplication::screenRemoved
|
// This may result in the application responding to QGuiApplication::screenRemoved
|
||||||
// by moving the window to another screen, either by setGeometry, or by setScreen.
|
// 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
|
// If the window isn't moved by the application, Qt will as a fallback move it to
|
||||||
@ -140,6 +256,7 @@ void QCocoaScreen::remove()
|
|||||||
// QCocoaWindow::windowDidChangeScreen. At that point the window will appear to have
|
// 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,
|
// already changed its screen, but that's only true if comparing the Qt screens,
|
||||||
// not when comparing the NSScreens.
|
// not when comparing the NSScreens.
|
||||||
|
qCInfo(lcQpaScreen) << "Removing " << this;
|
||||||
QWindowSystemInterface::handleScreenRemoved(this);
|
QWindowSystemInterface::handleScreenRemoved(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -186,9 +303,14 @@ static QString displayName(CGDirectDisplayID displayID)
|
|||||||
return QString();
|
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 previousGeometry = m_geometry;
|
||||||
const QRect previousAvailableGeometry = m_availableGeometry;
|
const QRect previousAvailableGeometry = m_availableGeometry;
|
||||||
@ -326,8 +448,8 @@ struct DeferredDebugHelper
|
|||||||
|
|
||||||
void QCocoaScreen::deliverUpdateRequests()
|
void QCocoaScreen::deliverUpdateRequests()
|
||||||
{
|
{
|
||||||
if (!m_displayId)
|
if (!isOnline())
|
||||||
return; // Screen removed
|
return;
|
||||||
|
|
||||||
QMacAutoReleasePool pool;
|
QMacAutoReleasePool pool;
|
||||||
|
|
||||||
@ -351,15 +473,6 @@ void QCocoaScreen::deliverUpdateRequests()
|
|||||||
// it on the main thread yet, because the processing of the update request is taking
|
// it on the main thread yet, because the processing of the update request is taking
|
||||||
// too long, or because the update request was deferred due to window live resizing.
|
// too long, or because the update request was deferred due to window live resizing.
|
||||||
qDeferredDebug(screenUpdates) << ", " << framesAheadOfDelivery << " frame(s) ahead";
|
qDeferredDebug(screenUpdates) << ", " << framesAheadOfDelivery << " frame(s) ahead";
|
||||||
|
|
||||||
// We skip the frame completely if we're live-resizing, to not put any extra
|
|
||||||
// strain on the main thread runloop. Otherwise we assume we should push frames
|
|
||||||
// as fast as possible, and hopefully the callback will be delivered on the
|
|
||||||
// main thread just when the previous finished.
|
|
||||||
if (qt_apple_sharedApplication().keyWindow.inLiveResize) {
|
|
||||||
qDeferredDebug(screenUpdates) << "; waiting for main thread to catch up";
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
qDeferredDebug(screenUpdates) << "; signaling dispatch source";
|
qDeferredDebug(screenUpdates) << "; signaling dispatch source";
|
||||||
@ -547,15 +660,38 @@ QPixmap QCocoaScreen::grabWindow(WId view, int x, int y, int width, int height)
|
|||||||
return windowPixmap;
|
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
|
The screen used as a reference for global window geometry
|
||||||
*/
|
*/
|
||||||
QCocoaScreen *QCocoaScreen::primaryScreen()
|
QCocoaScreen *QCocoaScreen::primaryScreen()
|
||||||
{
|
{
|
||||||
auto screen = static_cast<QCocoaScreen *>(QGuiApplication::primaryScreen()->handle());
|
// Note: The primary screen that Qt knows about may not match the current CGMainDisplayID()
|
||||||
Q_ASSERT_X(screen == get(CGMainDisplayID()), "QCocoaScreen",
|
// if macOS has not yet been able to inform us that the main display has changed, but we
|
||||||
"The application's primary screen should always be in sync with the main display");
|
// will update the primary screen accordingly once the reconfiguration callback comes in.
|
||||||
return screen;
|
return static_cast<QCocoaScreen *>(QGuiApplication::primaryScreen()->handle());
|
||||||
}
|
}
|
||||||
|
|
||||||
QList<QPlatformScreen*> QCocoaScreen::virtualSiblings() const
|
QList<QPlatformScreen*> QCocoaScreen::virtualSiblings() const
|
||||||
@ -571,6 +707,12 @@ QList<QPlatformScreen*> QCocoaScreen::virtualSiblings() const
|
|||||||
|
|
||||||
QCocoaScreen *QCocoaScreen::get(NSScreen *nsScreen)
|
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);
|
return get(nsScreen.qt_displayId);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -585,23 +727,34 @@ QCocoaScreen *QCocoaScreen::get(CGDirectDisplayID displayId)
|
|||||||
return nullptr;
|
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
|
NSScreen *QCocoaScreen::nativeScreen() const
|
||||||
{
|
{
|
||||||
if (!m_displayId)
|
if (!m_displayId)
|
||||||
return nil; // The display has been disconnected
|
return nil; // The display has been disconnected
|
||||||
|
|
||||||
// A single display may have different displayIds depending on
|
for (NSScreen *screen in NSScreen.screens) {
|
||||||
// which GPU is in use or which physical port the display is
|
if (screen.qt_displayId == m_displayId)
|
||||||
// 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;
|
return screen;
|
||||||
}
|
}
|
||||||
|
|
||||||
qCWarning(lcQpaScreen) << "Could not find NSScreen for display ID" << m_displayId;
|
|
||||||
return nil;
|
return nil;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -636,16 +789,29 @@ QDebug operator<<(QDebug debug, const QCocoaScreen *screen)
|
|||||||
debug.nospace();
|
debug.nospace();
|
||||||
debug << "QCocoaScreen(" << (const void *)screen;
|
debug << "QCocoaScreen(" << (const void *)screen;
|
||||||
if (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 << ", dpr=" << screen->devicePixelRatio();
|
||||||
debug << ", name=" << screen->name();
|
debug << ", displayId=" << screen->displayId();
|
||||||
debug << ", native=" << screen->nativeScreen();
|
|
||||||
|
if (auto nativeScreen = screen->nativeScreen())
|
||||||
|
debug << ", " << nativeScreen;
|
||||||
}
|
}
|
||||||
debug << ')';
|
debug << ')';
|
||||||
return debug;
|
return debug;
|
||||||
}
|
}
|
||||||
#endif // !QT_NO_DEBUG_STREAM
|
#endif // !QT_NO_DEBUG_STREAM
|
||||||
|
|
||||||
|
#include "qcocoascreen.moc"
|
||||||
|
|
||||||
QT_END_NAMESPACE
|
QT_END_NAMESPACE
|
||||||
|
|
||||||
@implementation NSScreen (QtExtras)
|
@implementation NSScreen (QtExtras)
|
||||||
|
Loading…
Reference in New Issue
Block a user