From 5f940253bfde4b5336ff13be063f64bd1c1dbe49 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tor=20Arne=20Vestb=C3=B8?= Date: Wed, 11 Mar 2020 11:46:59 +0100 Subject: macOS: Merge qcore_mac cpp and mm files Files ending with .mm are Objective-C++ files, so we don't need a separate file for the C++ parts. Change-Id: I3ef52bc98291fd461b889978a538e81630d17c6e Reviewed-by: Timur Pocheptsov --- src/corelib/kernel/qcore_mac.mm | 673 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 673 insertions(+) create mode 100644 src/corelib/kernel/qcore_mac.mm (limited to 'src/corelib/kernel/qcore_mac.mm') diff --git a/src/corelib/kernel/qcore_mac.mm b/src/corelib/kernel/qcore_mac.mm new file mode 100644 index 0000000000..0d334e7300 --- /dev/null +++ b/src/corelib/kernel/qcore_mac.mm @@ -0,0 +1,673 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Copyright (C) 2014 Petroules Corporation. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include + +#ifdef Q_OS_MACOS +#include +#endif + +#if defined(QT_PLATFORM_UIKIT) +#include +#endif + +#include +#include +#include +#include +#include + +#include + +#include "qhash.h" +#include "qpair.h" +#include "qmutex.h" +#include "qvarlengtharray.h" +#include "private/qlocking_p.h" + +QT_BEGIN_NAMESPACE + +// -------------------------------------------------------------------------- + +QCFString::operator QString() const +{ + if (string.isEmpty() && value) + const_cast(this)->string = QString::fromCFString(value); + return string; +} + +QCFString::operator CFStringRef() const +{ + if (!value) + const_cast(this)->value = string.toCFString(); + return value; +} + +// -------------------------------------------------------------------------- + +#if defined(QT_USE_APPLE_UNIFIED_LOGGING) + +bool AppleUnifiedLogger::willMirrorToStderr() +{ + // When running under Xcode or LLDB, one or more of these variables will + // be set, which triggers libsystem_trace.dyld to log messages to stderr + // as well, via_os_log_impl_mirror_to_stderr. Un-setting these variables + // is not an option, as that would silence normal NSLog or os_log calls, + // so instead we skip our own stderr output. See rdar://36919139. + static bool willMirror = qEnvironmentVariableIsSet("OS_ACTIVITY_DT_MODE") + || qEnvironmentVariableIsSet("ACTIVITY_LOG_STDERR") + || qEnvironmentVariableIsSet("CFLOG_FORCE_STDERR"); + return willMirror; +} + +QT_MAC_WEAK_IMPORT(_os_log_default); +bool AppleUnifiedLogger::messageHandler(QtMsgType msgType, const QMessageLogContext &context, + const QString &message, const QString &optionalSubsystem) +{ + QString subsystem = optionalSubsystem; + if (subsystem.isNull()) { + static QString bundleIdentifier = []() { + if (CFBundleRef bundle = CFBundleGetMainBundle()) { + if (CFStringRef identifier = CFBundleGetIdentifier(bundle)) + return QString::fromCFString(identifier); + } + return QString(); + }(); + subsystem = bundleIdentifier; + } + + const bool isDefault = !context.category || !strcmp(context.category, "default"); + os_log_t log = isDefault ? OS_LOG_DEFAULT : + cachedLog(subsystem, QString::fromLatin1(context.category)); + os_log_type_t logType = logTypeForMessageType(msgType); + + if (!os_log_type_enabled(log, logType)) + return false; + + // Logging best practices says we should not include symbolication + // information or source file line numbers in messages, as the system + // will automatically captures this information. In our case, what + // the system captures is the call to os_log_with_type below, which + // isn't really useful, but we still don't want to include the context's + // info, as that would clutter the logging output. See rdar://35958308. + + // The format must be a string constant, so we can't pass on the + // message. This means we won't be able to take advantage of the + // unified logging's custom format specifiers such as %{BOOL}d. + // We use the 'public' format specifier to prevent the logging + // system from redacting our log message. + os_log_with_type(log, logType, "%{public}s", qPrintable(message)); + + return willMirrorToStderr(); +} + +os_log_type_t AppleUnifiedLogger::logTypeForMessageType(QtMsgType msgType) +{ + switch (msgType) { + case QtDebugMsg: return OS_LOG_TYPE_DEBUG; + case QtInfoMsg: return OS_LOG_TYPE_INFO; + case QtWarningMsg: return OS_LOG_TYPE_DEFAULT; + case QtCriticalMsg: return OS_LOG_TYPE_ERROR; + case QtFatalMsg: return OS_LOG_TYPE_FAULT; + } + + return OS_LOG_TYPE_DEFAULT; +} + +os_log_t AppleUnifiedLogger::cachedLog(const QString &subsystem, const QString &category) +{ + static QBasicMutex mutex; + const auto locker = qt_scoped_lock(mutex); + + static QHash, os_log_t> logs; + const auto cacheKey = qMakePair(subsystem, category); + os_log_t log = logs.value(cacheKey); + + if (!log) { + log = os_log_create(subsystem.toLatin1().constData(), + category.toLatin1().constData()); + logs.insert(cacheKey, log); + + // Technically we should release the os_log_t resource when done + // with it, but since we don't know when a category is disabled + // we keep all cached os_log_t instances until shutdown, where + // the OS will clean them up for us. + } + + return log; +} + +#endif // QT_USE_APPLE_UNIFIED_LOGGING + +// ------------------------------------------------------------------------- + +QDebug operator<<(QDebug dbg, const NSObject *nsObject) +{ + return dbg << (nsObject ? + dbg.verbosity() > 2 ? + nsObject.debugDescription.UTF8String : + nsObject.description.UTF8String + : "NSObject(0x0)"); +} + +QDebug operator<<(QDebug dbg, CFStringRef stringRef) +{ + if (!stringRef) + return dbg << "CFStringRef(0x0)"; + + if (const UniChar *chars = CFStringGetCharactersPtr(stringRef)) + dbg << QString::fromRawData(reinterpret_cast(chars), CFStringGetLength(stringRef)); + else + dbg << QString::fromCFString(stringRef); + + return dbg; +} + +// Prevents breaking the ODR in case we introduce support for more types +// later on, and lets the user override our default QDebug operators. +#define QT_DECLARE_WEAK_QDEBUG_OPERATOR_FOR_CF_TYPE(CFType) \ + __attribute__((weak)) Q_DECLARE_QDEBUG_OPERATOR_FOR_CF_TYPE(CFType) + +QT_FOR_EACH_CORE_FOUNDATION_TYPE(QT_DECLARE_WEAK_QDEBUG_OPERATOR_FOR_CF_TYPE); +QT_FOR_EACH_MUTABLE_CORE_FOUNDATION_TYPE(QT_DECLARE_WEAK_QDEBUG_OPERATOR_FOR_CF_TYPE); +QT_FOR_EACH_CORE_GRAPHICS_TYPE(QT_DECLARE_WEAK_QDEBUG_OPERATOR_FOR_CF_TYPE); +QT_FOR_EACH_MUTABLE_CORE_GRAPHICS_TYPE(QT_DECLARE_WEAK_QDEBUG_OPERATOR_FOR_CF_TYPE); + +// ------------------------------------------------------------------------- + +QT_END_NAMESPACE +QT_USE_NAMESPACE +@interface QT_MANGLE_NAMESPACE(QMacAutoReleasePoolTracker) : NSObject +@end + +@implementation QT_MANGLE_NAMESPACE(QMacAutoReleasePoolTracker) { + NSAutoreleasePool **m_pool; +} + +- (instancetype)initWithPool:(NSAutoreleasePool **)pool +{ + if ((self = [self init])) + m_pool = pool; + return self; +} + +- (void)dealloc +{ + if (*m_pool) { + // The pool is still valid, which means we're not being drained from + // the corresponding QMacAutoReleasePool (see below). + + // QMacAutoReleasePool has only a single member, the NSAutoreleasePool* + // so the address of that member is also the QMacAutoReleasePool itself. + QMacAutoReleasePool *pool = reinterpret_cast(m_pool); + qWarning() << "Premature drain of" << pool << "This can happen if you've allocated" + << "the pool on the heap, or as a member of a heap-allocated object. This is not a" + << "supported use of QMacAutoReleasePool, and might result in crashes when objects" + << "in the pool are deallocated and then used later on under the assumption they" + << "will be valid until" << pool << "has been drained."; + + // Reset the pool so that it's not drained again later on + *m_pool = nullptr; + } + + [super dealloc]; +} +@end +QT_NAMESPACE_ALIAS_OBJC_CLASS(QMacAutoReleasePoolTracker); + +QT_BEGIN_NAMESPACE + +QMacAutoReleasePool::QMacAutoReleasePool() + : pool([[NSAutoreleasePool alloc] init]) +{ + Class trackerClass = [QMacAutoReleasePoolTracker class]; + +#ifdef QT_DEBUG + void *poolFrame = nullptr; + if (__builtin_available(macOS 10.14, iOS 12.0, tvOS 12.0, watchOS 5.0, *)) { + void *frame; + if (backtrace_from_fp(__builtin_frame_address(0), &frame, 1)) + poolFrame = frame; + } else { + static const int maxFrames = 3; + void *callstack[maxFrames]; + if (backtrace(callstack, maxFrames) == maxFrames) + poolFrame = callstack[maxFrames - 1]; + } + + if (poolFrame) { + Dl_info info; + if (dladdr(poolFrame, &info) && info.dli_sname) { + const char *symbolName = info.dli_sname; + if (symbolName[0] == '_') { + int status; + if (char *demangled = abi::__cxa_demangle(info.dli_sname, nullptr, 0, &status)) + symbolName = demangled; + } + + char *className = nullptr; + asprintf(&className, " ^-- allocated in function: %s", symbolName); + + if (Class existingClass = objc_getClass(className)) + trackerClass = existingClass; + else + trackerClass = objc_duplicateClass(trackerClass, className, 0); + + free(className); + + if (symbolName != info.dli_sname) + free((char*)symbolName); + } + } +#endif + + [[[trackerClass alloc] initWithPool: + reinterpret_cast(&pool)] autorelease]; +} + +QMacAutoReleasePool::~QMacAutoReleasePool() +{ + if (!pool) { + qWarning() << "Prematurely drained pool" << this << "finally drained. Any objects belonging" + << "to this pool have already been released, and have potentially been invalid since the" + << "premature drain earlier on."; + return; + } + + // Save and reset pool before draining, so that the pool tracker can know + // that it's being drained by its owning pool. + NSAutoreleasePool *savedPool = static_cast(pool); + pool = nullptr; + + // Drain behaves the same as release, with the advantage that + // if we're ever used in a garbage-collected environment, the + // drain acts as a hint to the garbage collector to collect. + [savedPool drain]; +} + +#ifndef QT_NO_DEBUG_STREAM +QDebug operator<<(QDebug debug, const QMacAutoReleasePool *pool) +{ + QDebugStateSaver saver(debug); + debug.nospace(); + debug << "QMacAutoReleasePool(" << (const void *)pool << ')'; + return debug; +} + +QDebug operator<<(QDebug debug, const QCFString &string) +{ + debug << static_cast(string); + return debug; +} +#endif // !QT_NO_DEBUG_STREAM + +#ifdef Q_OS_MACOS +bool qt_mac_applicationIsInDarkMode() +{ +#if QT_MACOS_PLATFORM_SDK_EQUAL_OR_ABOVE(__MAC_10_14) + if (__builtin_available(macOS 10.14, *)) { + auto appearance = [NSApp.effectiveAppearance bestMatchFromAppearancesWithNames: + @[ NSAppearanceNameAqua, NSAppearanceNameDarkAqua ]]; + return [appearance isEqualToString:NSAppearanceNameDarkAqua]; + } +#endif + return false; +} +#endif + +bool qt_apple_isApplicationExtension() +{ + static bool isExtension = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"NSExtension"]; + return isExtension; +} + +#if !defined(QT_BOOTSTRAPPED) && !defined(Q_OS_WATCHOS) +AppleApplication *qt_apple_sharedApplication() +{ + // Application extensions are not allowed to access the shared application + if (qt_apple_isApplicationExtension()) { + qWarning() << "accessing the shared" << [AppleApplication class] + << "is not allowed in application extensions"; + + // In practice the application is actually available, but the App + // review process will likely catch uses of it, so we return nil just + // in case, unless we don't care about being App Store compliant. +#if QT_CONFIG(appstore_compliant) + return nil; +#endif + } + + // We use performSelector so that building with -fapplication-extension will + // not mistakenly think we're using the shared application in extensions. + return [[AppleApplication class] performSelector:@selector(sharedApplication)]; +} +#endif + +#if defined(Q_OS_MACOS) && !defined(QT_BOOTSTRAPPED) +bool qt_apple_isSandboxed() +{ + static bool isSandboxed = []() { + QCFType staticCode = nullptr; + NSURL *bundleUrl = [[NSBundle mainBundle] bundleURL]; + if (SecStaticCodeCreateWithPath((__bridge CFURLRef)bundleUrl, + kSecCSDefaultFlags, &staticCode) != errSecSuccess) + return false; + + QCFType sandboxRequirement; + if (SecRequirementCreateWithString(CFSTR("entitlement[\"com.apple.security.app-sandbox\"] exists"), + kSecCSDefaultFlags, &sandboxRequirement) != errSecSuccess) + return false; + + if (SecStaticCodeCheckValidityWithErrors(staticCode, + kSecCSBasicValidateOnly, sandboxRequirement, nullptr) != errSecSuccess) + return false; + + return true; + }(); + return isSandboxed; +} + +QT_END_NAMESPACE +@implementation NSObject (QtSandboxHelpers) +- (id)qt_valueForPrivateKey:(NSString *)key +{ + if (qt_apple_isSandboxed()) + return nil; + + return [self valueForKey:key]; +} +@end +QT_BEGIN_NAMESPACE +#endif + +#ifdef Q_OS_MACOS +/* + Ensure that Objective-C objects auto-released in main(), directly or indirectly, + after QCoreApplication construction, are released when the app goes out of scope. + The memory will be reclaimed by the system either way when the process exits, + but by having a root level pool we ensure that the objects get their dealloc + methods called, which is useful for debugging object ownership graphs, etc. +*/ + +QT_END_NAMESPACE +#define ROOT_LEVEL_POOL_MARKER QT_ROOT_LEVEL_POOL__THESE_OBJECTS_WILL_BE_RELEASED_WHEN_QAPP_GOES_OUT_OF_SCOPE +@interface QT_MANGLE_NAMESPACE(ROOT_LEVEL_POOL_MARKER) : NSObject @end +@implementation QT_MANGLE_NAMESPACE(ROOT_LEVEL_POOL_MARKER) @end +QT_NAMESPACE_ALIAS_OBJC_CLASS(ROOT_LEVEL_POOL_MARKER); +QT_BEGIN_NAMESPACE + +const char ROOT_LEVEL_POOL_DISABLE_SWITCH[] = "QT_DISABLE_ROOT_LEVEL_AUTORELEASE_POOL"; + +QMacRootLevelAutoReleasePool::QMacRootLevelAutoReleasePool() +{ + if (qEnvironmentVariableIsSet(ROOT_LEVEL_POOL_DISABLE_SWITCH)) + return; + + pool.reset(new QMacAutoReleasePool); + + [[[ROOT_LEVEL_POOL_MARKER alloc] init] autorelease]; + + if (qstrcmp(qgetenv("OBJC_DEBUG_MISSING_POOLS"), "YES") == 0) { + qDebug("QCoreApplication root level NSAutoreleasePool in place. Break on ~%s and use\n" \ + "'p [NSAutoreleasePool showPools]' to show leaked objects, or set %s", + __FUNCTION__, ROOT_LEVEL_POOL_DISABLE_SWITCH); + } +} + +QMacRootLevelAutoReleasePool::~QMacRootLevelAutoReleasePool() +{ +} +#endif + +// ------------------------------------------------------------------------- + +#ifdef Q_OS_OSX + +// Use this method to keep all the information in the TextSegment. As long as it is ordered +// we are in OK shape, and we can influence that ourselves. +struct KeyPair +{ + QChar cocoaKey; + Qt::Key qtKey; +}; + +bool operator==(const KeyPair &entry, QChar qchar) +{ + return entry.cocoaKey == qchar; +} + +bool operator<(const KeyPair &entry, QChar qchar) +{ + return entry.cocoaKey < qchar; +} + +bool operator<(QChar qchar, const KeyPair &entry) +{ + return qchar < entry.cocoaKey; +} + +bool operator<(const Qt::Key &key, const KeyPair &entry) +{ + return key < entry.qtKey; +} + +bool operator<(const KeyPair &entry, const Qt::Key &key) +{ + return entry.qtKey < key; +} + +struct qtKey2CocoaKeySortLessThan +{ + typedef bool result_type; + Q_DECL_CONSTEXPR result_type operator()(const KeyPair &entry1, const KeyPair &entry2) const noexcept + { + return entry1.qtKey < entry2.qtKey; + } +}; + +static const int NSEscapeCharacter = 27; // not defined by Cocoa headers +static const int NumEntries = 59; +static const KeyPair entries[NumEntries] = { + { NSEnterCharacter, Qt::Key_Enter }, + { NSBackspaceCharacter, Qt::Key_Backspace }, + { NSTabCharacter, Qt::Key_Tab }, + { NSNewlineCharacter, Qt::Key_Return }, + { NSCarriageReturnCharacter, Qt::Key_Return }, + { NSBackTabCharacter, Qt::Key_Backtab }, + { NSEscapeCharacter, Qt::Key_Escape }, + // Cocoa sends us delete when pressing backspace! + // (NB when we reverse this list in qtKey2CocoaKey, there + // will be two indices of Qt::Key_Backspace. But is seems to work + // ok for menu shortcuts (which uses that function): + { NSDeleteCharacter, Qt::Key_Backspace }, + { NSUpArrowFunctionKey, Qt::Key_Up }, + { NSDownArrowFunctionKey, Qt::Key_Down }, + { NSLeftArrowFunctionKey, Qt::Key_Left }, + { NSRightArrowFunctionKey, Qt::Key_Right }, + { NSF1FunctionKey, Qt::Key_F1 }, + { NSF2FunctionKey, Qt::Key_F2 }, + { NSF3FunctionKey, Qt::Key_F3 }, + { NSF4FunctionKey, Qt::Key_F4 }, + { NSF5FunctionKey, Qt::Key_F5 }, + { NSF6FunctionKey, Qt::Key_F6 }, + { NSF7FunctionKey, Qt::Key_F7 }, + { NSF8FunctionKey, Qt::Key_F8 }, + { NSF9FunctionKey, Qt::Key_F9 }, + { NSF10FunctionKey, Qt::Key_F10 }, + { NSF11FunctionKey, Qt::Key_F11 }, + { NSF12FunctionKey, Qt::Key_F12 }, + { NSF13FunctionKey, Qt::Key_F13 }, + { NSF14FunctionKey, Qt::Key_F14 }, + { NSF15FunctionKey, Qt::Key_F15 }, + { NSF16FunctionKey, Qt::Key_F16 }, + { NSF17FunctionKey, Qt::Key_F17 }, + { NSF18FunctionKey, Qt::Key_F18 }, + { NSF19FunctionKey, Qt::Key_F19 }, + { NSF20FunctionKey, Qt::Key_F20 }, + { NSF21FunctionKey, Qt::Key_F21 }, + { NSF22FunctionKey, Qt::Key_F22 }, + { NSF23FunctionKey, Qt::Key_F23 }, + { NSF24FunctionKey, Qt::Key_F24 }, + { NSF25FunctionKey, Qt::Key_F25 }, + { NSF26FunctionKey, Qt::Key_F26 }, + { NSF27FunctionKey, Qt::Key_F27 }, + { NSF28FunctionKey, Qt::Key_F28 }, + { NSF29FunctionKey, Qt::Key_F29 }, + { NSF30FunctionKey, Qt::Key_F30 }, + { NSF31FunctionKey, Qt::Key_F31 }, + { NSF32FunctionKey, Qt::Key_F32 }, + { NSF33FunctionKey, Qt::Key_F33 }, + { NSF34FunctionKey, Qt::Key_F34 }, + { NSF35FunctionKey, Qt::Key_F35 }, + { NSInsertFunctionKey, Qt::Key_Insert }, + { NSDeleteFunctionKey, Qt::Key_Delete }, + { NSHomeFunctionKey, Qt::Key_Home }, + { NSEndFunctionKey, Qt::Key_End }, + { NSPageUpFunctionKey, Qt::Key_PageUp }, + { NSPageDownFunctionKey, Qt::Key_PageDown }, + { NSPrintScreenFunctionKey, Qt::Key_Print }, + { NSScrollLockFunctionKey, Qt::Key_ScrollLock }, + { NSPauseFunctionKey, Qt::Key_Pause }, + { NSSysReqFunctionKey, Qt::Key_SysReq }, + { NSMenuFunctionKey, Qt::Key_Menu }, + { NSHelpFunctionKey, Qt::Key_Help }, +}; +static const KeyPair * const end = entries + NumEntries; + +QChar qt_mac_qtKey2CocoaKey(Qt::Key key) +{ + // The first time this function is called, create a reverse + // lookup table sorted on Qt Key rather than Cocoa key: + static QVector rev_entries(NumEntries); + static bool mustInit = true; + if (mustInit){ + mustInit = false; + for (int i=0; i::iterator i + = std::lower_bound(rev_entries.begin(), rev_entries.end(), key); + if ((i == rev_entries.end()) || (key < *i)) + return QChar(); + return i->cocoaKey; +} + +Qt::Key qt_mac_cocoaKey2QtKey(QChar keyCode) +{ + const KeyPair *i = std::lower_bound(entries, end, keyCode); + if ((i == end) || (keyCode < *i)) + return Qt::Key(keyCode.toUpper().unicode()); + return i->qtKey; +} + +#endif // Q_OS_OSX + +void qt_apple_check_os_version() +{ +#if defined(__WATCH_OS_VERSION_MIN_REQUIRED) + const char *os = "watchOS"; + const int version = __WATCH_OS_VERSION_MIN_REQUIRED; +#elif defined(__TV_OS_VERSION_MIN_REQUIRED) + const char *os = "tvOS"; + const int version = __TV_OS_VERSION_MIN_REQUIRED; +#elif defined(__IPHONE_OS_VERSION_MIN_REQUIRED) + const char *os = "iOS"; + const int version = __IPHONE_OS_VERSION_MIN_REQUIRED; +#elif defined(__MAC_OS_X_VERSION_MIN_REQUIRED) + const char *os = "macOS"; + const int version = __MAC_OS_X_VERSION_MIN_REQUIRED; +#endif + const NSOperatingSystemVersion required = (NSOperatingSystemVersion){ + version / 10000, version / 100 % 100, version % 100}; + const NSOperatingSystemVersion current = NSProcessInfo.processInfo.operatingSystemVersion; + if (![NSProcessInfo.processInfo isOperatingSystemAtLeastVersion:required]) { + NSDictionary *plist = NSBundle.mainBundle.infoDictionary; + NSString *applicationName = plist[@"CFBundleDisplayName"]; + if (!applicationName) + applicationName = plist[@"CFBundleName"]; + if (!applicationName) + applicationName = NSProcessInfo.processInfo.processName; + + fprintf(stderr, "Sorry, \"%s\" cannot be run on this version of %s. " + "Qt requires %s %ld.%ld.%ld or later, you have %s %ld.%ld.%ld.\n", + applicationName.UTF8String, os, + os, long(required.majorVersion), long(required.minorVersion), long(required.patchVersion), + os, long(current.majorVersion), long(current.minorVersion), long(current.patchVersion)); + + exit(1); + } +} +Q_CONSTRUCTOR_FUNCTION(qt_apple_check_os_version); + +// ------------------------------------------------------------------------- + +void QMacKeyValueObserver::addObserver(NSKeyValueObservingOptions options) +{ + [object addObserver:observer forKeyPath:keyPath options:options context:callback.get()]; +} + +void QMacKeyValueObserver::removeObserver() { + if (object) + [object removeObserver:observer forKeyPath:keyPath context:callback.get()]; + object = nil; +} + +KeyValueObserver *QMacKeyValueObserver::observer = [[KeyValueObserver alloc] init]; + +QT_END_NAMESPACE +@implementation KeyValueObserver +- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object + change:(NSDictionary *)change context:(void *)context +{ + Q_UNUSED(keyPath); + Q_UNUSED(object); + Q_UNUSED(change); + + (*reinterpret_cast(context))(); +} +@end +QT_BEGIN_NAMESPACE + +// ------------------------------------------------------------------------- + + +QT_END_NAMESPACE + -- cgit v1.2.3