11#include <qpa/qwindowsysteminterface.h>
18#include <QtCore/private/qcore_mac_p.h>
20#include <QtGui/qpointingdevice.h>
21#include <QtGui/private/qwindow_p.h>
22#include <QtGui/private/qguiapplication_p.h>
23#include <private/qcoregraphics_p.h>
24#include <qpa/qwindowsysteminterface.h>
26#include <sys/sysctl.h>
30typedef void (^DisplayLinkBlock)(CADisplayLink *displayLink);
32@implementation UIScreen (DisplayLinkBlock)
33- (CADisplayLink*)displayLinkWithBlock:(DisplayLinkBlock)block
35 return [self displayLinkWithTarget:[[block copy] autorelease]
36 selector:@selector(invokeDisplayLinkBlock:)];
40@implementation NSObject (DisplayLinkBlock)
41- (
void)invokeDisplayLinkBlock:(CADisplayLink *)sender
43 DisplayLinkBlock block =
static_cast<id>(self);
51#if !defined(Q_OS_VISIONOS)
52static QIOSScreen* qtPlatformScreenFor(UIScreen *uiScreen)
54 foreach (QScreen *screen, QGuiApplication::screens()) {
55 QIOSScreen *platformScreen =
static_cast<QIOSScreen *>(screen->handle());
56 if (platformScreen->uiScreen() == uiScreen)
57 return platformScreen;
63@interface QIOSScreenTracker : NSObject
66@implementation QIOSScreenTracker
70 NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
71 [center addObserver:self selector:@selector(screenConnected:)
72 name:UIScreenDidConnectNotification object:nil];
73 [center addObserver:self selector:@selector(screenDisconnected:)
74 name:UIScreenDidDisconnectNotification object:nil];
75 [center addObserver:self selector:@selector(screenModeChanged:)
76 name:UIScreenModeDidChangeNotification object:nil];
79+ (
void)screenConnected:(NSNotification*)notification
81 if (!QIOSIntegration::instance())
84 QWindowSystemInterface::handleScreenAdded(
new QIOSScreen([notification object]));
87+ (
void)screenDisconnected:(NSNotification*)notification
89 if (!QIOSIntegration::instance())
92 QIOSScreen *screen = qtPlatformScreenFor([notification object]);
93 Q_ASSERT_X(screen, Q_FUNC_INFO,
"Screen disconnected that we didn't know about");
95 QWindowSystemInterface::handleScreenRemoved(screen);
98+ (
void)screenModeChanged:(NSNotification*)notification
100 if (!QIOSIntegration::instance())
103 QIOSScreen *screen = qtPlatformScreenFor([notification object]);
104 Q_ASSERT_X(screen, Q_FUNC_INFO,
"Screen changed that we didn't know about");
106 screen->updateProperties();
117using namespace Qt::StringLiterals;
119#if !defined(Q_OS_VISIONOS)
121
122
125#if TARGET_OS_SIMULATOR
126 return QString::fromLocal8Bit(qgetenv(
"SIMULATOR_MODEL_IDENTIFIER"));
128 static const char key[] =
"hw.machine";
131 sysctlbyname(key, NULL, &size, NULL, 0);
133 QVarLengthArray<
char> value(size);
134 sysctlbyname(key, value.data(), &size, NULL, 0);
136 return QString::fromLatin1(QByteArrayView(value.constData(), qsizetype(size)));
142void QIOSScreen::initializeScreens()
144#if defined(Q_OS_VISIONOS)
146 QWindowSystemInterface::handleScreenAdded(
new QIOSScreen);
148 Q_ASSERT([UIScreen.screens containsObject:UIScreen.mainScreen]);
149 for (UIScreen *screen in UIScreen.screens)
150 QWindowSystemInterface::handleScreenAdded(
new QIOSScreen(screen));
154#if defined(Q_OS_VISIONOS)
155QIOSScreen::QIOSScreen()
158QIOSScreen::QIOSScreen(UIScreen *screen)
161 QString deviceIdentifier = deviceModelIdentifier();
163 if (screen == [UIScreen mainScreen] && !deviceIdentifier.startsWith(
"AppleTV")) {
167 static QRegularExpression lowBitDepthDevices(
"^(iPhone1,[12]|iPhone2,1|iPod[1-3],1)$");
168 m_depth = deviceIdentifier.contains(lowBitDepthDevices) ? 18 : 24;
170 static QRegularExpression iPhoneXModels(
"^iPhone(10,[36])$");
171 static QRegularExpression iPhonePlusModels(
"^iPhone(7,1|8,2|9,[24]|10,[25])$");
172 static QRegularExpression iPadMiniModels(
"^iPad(2,[567]|4,[4-9]|5,[12])$");
174 if (deviceIdentifier.contains(iPhoneXModels)) {
176 }
else if (deviceIdentifier.contains(iPhonePlusModels)) {
178 }
else if (deviceIdentifier.startsWith(
"iPad")) {
179 if (deviceIdentifier.contains(iPadMiniModels))
180 m_physicalDpi = 163 * devicePixelRatio();
182 m_physicalDpi = 132 * devicePixelRatio();
185 m_physicalDpi = 163 * devicePixelRatio();
193 m_displayLink = [m_uiScreen displayLinkWithBlock:^(CADisplayLink *) { deliverUpdateRequests(); }];
194 m_displayLink.paused = YES;
195 [m_displayLink addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode];
200 m_screenBrightnessObserver = QMacNotificationObserver(m_uiScreen,
201 UIScreenBrightnessDidChangeNotification, [&]() {
202 if (@available(iOS 17, *)) {
203 for (
auto *window : QPlatformScreen::windows()) {
204 auto *platformWindow =
static_cast<QIOSWindow*>(window->handle());
208 UIView *view = platformWindow->view();
210 if (!view.layer.wantsExtendedDynamicRangeContent)
213 [view setNeedsDisplay];
220 QObject::connect(qGuiApp, &QGuiApplication::applicationStateChanged,
this, [
this](
auto newState) {
221 if (newState == Qt::ApplicationActive) {
222 qCDebug(lcQpaApplication) <<
"Attempting update request delivery after becoming active";
223 deliverUpdateRequests();
232QIOSScreen::~QIOSScreen()
234 [m_displayLink invalidate];
237QString QIOSScreen::name()
const
239#if defined(Q_OS_VISIONOS)
242 if (m_uiScreen == [UIScreen mainScreen])
243 return QString::fromNSString([UIDevice currentDevice].model) +
" built-in display"_L1;
245 return "External display"_L1;
249void QIOSScreen::updateProperties()
251 QRect previousGeometry = m_geometry;
252 QRect previousAvailableGeometry = m_availableGeometry;
254#if defined(Q_OS_VISIONOS)
256 m_geometry = QRectF::fromCGRect(rootViewForScreen(
this).bounds).toRect();
259 m_geometry = QRectF::fromCGRect(m_uiScreen.bounds).toRect();
261 if (m_geometry != previousGeometry) {
263 Qt::ScreenOrientation primaryOrientation = m_geometry.width() >= m_geometry.height() ?
264 Qt::LandscapeOrientation : Qt::PortraitOrientation;
270 const QRectF physicalGeometry = mapBetween(nativeOrientation(), primaryOrientation, QRectF::fromCGRect(m_uiScreen.nativeBounds).toRect());
272 static const qreal millimetersPerInch = 25.4;
273 m_physicalSize = physicalGeometry.size() / m_physicalDpi * millimetersPerInch;
282 m_availableGeometry = m_geometry;
291 if (screen()->orientation() != orientation())
292 QWindowSystemInterface::handleScreenOrientationChange(screen(), orientation());
299 if (m_geometry != previousGeometry || m_availableGeometry != previousAvailableGeometry)
300 QWindowSystemInterface::handleScreenGeometryChange(screen(), m_geometry, m_availableGeometry);
303void QIOSScreen::setUpdatesPaused(
bool paused)
305 m_displayLink.paused = paused;
308void QIOSScreen::deliverUpdateRequests()
const
310 bool pauseUpdates =
true;
312 if (QGuiApplication::applicationState() != Qt::ApplicationActive) {
318 qCDebug(lcQpaApplication) <<
"Skipping update request delivery and pausing display link";
319 m_displayLink.paused =
true;
323 QList<QWindow*> windows = QGuiApplication::allWindows();
324 for (
int i = 0; i < windows.size(); ++i) {
325 QWindow *window = windows.at(i);
326 if (platformScreenForWindow(window) !=
this)
329 QPlatformWindow *platformWindow = window->handle();
333 if (!platformWindow->hasPendingUpdateRequest())
336 platformWindow->deliverUpdateRequest();
339 if (platformWindow->hasPendingUpdateRequest())
340 pauseUpdates =
false;
344 m_displayLink.paused = pauseUpdates;
347QRect QIOSScreen::geometry()
const
352QRect QIOSScreen::availableGeometry()
const
354 return m_availableGeometry;
357int QIOSScreen::depth()
const
362QImage::Format QIOSScreen::format()
const
364 return QImage::Format_ARGB32_Premultiplied;
367QSizeF QIOSScreen::physicalSize()
const
369 return m_physicalSize;
372QDpi QIOSScreen::logicalBaseDpi()
const
377qreal QIOSScreen::devicePixelRatio()
const
379#if defined(Q_OS_VISIONOS)
385 return [m_uiScreen scale];
389qreal QIOSScreen::refreshRate()
const
391#if defined(Q_OS_VISIONOS)
394 return m_uiScreen.maximumFramesPerSecond;
398Qt::ScreenOrientation QIOSScreen::nativeOrientation()
const
400#if defined(Q_OS_VISIONOS)
402 return Qt::PortraitOrientation;
404 CGRect nativeBounds =
406 m_uiScreen.nativeBounds;
413 return nativeBounds.size.width >= nativeBounds.size.height ?
414 Qt::LandscapeOrientation : Qt::PortraitOrientation;
418Qt::ScreenOrientation QIOSScreen::orientation()
const
425 auto *windowScene = rootViewForScreen(
this).window.windowScene;
426 auto interfaceOrientation = windowScene ?
427 windowScene.interfaceOrientation : UIInterfaceOrientationUnknown;
433 switch (interfaceOrientation) {
434 case UIInterfaceOrientationPortrait:
435 return Qt::PortraitOrientation;
436 case UIInterfaceOrientationPortraitUpsideDown:
437 return Qt::InvertedPortraitOrientation;
438 case UIInterfaceOrientationLandscapeLeft:
439 return Qt::LandscapeOrientation;
440 case UIInterfaceOrientationLandscapeRight:
441 return Qt::InvertedLandscapeOrientation;
442 case UIInterfaceOrientationUnknown:
448 return m_geometry.width() >= m_geometry.height() ?
449 Qt::LandscapeOrientation : Qt::PortraitOrientation;
453QPixmap QIOSScreen::grabWindow(WId window,
int x,
int y,
int width,
int height)
const
455 if (window && ![
reinterpret_cast<id>(window) isKindOfClass:[UIView
class]])
458 UIView *view = window ?
reinterpret_cast<UIView *>(window)
459 : rootViewForScreen(
this);
462 width = qMax(view.bounds.size.width - x, CGFloat(0));
464 height = qMax(view.bounds.size.height - y, CGFloat(0));
466 CGRect captureRect = [view.window convertRect:CGRectMake(x, y, width, height) fromView:view];
467 captureRect = CGRectIntersection(captureRect, view.window.bounds);
469 QMacAutoReleasePool autoReleasePool;
471 UIGraphicsImageRendererFormat *format = [UIGraphicsImageRendererFormat defaultFormat];
473 format.scale = devicePixelRatio();
475 format.preferredRange = UIGraphicsImageRendererFormatRangeStandard;
477 UIGraphicsImageRenderer *renderer = [[[UIGraphicsImageRenderer alloc]
478 initWithSize:captureRect.size format:format]
481 UIImage *screenshot = [renderer imageWithActions:^(UIGraphicsImageRendererContext *rendererContext) {
482 CGContextRef context = rendererContext.CGContext;
483 CGContextTranslateCTM(context, -captureRect.origin.x, -captureRect.origin.y);
494 [view.window drawViewHierarchyInRect:view.window.bounds afterScreenUpdates:NO];
497 return QPixmap::fromImage(qt_mac_toQImage(screenshot.CGImage));
500#if !defined(Q_OS_VISIONOS)
501UIScreen *QIOSScreen::uiScreen()
const
509#include "moc_qiosscreen.cpp"
static QString deviceModelIdentifier()
Returns the model identifier of the device.