12#include <QtCore/qcoreapplication.h>
13#include <QtGui/private/qcoregraphics_p.h>
15#include <IOKit/graphics/IOGraphicsLib.h>
17#include <QtGui/private/qwindow_p.h>
19#include <QtCore/private/qcore_mac_p.h>
20#include <QtCore/private/qeventdispatcher_cf_p.h>
43CGDisplayReconfigurationCallBack QCocoaScreen::s_displayReconfigurationCallBack =
nullptr;
45void QCocoaScreen::initializeScreens()
49 s_displayReconfigurationCallBack = [](CGDirectDisplayID displayId, CGDisplayChangeSummaryFlags
flags,
void *userInfo) {
52 const bool beforeReconfigure =
flags & kCGDisplayBeginConfigurationFlag;
53 qCDebug(lcQpaScreen).verbosity(0) <<
"Display" << displayId
54 << (beforeReconfigure ?
"beginning" :
"finished") <<
"reconfigure"
57 if (!beforeReconfigure)
60 CGDisplayRegisterReconfigurationCallback(s_displayReconfigurationCallBack,
nullptr);
63 NSApplicationDidChangeScreenParametersNotification, [&]() {
64 qCDebug(lcQpaScreen) <<
"Received screen parameter change notification";
74void QCocoaScreen::updateScreens()
81 static bool updatingScreens =
false;
82 if (updatingScreens) {
83 qCInfo(lcQpaScreen) <<
"Skipping screen update, already updating";
88 uint32_t displayCount = 0;
89 if (CGGetOnlineDisplayList(0,
nullptr, &displayCount) != kCGErrorSuccess)
90 qFatal(
"Failed to get number of online displays");
93 if (CGGetOnlineDisplayList(displayCount, onlineDisplays.data(), &displayCount) != kCGErrorSuccess)
94 qFatal(
"Failed to get online displays");
96 qCInfo(lcQpaScreen) <<
"Updating screens with" << displayCount
97 <<
"online displays:" << onlineDisplays;
100 int mainDisplayIndex = onlineDisplays.indexOf(CGMainDisplayID());
101 if (mainDisplayIndex < 0) {
102 qCWarning(lcQpaScreen) <<
"Main display not in list of online displays!";
103 }
else if (mainDisplayIndex > 0) {
104 qCWarning(lcQpaScreen) <<
"Main display not first display, making sure it is";
105 onlineDisplays.move(mainDisplayIndex, 0);
108 for (CGDirectDisplayID displayId : onlineDisplays) {
109 Q_ASSERT(CGDisplayIsOnline(displayId));
111 if (CGDisplayMirrorsDisplay(displayId))
124 existingScreen->update(displayId);
125 qCInfo(lcQpaScreen) <<
"Updated" << existingScreen;
126 if (CGDisplayIsMain(displayId) && existingScreen !=
qGuiApp->primaryScreen()->handle()) {
127 qCInfo(lcQpaScreen) <<
"Primary screen changed to" << existingScreen;
131 QCocoaScreen::add(displayId);
137 if (!platformScreen->isOnline() || platformScreen->isMirroring())
138 platformScreen->remove();
142void QCocoaScreen::add(CGDirectDisplayID displayId)
144 const bool isPrimary = CGDisplayIsMain(displayId);
146 qCInfo(lcQpaScreen) <<
"Adding" << cocoaScreen
147 << (isPrimary ?
"as new primary screen" :
"");
151QCocoaScreen::QCocoaScreen(CGDirectDisplayID displayId)
158void QCocoaScreen::cleanupScreens()
164 Q_ASSERT(s_displayReconfigurationCallBack);
165 CGDisplayRemoveReconfigurationCallback(s_displayReconfigurationCallBack,
nullptr);
166 s_displayReconfigurationCallBack =
nullptr;
168 s_screenParameterObserver.
remove();
171void QCocoaScreen::remove()
184 qCInfo(lcQpaScreen) <<
"Removing " <<
this;
194 CVDisplayLinkRelease(m_displayLink);
195 if (m_displayLinkSource)
196 dispatch_release(m_displayLinkSource);
201 QIOType<io_iterator_t> iterator;
202 if (IOServiceGetMatchingServices(kIOMasterPortDefault,
203 IOServiceMatching(
"IODisplayConnect"), &iterator))
207 while ((
display = IOIteratorNext(iterator)) != 0)
209 NSDictionary *
info = [(__bridge NSDictionary*)IODisplayCreateInfoDictionary(
210 display, kIODisplayOnlyPreferredName) autorelease];
212 if ([[
info objectForKey:@kDisplayVendorID] unsignedIntValue] != CGDisplayVendorNumber(displayID))
215 if ([[
info objectForKey:@kDisplayProductID] unsignedIntValue] != CGDisplayModelNumber(displayID))
218 if ([[
info objectForKey:@kDisplaySerialNumber] unsignedIntValue] != CGDisplaySerialNumber(displayID))
221 NSDictionary *localizedNames = [
info objectForKey:@kDisplayProductName];
222 if (![localizedNames
count])
225 return QString::fromNSString([localizedNames objectForKey:[[localizedNames
allKeys] objectAtIndex:0]]);
231void QCocoaScreen::update(CGDirectDisplayID displayId)
233 if (displayId != m_displayId) {
234 qCDebug(lcQpaScreen) <<
"Reconnecting" <<
this <<
"as display" << displayId;
235 m_displayId = displayId;
243 qCDebug(lcQpaScreen) <<
"Corresponding NSScreen not yet available. Deferring update";
247 const QRect previousGeometry = m_geometry;
248 const QRect previousAvailableGeometry = m_availableGeometry;
249 const qreal previousRefreshRate = m_refreshRate;
252 QRectF primaryScreenGeometry = QRectF::fromCGRect(CGDisplayBounds(CGMainDisplayID()));
253 m_geometry =
qt_mac_flip(QRectF::fromCGRect(nsScreen.frame), primaryScreenGeometry).toRect();
254 m_availableGeometry =
qt_mac_flip(QRectF::fromCGRect(nsScreen.visibleFrame), primaryScreenGeometry).toRect();
256 m_devicePixelRatio = nsScreen.backingScaleFactor;
259 m_depth = NSBitsPerPixelFromDepth(nsScreen.depth);
262 qCWarning(lcQpaScreen) <<
"Failed to parse ICC profile for" << nsScreen.colorSpace
263 <<
"with ICC data" << nsScreen.colorSpace.ICCProfileData
264 <<
"- Falling back to sRGB";
268 CGSize
size = CGDisplayScreenSize(m_displayId);
272 float refresh = CGDisplayModeGetRefreshRate(displayMode);
273 m_refreshRate = refresh > 0 ? refresh : 60.0;
275 if (@available(macOS 10.15, *))
276 m_name = QString::fromNSString(nsScreen.localizedName);
280 const bool didChangeGeometry = m_geometry != previousGeometry || m_availableGeometry != previousAvailableGeometry;
282 if (didChangeGeometry)
284 if (m_refreshRate != previousRefreshRate)
297 qCDebug(lcQpaScreenUpdates) <<
this <<
"is not online. Ignoring update request";
301 if (!m_displayLink) {
302 qCDebug(lcQpaScreenUpdates) <<
"Creating display link for" <<
this;
303 if (CVDisplayLinkCreateWithCGDisplay(m_displayId, &m_displayLink) != kCVReturnSuccess) {
304 qCWarning(lcQpaScreenUpdates) <<
"Failed to create display link for" <<
this;
307 if (
auto displayId = CVDisplayLinkGetCurrentCGDisplay(m_displayLink); displayId != m_displayId) {
308 qCWarning(lcQpaScreenUpdates) <<
"Unexpected display" << displayId <<
"for display link";
309 CVDisplayLinkRelease(m_displayLink);
310 m_displayLink =
nullptr;
313 CVDisplayLinkSetOutputCallback(m_displayLink, [](CVDisplayLinkRef,
const CVTimeStamp*,
314 const CVTimeStamp*, CVOptionFlags, CVOptionFlags*,
void* displayLinkContext) ->
int {
317 return kCVReturnSuccess;
339 static CFMachPortRef eventTap = []() {
340 CFMachPortRef eventTap = CGEventTapCreateForPid(getpid(), kCGTailAppendEventTap,
341 kCGEventTapOptionListenOnly, NSEventMaskLeftMouseDragged,
342 [](CGEventTapProxy, CGEventType
type, CGEventRef
event,
void *) -> CGEventRef {
343 if (
type == kCGEventTapDisabledByTimeout)
344 qCWarning(lcQpaScreenUpdates) <<
"Event tap disabled due to timeout!";
347 CGEventTapEnable(eventTap,
false);
348 static CFRunLoopSourceRef runLoopSource = CFMachPortCreateRunLoopSource(kCFAllocatorDefault, eventTap, 0);
349 CFRunLoopAddSource(CFRunLoopGetCurrent(), runLoopSource, kCFRunLoopCommonModes);
351 NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
352 [center addObserverForName:NSWindowWillStartLiveResizeNotification
object:nil
queue:nil
353 usingBlock:^(NSNotification *notification) {
354 qCDebug(lcQpaScreenUpdates) <<
"Live resize of" << notification.object
355 <<
"started. Enabling event tap";
356 CGEventTapEnable(eventTap,
true);
358 [center addObserverForName:NSWindowDidEndLiveResizeNotification
object:nil
queue:nil
359 usingBlock:^(NSNotification *notification) {
360 qCDebug(lcQpaScreenUpdates) <<
"Live resize of" << notification.object
361 <<
"ended. Disabling event tap";
362 CGEventTapEnable(eventTap,
false);
369 if (!CVDisplayLinkIsRunning(m_displayLink)) {
370 qCDebug(lcQpaScreenUpdates) <<
"Starting display link for" <<
this;
371 CVDisplayLinkStart(m_displayLink);
396#define qDeferredDebug(helper) if (Q_UNLIKELY(helper.debug)) *helper.debug
410 if (!NSThread.isMainThread) {
414 const int pendingUpdates = ++m_pendingUpdates;
417 qDeferredDebug(screenUpdates) <<
"display link callback for screen " << m_displayId;
419 if (
const int framesAheadOfDelivery = pendingUpdates - 1) {
424 qDeferredDebug(screenUpdates) <<
", " << framesAheadOfDelivery <<
" frame(s) ahead";
429 if (!m_displayLinkSource) {
430 m_displayLinkSource = dispatch_source_create(DISPATCH_SOURCE_TYPE_DATA_ADD, 0, 0, dispatch_get_main_queue());
431 dispatch_source_set_event_handler(m_displayLinkSource, ^{
434 dispatch_resume(m_displayLinkSource);
437 dispatch_source_merge_data(m_displayLinkSource, 1);
441 qDeferredDebug(screenUpdates) <<
"gcd event handler on main thread";
443 const int pendingUpdates = m_pendingUpdates;
444 if (pendingUpdates > 1)
445 qDeferredDebug(screenUpdates) <<
", " << (pendingUpdates - 1) <<
" frame(s) behind display link";
449 bool pauseUpdates =
true;
458 if (!platformWindow->hasPendingUpdateRequest())
465 if (!platformWindow->updatesWithDisplayLink())
469 if (platformWindow->isContentView() && platformWindow->view().inLiveResize) {
472 const bool usesNonDefaultContentsPlacement = [platformWindow->view() layerContentsPlacement]
473 != NSViewLayerContentsPlacementScaleAxesIndependently;
474 if (usesMetalLayer && usesNonDefaultContentsPlacement) {
475 static bool deliverDisplayLinkUpdatesDuringLiveResize =
477 if (!deliverDisplayLinkUpdatesDuringLiveResize) {
481 pauseUpdates =
false;
487 platformWindow->deliverUpdateRequest();
490 if (platformWindow->hasPendingUpdateRequest())
491 pauseUpdates =
false;
496 qCDebug(lcQpaScreenUpdates) <<
"Stopping display link for" <<
this;
497 CVDisplayLinkStop(m_displayLink);
501 qCWarning(lcQpaScreenUpdates) <<
"main thread missed" << missedUpdates
502 <<
"update(s) from display link during update request delivery";
509 return m_displayLink && CVDisplayLinkIsRunning(m_displayLink);
535 topWindowNumber = [NSWindow windowNumberAtPoint:screenPoint
536 belowWindowWithWindowNumber:topWindowNumber];
539 NSWindow *nsWindow = [NSApp windowWithWindowNumber:topWindowNumber];
544 if (![nsWindow conformsToProtocol:
@protocol(QNSWindowProtocol)])
553 if (!
window->isTopLevel())
558 }
while (topWindowNumber > 0);
579 auto grabFromDisplay = [](CGDirectDisplayID displayId,
const QRect &grabRect) ->
QPixmap {
582 if (CGImageGetColorSpace(
image) != sRGBcolorSpace) {
583 qCDebug(lcQpaScreen) <<
"applying color correction for display" << displayId;
584 image = CGImageCreateCopyWithColorSpace(
image, sRGBcolorSpace);
592 qCDebug(lcQpaScreen) <<
"input grab rect" << grabRect;
600 return grabFromDisplay(displayId(), grabRect);
604 NSView *nsView =
reinterpret_cast<NSView*
>(
view);
605 NSPoint windowPoint = [nsView convertPoint:NSMakePoint(0, 0) toView:nil];
606 NSRect screenRect = [nsView.window convertRectToScreen:NSMakeRect(windowPoint.x, windowPoint.y, 1, 1)];
608 QSize size = QRectF::fromCGRect(NSRectToCGRect(nsView.bounds)).toRect().size();
611 grabRect = windowRect;
616 const int maxDisplays = 128;
617 CGDirectDisplayID displays[maxDisplays];
618 CGDisplayCount displayCount;
619 CGRect cgRect = grabRect.
isValid() ? grabRect.toCGRect() : CGRectInfinite;
620 const CGDisplayErr err = CGGetDisplaysWithRect(cgRect, maxDisplays, displays, &displayCount);
621 if (err || displayCount == 0)
624 qCDebug(lcQpaScreen) <<
"final grab rect" << grabRect <<
"from" << displayCount <<
"displays";
629 for (
uint i = 0;
i < displayCount; ++
i) {
631 const QRect displayBounds = QRectF::fromCGRect(CGDisplayBounds(
display)).toRect();
633 if (grabBounds.
isNull()) {
640 qCDebug(lcQpaScreen) <<
"grab display" <<
i <<
"global" << grabBounds <<
"local" << displayLocalGrabBounds;
641 QPixmap displayPixmap = grabFromDisplay(
display, displayLocalGrabBounds);
643 if (displayCount == 1)
644 return displayPixmap;
647 pixmaps.
append(displayPixmap);
649 destinations.
append(destBounds);
654 for (
uint i = 0;
i < displayCount; ++
i)
658 qCDebug(lcQpaScreen) <<
"Create grap pixmap" << grabRect.
size() <<
"at devicePixelRatio" <<
dpr;
663 for (
uint i = 0;
i < displayCount; ++
i)
669bool QCocoaScreen::isOnline()
const
676 int isOnline = CGDisplayIsOnline(m_displayId);
677 static const int kCGDisplayIsDisconnected = 0xffffffff;
678 return isOnline != kCGDisplayIsDisconnected && isOnline;
684bool QCocoaScreen::isMirroring()
const
689 return CGDisplayMirrorsDisplay(m_displayId);
716 auto displayId = nsScreen.qt_displayId;
717 auto *cocoaScreen =
get(displayId);
719 qCWarning(lcQpaScreen) <<
"Failed to map" << nsScreen
720 <<
"to QCocoaScreen. Doing last minute update.";
722 cocoaScreen =
get(displayId);
724 qCWarning(lcQpaScreen) <<
"Last minute update failed!";
733 if (cocoaScreen->m_displayId == displayId)
744 if (!platformScreen->isOnline())
747 auto displayId = platformScreen->displayId();
751 if (candidateUuid == uuid)
752 return platformScreen;
760 for (NSScreen *
screen in NSScreen.screens) {
761 if (
screen.qt_displayId == displayId)
799#ifndef QT_NO_DEBUG_STREAM
808 if (CGDisplayIsAsleep(
screen->displayId()))
809 debug <<
", Sleeping";
810 if (
auto mirroring = CGDisplayMirrorsDisplay(
screen->displayId()))
811 debug <<
", mirroring=" << mirroring;
813 debug <<
", Offline";
819 if (
auto nativeScreen =
screen->nativeScreen())
820 debug <<
", " << nativeScreen;
829#include "qcocoascreen.moc"
831@implementation NSScreen (QtExtras)
833- (CGDirectDisplayID)qt_displayId
835 return [
self.deviceDescription[@"NSScreenNumber"] unsignedIntValue];
T fetchAndStoreRelaxed(T newValue) noexcept
QRect availableGeometry() const override
Reimplement in subclass to return the pixel geometry of the available space This normally is the desk...
static CGPoint mapToNative(const QPointF &pos, QCocoaScreen *screen=QCocoaScreen::primaryScreen())
static NSScreen * nativeScreenForDisplayId(CGDirectDisplayID displayId)
QPixmap grabWindow(WId window, int x, int y, int width, int height) const override
static QCocoaScreen * get(NSScreen *nsScreen)
void deliverUpdateRequests()
bool isRunningDisplayLink() const
QPlatformScreen::SubpixelAntialiasingType subpixelAntialiasingTypeHint() const override
Returns a hint about this screen's subpixel layout structure.
static QCocoaScreen * primaryScreen()
The screen used as a reference for global window geometry.
static QPointF mapFromNative(CGPoint pos, QCocoaScreen *screen=QCocoaScreen::primaryScreen())
QRect geometry() const override
Reimplement in subclass to return the pixel geometry of the screen.
QList< QPlatformScreen * > virtualSiblings() const override
Returns a list of all the platform screens that are part of the same virtual desktop.
QWindow * topLevelAt(const QPoint &point) const override
Return the given top level window for a given position.
NSScreen * nativeScreen() const
bool isValid() const noexcept
Returns true if the color space is valid.
static QColorSpace fromIccProfile(const QByteArray &iccProfile)
Creates a QColorSpace from ICC profile iccProfile.
static QWindowList allWindows()
Returns a list of all the windows in the application.
QScreen * primaryScreen
the primary (or default) screen of the application.
static QList< QScreen * > screens()
Returns a list of all the screens associated with the windowing system the application is connected t...
qsizetype size() const noexcept
const_reference at(qsizetype i) const noexcept
void append(parameter_type t)
bool isDebugEnabled() const
Returns true if debug messages should be shown for this category; false otherwise.
The QPainter class performs low-level painting on widgets and other paint devices.
void drawPixmap(const QRectF &targetRect, const QPixmap &pixmap, const QRectF &sourceRect)
Draws the rectangular portion source of the given pixmap into the given target in the paint device.
Returns a copy of the pixmap that is transformed using the given transformation transform and transfo...
QSize size() const
Returns the size of the pixmap.
void setDevicePixelRatio(qreal scaleFactor)
Sets the device pixel ratio for the pixmap.
void fill(const QColor &fillColor=Qt::white)
Fills the pixmap with the given color.
qreal devicePixelRatio() const
Returns the device pixel ratio for the pixmap.
static QPixmap fromImage(const QImage &image, Qt::ImageConversionFlags flags=Qt::AutoColor)
Converts the given image to a pixmap using the specified flags to control the conversion.
\inmodule QtCore\reentrant
constexpr QPoint toPoint() const
Rounds the coordinates of this point to the nearest integer, and returns a QPoint object with the rou...
\inmodule QtCore\reentrant
\inmodule QtCore\reentrant
\inmodule QtCore\reentrant
constexpr bool isValid() const noexcept
Returns true if the rectangle is valid, otherwise returns false.
constexpr bool isNull() const noexcept
Returns true if the rectangle is a null rectangle, otherwise returns false.
QRect intersected(const QRect &other) const noexcept
constexpr QPoint topLeft() const noexcept
Returns the position of the rectangle's top-left corner.
constexpr QSize size() const noexcept
Returns the size of the rectangle.
constexpr void translate(int dx, int dy) noexcept
Moves the rectangle dx along the x axis and dy along the y axis, relative to the current position.
The QScreen class is used to query screen properties. \inmodule QtGui.
qreal devicePixelRatio
the screen's ratio between physical pixels and device-independent pixels
QRect geometry
the screen's geometry in pixels
QString name
a user presentable string representing the screen
QPlatformScreen * handle() const
Get the platform screen handle.
\macro QT_RESTRICTED_CAST_FROM_ASCII
SurfaceType
The SurfaceType enum describes what type of surface this is.
static void handleScreenGeometryChange(QScreen *screen, const QRect &newGeometry, const QRect &newAvailableGeometry)
static void handlePrimaryScreenChanged(QPlatformScreen *newPrimary)
Should be called whenever the primary screen changes.
static void handleScreenAdded(QPlatformScreen *screen, bool isPrimary=false)
Should be called by the implementation whenever a new screen is added.
static void handleScreenRemoved(QPlatformScreen *screen)
Should be called by the implementation whenever a screen is removed.
static void handleScreenRefreshRateChange(QScreen *screen, qreal newRefreshRate)
struct wl_display * display
@ ReconfiguredWithFlagsMissing
Combined button and popup list for selecting options.
QNSView * qnsview_cast(NSView *view)
Returns the view cast to a QNSview if possible.
QPointF qt_mac_flip(const QPointF &pos, const QRectF &reference)
#define qDeferredDebug(helper)
static QString displayName(CGDirectDisplayID displayID)
QDebug operator<<(QDebug debug, const QCocoaScreen *screen)
QImage qt_mac_toQImage(CGImageRef image)
#define Q_LOGGING_CATEGORY(name,...)
#define qCInfo(category,...)
#define qCWarning(category,...)
#define qCDebug(category,...)
constexpr const T & qMax(const T &a, const T &b)
GLuint64 GLenum void * handle
GLint GLint GLint GLint GLint x
[0]
GLint GLsizei GLsizei height
GLenum GLuint GLintptr GLsizeiptr size
[1]
GLenum GLenum GLsizei count
static qreal position(const QQuickItem *item, QQuickAnchors::Anchor anchorLine)
static QT_BEGIN_NAMESPACE qreal dpr(const QWindow *w)
#define Q_ASSERT_X(cond, x, msg)
static void allKeys(HKEY parentHandle, const QString &rSubKey, NameSet *result, REGSAM access=0)
Q_CORE_EXPORT bool qEnvironmentVariableIsSet(const char *varName) noexcept
QFileInfo info(fileName)
[8]
DeferredDebugHelper(const QLoggingCategory &cat)