Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qcore_mac.mm
Go to the documentation of this file.
1// Copyright (C) 2016 The Qt Company Ltd.
2// Copyright (C) 2014 Petroules Corporation.
3// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
4
5#include <private/qcore_mac_p.h>
6
7#ifdef Q_OS_MACOS
8#include <AppKit/AppKit.h>
9#endif
10
11#if defined(QT_PLATFORM_UIKIT)
12#include <UIKit/UIKit.h>
13#endif
14
15#include <new>
16#include <execinfo.h>
17#include <dlfcn.h>
18#include <cxxabi.h>
19#include <objc/runtime.h>
20#include <mach-o/dyld.h>
21#include <sys/sysctl.h>
22#include <spawn.h>
23
24#include <qdebug.h>
25
26#include "qendian.h"
27#include "qhash.h"
28#include "qmutex.h"
30#include "private/qlocking_p.h"
31
32#if !defined(QT_BOOTSTRAPPED)
33#include <thread>
34#endif
35
36#if !defined(QT_APPLE_NO_PRIVATE_APIS)
37extern "C" {
38typedef uint32_t csr_config_t;
39extern int csr_get_active_config(csr_config_t *) __attribute__((weak_import));
40
41#ifdef QT_BUILD_INTERNAL
42int responsibility_spawnattrs_setdisclaim(posix_spawnattr_t attrs, int disclaim)
43__attribute__((availability(macos,introduced=10.14),weak_import));
44pid_t responsibility_get_pid_responsible_for_pid(pid_t) __attribute__((weak_import));
45char *** _NSGetArgv();
46extern char **environ;
47#endif
48}
49#endif
50
51QT_BEGIN_NAMESPACE
52
53// --------------------------------------------------------------------------
54
55#if defined(Q_OS_MACOS)
56static void initializeStandardUserDefaults()
57{
58 // The standard user defaults are initialized from an ordered list of domains,
59 // as documented by NSUserDefaults.standardUserDefaults. This includes e.g.
60 // parsing command line arguments, such as -AppleFooBar "baz", as well as
61 // global defaults. To ensure that these defaults are available through
62 // the lower level Core Foundation preferences APIs, we need to initialize
63 // them as early as possible via the Foundation-API, as the lower level APIs
64 // do not do this initialization.
65 Q_UNUSED(NSUserDefaults.standardUserDefaults);
66}
67Q_CONSTRUCTOR_FUNCTION(initializeStandardUserDefaults);
68#endif
69
70// --------------------------------------------------------------------------
71
72QCFString::operator QString() const
73{
74 if (string.isEmpty() && value)
75 const_cast<QCFString*>(this)->string = QString::fromCFString(value);
76 return string;
77}
78
79QCFString::operator CFStringRef() const
80{
81 if (!value)
82 const_cast<QCFString*>(this)->value = string.toCFString();
83 return value;
84}
85
86// --------------------------------------------------------------------------
87
88#if defined(QT_USE_APPLE_UNIFIED_LOGGING)
89
90bool AppleUnifiedLogger::preventsStderrLogging()
91{
92 // os_log will mirror to stderr if OS_ACTIVITY_DT_MODE is set,
93 // regardless of its value. OS_ACTIVITY_MODE then controls whether
94 // to include info and/or debug messages in this mirroring.
95 // For some reason, when launched under lldb (via Xcode or not),
96 // all levels are included.
97
98 // CFLog will normally log to both stderr, and via os_log.
99 // Setting CFLOG_FORCE_DISABLE_STDERR disables the stderr
100 // logging. Setting CFLOG_FORCE_STDERR will both duplicate
101 // CFLog's output to stderr, and trigger OS_ACTIVITY_DT_MODE,
102 // resulting in os_log calls also being mirrored to stderr.
103 // Setting ACTIVITY_LOG_STDERR has the same effect.
104
105 // NSLog is plumbed to CFLog, and will respond to the same
106 // environment variables as CFLog.
107
108 // We want to disable Qt's default stderr log handler when
109 // os_log has already mirrored to stderr.
110 static bool willMirror = qEnvironmentVariableIsSet("OS_ACTIVITY_DT_MODE")
111 || qEnvironmentVariableIsSet("ACTIVITY_LOG_STDERR")
112 || qEnvironmentVariableIsSet("CFLOG_FORCE_STDERR");
113
114 // As well as when we suspect that Xcode is going to present os_log
115 // as structured log messages.
116 static bool disableStderr = qEnvironmentVariableIsSet("CFLOG_FORCE_DISABLE_STDERR");
117
118 return willMirror || disableStderr;
119}
120
121QT_MAC_WEAK_IMPORT(_os_log_default);
122bool AppleUnifiedLogger::messageHandler(QtMsgType msgType, const QMessageLogContext &context,
123 const QString &message, const QString &optionalSubsystem)
124{
125 QString subsystem = optionalSubsystem;
126 if (subsystem.isNull()) {
127 static QString bundleIdentifier = []() {
128 if (CFBundleRef bundle = CFBundleGetMainBundle()) {
129 if (CFStringRef identifier = CFBundleGetIdentifier(bundle))
130 return QString::fromCFString(identifier);
131 }
132 return QString();
133 }();
134 subsystem = bundleIdentifier;
135 }
136
137 const bool isDefault = !context.category || !strcmp(context.category, "default");
138 os_log_t log = isDefault ? OS_LOG_DEFAULT :
139 os_log_create(subsystem.toLatin1().constData(), context.category);
140 os_log_type_t logType = logTypeForMessageType(msgType);
141
142 if (!os_log_type_enabled(log, logType))
143 return false;
144
145 // Logging best practices says we should not include symbolication
146 // information or source file line numbers in messages, as the system
147 // will automatically captures this information. In our case, what
148 // the system captures is the call to os_log_with_type below, which
149 // isn't really useful, but we still don't want to include the context's
150 // info, as that would clutter the logging output. See rdar://35958308.
151
152 // The format must be a string constant, so we can't pass on the
153 // message. This means we won't be able to take advantage of the
154 // unified logging's custom format specifiers such as %{BOOL}d.
155 // We use the 'public' format specifier to prevent the logging
156 // system from redacting our log message.
157 os_log_with_type(log, logType, "%{public}s", qPrintable(message));
158
159 return preventsStderrLogging();
160}
161
162os_log_type_t AppleUnifiedLogger::logTypeForMessageType(QtMsgType msgType)
163{
164 switch (msgType) {
165 case QtDebugMsg: return OS_LOG_TYPE_DEBUG;
166 case QtInfoMsg: return OS_LOG_TYPE_INFO;
167 case QtWarningMsg: return OS_LOG_TYPE_DEFAULT;
168 case QtCriticalMsg: return OS_LOG_TYPE_ERROR;
169 case QtFatalMsg: return OS_LOG_TYPE_FAULT;
170 }
171
172 return OS_LOG_TYPE_DEFAULT;
173}
174
175#endif // QT_USE_APPLE_UNIFIED_LOGGING
176
177#ifndef QT_NO_DEBUG_STREAM
178// -------------------------------------------------------------------------
179
180QDebug operator<<(QDebug dbg, id obj)
181{
182 if (!obj) {
183 // Match NSLog
184 dbg << "(null)";
185 return dbg;
186 }
187
188 for (Class cls = object_getClass(obj); cls; cls = class_getSuperclass(cls)) {
189 if (cls == NSObject.class) {
190 dbg << static_cast<NSObject*>(obj);
191 return dbg;
192 }
193 }
194
195 // Match NSObject.debugDescription
196 const QDebugStateSaver saver(dbg);
197 dbg.nospace() << '<' << object_getClassName(obj) << ": " << static_cast<void*>(obj) << '>';
198 return dbg;
199}
200
201QDebug operator<<(QDebug dbg, const NSObject *nsObject)
202{
203 return dbg << (nsObject ?
204 dbg.verbosity() > 2 ?
205 nsObject.debugDescription.UTF8String :
206 nsObject.description.UTF8String
207 : "NSObject(0x0)");
208}
209
210QDebug operator<<(QDebug dbg, CFStringRef stringRef)
211{
212 if (!stringRef)
213 return dbg << "CFStringRef(0x0)";
214
215 if (const UniChar *chars = CFStringGetCharactersPtr(stringRef))
216 dbg << QStringView(reinterpret_cast<const QChar *>(chars), CFStringGetLength(stringRef));
217 else
218 dbg << QString::fromCFString(stringRef);
219
220 return dbg;
221}
222
223// Prevents breaking the ODR in case we introduce support for more types
224// later on, and lets the user override our default QDebug operators.
225#define QT_DECLARE_WEAK_QDEBUG_OPERATOR_FOR_CF_TYPE(CFType)
226 __attribute__((weak)) Q_DECLARE_QDEBUG_OPERATOR_FOR_CF_TYPE(CFType)
227
232#endif // QT_NO_DEBUG_STREAM
233
234// -------------------------------------------------------------------------
235
236QT_END_NAMESPACE
237QT_USE_NAMESPACE
238
239#ifdef QT_DEBUG
240@interface QT_MANGLE_NAMESPACE(QMacAutoReleasePoolTracker) : NSObject
241@end
242
243@implementation QT_MANGLE_NAMESPACE(QMacAutoReleasePoolTracker)
244@end
245QT_NAMESPACE_ALIAS_OBJC_CLASS(QMacAutoReleasePoolTracker);
246#endif // QT_DEBUG
247
248// Use the direct runtime interface to manage autorelease pools, as it
249// has less overhead then allocating NSAutoreleasePools, and allows for
250// a future where we use ARC (where NSAutoreleasePool is not allowed).
251// https://clang.llvm.org/docs/AutomaticReferenceCounting.html#runtime-support
252
253extern "C" {
255void objc_autoreleasePoolPop(void *pool);
256}
257
258QT_BEGIN_NAMESPACE
259
260QMacAutoReleasePool::QMacAutoReleasePool()
261 : pool(objc_autoreleasePoolPush())
262{
263#ifdef QT_DEBUG
264 static const bool debugAutoReleasePools = qEnvironmentVariableIsSet("QT_DARWIN_DEBUG_AUTORELEASEPOOLS");
265 if (!debugAutoReleasePools)
266 return;
267
268 Class trackerClass = [QMacAutoReleasePoolTracker class];
269
270 void *poolFrame = nullptr;
271 void *frames[2];
272 if (backtrace_from_fp(__builtin_frame_address(0), frames, 2))
273 poolFrame = frames[1];
274
275 if (poolFrame) {
276 Dl_info info;
277 if (dladdr(poolFrame, &info) && info.dli_sname) {
278 const char *symbolName = info.dli_sname;
279 if (symbolName[0] == '_') {
280 int status;
281 if (char *demangled = abi::__cxa_demangle(info.dli_sname, nullptr, 0, &status))
282 symbolName = demangled;
283 }
284
285 char *className = nullptr;
286 asprintf(&className, " ^-- allocated in function: %s", symbolName);
287
288 if (Class existingClass = objc_getClass(className))
289 trackerClass = existingClass;
290 else
291 trackerClass = objc_duplicateClass(trackerClass, className, 0);
292
293 free(className);
294
295 if (symbolName != info.dli_sname)
296 free((char*)symbolName);
297 }
298 }
299
300 [[trackerClass new] autorelease];
301#endif // QT_DEBUG
302}
303
304QMacAutoReleasePool::~QMacAutoReleasePool()
305{
306 objc_autoreleasePoolPop(pool);
307}
308
309#ifndef QT_NO_DEBUG_STREAM
310QDebug operator<<(QDebug debug, const QMacAutoReleasePool *pool)
311{
312 QDebugStateSaver saver(debug);
313 debug.nospace();
314 debug << "QMacAutoReleasePool(" << (const void *)pool << ')';
315 return debug;
316}
317
318QDebug operator<<(QDebug debug, const QCFString &string)
319{
320 debug << static_cast<QString>(string);
321 return debug;
322}
323#endif // !QT_NO_DEBUG_STREAM
324
325#if defined(Q_OS_MACOS) && !defined(QT_BOOTSTRAPPED)
326bool qt_mac_runningUnderRosetta()
327{
328 int translated = 0;
329 auto size = sizeof(translated);
330 if (sysctlbyname("sysctl.proc_translated", &translated, &size, nullptr, 0) == 0)
331 return translated;
332 return false;
333}
334
335bool qt_apple_runningWithLiquidGlass()
336{
337 static const bool runningWithLiquidGlass = []{
338 if (QMacVersion::buildSDK(QMacVersion::ApplicationBinary).majorVersion() < 26)
339 return false;
340
341 if (QMacVersion::currentRuntime().majorVersion() < 26)
342 return false;
343
344 // Word on the street is that the opt out will only work for
345 // macOS 26, but it's not clear whether building against the
346 // Xcode 27 SDK is what will disable it, or simply running on
347 // macOS 27. Let's go with the latter for now.
348 if (QMacVersion::currentRuntime().majorVersion() < 27) {
349 const id liquidGlassOptOut = [NSBundle.mainBundle
350 objectForInfoDictionaryKey:@"UIDesignRequiresCompatibility"];
351 if (liquidGlassOptOut && [liquidGlassOptOut boolValue])
352 return false;
353 }
354
355 return true;
356 }();
357
358 return runningWithLiquidGlass;
359}
360
361std::optional<uint32_t> qt_mac_sipConfiguration()
362{
363 static auto configuration = []() -> std::optional<uint32_t> {
364#if !defined(QT_APPLE_NO_PRIVATE_APIS)
365 csr_config_t config;
366 if (csr_get_active_config && csr_get_active_config(&config) == 0)
367 return config;
368#endif
369
370 QIOType<io_registry_entry_t> nvram = IORegistryEntryFromPath(kIOMainPortDefault, "IODeviceTree:/options");
371 if (!nvram) {
372 qWarning("Failed to locate NVRAM entry in IO registry");
373 return {};
374 }
375
376 QCFType<CFTypeRef> csrConfig = IORegistryEntryCreateCFProperty(nvram,
377 CFSTR("csr-active-config"), kCFAllocatorDefault, IOOptionBits{});
378 if (!csrConfig)
379 return {}; // SIP config is not available
380
381 if (auto type = CFGetTypeID(csrConfig); type != CFDataGetTypeID()) {
382#ifndef QT_NO_DEBUG_STREAM
383 qWarning() << "Unexpected SIP config type" << CFCopyTypeIDDescription(type);
384#endif
385 return {};
386 }
387
388 QByteArray data = QByteArray::fromRawCFData(csrConfig.as<CFDataRef>());
389 if (data.size() != sizeof(uint32_t)) {
390 qWarning("Unexpected SIP config size %td", ptrdiff_t(data.size()));
391 return {};
392 }
393
394 return qFromLittleEndian<uint32_t>(data.constData());
395 }();
396 return configuration;
397}
398
399#define CHECK_SPAWN(expr)
400 if ((expr) != 0) {
401 posix_spawnattr_destroy(&attr);
402 return;
403 }
404
405#ifdef QT_BUILD_INTERNAL
406void qt_mac_ensureResponsible()
407{
408#if !defined(QT_APPLE_NO_PRIVATE_APIS)
409 if (!responsibility_get_pid_responsible_for_pid || !responsibility_spawnattrs_setdisclaim)
410 return;
411
412 auto pid = getpid();
413 if (responsibility_get_pid_responsible_for_pid(pid) == pid)
414 return; // Already responsible
415
416 posix_spawnattr_t attr = {};
417 CHECK_SPAWN(posix_spawnattr_init(&attr));
418
419 // Behave as exec
420 short flags = POSIX_SPAWN_SETEXEC;
421
422 // Reset signal mask
423 sigset_t no_signals;
424 sigemptyset(&no_signals);
425 CHECK_SPAWN(posix_spawnattr_setsigmask(&attr, &no_signals));
426 flags |= POSIX_SPAWN_SETSIGMASK;
427
428 // Reset all signals to their default handlers
429 sigset_t all_signals;
430 sigfillset(&all_signals);
431 CHECK_SPAWN(posix_spawnattr_setsigdefault(&attr, &all_signals));
432 flags |= POSIX_SPAWN_SETSIGDEF;
433
434 CHECK_SPAWN(posix_spawnattr_setflags(&attr, flags));
435
436 CHECK_SPAWN(responsibility_spawnattrs_setdisclaim(&attr, 1));
437
438 char **argv = *_NSGetArgv();
439 posix_spawnp(&pid, argv[0], nullptr, &attr, argv, environ);
440 posix_spawnattr_destroy(&attr);
441#endif
442}
443#endif // QT_BUILD_INTERNAL
444
445#endif
446
448{
449 static bool isExtension = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"NSExtension"];
450 return isExtension;
451}
452
453#if !defined(QT_BOOTSTRAPPED) && !defined(Q_OS_WATCHOS)
455{
456 // Application extensions are not allowed to access the shared application
458 qWarning() << "accessing the shared" << [AppleApplication class]
459 << "is not allowed in application extensions";
460
461 // In practice the application is actually available, but the App
462 // review process will likely catch uses of it, so we return nil just
463 // in case, unless we don't care about being App Store compliant.
464#if QT_CONFIG(appstore_compliant)
465 return nil;
466#endif
467 }
468
469 // We use performSelector so that building with -fapplication-extension will
470 // not mistakenly think we're using the shared application in extensions.
471 return [[AppleApplication class] performSelector:@selector(sharedApplication)];
472}
473#endif
474
475#if !defined(QT_BOOTSTRAPPED)
476
477#if defined(Q_OS_MACOS)
478namespace {
479struct SandboxChecker
480{
481 SandboxChecker() : m_thread([this]{
482 m_isSandboxed = []{
483 QCFType<SecStaticCodeRef> staticCode = nullptr;
484 NSURL *executableUrl = NSBundle.mainBundle.executableURL;
485 if (SecStaticCodeCreateWithPath((__bridge CFURLRef)executableUrl,
486 kSecCSDefaultFlags, &staticCode) != errSecSuccess)
487 return false;
488
489 QCFType<SecRequirementRef> sandboxRequirement;
490 if (SecRequirementCreateWithString(CFSTR("entitlement[\"com.apple.security.app-sandbox\"] exists"),
491 kSecCSDefaultFlags, &sandboxRequirement) != errSecSuccess)
492 return false;
493
494 if (SecStaticCodeCheckValidityWithErrors(staticCode,
495 kSecCSBasicValidateOnly, sandboxRequirement, nullptr) != errSecSuccess)
496 return false;
497
498 return true;
499 }();
500 })
501 {}
502 ~SandboxChecker() {
503 std::scoped_lock lock(m_mutex);
504 if (m_thread.joinable())
505 m_thread.detach();
506 }
507 bool isSandboxed() const {
508 std::scoped_lock lock(m_mutex);
509 if (m_thread.joinable())
510 m_thread.join();
511 return m_isSandboxed;
512 }
513private:
514 bool m_isSandboxed;
515 mutable std::thread m_thread;
516 mutable std::mutex m_mutex;
517};
518} // namespace
519static SandboxChecker sandboxChecker;
520#endif // Q_OS_MACOS
521
523{
524#if defined(Q_OS_MACOS)
525 return sandboxChecker.isSandboxed();
526#else
527 return true; // All other Apple platforms
528#endif
529}
530
531QT_END_NAMESPACE
532@implementation NSObject (QtExtras)
533- (id)qt_valueForPrivateKey:(NSString *)key
534{
535 if (qt_apple_isSandboxed())
536 return nil;
537
538 return [self valueForKey:key];
539}
540@end
541QT_BEGIN_NAMESPACE
542#endif // !QT_BOOTSTRAPPED
543
544#ifdef Q_OS_MACOS
545/*
546 Ensure that Objective-C objects auto-released in main(), directly or indirectly,
547 after QCoreApplication construction, are released when the app goes out of scope.
548 The memory will be reclaimed by the system either way when the process exits,
549 but by having a root level pool we ensure that the objects get their dealloc
550 methods called, which is useful for debugging object ownership graphs, etc.
551*/
552
553QT_END_NAMESPACE
554#define ROOT_LEVEL_POOL_MARKER QT_ROOT_LEVEL_POOL__THESE_OBJECTS_WILL_BE_RELEASED_WHEN_QAPP_GOES_OUT_OF_SCOPE
555@interface QT_MANGLE_NAMESPACE(ROOT_LEVEL_POOL_MARKER) : NSObject @end
556@implementation QT_MANGLE_NAMESPACE(ROOT_LEVEL_POOL_MARKER) @end
557QT_NAMESPACE_ALIAS_OBJC_CLASS(ROOT_LEVEL_POOL_MARKER);
558QT_BEGIN_NAMESPACE
559
560const char ROOT_LEVEL_POOL_DISABLE_SWITCH[] = "QT_DISABLE_ROOT_LEVEL_AUTORELEASE_POOL";
561
562QMacRootLevelAutoReleasePool::QMacRootLevelAutoReleasePool()
563{
564 if (qEnvironmentVariableIsSet(ROOT_LEVEL_POOL_DISABLE_SWITCH))
565 return;
566
567 pool.emplace();
568
569 [[[ROOT_LEVEL_POOL_MARKER alloc] init] autorelease];
570
571 if (qstrcmp(qgetenv("OBJC_DEBUG_MISSING_POOLS"), "YES") == 0) {
572 qDebug("QCoreApplication root level NSAutoreleasePool in place. Break on ~%s and use\n"
573 "'p [NSAutoreleasePool showPools]' to show leaked objects, or set %s",
574 __FUNCTION__, ROOT_LEVEL_POOL_DISABLE_SWITCH);
575 }
576}
577
578QMacRootLevelAutoReleasePool::~QMacRootLevelAutoReleasePool()
579{
580}
581#endif
582
583// -------------------------------------------------------------------------
584
585#ifndef QT_BOOTSTRAPPED
586void qt_apple_check_os_version()
587{
588#if defined(__WATCH_OS_VERSION_MIN_REQUIRED)
589 const char *os = "watchOS";
590 const int version = __WATCH_OS_VERSION_MIN_REQUIRED;
591#elif defined(__TV_OS_VERSION_MIN_REQUIRED)
592 const char *os = "tvOS";
593 const int version = __TV_OS_VERSION_MIN_REQUIRED;
594#elif defined(__VISION_OS_VERSION_MIN_REQUIRED)
595 const char *os = "visionOS";
596 const int version = __VISION_OS_VERSION_MIN_REQUIRED;
597#elif defined(__IPHONE_OS_VERSION_MIN_REQUIRED)
598 const char *os = "iOS";
599 const int version = __IPHONE_OS_VERSION_MIN_REQUIRED;
600#elif defined(__MAC_OS_X_VERSION_MIN_REQUIRED)
601 const char *os = "macOS";
602 const int version = __MAC_OS_X_VERSION_MIN_REQUIRED;
603#endif
604
605 const auto required = QVersionNumber(version / 10000, version / 100 % 100, version % 100);
606 const auto current = QOperatingSystemVersion::current().version();
607
608#if defined(Q_OS_MACOS)
609 // Check for compatibility version, in which case we can't do a
610 // comparison to the deployment target, which might be e.g. 11.0
611 if (current.majorVersion() == 10 && current.minorVersion() >= 16)
612 return;
613#endif
614
615 if (current < required) {
616 NSDictionary *plist = NSBundle.mainBundle.infoDictionary;
617 NSString *applicationName = plist[@"CFBundleDisplayName"];
618 if (!applicationName)
619 applicationName = plist[@"CFBundleName"];
620 if (!applicationName)
621 applicationName = NSProcessInfo.processInfo.processName;
622
623 fprintf(stderr, "Sorry, \"%s\" cannot be run on this version of %s. "
624 "Qt requires %s %ld.%ld.%ld or later, you have %s %ld.%ld.%ld.\n",
625 applicationName.UTF8String, os,
626 os, long(required.majorVersion()), long(required.minorVersion()), long(required.microVersion()),
627 os, long(current.majorVersion()), long(current.minorVersion()), long(current.microVersion()));
628
629 exit(1);
630 }
631}
633#endif // QT_BOOTSTRAPPED
634
635// -------------------------------------------------------------------------
636
637void QMacNotificationObserver::remove()
638{
639 if (observer)
640 [[NSNotificationCenter defaultCenter] removeObserver:observer];
641 observer = nullptr;
642}
643
644// -------------------------------------------------------------------------
645
646QMacKeyValueObserver::QMacKeyValueObserver(const QMacKeyValueObserver &other)
647 : QMacKeyValueObserver(other.object, other.keyPath, *other.callback.get())
648{
649}
650
651void QMacKeyValueObserver::addObserver(NSKeyValueObservingOptions options)
652{
653 [object addObserver:observer forKeyPath:keyPath options:options context:callback.get()];
654}
655
656void QMacKeyValueObserver::removeObserver() {
657 if (object)
658 [object removeObserver:observer forKeyPath:keyPath context:callback.get()];
659 object = nil;
660}
661
662KeyValueObserver *QMacKeyValueObserver::observer = [[KeyValueObserver alloc] init];
663
664QT_END_NAMESPACE
665@implementation QT_MANGLE_NAMESPACE(KeyValueObserver)
666- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object
667 change:(NSDictionary<NSKeyValueChangeKey, id> *)change context:(void *)context
668{
669 Q_UNUSED(keyPath);
670 Q_UNUSED(object);
671 Q_UNUSED(change);
672
673 (*reinterpret_cast<QMacKeyValueObserver::Callback*>(context))();
674}
675@end
676QT_BEGIN_NAMESPACE
677
678// -------------------------------------------------------------------------
679
680#ifndef QT_BOOTSTRAPPED
681QOperatingSystemVersion QMacVersion::buildSDK(VersionTarget target)
682{
683 switch (target) {
684 case ApplicationBinary: return applicationVersion().second;
685 case QtLibraries: return libraryVersion().second;
686 }
687 Q_UNREACHABLE();
688}
689
690QOperatingSystemVersion QMacVersion::deploymentTarget(VersionTarget target)
691{
692 switch (target) {
693 case ApplicationBinary: return applicationVersion().first;
694 case QtLibraries: return libraryVersion().first;
695 }
696 Q_UNREACHABLE();
697}
698
699QOperatingSystemVersion QMacVersion::currentRuntime()
700{
701 return QOperatingSystemVersion::current();
702}
703
704// Mach-O platforms
705enum Platform {
706 macOS = 1,
707 iOS = 2,
708 tvOS = 3,
709 watchOS = 4,
710 bridgeOS = 5,
711 macCatalyst = 6,
712 iOSSimulator = 7,
713 tvOSSimulator = 8,
714 watchOSSimulator = 9
715};
716
717QMacVersion::VersionTuple QMacVersion::versionsForImage(const mach_header *machHeader)
718{
719 static auto osForLoadCommand = [](uint32_t cmd) {
720 switch (cmd) {
721 case LC_VERSION_MIN_MACOSX: return QOperatingSystemVersion::MacOS;
722 case LC_VERSION_MIN_IPHONEOS: return QOperatingSystemVersion::IOS;
723 case LC_VERSION_MIN_TVOS: return QOperatingSystemVersion::TvOS;
724 case LC_VERSION_MIN_WATCHOS: return QOperatingSystemVersion::WatchOS;
725 default: return QOperatingSystemVersion::Unknown;
726 }
727 };
728
729 static auto osForPlatform = [](uint32_t platform) {
730 switch (platform) {
731 case Platform::macOS:
732 return QOperatingSystemVersion::MacOS;
733 case Platform::iOS:
734 case Platform::iOSSimulator:
735 return QOperatingSystemVersion::IOS;
736 case Platform::tvOS:
737 case Platform::tvOSSimulator:
738 return QOperatingSystemVersion::TvOS;
739 case Platform::watchOS:
740 case Platform::watchOSSimulator:
741 return QOperatingSystemVersion::WatchOS;
742 default:
743 return QOperatingSystemVersion::Unknown;
744 }
745 };
746
747 static auto makeVersionTuple = [](uint32_t dt, uint32_t sdk, QOperatingSystemVersion::OSType osType) {
748 return std::pair(
749 QOperatingSystemVersion(osType, dt >> 16 & 0xffff, dt >> 8 & 0xff, dt & 0xff),
750 QOperatingSystemVersion(osType, sdk >> 16 & 0xffff, sdk >> 8 & 0xff, sdk & 0xff)
751 );
752 };
753
754 const bool is64Bit = machHeader->magic == MH_MAGIC_64 || machHeader->magic == MH_CIGAM_64;
755 auto commandCursor = uintptr_t(machHeader) + (is64Bit ? sizeof(mach_header_64) : sizeof(mach_header));
756
757 for (uint32_t i = 0; i < machHeader->ncmds; ++i) {
758 load_command *loadCommand = reinterpret_cast<load_command *>(commandCursor);
759 if (loadCommand->cmd == LC_VERSION_MIN_MACOSX || loadCommand->cmd == LC_VERSION_MIN_IPHONEOS
760 || loadCommand->cmd == LC_VERSION_MIN_TVOS || loadCommand->cmd == LC_VERSION_MIN_WATCHOS) {
761 auto versionCommand = reinterpret_cast<version_min_command *>(loadCommand);
762 return makeVersionTuple(versionCommand->version, versionCommand->sdk, osForLoadCommand(loadCommand->cmd));
763 } else if (loadCommand->cmd == LC_BUILD_VERSION) {
764 auto versionCommand = reinterpret_cast<build_version_command *>(loadCommand);
765 return makeVersionTuple(versionCommand->minos, versionCommand->sdk, osForPlatform(versionCommand->platform));
766 }
767 commandCursor += loadCommand->cmdsize;
768 }
769 Q_ASSERT_X(false, "QMacVersion", "Could not find any version load command");
770 Q_UNREACHABLE();
771}
772
773QMacVersion::VersionTuple QMacVersion::applicationVersion()
774{
775 static VersionTuple version = []() {
776 const mach_header *executableHeader = nullptr;
777 for (uint32_t i = 0; i < _dyld_image_count(); ++i) {
778 auto header = _dyld_get_image_header(i);
779 if (header->filetype == MH_EXECUTE) {
780 executableHeader = header;
781 break;
782 }
783 }
784 Q_ASSERT_X(executableHeader, "QMacVersion", "Failed to resolve Mach-O header of executable");
785 return versionsForImage(executableHeader);
786 }();
787 return version;
788}
789
790QMacVersion::VersionTuple QMacVersion::libraryVersion()
791{
792 static VersionTuple version = []() {
793 Dl_info qtCoreImage;
794 dladdr((const void *)&QMacVersion::libraryVersion, &qtCoreImage);
795 Q_ASSERT_X(qtCoreImage.dli_fbase, "QMacVersion", "Failed to resolve Mach-O header of QtCore");
796 return versionsForImage(static_cast<mach_header*>(qtCoreImage.dli_fbase));
797 }();
798 return version;
799}
800#endif // QT_BOOTSTRAPPED
801
802// -------------------------------------------------------------------------
803
804QObjCWeakPointerBase::QObjCWeakPointerBase(NSObject *object)
805 : m_weakReference(object)
806{
807}
808
809QObjCWeakPointerBase::QObjCWeakPointerBase(const QObjCWeakPointerBase &other)
810{
811 QMacAutoReleasePool pool;
812 m_weakReference = other.m_weakReference;
813}
814
815QObjCWeakPointerBase &QObjCWeakPointerBase::operator=(const QObjCWeakPointerBase &other)
816{
817 QMacAutoReleasePool pool;
818 m_weakReference = other.m_weakReference;
819 return *this;
820}
821
822QObjCWeakPointerBase::~QObjCWeakPointerBase()
823{
824 QMacAutoReleasePool pool;
825 m_weakReference = nil;
826}
827
828NSObject *QObjCWeakPointerBase::get() const
829{
830 // Loading from a __weak variable will retain and auto-release (in non-ARC).
831 // Unlike cases above, we want the object to stay alive until the outer
832 // auto-release pool is drained, so that consumers of QObjCWeakPointer
833 // can trust that the variable they get back will be alive, similar to
834 // the semantics of loading from __weak.
835 return m_weakReference;
836}
837
838// -------------------------------------------------------------------------
839
840QT_END_NAMESPACE
QT_FOR_EACH_CORE_FOUNDATION_TYPE(QT_DECLARE_WEAK_QDEBUG_OPERATOR_FOR_CF_TYPE)
int csr_get_active_config(csr_config_t *) __attribute__((weak_import))
uint32_t csr_config_t
Definition qcore_mac.mm:38
void objc_autoreleasePoolPop(void *pool)
QT_END_NAMESPACE QT_USE_NAMESPACE void * objc_autoreleasePoolPush(void)
QDebug operator<<(QDebug dbg, const NSObject *nsObject)
Definition qcore_mac.mm:201
AppleApplication * qt_apple_sharedApplication()
Definition qcore_mac.mm:454
Q_CONSTRUCTOR_FUNCTION(qt_apple_check_os_version)
Q_CORE_EXPORT bool qt_apple_isApplicationExtension()
Definition qcore_mac.mm:447
Q_CORE_EXPORT bool qt_apple_isSandboxed()
Definition qcore_mac.mm:522
QDebug operator<<(QDebug dbg, const QFileInfo &fi)
QDebug operator<<(QDebug debug, QIODevice::OpenMode modes)