5#include <private/qcore_mac_p.h>
8#include <AppKit/AppKit.h>
11#if defined(QT_PLATFORM_UIKIT)
12#include <UIKit/UIKit.h>
15#include <UniformTypeIdentifiers/UniformTypeIdentifiers.h>
21#include <objc/runtime.h>
22#include <mach-o/dyld.h>
23#include <sys/sysctl.h>
36#include "private/qlocking_p.h"
38#if !defined(QT_BOOTSTRAPPED)
42#if !defined(QT_APPLE_NO_PRIVATE_APIS)
47#ifdef QT_BUILD_INTERNAL
48int responsibility_spawnattrs_setdisclaim(posix_spawnattr_t attrs,
int disclaim)
49__attribute__((availability(macos,introduced=10.14),weak_import));
50pid_t responsibility_get_pid_responsible_for_pid(pid_t)
__attribute__((weak_import));
59using namespace Qt::StringLiterals;
63#if defined(Q_OS_MACOS)
64static void initializeStandardUserDefaults()
73 Q_UNUSED(NSUserDefaults.standardUserDefaults);
75Q_CONSTRUCTOR_FUNCTION(initializeStandardUserDefaults);
80QCFString::operator QString()
const
82 if (string.isEmpty() && value)
83 const_cast<QCFString*>(
this)->string = QString::fromCFString(value);
87QCFString::operator CFStringRef()
const
90 const_cast<QCFString*>(
this)->value = string.toCFString();
96#if defined(QT_USE_APPLE_UNIFIED_LOGGING)
98bool AppleUnifiedLogger::preventsStderrLogging()
118 static bool willMirror = qEnvironmentVariableIsSet(
"OS_ACTIVITY_DT_MODE")
119 || qEnvironmentVariableIsSet(
"ACTIVITY_LOG_STDERR")
120 || qEnvironmentVariableIsSet(
"CFLOG_FORCE_STDERR");
124 static bool disableStderr = qEnvironmentVariableIsSet(
"CFLOG_FORCE_DISABLE_STDERR");
126 return willMirror || disableStderr;
129QT_MAC_WEAK_IMPORT(_os_log_default);
130bool AppleUnifiedLogger::messageHandler(QtMsgType msgType,
const QMessageLogContext &context,
131 const QString &message,
const QString &optionalSubsystem)
133 QString subsystem = optionalSubsystem;
134 if (subsystem.isNull()) {
135 static QString bundleIdentifier = []() {
136 if (CFBundleRef bundle = CFBundleGetMainBundle()) {
137 if (CFStringRef identifier = CFBundleGetIdentifier(bundle))
138 return QString::fromCFString(identifier);
142 subsystem = bundleIdentifier;
145 const bool isDefault = !context.category || !strcmp(context.category,
"default");
146 os_log_t log = isDefault ? OS_LOG_DEFAULT :
147 os_log_create(subsystem.toLatin1().constData(), context.category);
148 os_log_type_t logType = logTypeForMessageType(msgType);
150 if (!os_log_type_enabled(log, logType))
165 os_log_with_type(log, logType,
"%{public}s", qPrintable(message));
167 return preventsStderrLogging();
170os_log_type_t AppleUnifiedLogger::logTypeForMessageType(QtMsgType msgType)
173 case QtDebugMsg:
return OS_LOG_TYPE_DEBUG;
174 case QtInfoMsg:
return OS_LOG_TYPE_INFO;
175 case QtWarningMsg:
return OS_LOG_TYPE_DEFAULT;
176 case QtCriticalMsg:
return OS_LOG_TYPE_ERROR;
177 case QtFatalMsg:
return OS_LOG_TYPE_FAULT;
180 return OS_LOG_TYPE_DEFAULT;
185#ifndef QT_NO_DEBUG_STREAM
196 for (Class cls = object_getClass(obj); cls; cls = class_getSuperclass(cls)) {
197 if (cls == NSObject.
class) {
198 dbg <<
static_cast<NSObject*>(obj);
204 const QDebugStateSaver saver(dbg);
205 dbg.nospace() <<
'<' << object_getClassName(obj) <<
": " <<
static_cast<
void*>(obj) <<
'>';
211 return dbg << (nsObject ?
212 dbg.verbosity() > 2 ?
213 nsObject.debugDescription.UTF8String :
214 nsObject.description.UTF8String
221 return dbg <<
"NSString(0x0)";
223 return dbg << (__bridge CFStringRef)nsString;
229 return dbg <<
"CFStringRef(0x0)";
231 if (
const UniChar *chars = CFStringGetCharactersPtr(stringRef))
232 dbg << QStringView(
reinterpret_cast<
const QChar *>(chars), CFStringGetLength(stringRef));
234 dbg << QString::fromCFString(stringRef);
241 dbg << QPointF::fromCGPoint(point);
247 dbg << QSizeF::fromCGSize(size);
253 dbg << QRectF::fromCGRect(rect);
257#if defined(Q_OS_MACOS)
258QDebug operator<<(QDebug dbg, NSEdgeInsets insets)
263 dbg <<
QMargins(insets.left, insets.top, insets.right, insets.bottom);
269#define QT_DECLARE_WEAK_QDEBUG_OPERATOR_FOR_CF_TYPE(CFType)
270 __attribute__((weak)) Q_DECLARE_QDEBUG_OPERATOR_FOR_CF_TYPE(CFType)
284@interface QT_MANGLE_NAMESPACE(QMacAutoReleasePoolTracker) : NSObject
287@implementation QT_MANGLE_NAMESPACE(QMacAutoReleasePoolTracker)
289QT_NAMESPACE_ALIAS_OBJC_CLASS(QMacAutoReleasePoolTracker);
304QMacAutoReleasePool::QMacAutoReleasePool()
305 : pool(objc_autoreleasePoolPush())
308 static const bool debugAutoReleasePools = qEnvironmentVariableIsSet(
"QT_DARWIN_DEBUG_AUTORELEASEPOOLS");
309 if (!debugAutoReleasePools)
312 Class trackerClass = [QMacAutoReleasePoolTracker
class];
314 void *poolFrame =
nullptr;
316 if (backtrace_from_fp(__builtin_frame_address(0), frames, 2))
317 poolFrame = frames[1];
321 if (dladdr(poolFrame, &info) && info.dli_sname) {
322 const char *symbolName = info.dli_sname;
323 if (symbolName[0] ==
'_') {
325 if (
char *demangled = abi::__cxa_demangle(info.dli_sname,
nullptr, 0, &status))
326 symbolName = demangled;
329 char *className =
nullptr;
330 asprintf(&className,
" ^-- allocated in function: %s", symbolName);
332 if (Class existingClass = objc_getClass(className))
333 trackerClass = existingClass;
335 trackerClass = objc_duplicateClass(trackerClass, className, 0);
339 if (symbolName != info.dli_sname)
340 free((
char*)symbolName);
344 [[trackerClass
new] autorelease];
348QMacAutoReleasePool::~QMacAutoReleasePool()
350 objc_autoreleasePoolPop(pool);
353#ifndef QT_NO_DEBUG_STREAM
356 QDebugStateSaver saver(debug);
358 debug <<
"QMacAutoReleasePool(" << (
const void *)pool <<
')';
364 debug <<
static_cast<QString>(string);
369#if defined(Q_OS_MACOS) && !defined(QT_BOOTSTRAPPED)
370bool qt_mac_runningUnderRosetta()
373 auto size =
sizeof(translated);
374 if (sysctlbyname(
"sysctl.proc_translated", &translated, &size,
nullptr, 0) == 0)
379bool qt_apple_runningWithLiquidGlass()
381 static const bool runningWithLiquidGlass = []{
382 QMacAutoReleasePool pool;
384 if (QMacVersion::buildSDK(QMacVersion::ApplicationBinary).majorVersion() < 26)
387 if (QMacVersion::currentRuntime().majorVersion() < 26)
394 if (QMacVersion::currentRuntime().majorVersion() < 27) {
395 const id liquidGlassOptOut = [NSBundle.mainBundle
396 objectForInfoDictionaryKey:@
"UIDesignRequiresCompatibility"];
397 if (liquidGlassOptOut && [liquidGlassOptOut boolValue])
404 return runningWithLiquidGlass;
407std::optional<uint32_t> qt_mac_sipConfiguration()
409 static auto configuration = []() -> std::optional<uint32_t> {
410#if !defined(QT_APPLE_NO_PRIVATE_APIS)
412 if (csr_get_active_config && csr_get_active_config(&config) == 0)
416 QIOType<io_registry_entry_t> nvram = IORegistryEntryFromPath(kIOMainPortDefault,
"IODeviceTree:/options");
418 qWarning(
"Failed to locate NVRAM entry in IO registry");
422 QCFType<CFTypeRef> csrConfig = IORegistryEntryCreateCFProperty(nvram,
423 CFSTR(
"csr-active-config"), kCFAllocatorDefault, IOOptionBits{});
427 if (
auto type = CFGetTypeID(csrConfig); type != CFDataGetTypeID()) {
428#ifndef QT_NO_DEBUG_STREAM
429 qWarning() <<
"Unexpected SIP config type" << CFCopyTypeIDDescription(type);
434 QByteArray data = QByteArray::fromRawCFData(csrConfig.as<CFDataRef>());
435 if (data.size() !=
sizeof(uint32_t)) {
436 qWarning(
"Unexpected SIP config size %td", ptrdiff_t(data.size()));
440 return qFromLittleEndian<uint32_t>(data.constData());
442 return configuration;
445bool qt_mac_processHasEntitlement(
const QString &entitlement)
447 if (QCFType<SecTaskRef> task = SecTaskCreateFromSelf(kCFAllocatorDefault)) {
448 if (QCFType<CFTypeRef> value = SecTaskCopyValueForEntitlement(task,
449 entitlement.toCFString(),
nullptr)) {
451 if (CFGetTypeID(value) != CFBooleanGetTypeID())
454 return CFBooleanGetValue(value.as<CFBooleanRef>());
460#define CHECK_SPAWN(expr)
462 posix_spawnattr_destroy(&attr);
466#ifdef QT_BUILD_INTERNAL
467void qt_mac_ensureResponsible()
469#if !defined(QT_APPLE_NO_PRIVATE_APIS)
470 if (!responsibility_get_pid_responsible_for_pid || !responsibility_spawnattrs_setdisclaim)
474 if (responsibility_get_pid_responsible_for_pid(pid) == pid)
477 posix_spawnattr_t attr = {};
478 CHECK_SPAWN(posix_spawnattr_init(&attr));
481 short flags = POSIX_SPAWN_SETEXEC;
485 sigemptyset(&no_signals);
486 CHECK_SPAWN(posix_spawnattr_setsigmask(&attr, &no_signals));
487 flags |= POSIX_SPAWN_SETSIGMASK;
490 sigset_t all_signals;
491 sigfillset(&all_signals);
492 CHECK_SPAWN(posix_spawnattr_setsigdefault(&attr, &all_signals));
493 flags |= POSIX_SPAWN_SETSIGDEF;
495 CHECK_SPAWN(posix_spawnattr_setflags(&attr, flags));
497 CHECK_SPAWN(responsibility_spawnattrs_setdisclaim(&attr, 1));
499 char **argv = *_NSGetArgv();
500 posix_spawnp(&pid, argv[0],
nullptr, &attr, argv, environ);
501 posix_spawnattr_destroy(&attr);
510 static bool isExtension = [[NSBundle mainBundle] objectForInfoDictionaryKey:@
"NSExtension"];
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
533 NSURL *url = [NSURL fileURLWithPath:path.toNSString() isDirectory:YES];
535 if (![url getResourceValue:&type forKey:NSURLContentTypeKey error:nil] || !type)
537 if (![type conformsToType:UTTypeBundle])
542#if !defined(QT_BOOTSTRAPPED) && !defined(Q_OS_WATCHOS)
547 qWarning() <<
"accessing the shared" << [AppleApplication
class]
548 <<
"is not allowed in application extensions";
553#if QT_CONFIG(appstore_compliant)
560 return [[AppleApplication
class] performSelector:@selector(sharedApplication)];
564#if !defined(QT_BOOTSTRAPPED)
568#if defined(Q_OS_MACOS)
569 static bool isSandboxed = qt_mac_processHasEntitlement(u"com.apple.security.app-sandbox"_s);
577@implementation NSObject (QtExtras)
578- (id)qt_valueForPrivateKey:(NSString *)key
580 if (qt_apple_isSandboxed())
583 return [self valueForKey:key];
591
592
593
594
595
596
599#define ROOT_LEVEL_POOL_MARKER QT_ROOT_LEVEL_POOL__THESE_OBJECTS_WILL_BE_RELEASED_WHEN_QAPP_GOES_OUT_OF_SCOPE
600@interface QT_MANGLE_NAMESPACE(ROOT_LEVEL_POOL_MARKER) : NSObject @end
601@implementation QT_MANGLE_NAMESPACE(ROOT_LEVEL_POOL_MARKER) @end
602QT_NAMESPACE_ALIAS_OBJC_CLASS(ROOT_LEVEL_POOL_MARKER);
605const char ROOT_LEVEL_POOL_DISABLE_SWITCH[] =
"QT_DISABLE_ROOT_LEVEL_AUTORELEASE_POOL";
607QMacRootLevelAutoReleasePool::QMacRootLevelAutoReleasePool()
609 if (qEnvironmentVariableIsSet(ROOT_LEVEL_POOL_DISABLE_SWITCH))
614 [[[ROOT_LEVEL_POOL_MARKER alloc] init] autorelease];
616 if (qstrcmp(qgetenv(
"OBJC_DEBUG_MISSING_POOLS"),
"YES") == 0) {
617 qDebug(
"QCoreApplication root level NSAutoreleasePool in place. Break on ~%s and use\n"
618 "'p [NSAutoreleasePool showPools]' to show leaked objects, or set %s",
619 __FUNCTION__, ROOT_LEVEL_POOL_DISABLE_SWITCH);
623QMacRootLevelAutoReleasePool::~QMacRootLevelAutoReleasePool()
630#ifndef QT_BOOTSTRAPPED
631void qt_apple_check_os_version()
633#if defined(__WATCH_OS_VERSION_MIN_REQUIRED)
634 const char *os =
"watchOS";
635 const int version = __WATCH_OS_VERSION_MIN_REQUIRED;
636#elif defined(__TV_OS_VERSION_MIN_REQUIRED)
637 const char *os =
"tvOS";
638 const int version = __TV_OS_VERSION_MIN_REQUIRED;
639#elif defined(__VISION_OS_VERSION_MIN_REQUIRED)
640 const char *os =
"visionOS";
641 const int version = __VISION_OS_VERSION_MIN_REQUIRED;
642#elif defined(__IPHONE_OS_VERSION_MIN_REQUIRED)
643 const char *os =
"iOS";
644 const int version = __IPHONE_OS_VERSION_MIN_REQUIRED;
645#elif defined(__MAC_OS_X_VERSION_MIN_REQUIRED)
646 const char *os =
"macOS";
647 const int version = __MAC_OS_X_VERSION_MIN_REQUIRED;
650 const auto required = QVersionNumber(version / 10000, version / 100 % 100, version % 100);
651 const auto current = QOperatingSystemVersion::current().version();
653#if defined(Q_OS_MACOS)
656 if (current.majorVersion() == 10 && current.minorVersion() >= 16)
660 if (current < required) {
661 NSDictionary *plist = NSBundle.mainBundle.infoDictionary;
662 NSString *applicationName = plist[@
"CFBundleDisplayName"];
663 if (!applicationName)
664 applicationName = plist[@
"CFBundleName"];
665 if (!applicationName)
666 applicationName = NSProcessInfo.processInfo.processName;
668 fprintf(stderr,
"Sorry, \"%s\" cannot be run on this version of %s. "
669 "Qt requires %s %ld.%ld.%ld or later, you have %s %ld.%ld.%ld.\n",
670 applicationName.UTF8String, os,
671 os,
long(required.majorVersion()),
long(required.minorVersion()),
long(required.microVersion()),
672 os,
long(current.majorVersion()),
long(current.minorVersion()),
long(current.microVersion()));
682void QMacNotificationObserver::remove()
685 [[NSNotificationCenter defaultCenter] removeObserver:observer];
691QMacKeyValueObserver::QMacKeyValueObserver(
const QMacKeyValueObserver &other)
692 : QMacKeyValueObserver(other.object, other.keyPath, *other.callback.get())
696void QMacKeyValueObserver::addObserver(NSKeyValueObservingOptions options)
698 [object addObserver:observer forKeyPath:keyPath options:options context:callback.get()];
701void QMacKeyValueObserver::removeObserver() {
703 [object removeObserver:observer forKeyPath:keyPath context:callback.get()];
707KeyValueObserver *QMacKeyValueObserver::observer = [[KeyValueObserver alloc] init];
710@implementation QT_MANGLE_NAMESPACE(KeyValueObserver)
711- (
void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object
712 change:(NSDictionary<NSKeyValueChangeKey, id> *)change context:(
void *)context
718 (*
reinterpret_cast<QMacKeyValueObserver::Callback*>(context))();
725#ifndef QT_BOOTSTRAPPED
726QOperatingSystemVersion QMacVersion::buildSDK(VersionTarget target)
729 case ApplicationBinary:
return applicationVersion().second;
730 case QtLibraries:
return libraryVersion().second;
735QOperatingSystemVersion QMacVersion::deploymentTarget(VersionTarget target)
738 case ApplicationBinary:
return applicationVersion().first;
739 case QtLibraries:
return libraryVersion().first;
744QOperatingSystemVersion QMacVersion::currentRuntime()
746 return QOperatingSystemVersion::current();
762QMacVersion::VersionTuple QMacVersion::versionsForImage(
const mach_header *machHeader)
764 static auto osForLoadCommand = [](uint32_t cmd) {
766 case LC_VERSION_MIN_MACOSX:
return QOperatingSystemVersion::MacOS;
767 case LC_VERSION_MIN_IPHONEOS:
return QOperatingSystemVersion::IOS;
768 case LC_VERSION_MIN_TVOS:
return QOperatingSystemVersion::TvOS;
769 case LC_VERSION_MIN_WATCHOS:
return QOperatingSystemVersion::WatchOS;
770 default:
return QOperatingSystemVersion::Unknown;
774 static auto osForPlatform = [](uint32_t platform) {
776 case Platform::macOS:
777 return QOperatingSystemVersion::MacOS;
779 case Platform::iOSSimulator:
780 return QOperatingSystemVersion::IOS;
782 case Platform::tvOSSimulator:
783 return QOperatingSystemVersion::TvOS;
784 case Platform::watchOS:
785 case Platform::watchOSSimulator:
786 return QOperatingSystemVersion::WatchOS;
788 return QOperatingSystemVersion::Unknown;
792 static auto makeVersionTuple = [](uint32_t dt, uint32_t sdk, QOperatingSystemVersion::OSType osType) {
794 QOperatingSystemVersion(osType, dt >> 16 & 0xffff, dt >> 8 & 0xff, dt & 0xff),
795 QOperatingSystemVersion(osType, sdk >> 16 & 0xffff, sdk >> 8 & 0xff, sdk & 0xff)
799 const bool is64Bit = machHeader->magic == MH_MAGIC_64 || machHeader->magic == MH_CIGAM_64;
800 auto commandCursor = uintptr_t(machHeader) + (is64Bit ?
sizeof(mach_header_64) :
sizeof(mach_header));
802 for (uint32_t i = 0; i < machHeader->ncmds; ++i) {
803 load_command *loadCommand =
reinterpret_cast<load_command *>(commandCursor);
804 if (loadCommand->cmd == LC_VERSION_MIN_MACOSX || loadCommand->cmd == LC_VERSION_MIN_IPHONEOS
805 || loadCommand->cmd == LC_VERSION_MIN_TVOS || loadCommand->cmd == LC_VERSION_MIN_WATCHOS) {
806 auto versionCommand =
reinterpret_cast<version_min_command *>(loadCommand);
807 return makeVersionTuple(versionCommand->version, versionCommand->sdk, osForLoadCommand(loadCommand->cmd));
808 }
else if (loadCommand->cmd == LC_BUILD_VERSION) {
809 auto versionCommand =
reinterpret_cast<build_version_command *>(loadCommand);
810 return makeVersionTuple(versionCommand->minos, versionCommand->sdk, osForPlatform(versionCommand->platform));
812 commandCursor += loadCommand->cmdsize;
814 Q_ASSERT_X(
false,
"QMacVersion",
"Could not find any version load command");
818QMacVersion::VersionTuple QMacVersion::applicationVersion()
820 static VersionTuple version = []() {
821 const mach_header *executableHeader =
nullptr;
822 for (uint32_t i = 0; i < _dyld_image_count(); ++i) {
823 auto header = _dyld_get_image_header(i);
824 if (header->filetype == MH_EXECUTE) {
825 executableHeader = header;
829 Q_ASSERT_X(executableHeader,
"QMacVersion",
"Failed to resolve Mach-O header of executable");
830 return versionsForImage(executableHeader);
835QMacVersion::VersionTuple QMacVersion::libraryVersion()
837 static VersionTuple version = []() {
839 dladdr((
const void *)&QMacVersion::libraryVersion, &qtCoreImage);
840 Q_ASSERT_X(qtCoreImage.dli_fbase,
"QMacVersion",
"Failed to resolve Mach-O header of QtCore");
841 return versionsForImage(
static_cast<mach_header*>(qtCoreImage.dli_fbase));
849QObjCWeakPointerBase::QObjCWeakPointerBase(NSObject *object)
850 : m_weakReference(object)
854QObjCWeakPointerBase::QObjCWeakPointerBase(
const QObjCWeakPointerBase &other)
856 QMacAutoReleasePool pool;
857 m_weakReference = other.m_weakReference;
860QObjCWeakPointerBase &QObjCWeakPointerBase::operator=(
const QObjCWeakPointerBase &other)
862 QMacAutoReleasePool pool;
863 m_weakReference = other.m_weakReference;
867QObjCWeakPointerBase::~QObjCWeakPointerBase()
869 QMacAutoReleasePool pool;
870 m_weakReference = nil;
873NSObject *QObjCWeakPointerBase::get()
const
880 return m_weakReference;
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))
void objc_autoreleasePoolPop(void *pool)
std::optional< UTType * > qt_apple_bundleType(const QString &path)
QT_END_NAMESPACE QT_USE_NAMESPACE void * objc_autoreleasePoolPush(void)
QDebug operator<<(QDebug dbg, const NSObject *nsObject)
AppleApplication * qt_apple_sharedApplication()
Q_CONSTRUCTOR_FUNCTION(qt_apple_check_os_version)
Q_CORE_EXPORT bool qt_apple_isApplicationExtension()
Q_CORE_EXPORT bool qt_apple_isSandboxed()
QDebug operator<<(QDebug debug, QDir::Filters filters)
QDebug operator<<(QDebug dbg, const QFileInfo &fi)