5#include <private/qcore_mac_p.h>
8#include <AppKit/AppKit.h>
11#if defined(QT_PLATFORM_UIKIT)
12#include <UIKit/UIKit.h>
19#include <objc/runtime.h>
20#include <mach-o/dyld.h>
21#include <sys/sysctl.h>
34#include "private/qlocking_p.h"
36#if !defined(QT_BOOTSTRAPPED)
40#if !defined(QT_APPLE_NO_PRIVATE_APIS)
45#ifdef QT_BUILD_INTERNAL
46int responsibility_spawnattrs_setdisclaim(posix_spawnattr_t attrs,
int disclaim)
47__attribute__((availability(macos,introduced=10.14),weak_import));
48pid_t responsibility_get_pid_responsible_for_pid(pid_t)
__attribute__((weak_import));
57using namespace Qt::StringLiterals;
61#if defined(Q_OS_MACOS)
62static void initializeStandardUserDefaults()
71 Q_UNUSED(NSUserDefaults.standardUserDefaults);
73Q_CONSTRUCTOR_FUNCTION(initializeStandardUserDefaults);
78QCFString::operator QString()
const
80 if (string.isEmpty() && value)
81 const_cast<QCFString*>(
this)->string = QString::fromCFString(value);
85QCFString::operator CFStringRef()
const
88 const_cast<QCFString*>(
this)->value = string.toCFString();
94#if defined(QT_USE_APPLE_UNIFIED_LOGGING)
96bool AppleUnifiedLogger::preventsStderrLogging()
116 static bool willMirror = qEnvironmentVariableIsSet(
"OS_ACTIVITY_DT_MODE")
117 || qEnvironmentVariableIsSet(
"ACTIVITY_LOG_STDERR")
118 || qEnvironmentVariableIsSet(
"CFLOG_FORCE_STDERR");
122 static bool disableStderr = qEnvironmentVariableIsSet(
"CFLOG_FORCE_DISABLE_STDERR");
124 return willMirror || disableStderr;
127QT_MAC_WEAK_IMPORT(_os_log_default);
128bool AppleUnifiedLogger::messageHandler(QtMsgType msgType,
const QMessageLogContext &context,
129 const QString &message,
const QString &optionalSubsystem)
131 QString subsystem = optionalSubsystem;
132 if (subsystem.isNull()) {
133 static QString bundleIdentifier = []() {
134 if (CFBundleRef bundle = CFBundleGetMainBundle()) {
135 if (CFStringRef identifier = CFBundleGetIdentifier(bundle))
136 return QString::fromCFString(identifier);
140 subsystem = bundleIdentifier;
143 const bool isDefault = !context.category || !strcmp(context.category,
"default");
144 os_log_t log = isDefault ? OS_LOG_DEFAULT :
145 os_log_create(subsystem.toLatin1().constData(), context.category);
146 os_log_type_t logType = logTypeForMessageType(msgType);
148 if (!os_log_type_enabled(log, logType))
163 os_log_with_type(log, logType,
"%{public}s", qPrintable(message));
165 return preventsStderrLogging();
168os_log_type_t AppleUnifiedLogger::logTypeForMessageType(QtMsgType msgType)
171 case QtDebugMsg:
return OS_LOG_TYPE_DEBUG;
172 case QtInfoMsg:
return OS_LOG_TYPE_INFO;
173 case QtWarningMsg:
return OS_LOG_TYPE_DEFAULT;
174 case QtCriticalMsg:
return OS_LOG_TYPE_ERROR;
175 case QtFatalMsg:
return OS_LOG_TYPE_FAULT;
178 return OS_LOG_TYPE_DEFAULT;
183#ifndef QT_NO_DEBUG_STREAM
194 for (Class cls = object_getClass(obj); cls; cls = class_getSuperclass(cls)) {
195 if (cls == NSObject.
class) {
196 dbg <<
static_cast<NSObject*>(obj);
202 const QDebugStateSaver saver(dbg);
203 dbg.nospace() <<
'<' << object_getClassName(obj) <<
": " <<
static_cast<
void*>(obj) <<
'>';
209 return dbg << (nsObject ?
210 dbg.verbosity() > 2 ?
211 nsObject.debugDescription.UTF8String :
212 nsObject.description.UTF8String
219 return dbg <<
"NSString(0x0)";
221 return dbg << (__bridge CFStringRef)nsString;
227 return dbg <<
"CFStringRef(0x0)";
229 if (
const UniChar *chars = CFStringGetCharactersPtr(stringRef))
230 dbg << QStringView(
reinterpret_cast<
const QChar *>(chars), CFStringGetLength(stringRef));
232 dbg << QString::fromCFString(stringRef);
239 dbg << QPointF::fromCGPoint(point);
245 dbg << QSizeF::fromCGSize(size);
251 dbg << QRectF::fromCGRect(rect);
255#if defined(Q_OS_MACOS)
256QDebug operator<<(QDebug dbg, NSEdgeInsets insets)
261 dbg <<
QMargins(insets.left, insets.top, insets.right, insets.bottom);
267#define QT_DECLARE_WEAK_QDEBUG_OPERATOR_FOR_CF_TYPE(CFType)
268 __attribute__((weak)) Q_DECLARE_QDEBUG_OPERATOR_FOR_CF_TYPE(CFType)
282@interface QT_MANGLE_NAMESPACE(QMacAutoReleasePoolTracker) : NSObject
285@implementation QT_MANGLE_NAMESPACE(QMacAutoReleasePoolTracker)
287QT_NAMESPACE_ALIAS_OBJC_CLASS(QMacAutoReleasePoolTracker);
302QMacAutoReleasePool::QMacAutoReleasePool()
303 : pool(objc_autoreleasePoolPush())
306 static const bool debugAutoReleasePools = qEnvironmentVariableIsSet(
"QT_DARWIN_DEBUG_AUTORELEASEPOOLS");
307 if (!debugAutoReleasePools)
310 Class trackerClass = [QMacAutoReleasePoolTracker
class];
312 void *poolFrame =
nullptr;
314 if (backtrace_from_fp(__builtin_frame_address(0), frames, 2))
315 poolFrame = frames[1];
319 if (dladdr(poolFrame, &info) && info.dli_sname) {
320 const char *symbolName = info.dli_sname;
321 if (symbolName[0] ==
'_') {
323 if (
char *demangled = abi::__cxa_demangle(info.dli_sname,
nullptr, 0, &status))
324 symbolName = demangled;
327 char *className =
nullptr;
328 asprintf(&className,
" ^-- allocated in function: %s", symbolName);
330 if (Class existingClass = objc_getClass(className))
331 trackerClass = existingClass;
333 trackerClass = objc_duplicateClass(trackerClass, className, 0);
337 if (symbolName != info.dli_sname)
338 free((
char*)symbolName);
342 [[trackerClass
new] autorelease];
346QMacAutoReleasePool::~QMacAutoReleasePool()
348 objc_autoreleasePoolPop(pool);
351#ifndef QT_NO_DEBUG_STREAM
354 QDebugStateSaver saver(debug);
356 debug <<
"QMacAutoReleasePool(" << (
const void *)pool <<
')';
362 debug <<
static_cast<QString>(string);
367#if defined(Q_OS_MACOS) && !defined(QT_BOOTSTRAPPED)
368bool qt_mac_runningUnderRosetta()
371 auto size =
sizeof(translated);
372 if (sysctlbyname(
"sysctl.proc_translated", &translated, &size,
nullptr, 0) == 0)
377bool qt_apple_runningWithLiquidGlass()
379 static const bool runningWithLiquidGlass = []{
380 QMacAutoReleasePool pool;
382 if (QMacVersion::buildSDK(QMacVersion::ApplicationBinary).majorVersion() < 26)
385 if (QMacVersion::currentRuntime().majorVersion() < 26)
392 if (QMacVersion::currentRuntime().majorVersion() < 27) {
393 const id liquidGlassOptOut = [NSBundle.mainBundle
394 objectForInfoDictionaryKey:@
"UIDesignRequiresCompatibility"];
395 if (liquidGlassOptOut && [liquidGlassOptOut boolValue])
402 return runningWithLiquidGlass;
405std::optional<uint32_t> qt_mac_sipConfiguration()
407 static auto configuration = []() -> std::optional<uint32_t> {
408#if !defined(QT_APPLE_NO_PRIVATE_APIS)
410 if (csr_get_active_config && csr_get_active_config(&config) == 0)
414 QIOType<io_registry_entry_t> nvram = IORegistryEntryFromPath(kIOMainPortDefault,
"IODeviceTree:/options");
416 qWarning(
"Failed to locate NVRAM entry in IO registry");
420 QCFType<CFTypeRef> csrConfig = IORegistryEntryCreateCFProperty(nvram,
421 CFSTR(
"csr-active-config"), kCFAllocatorDefault, IOOptionBits{});
425 if (
auto type = CFGetTypeID(csrConfig); type != CFDataGetTypeID()) {
426#ifndef QT_NO_DEBUG_STREAM
427 qWarning() <<
"Unexpected SIP config type" << CFCopyTypeIDDescription(type);
432 QByteArray data = QByteArray::fromRawCFData(csrConfig.as<CFDataRef>());
433 if (data.size() !=
sizeof(uint32_t)) {
434 qWarning(
"Unexpected SIP config size %td", ptrdiff_t(data.size()));
438 return qFromLittleEndian<uint32_t>(data.constData());
440 return configuration;
443bool qt_mac_processHasEntitlement(
const QString &entitlement)
445 if (QCFType<SecTaskRef> task = SecTaskCreateFromSelf(kCFAllocatorDefault)) {
446 if (QCFType<CFTypeRef> value = SecTaskCopyValueForEntitlement(task,
447 entitlement.toCFString(),
nullptr)) {
449 if (CFGetTypeID(value) != CFBooleanGetTypeID())
452 return CFBooleanGetValue(value.as<CFBooleanRef>());
458#define CHECK_SPAWN(expr)
460 posix_spawnattr_destroy(&attr);
464#ifdef QT_BUILD_INTERNAL
465void qt_mac_ensureResponsible()
467#if !defined(QT_APPLE_NO_PRIVATE_APIS)
468 if (!responsibility_get_pid_responsible_for_pid || !responsibility_spawnattrs_setdisclaim)
472 if (responsibility_get_pid_responsible_for_pid(pid) == pid)
475 posix_spawnattr_t attr = {};
476 CHECK_SPAWN(posix_spawnattr_init(&attr));
479 short flags = POSIX_SPAWN_SETEXEC;
483 sigemptyset(&no_signals);
484 CHECK_SPAWN(posix_spawnattr_setsigmask(&attr, &no_signals));
485 flags |= POSIX_SPAWN_SETSIGMASK;
488 sigset_t all_signals;
489 sigfillset(&all_signals);
490 CHECK_SPAWN(posix_spawnattr_setsigdefault(&attr, &all_signals));
491 flags |= POSIX_SPAWN_SETSIGDEF;
493 CHECK_SPAWN(posix_spawnattr_setflags(&attr, flags));
495 CHECK_SPAWN(responsibility_spawnattrs_setdisclaim(&attr, 1));
497 char **argv = *_NSGetArgv();
498 posix_spawnp(&pid, argv[0],
nullptr, &attr, argv, environ);
499 posix_spawnattr_destroy(&attr);
508 static bool isExtension = [[NSBundle mainBundle] objectForInfoDictionaryKey:@
"NSExtension"];
512#if !defined(QT_BOOTSTRAPPED) && !defined(Q_OS_WATCHOS)
517 qWarning() <<
"accessing the shared" << [AppleApplication
class]
518 <<
"is not allowed in application extensions";
523#if QT_CONFIG(appstore_compliant)
530 return [[AppleApplication
class] performSelector:@selector(sharedApplication)];
534#if !defined(QT_BOOTSTRAPPED)
538#if defined(Q_OS_MACOS)
539 static bool isSandboxed = qt_mac_processHasEntitlement(u"com.apple.security.app-sandbox"_s);
547@implementation NSObject (QtExtras)
548- (id)qt_valueForPrivateKey:(NSString *)key
550 if (qt_apple_isSandboxed())
553 return [self valueForKey:key];
561
562
563
564
565
566
569#define ROOT_LEVEL_POOL_MARKER QT_ROOT_LEVEL_POOL__THESE_OBJECTS_WILL_BE_RELEASED_WHEN_QAPP_GOES_OUT_OF_SCOPE
570@interface QT_MANGLE_NAMESPACE(ROOT_LEVEL_POOL_MARKER) : NSObject @end
571@implementation QT_MANGLE_NAMESPACE(ROOT_LEVEL_POOL_MARKER) @end
572QT_NAMESPACE_ALIAS_OBJC_CLASS(ROOT_LEVEL_POOL_MARKER);
575const char ROOT_LEVEL_POOL_DISABLE_SWITCH[] =
"QT_DISABLE_ROOT_LEVEL_AUTORELEASE_POOL";
577QMacRootLevelAutoReleasePool::QMacRootLevelAutoReleasePool()
579 if (qEnvironmentVariableIsSet(ROOT_LEVEL_POOL_DISABLE_SWITCH))
584 [[[ROOT_LEVEL_POOL_MARKER alloc] init] autorelease];
586 if (qstrcmp(qgetenv(
"OBJC_DEBUG_MISSING_POOLS"),
"YES") == 0) {
587 qDebug(
"QCoreApplication root level NSAutoreleasePool in place. Break on ~%s and use\n"
588 "'p [NSAutoreleasePool showPools]' to show leaked objects, or set %s",
589 __FUNCTION__, ROOT_LEVEL_POOL_DISABLE_SWITCH);
593QMacRootLevelAutoReleasePool::~QMacRootLevelAutoReleasePool()
600#ifndef QT_BOOTSTRAPPED
601void qt_apple_check_os_version()
603#if defined(__WATCH_OS_VERSION_MIN_REQUIRED)
604 const char *os =
"watchOS";
605 const int version = __WATCH_OS_VERSION_MIN_REQUIRED;
606#elif defined(__TV_OS_VERSION_MIN_REQUIRED)
607 const char *os =
"tvOS";
608 const int version = __TV_OS_VERSION_MIN_REQUIRED;
609#elif defined(__VISION_OS_VERSION_MIN_REQUIRED)
610 const char *os =
"visionOS";
611 const int version = __VISION_OS_VERSION_MIN_REQUIRED;
612#elif defined(__IPHONE_OS_VERSION_MIN_REQUIRED)
613 const char *os =
"iOS";
614 const int version = __IPHONE_OS_VERSION_MIN_REQUIRED;
615#elif defined(__MAC_OS_X_VERSION_MIN_REQUIRED)
616 const char *os =
"macOS";
617 const int version = __MAC_OS_X_VERSION_MIN_REQUIRED;
620 const auto required = QVersionNumber(version / 10000, version / 100 % 100, version % 100);
621 const auto current = QOperatingSystemVersion::current().version();
623#if defined(Q_OS_MACOS)
626 if (current.majorVersion() == 10 && current.minorVersion() >= 16)
630 if (current < required) {
631 NSDictionary *plist = NSBundle.mainBundle.infoDictionary;
632 NSString *applicationName = plist[@
"CFBundleDisplayName"];
633 if (!applicationName)
634 applicationName = plist[@
"CFBundleName"];
635 if (!applicationName)
636 applicationName = NSProcessInfo.processInfo.processName;
638 fprintf(stderr,
"Sorry, \"%s\" cannot be run on this version of %s. "
639 "Qt requires %s %ld.%ld.%ld or later, you have %s %ld.%ld.%ld.\n",
640 applicationName.UTF8String, os,
641 os,
long(required.majorVersion()),
long(required.minorVersion()),
long(required.microVersion()),
642 os,
long(current.majorVersion()),
long(current.minorVersion()),
long(current.microVersion()));
652void QMacNotificationObserver::remove()
655 [[NSNotificationCenter defaultCenter] removeObserver:observer];
661QMacKeyValueObserver::QMacKeyValueObserver(
const QMacKeyValueObserver &other)
662 : QMacKeyValueObserver(other.object, other.keyPath, *other.callback.get())
666void QMacKeyValueObserver::addObserver(NSKeyValueObservingOptions options)
668 [object addObserver:observer forKeyPath:keyPath options:options context:callback.get()];
671void QMacKeyValueObserver::removeObserver() {
673 [object removeObserver:observer forKeyPath:keyPath context:callback.get()];
677KeyValueObserver *QMacKeyValueObserver::observer = [[KeyValueObserver alloc] init];
680@implementation QT_MANGLE_NAMESPACE(KeyValueObserver)
681- (
void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object
682 change:(NSDictionary<NSKeyValueChangeKey, id> *)change context:(
void *)context
688 (*
reinterpret_cast<QMacKeyValueObserver::Callback*>(context))();
695#ifndef QT_BOOTSTRAPPED
696QOperatingSystemVersion QMacVersion::buildSDK(VersionTarget target)
699 case ApplicationBinary:
return applicationVersion().second;
700 case QtLibraries:
return libraryVersion().second;
705QOperatingSystemVersion QMacVersion::deploymentTarget(VersionTarget target)
708 case ApplicationBinary:
return applicationVersion().first;
709 case QtLibraries:
return libraryVersion().first;
714QOperatingSystemVersion QMacVersion::currentRuntime()
716 return QOperatingSystemVersion::current();
732QMacVersion::VersionTuple QMacVersion::versionsForImage(
const mach_header *machHeader)
734 static auto osForLoadCommand = [](uint32_t cmd) {
736 case LC_VERSION_MIN_MACOSX:
return QOperatingSystemVersion::MacOS;
737 case LC_VERSION_MIN_IPHONEOS:
return QOperatingSystemVersion::IOS;
738 case LC_VERSION_MIN_TVOS:
return QOperatingSystemVersion::TvOS;
739 case LC_VERSION_MIN_WATCHOS:
return QOperatingSystemVersion::WatchOS;
740 default:
return QOperatingSystemVersion::Unknown;
744 static auto osForPlatform = [](uint32_t platform) {
746 case Platform::macOS:
747 return QOperatingSystemVersion::MacOS;
749 case Platform::iOSSimulator:
750 return QOperatingSystemVersion::IOS;
752 case Platform::tvOSSimulator:
753 return QOperatingSystemVersion::TvOS;
754 case Platform::watchOS:
755 case Platform::watchOSSimulator:
756 return QOperatingSystemVersion::WatchOS;
758 return QOperatingSystemVersion::Unknown;
762 static auto makeVersionTuple = [](uint32_t dt, uint32_t sdk, QOperatingSystemVersion::OSType osType) {
764 QOperatingSystemVersion(osType, dt >> 16 & 0xffff, dt >> 8 & 0xff, dt & 0xff),
765 QOperatingSystemVersion(osType, sdk >> 16 & 0xffff, sdk >> 8 & 0xff, sdk & 0xff)
769 const bool is64Bit = machHeader->magic == MH_MAGIC_64 || machHeader->magic == MH_CIGAM_64;
770 auto commandCursor = uintptr_t(machHeader) + (is64Bit ?
sizeof(mach_header_64) :
sizeof(mach_header));
772 for (uint32_t i = 0; i < machHeader->ncmds; ++i) {
773 load_command *loadCommand =
reinterpret_cast<load_command *>(commandCursor);
774 if (loadCommand->cmd == LC_VERSION_MIN_MACOSX || loadCommand->cmd == LC_VERSION_MIN_IPHONEOS
775 || loadCommand->cmd == LC_VERSION_MIN_TVOS || loadCommand->cmd == LC_VERSION_MIN_WATCHOS) {
776 auto versionCommand =
reinterpret_cast<version_min_command *>(loadCommand);
777 return makeVersionTuple(versionCommand->version, versionCommand->sdk, osForLoadCommand(loadCommand->cmd));
778 }
else if (loadCommand->cmd == LC_BUILD_VERSION) {
779 auto versionCommand =
reinterpret_cast<build_version_command *>(loadCommand);
780 return makeVersionTuple(versionCommand->minos, versionCommand->sdk, osForPlatform(versionCommand->platform));
782 commandCursor += loadCommand->cmdsize;
784 Q_ASSERT_X(
false,
"QMacVersion",
"Could not find any version load command");
788QMacVersion::VersionTuple QMacVersion::applicationVersion()
790 static VersionTuple version = []() {
791 const mach_header *executableHeader =
nullptr;
792 for (uint32_t i = 0; i < _dyld_image_count(); ++i) {
793 auto header = _dyld_get_image_header(i);
794 if (header->filetype == MH_EXECUTE) {
795 executableHeader = header;
799 Q_ASSERT_X(executableHeader,
"QMacVersion",
"Failed to resolve Mach-O header of executable");
800 return versionsForImage(executableHeader);
805QMacVersion::VersionTuple QMacVersion::libraryVersion()
807 static VersionTuple version = []() {
809 dladdr((
const void *)&QMacVersion::libraryVersion, &qtCoreImage);
810 Q_ASSERT_X(qtCoreImage.dli_fbase,
"QMacVersion",
"Failed to resolve Mach-O header of QtCore");
811 return versionsForImage(
static_cast<mach_header*>(qtCoreImage.dli_fbase));
819QObjCWeakPointerBase::QObjCWeakPointerBase(NSObject *object)
820 : m_weakReference(object)
824QObjCWeakPointerBase::QObjCWeakPointerBase(
const QObjCWeakPointerBase &other)
826 QMacAutoReleasePool pool;
827 m_weakReference = other.m_weakReference;
830QObjCWeakPointerBase &QObjCWeakPointerBase::operator=(
const QObjCWeakPointerBase &other)
832 QMacAutoReleasePool pool;
833 m_weakReference = other.m_weakReference;
837QObjCWeakPointerBase::~QObjCWeakPointerBase()
839 QMacAutoReleasePool pool;
840 m_weakReference = nil;
843NSObject *QObjCWeakPointerBase::get()
const
850 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)
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 dbg, const QFileInfo &fi)
QDebug operator<<(QDebug debug, QIODevice::OpenMode modes)