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 <Metal/Metal.h>
28#include <sys/sysctl.h>
32typedef void (^DisplayLinkBlock)(CADisplayLink *displayLink);
34@implementation UIScreen (DisplayLinkBlock)
35- (CADisplayLink*)displayLinkWithBlock:(DisplayLinkBlock)block
37 return [self displayLinkWithTarget:[[block copy] autorelease]
38 selector:@selector(invokeDisplayLinkBlock:)];
42@implementation NSObject (DisplayLinkBlock)
43- (
void)invokeDisplayLinkBlock:(CADisplayLink *)sender
45 DisplayLinkBlock block =
static_cast<id>(self);
53#if !defined(Q_OS_VISIONOS)
54static QIOSScreen* qtPlatformScreenFor(UIScreen *uiScreen)
56 foreach (QScreen *screen, QGuiApplication::screens()) {
57 QIOSScreen *platformScreen =
static_cast<QIOSScreen *>(screen->handle());
58 if (platformScreen->uiScreen() == uiScreen)
59 return platformScreen;
65@interface QIOSScreenTracker : NSObject
68@implementation QIOSScreenTracker
72 NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
73 [center addObserver:self selector:@selector(screenConnected:)
74 name:UIScreenDidConnectNotification object:nil];
75 [center addObserver:self selector:@selector(screenDisconnected:)
76 name:UIScreenDidDisconnectNotification object:nil];
77 [center addObserver:self selector:@selector(screenModeChanged:)
78 name:UIScreenModeDidChangeNotification object:nil];
81+ (
void)screenConnected:(NSNotification*)notification
83 if (!QIOSIntegration::instance())
86 QWindowSystemInterface::handleScreenAdded(
new QIOSScreen([notification object]));
89+ (
void)screenDisconnected:(NSNotification*)notification
91 if (!QIOSIntegration::instance())
94 QIOSScreen *screen = qtPlatformScreenFor([notification object]);
95 Q_ASSERT_X(screen, Q_FUNC_INFO,
"Screen disconnected that we didn't know about");
97 QWindowSystemInterface::handleScreenRemoved(screen);
100+ (
void)screenModeChanged:(NSNotification*)notification
102 if (!QIOSIntegration::instance())
105 QIOSScreen *screen = qtPlatformScreenFor([notification object]);
106 Q_ASSERT_X(screen, Q_FUNC_INFO,
"Screen changed that we didn't know about");
108 screen->updateProperties();
119using namespace Qt::StringLiterals;
121#if !defined(Q_OS_VISIONOS)
123
124
127#if TARGET_OS_SIMULATOR
128 return QString::fromLocal8Bit(qgetenv(
"SIMULATOR_MODEL_IDENTIFIER"));
130 static const char key[] =
"hw.machine";
133 sysctlbyname(key, NULL, &size, NULL, 0);
135 QVarLengthArray<
char> value(size);
136 sysctlbyname(key, value.data(), &size, NULL, 0);
138 return QString::fromLatin1(QByteArrayView(value.constData(), qsizetype(size)));
144void QIOSScreen::initializeScreens()
146#if defined(Q_OS_VISIONOS)
148 QWindowSystemInterface::handleScreenAdded(
new QIOSScreen);
150 Q_ASSERT([UIScreen.screens containsObject:UIScreen.mainScreen]);
151 for (UIScreen *screen in UIScreen.screens)
152 QWindowSystemInterface::handleScreenAdded(
new QIOSScreen(screen));
156#if defined(Q_OS_VISIONOS)
157QIOSScreen::QIOSScreen()
160QIOSScreen::QIOSScreen(UIScreen *screen)
163 QString deviceIdentifier = deviceModelIdentifier();
165 if (screen == [UIScreen mainScreen] && !deviceIdentifier.startsWith(
"AppleTV")) {
169 static QRegularExpression lowBitDepthDevices(
"^(iPhone1,[12]|iPhone2,1|iPod[1-3],1)$");
170 m_depth = deviceIdentifier.contains(lowBitDepthDevices) ? 18 : 24;
172 static QRegularExpression iPhoneXModels(
"^iPhone(10,[36])$");
173 static QRegularExpression iPhonePlusModels(
"^iPhone(7,1|8,2|9,[24]|10,[25])$");
174 static QRegularExpression iPadMiniModels(
"^iPad(2,[567]|4,[4-9]|5,[12])$");
176 if (deviceIdentifier.contains(iPhoneXModels)) {
178 }
else if (deviceIdentifier.contains(iPhonePlusModels)) {
180 }
else if (deviceIdentifier.startsWith(
"iPad")) {
181 if (deviceIdentifier.contains(iPadMiniModels))
182 m_physicalDpi = 163 * devicePixelRatio();
184 m_physicalDpi = 132 * devicePixelRatio();
187 m_physicalDpi = 163 * devicePixelRatio();
195 m_displayLink = [m_uiScreen displayLinkWithBlock:^(CADisplayLink *) { deliverUpdateRequests(); }];
196 m_displayLink.paused = YES;
197 [m_displayLink addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode];
202 m_screenBrightnessObserver = QMacNotificationObserver(m_uiScreen,
203 UIScreenBrightnessDidChangeNotification, [&]() {
204 for (
auto *window : QPlatformScreen::windows()) {
205 auto *platformWindow =
static_cast<QIOSWindow*>(window->handle());
209 UIView *view = platformWindow->view();
211 if (!view.layer.wantsExtendedDynamicRangeContent)
214 [view setNeedsDisplay];
218 if (shouldPauseDisplayLinkWhenInactive()) {
221 QObject::connect(qGuiApp, &QGuiApplication::applicationStateChanged,
this, [
this](
auto newState) {
222 if (newState == Qt::ApplicationActive) {
223 qCDebug(lcQpaApplication) <<
"Attempting update request delivery after becoming active";
224 deliverUpdateRequests();
234QIOSScreen::~QIOSScreen()
236 [m_displayLink invalidate];
239QString QIOSScreen::name()
const
241#if defined(Q_OS_VISIONOS)
244 if (m_uiScreen == [UIScreen mainScreen])
245 return QString::fromNSString([UIDevice currentDevice].model) +
" built-in display"_L1;
247 return "External display"_L1;
251void QIOSScreen::updateProperties()
253 QRect previousGeometry = m_geometry;
254 QRect previousAvailableGeometry = m_availableGeometry;
256#if defined(Q_OS_VISIONOS)
258 m_geometry = QRectF::fromCGRect(rootViewForScreen(
this).bounds).toRect();
261 m_geometry = QRectF::fromCGRect(m_uiScreen.bounds).toRect();
263 if (m_geometry != previousGeometry) {
265 Qt::ScreenOrientation primaryOrientation = m_geometry.width() >= m_geometry.height() ?
266 Qt::LandscapeOrientation : Qt::PortraitOrientation;
272 const QRectF physicalGeometry = mapBetween(nativeOrientation(), primaryOrientation, QRectF::fromCGRect(m_uiScreen.nativeBounds).toRect());
274 static const qreal millimetersPerInch = 25.4;
275 m_physicalSize = physicalGeometry.size() / m_physicalDpi * millimetersPerInch;
284 m_availableGeometry = m_geometry;
293 if (screen()->orientation() != orientation())
294 QWindowSystemInterface::handleScreenOrientationChange(screen(), orientation());
301 if (m_geometry != previousGeometry || m_availableGeometry != previousAvailableGeometry)
302 QWindowSystemInterface::handleScreenGeometryChange(screen(), m_geometry, m_availableGeometry);
305void QIOSScreen::setUpdatesPaused(
bool paused)
307 m_displayLink.paused = paused;
310void QIOSScreen::deliverUpdateRequests()
const
312 bool pauseUpdates =
true;
314 if (shouldPauseDisplayLinkWhenInactive()
315 && QGuiApplication::applicationState() != Qt::ApplicationActive) {
321 qCDebug(lcQpaApplication) <<
"Skipping update request delivery and pausing display link";
322 m_displayLink.paused =
true;
326 QList<QWindow*> windows = QGuiApplication::allWindows();
327 for (
int i = 0; i < windows.size(); ++i) {
328 QWindow *window = windows.at(i);
329 if (platformScreenForWindow(window) !=
this)
332 QPlatformWindow *platformWindow = window->handle();
336 if (!platformWindow->hasPendingUpdateRequest())
339 platformWindow->deliverUpdateRequest();
342 if (platformWindow->hasPendingUpdateRequest())
343 pauseUpdates =
false;
347 m_displayLink.paused = pauseUpdates;
350QRect QIOSScreen::geometry()
const
355QRect QIOSScreen::availableGeometry()
const
357 return m_availableGeometry;
360int QIOSScreen::depth()
const
365QImage::Format QIOSScreen::format()
const
367 return QImage::Format_ARGB32_Premultiplied;
370QSizeF QIOSScreen::physicalSize()
const
372 return m_physicalSize;
375QDpi QIOSScreen::logicalBaseDpi()
const
380qreal QIOSScreen::devicePixelRatio()
const
382#if defined(Q_OS_VISIONOS)
388 return [m_uiScreen scale];
392qreal QIOSScreen::refreshRate()
const
394#if defined(Q_OS_VISIONOS)
397 return m_uiScreen.maximumFramesPerSecond;
401Qt::ScreenOrientation QIOSScreen::nativeOrientation()
const
403#if defined(Q_OS_VISIONOS)
405 return Qt::PortraitOrientation;
407 CGRect nativeBounds =
409 m_uiScreen.nativeBounds;
416 return nativeBounds.size.width >= nativeBounds.size.height ?
417 Qt::LandscapeOrientation : Qt::PortraitOrientation;
421Qt::ScreenOrientation QIOSScreen::orientation()
const
428 auto *windowScene = rootViewForScreen(
this).window.windowScene;
429 auto interfaceOrientation = windowScene ?
430 windowScene.interfaceOrientation : UIInterfaceOrientationUnknown;
436 switch (interfaceOrientation) {
437 case UIInterfaceOrientationPortrait:
438 return Qt::PortraitOrientation;
439 case UIInterfaceOrientationPortraitUpsideDown:
440 return Qt::InvertedPortraitOrientation;
441 case UIInterfaceOrientationLandscapeLeft:
442 return Qt::LandscapeOrientation;
443 case UIInterfaceOrientationLandscapeRight:
444 return Qt::InvertedLandscapeOrientation;
445 case UIInterfaceOrientationUnknown:
451 return m_geometry.width() >= m_geometry.height() ?
452 Qt::LandscapeOrientation : Qt::PortraitOrientation;
456QPixmap QIOSScreen::grabWindow(WId window,
int x,
int y,
int width,
int height)
const
458 if (window && ![
reinterpret_cast<id>(window) isKindOfClass:[UIView
class]])
461 UIView *view = window ?
reinterpret_cast<UIView *>(window)
462 : rootViewForScreen(
this);
465 width = qMax(view.bounds.size.width - x, CGFloat(0));
467 height = qMax(view.bounds.size.height - y, CGFloat(0));
469 CGRect captureRect = [view.window convertRect:CGRectMake(x, y, width, height) fromView:view];
470 captureRect = CGRectIntersection(captureRect, view.window.bounds);
472 QMacAutoReleasePool autoReleasePool;
474 UIGraphicsImageRendererFormat *format = [UIGraphicsImageRendererFormat defaultFormat];
476 format.scale = devicePixelRatio();
478 format.preferredRange = UIGraphicsImageRendererFormatRangeStandard;
480 UIGraphicsImageRenderer *renderer = [[[UIGraphicsImageRenderer alloc]
481 initWithSize:captureRect.size format:format]
484 UIImage *screenshot = [renderer imageWithActions:^(UIGraphicsImageRendererContext *rendererContext) {
485 CGContextRef context = rendererContext.CGContext;
486 CGContextTranslateCTM(context, -captureRect.origin.x, -captureRect.origin.y);
497 [view.window drawViewHierarchyInRect:view.window.bounds afterScreenUpdates:NO];
500 return QPixmap::fromImage(qt_mac_toQImage(screenshot.CGImage));
503bool QIOSScreen::shouldPauseDisplayLinkWhenInactive()
const
512 static const bool isA10GPU = []{
513 auto device = MTLCreateSystemDefaultDevice();
514 return [device.name containsString:@
"A10"];
517 static const bool pauseWhenInactive = qEnvironmentVariableIntegerValue(
518 "QT_IOS_PAUSE_DISPLAY_LINK_WHEN_INACTIVE").value_or(isA10GPU);
519 return pauseWhenInactive;
522#if !defined(Q_OS_VISIONOS)
523UIScreen *QIOSScreen::uiScreen()
const
531#include "moc_qiosscreen.cpp"
static QString deviceModelIdentifier()
Returns the model identifier of the device.