diff options
Diffstat (limited to 'src/corelib/kernel')
39 files changed, 1167 insertions, 286 deletions
diff --git a/src/corelib/kernel/kernel.pri b/src/corelib/kernel/kernel.pri index 9bc6e198f8..2336278b17 100644 --- a/src/corelib/kernel/kernel.pri +++ b/src/corelib/kernel/kernel.pri @@ -79,7 +79,8 @@ win32 { kernel/qsharedmemory_win.cpp \ kernel/qsystemsemaphore_win.cpp HEADERS += \ - kernel/qwineventnotifier.h + kernel/qwineventnotifier.h \ + kernel/qwineventnotifier_p.h winrt { SOURCES += kernel/qeventdispatcher_winrt.cpp diff --git a/src/corelib/kernel/qabstracteventdispatcher.cpp b/src/corelib/kernel/qabstracteventdispatcher.cpp index d234949d14..d1e436c371 100644 --- a/src/corelib/kernel/qabstracteventdispatcher.cpp +++ b/src/corelib/kernel/qabstracteventdispatcher.cpp @@ -375,8 +375,7 @@ void QAbstractEventDispatcher::closingDown() */ /*! - Installs an event filter \a filterObj for all native event filters - received by the application. + Installs an event filter \a filterObj for all native events received by the application. The event filter \a filterObj receives events via its \l {QAbstractNativeEventFilter::}{nativeEventFilter()} function, which is called for all events received by all threads. diff --git a/src/corelib/kernel/qabstracteventdispatcher.h b/src/corelib/kernel/qabstracteventdispatcher.h index 0d3e53e4a7..3a530cf1de 100644 --- a/src/corelib/kernel/qabstracteventdispatcher.h +++ b/src/corelib/kernel/qabstracteventdispatcher.h @@ -124,6 +124,8 @@ protected: QObject *parent); }; +Q_DECLARE_TYPEINFO(QAbstractEventDispatcher::TimerInfo, (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) ? Q_PRIMITIVE_TYPE : Q_RELOCATABLE_TYPE)); + QT_END_NAMESPACE #endif // QABSTRACTEVENTDISPATCHER_H diff --git a/src/corelib/kernel/qcore_mac.cpp b/src/corelib/kernel/qcore_mac.cpp index c689f47d8f..bfb3b2ff07 100644 --- a/src/corelib/kernel/qcore_mac.cpp +++ b/src/corelib/kernel/qcore_mac.cpp @@ -45,16 +45,16 @@ QT_BEGIN_NAMESPACE QCFString::operator QString() const { - if (string.isEmpty() && type) - const_cast<QCFString*>(this)->string = QString::fromCFString(type); + if (string.isEmpty() && value) + const_cast<QCFString*>(this)->string = QString::fromCFString(value); return string; } QCFString::operator CFStringRef() const { - if (!type) - const_cast<QCFString*>(this)->type = string.toCFString(); - return type; + if (!value) + const_cast<QCFString*>(this)->value = string.toCFString(); + return value; } QT_END_NAMESPACE diff --git a/src/corelib/kernel/qcore_mac_objc.mm b/src/corelib/kernel/qcore_mac_objc.mm index 231afb991c..24d73fa8be 100644 --- a/src/corelib/kernel/qcore_mac_objc.mm +++ b/src/corelib/kernel/qcore_mac_objc.mm @@ -42,7 +42,6 @@ #ifdef Q_OS_OSX #include <AppKit/NSText.h> -#include <Carbon/Carbon.h> #endif #include <qdebug.h> @@ -53,7 +52,11 @@ QT_BEGIN_NAMESPACE QDebug operator<<(QDebug dbg, const NSObject *nsObject) { - return dbg << (nsObject ? nsObject.description.UTF8String : "NSObject(0x0)"); + return dbg << (nsObject ? + dbg.verbosity() > 2 ? + nsObject.debugDescription.UTF8String : + nsObject.description.UTF8String + : "NSObject(0x0)"); } QDebug operator<<(QDebug dbg, CFStringRef stringRef) @@ -81,19 +84,122 @@ QT_FOR_EACH_MUTABLE_CORE_GRAPHICS_TYPE(QT_DECLARE_WEAK_QDEBUG_OPERATOR_FOR_CF_TY // ------------------------------------------------------------------------- +QT_END_NAMESPACE +QT_USE_NAMESPACE +@interface QT_MANGLE_NAMESPACE(QMacAutoReleasePoolTracker) : NSObject +{ + NSAutoreleasePool **m_pool; +} +-(id)initWithPool:(NSAutoreleasePool**)pool; +@end +@implementation QT_MANGLE_NAMESPACE(QMacAutoReleasePoolTracker) +-(id)initWithPool:(NSAutoreleasePool**)pool +{ + if (self = [super 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<QMacAutoReleasePool *>(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]) { + [[[QMacAutoReleasePoolTracker alloc] initWithPool: + reinterpret_cast<NSAutoreleasePool **>(&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<NSAutoreleasePool*>(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. - [static_cast<NSAutoreleasePool*>(pool) drain]; + [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; +} +#endif // !QT_NO_DEBUG_STREAM + +#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 @@ -140,6 +246,7 @@ struct qtKey2CocoaKeySortLessThan } }; +static const int NSEscapeCharacter = 27; // not defined by Cocoa headers static const int NumEntries = 59; static const KeyPair entries[NumEntries] = { { NSEnterCharacter, Qt::Key_Enter }, @@ -148,7 +255,7 @@ static const KeyPair entries[NumEntries] = { { NSNewlineCharacter, Qt::Key_Return }, { NSCarriageReturnCharacter, Qt::Key_Return }, { NSBackTabCharacter, Qt::Key_Backtab }, - { kEscapeCharCode, Qt::Key_Escape }, + { 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 @@ -237,6 +344,36 @@ Qt::Key qt_mac_cocoaKey2QtKey(QChar keyCode) #endif // Q_OS_OSX +void qt_apple_check_os_version() +{ +#if defined(__IPHONE_OS_VERSION_MIN_REQUIRED) + const char *os = "iOS"; + const int version = __IPHONE_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(__WATCH_OS_VERSION_MIN_REQUIRED) + const char *os = "watchOS"; + const int version = __WATCH_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]) { + fprintf(stderr, "You can't use this version of %s with this version of %s. " + "You have %s %ld.%ld.%ld. Qt requires %s %ld.%ld.%ld or later.\n", + (reinterpret_cast<const NSString *>( + NSBundle.mainBundle.infoDictionary[@"CFBundleName"]).UTF8String), + os, + os, long(current.majorVersion), long(current.minorVersion), long(current.patchVersion), + os, long(required.majorVersion), long(required.minorVersion), long(required.patchVersion)); + abort(); + } +} + // ------------------------------------------------------------------------- QT_END_NAMESPACE diff --git a/src/corelib/kernel/qcore_mac_p.h b/src/corelib/kernel/qcore_mac_p.h index b87babc07a..13143a08bb 100644 --- a/src/corelib/kernel/qcore_mac_p.h +++ b/src/corelib/kernel/qcore_mac_p.h @@ -68,6 +68,7 @@ #endif #include "qstring.h" +#include "qscopedpointer.h" #if defined( __OBJC__) && defined(QT_NAMESPACE) #define QT_NAMESPACE_ALIAS_OBJC_CLASS(__KLASS__) @compatibility_alias __KLASS__ QT_MANGLE_NAMESPACE(__KLASS__) @@ -76,6 +77,37 @@ #endif QT_BEGIN_NAMESPACE +template <typename T, typename U, U (*RetainFunction)(U), void (*ReleaseFunction)(U)> +class QAppleRefCounted +{ +public: + QAppleRefCounted(const T &t = T()) : value(t) {} + QAppleRefCounted(QAppleRefCounted &&other) : value(other.value) { other.value = T(); } + QAppleRefCounted(const QAppleRefCounted &other) : value(other.value) { if (value) RetainFunction(value); } + ~QAppleRefCounted() { if (value) ReleaseFunction(value); } + operator T() { return value; } + void swap(QAppleRefCounted &other) Q_DECL_NOEXCEPT_EXPR(noexcept(qSwap(value, other.value))) + { qSwap(value, other.value); } + QAppleRefCounted &operator=(const QAppleRefCounted &other) + { QAppleRefCounted copy(other); swap(copy); return *this; } + QAppleRefCounted &operator=(QAppleRefCounted &&other) + { QAppleRefCounted moved(std::move(other)); swap(moved); return *this; } + T *operator&() { return &value; } +protected: + T value; +}; + + +#ifdef Q_OS_MACOS +class QMacRootLevelAutoReleasePool +{ +public: + QMacRootLevelAutoReleasePool(); + ~QMacRootLevelAutoReleasePool(); +private: + QScopedPointer<QMacAutoReleasePool> pool; +}; +#endif /* Helper class that automates refernce counting for CFtypes. @@ -90,33 +122,17 @@ QT_BEGIN_NAMESPACE HIThemeGet*Shape functions, which in reality are "Copy" functions. */ template <typename T> -class Q_CORE_EXPORT QCFType +class QCFType : public QAppleRefCounted<T, CFTypeRef, CFRetain, CFRelease> { public: - inline QCFType(const T &t = 0) : type(t) {} - inline QCFType(const QCFType &helper) : type(helper.type) { if (type) CFRetain(type); } - inline ~QCFType() { if (type) CFRelease(type); } - inline operator T() { return type; } - inline QCFType operator =(const QCFType &helper) - { - if (helper.type) - CFRetain(helper.type); - CFTypeRef type2 = type; - type = helper.type; - if (type2) - CFRelease(type2); - return *this; - } - inline T *operator&() { return &type; } - template <typename X> X as() const { return reinterpret_cast<X>(type); } + using QAppleRefCounted<T, CFTypeRef, CFRetain, CFRelease>::QAppleRefCounted; + template <typename X> X as() const { return reinterpret_cast<X>(this->value); } static QCFType constructFromGet(const T &t) { if (t) CFRetain(t); return QCFType<T>(t); } -protected: - T type; }; class Q_CORE_EXPORT QCFString : public QCFType<CFStringRef> @@ -137,6 +153,12 @@ Q_CORE_EXPORT QChar qt_mac_qtKey2CocoaKey(Qt::Key key); Q_CORE_EXPORT Qt::Key qt_mac_cocoaKey2QtKey(QChar keyCode); #endif +#ifndef QT_NO_DEBUG_STREAM +QDebug operator<<(QDebug debug, const QMacAutoReleasePool *pool); +#endif + +Q_CORE_EXPORT void qt_apple_check_os_version(); + QT_END_NAMESPACE #endif // QCORE_MAC_P_H diff --git a/src/corelib/kernel/qcore_unix.cpp b/src/corelib/kernel/qcore_unix.cpp index 686143f8c7..3b0da136ca 100644 --- a/src/corelib/kernel/qcore_unix.cpp +++ b/src/corelib/kernel/qcore_unix.cpp @@ -50,6 +50,37 @@ QT_BEGIN_NAMESPACE +QByteArray qt_readlink(const char *path) +{ +#ifndef PATH_MAX + // suitably large value that won't consume too much memory +# define PATH_MAX 1024*1024 +#endif + + QByteArray buf(256, Qt::Uninitialized); + + ssize_t len = ::readlink(path, buf.data(), buf.size()); + while (len == buf.size()) { + // readlink(2) will fill our buffer and not necessarily terminate with NUL; + if (buf.size() >= PATH_MAX) { + errno = ENAMETOOLONG; + return QByteArray(); + } + + // double the size and try again + buf.resize(buf.size() * 2); + len = ::readlink(path, buf.data(), buf.size()); + } + + if (len == -1) + return QByteArray(); + + buf.resize(len); + return buf; +} + +#ifndef QT_BOOTSTRAPPED + #if QT_CONFIG(poll_pollts) # define ppoll pollts #endif @@ -121,4 +152,6 @@ int qt_safe_poll(struct pollfd *fds, nfds_t nfds, const struct timespec *timeout } } +#endif // QT_BOOTSTRAPPED + QT_END_NAMESPACE diff --git a/src/corelib/kernel/qcore_unix_p.h b/src/corelib/kernel/qcore_unix_p.h index 86bace3260..f78d2b9f24 100644 --- a/src/corelib/kernel/qcore_unix_p.h +++ b/src/corelib/kernel/qcore_unix_p.h @@ -55,6 +55,7 @@ #include <QtCore/private/qglobal_p.h> #include "qplatformdefs.h" #include "qatomic.h" +#include "qbytearray.h" #ifndef Q_OS_UNIX # error "qcore_unix_p.h included on a non-Unix system" @@ -102,6 +103,8 @@ struct sockaddr; QT_BEGIN_NAMESPACE +Q_DECLARE_TYPEINFO(pollfd, Q_PRIMITIVE_TYPE); + // Internal operator functions for timespecs inline timespec &normalizedTimespec(timespec &t) { @@ -183,10 +186,11 @@ static inline int qt_safe_open(const char *pathname, int flags, mode_t mode = 07 int fd; EINTR_LOOP(fd, QT_OPEN(pathname, flags, mode)); - // unknown flags are ignored, so we have no way of verifying if - // O_CLOEXEC was accepted +#ifndef O_CLOEXEC if (fd != -1) ::fcntl(fd, F_SETFD, FD_CLOEXEC); +#endif + return fd; } #undef QT_OPEN @@ -337,6 +341,22 @@ static inline pid_t qt_safe_waitpid(pid_t pid, int *status, int options) // in qelapsedtimer_mac.cpp or qtimestamp_unix.cpp timespec qt_gettime() Q_DECL_NOTHROW; void qt_nanosleep(timespec amount); +QByteArray qt_readlink(const char *path); + +/* non-static */ +inline bool qt_haveLinuxProcfs() +{ +#ifdef Q_OS_LINUX +# ifdef QT_LINUX_ALWAYS_HAVE_PROCFS + return true; +# else + static const bool present = (access("/proc/version", F_OK) == 0); + return present; +# endif +#else + return false; +#endif +} Q_CORE_EXPORT int qt_safe_poll(struct pollfd *fds, nfds_t nfds, const struct timespec *timeout_ts); diff --git a/src/corelib/kernel/qcoreapplication.cpp b/src/corelib/kernel/qcoreapplication.cpp index db40b37da9..34a3d7ef7e 100644 --- a/src/corelib/kernel/qcoreapplication.cpp +++ b/src/corelib/kernel/qcoreapplication.cpp @@ -263,7 +263,7 @@ Q_GLOBAL_STATIC(QStartUpFuncList, preRList) typedef QList<QtCleanUpFunction> QVFuncList; Q_GLOBAL_STATIC(QVFuncList, postRList) #ifndef QT_NO_QOBJECT -static QBasicMutex globalPreRoutinesMutex; +static QBasicMutex globalRoutinesMutex; #endif /*! @@ -277,13 +277,15 @@ void qAddPreRoutine(QtStartUpFunction p) QStartUpFuncList *list = preRList(); if (!list) return; + + if (QCoreApplication::instance()) + p(); + // Due to C++11 parallel dynamic initialization, this can be called // from multiple threads. #ifndef QT_NO_THREAD - QMutexLocker locker(&globalPreRoutinesMutex); + QMutexLocker locker(&globalRoutinesMutex); #endif - if (QCoreApplication::instance()) - p(); list->prepend(p); // in case QCoreApplication is re-created, see qt_call_pre_routines } @@ -292,6 +294,9 @@ void qAddPostRoutine(QtCleanUpFunction p) QVFuncList *list = postRList(); if (!list) return; +#ifndef QT_NO_THREAD + QMutexLocker locker(&globalRoutinesMutex); +#endif list->prepend(p); } @@ -300,6 +305,9 @@ void qRemovePostRoutine(QtCleanUpFunction p) QVFuncList *list = postRList(); if (!list) return; +#ifndef QT_NO_THREAD + QMutexLocker locker(&globalRoutinesMutex); +#endif list->removeAll(p); } @@ -308,15 +316,18 @@ static void qt_call_pre_routines() if (!preRList.exists()) return; + QVFuncList list; + { #ifndef QT_NO_THREAD - QMutexLocker locker(&globalPreRoutinesMutex); + QMutexLocker locker(&globalRoutinesMutex); #endif - QVFuncList *list = &(*preRList); - // Unlike qt_call_post_routines, we don't empty the list, because - // Q_COREAPP_STARTUP_FUNCTION is a macro, so the user expects - // the function to be executed every time QCoreApplication is created. - for (int i = 0; i < list->count(); ++i) - list->at(i)(); + // Unlike qt_call_post_routines, we don't empty the list, because + // Q_COREAPP_STARTUP_FUNCTION is a macro, so the user expects + // the function to be executed every time QCoreApplication is created. + list = *preRList; + } + for (int i = 0; i < list.count(); ++i) + list.at(i)(); } void Q_CORE_EXPORT qt_call_post_routines() @@ -324,9 +335,21 @@ void Q_CORE_EXPORT qt_call_post_routines() if (!postRList.exists()) return; - QVFuncList *list = &(*postRList); - while (!list->isEmpty()) - (list->takeFirst())(); + forever { + QVFuncList list; + { + // extract the current list and make the stored list empty +#ifndef QT_NO_THREAD + QMutexLocker locker(&globalRoutinesMutex); +#endif + qSwap(*postRList, list); + } + + if (list.isEmpty()) + break; + for (QtCleanUpFunction f : qAsConst(list)) + f(); + } } @@ -437,6 +460,9 @@ QCoreApplicationPrivate::QCoreApplicationPrivate(int &aargc, char **aargv, uint , q_ptr(0) #endif { +#if defined(Q_OS_DARWIN) + qt_apple_check_os_version(); +#endif app_compile_version = flags & 0xffffff; static const char *const empty = ""; if (argc == 0 || argv == 0) { @@ -1017,6 +1043,21 @@ bool QCoreApplication::notifyInternal2(QObject *receiver, QEvent *event) } /*! + \internal + \since 5.10 + + Forwards the \a event to the \a receiver, using the spontaneous + state of the \a originatingEvent if specified. +*/ +bool QCoreApplication::forwardEvent(QObject *receiver, QEvent *event, QEvent *originatingEvent) +{ + if (event && originatingEvent) + event->spont = originatingEvent->spont; + + return notifyInternal2(receiver, event); +} + +/*! Sends \a event to \a receiver: \a {receiver}->event(\a event). Returns the value that is returned from the receiver's event handler. Note that this function is called for all events sent to @@ -1914,7 +1955,10 @@ bool QCoreApplication::installTranslator(QTranslator *translationFile) if (!QCoreApplicationPrivate::checkInstance("installTranslator")) return false; QCoreApplicationPrivate *d = self->d_func(); - d->translators.prepend(translationFile); + { + QWriteLocker locker(&d->translateMutex); + d->translators.prepend(translationFile); + } #ifndef QT_NO_TRANSLATION_BUILDER if (translationFile->isEmpty()) @@ -1946,8 +1990,10 @@ bool QCoreApplication::removeTranslator(QTranslator *translationFile) if (!QCoreApplicationPrivate::checkInstance("removeTranslator")) return false; QCoreApplicationPrivate *d = self->d_func(); + QWriteLocker locker(&d->translateMutex); if (d->translators.removeAll(translationFile)) { #ifndef QT_NO_QOBJECT + locker.unlock(); if (!self->closingDown()) { QEvent ev(QEvent::LanguageChange); QCoreApplication::sendEvent(self, &ev); @@ -1983,7 +2029,7 @@ static void replacePercentN(QString *result, int n) } /*! - \reentrant + \threadsafe Returns the translation text for \a sourceText, by querying the installed translation files. The translation files are searched @@ -2012,13 +2058,7 @@ static void replacePercentN(QString *result, int n) This function is not virtual. You can use alternative translation techniques by subclassing \l QTranslator. - \warning This method is reentrant only if all translators are - installed \e before calling this method. Installing or removing - translators while performing translations is not supported. Doing - so will most likely result in crashes or other undesirable - behavior. - - \sa QObject::tr(), installTranslator() + \sa QObject::tr(), installTranslator(), removeTranslator(), translate() */ QString QCoreApplication::translate(const char *context, const char *sourceText, const char *disambiguation, int n) @@ -2028,14 +2068,18 @@ QString QCoreApplication::translate(const char *context, const char *sourceText, if (!sourceText) return result; - if (self && !self->d_func()->translators.isEmpty()) { - QList<QTranslator*>::ConstIterator it; - QTranslator *translationFile; - for (it = self->d_func()->translators.constBegin(); it != self->d_func()->translators.constEnd(); ++it) { - translationFile = *it; - result = translationFile->translate(context, sourceText, disambiguation, n); - if (!result.isNull()) - break; + if (self) { + QCoreApplicationPrivate *d = self->d_func(); + QReadLocker locker(&d->translateMutex); + if (!d->translators.isEmpty()) { + QList<QTranslator*>::ConstIterator it; + QTranslator *translationFile; + for (it = d->translators.constBegin(); it != d->translators.constEnd(); ++it) { + translationFile = *it; + result = translationFile->translate(context, sourceText, disambiguation, n); + if (!result.isNull()) + break; + } } } @@ -2059,8 +2103,11 @@ QString qtTrId(const char *id, int n) bool QCoreApplicationPrivate::isTranslatorInstalled(QTranslator *translator) { - return QCoreApplication::self - && QCoreApplication::self->d_func()->translators.contains(translator); + if (!QCoreApplication::self) + return false; + QCoreApplicationPrivate *d = QCoreApplication::self->d_func(); + QReadLocker locker(&d->translateMutex); + return d->translators.contains(translator); } #else @@ -2838,6 +2885,7 @@ void QCoreApplication::setEventDispatcher(QAbstractEventDispatcher *eventDispatc /*! \fn void qAddPostRoutine(QtCleanUpFunction ptr) + \threadsafe \relates QCoreApplication Adds a global routine that will be called from the QCoreApplication @@ -2851,10 +2899,10 @@ void QCoreApplication::setEventDispatcher(QAbstractEventDispatcher *eventDispatc \snippet code/src_corelib_kernel_qcoreapplication.cpp 4 - Note that for an application- or module-wide cleanup, qaddPostRoutine() + Note that for an application- or module-wide cleanup, qAddPostRoutine() is often not suitable. For example, if the program is split into dynamically loaded modules, the relevant module may be unloaded long before the - QCoreApplication destructor is called. In such cases, if using qaddPostRoutine() + QCoreApplication destructor is called. In such cases, if using qAddPostRoutine() is still desirable, qRemovePostRoutine() can be used to prevent a routine from being called by the QCoreApplication destructor. For example, if that routine was called before the module was unloaded. @@ -2870,11 +2918,14 @@ void QCoreApplication::setEventDispatcher(QAbstractEventDispatcher *eventDispatc By selecting the right parent object, this can often be made to clean up the module's data at the right moment. + \note This function has been thread-safe since Qt 5.10. + \sa qRemovePostRoutine() */ /*! \fn void qRemovePostRoutine(QtCleanUpFunction ptr) + \threadsafe \relates QCoreApplication \since 5.3 @@ -2883,6 +2934,8 @@ void QCoreApplication::setEventDispatcher(QAbstractEventDispatcher *eventDispatc must have been previously added to the list by a call to qAddPostRoutine(), otherwise this function has no effect. + \note This function has been thread-safe since Qt 5.10. + \sa qAddPostRoutine() */ diff --git a/src/corelib/kernel/qcoreapplication.h b/src/corelib/kernel/qcoreapplication.h index 0fee7b3de8..6555733c4e 100644 --- a/src/corelib/kernel/qcoreapplication.h +++ b/src/corelib/kernel/qcoreapplication.h @@ -139,7 +139,7 @@ public: static QString applicationDirPath(); static QString applicationFilePath(); - static qint64 applicationPid(); + static qint64 applicationPid() Q_DECL_CONST_FUNCTION; #if QT_CONFIG(library) static void setLibraryPaths(const QStringList &); @@ -206,6 +206,7 @@ private: QT_DEPRECATED bool notifyInternal(QObject *receiver, QEvent *event); // ### Qt6 BIC: remove me # endif static bool notifyInternal2(QObject *receiver, QEvent *); + static bool forwardEvent(QObject *receiver, QEvent *event, QEvent *originatingEvent = nullptr); #endif static QCoreApplication *self; diff --git a/src/corelib/kernel/qcoreapplication_p.h b/src/corelib/kernel/qcoreapplication_p.h index c646786296..cd995c17f1 100644 --- a/src/corelib/kernel/qcoreapplication_p.h +++ b/src/corelib/kernel/qcoreapplication_p.h @@ -58,6 +58,10 @@ #include "private/qobject_p.h" #endif +#ifdef Q_OS_MACOS +#include "private/qcore_mac_p.h" +#endif + QT_BEGIN_NAMESPACE typedef QList<QTranslator*> QTranslatorList; @@ -134,7 +138,7 @@ public: #ifndef QT_NO_TRANSLATION QTranslatorList translators; - + QReadWriteLock translateMutex; static bool isTranslatorInstalled(QTranslator *translator); #endif diff --git a/src/corelib/kernel/qcoreapplication_win.cpp b/src/corelib/kernel/qcoreapplication_win.cpp index 50888dd0aa..00548365d2 100644 --- a/src/corelib/kernel/qcoreapplication_win.cpp +++ b/src/corelib/kernel/qcoreapplication_win.cpp @@ -67,43 +67,33 @@ int appCmdShow = 0; Q_CORE_EXPORT QString qAppFileName() // get application file name { - // We do MAX_PATH + 2 here, and request with MAX_PATH + 1, so we can handle all paths - // up to, and including MAX_PATH size perfectly fine with string termination, as well - // as easily detect if the file path is indeed larger than MAX_PATH, in which case we - // need to use the heap instead. This is a work-around, since contrary to what the - // MSDN documentation states, GetModuleFileName sometimes doesn't set the - // ERROR_INSUFFICIENT_BUFFER error number, and we thus cannot rely on this value if - // GetModuleFileName(0, buffer, MAX_PATH) == MAX_PATH. - // GetModuleFileName(0, buffer, MAX_PATH + 1) == MAX_PATH just means we hit the normal - // file path limit, and we handle it normally, if the result is MAX_PATH + 1, we use - // heap (even if the result _might_ be exactly MAX_PATH + 1, but that's ok). - wchar_t buffer[MAX_PATH + 2]; - DWORD v = GetModuleFileName(0, buffer, MAX_PATH + 1); - buffer[MAX_PATH + 1] = 0; - - if (v == 0) - return QString(); - else if (v <= MAX_PATH) - return QString::fromWCharArray(buffer); - - // MAX_PATH sized buffer wasn't large enough to contain the full path, use heap - wchar_t *b = 0; - int i = 1; - size_t size; + /* + GetModuleFileName() returns the length of the module name, when it has + space to store it and 0-terminate; this return is necessarily smaller than + the buffer size, as it doesn't include the terminator. When it lacks + space, the function returns the full size of the buffer and fills the + buffer, truncating the full path to make it fit. We have reports that + GetModuleFileName sometimes doesn't set the error number to + ERROR_INSUFFICIENT_BUFFER, as claimed by the MSDN documentation; so we + only trust the answer when the return is actually less than the buffer + size we pass in. (When truncating, except on XP, it does so by enough to + still have space to 0-terminate; in either case, it fills the claimed + space and returns the size of the space. While XP might thus give us the + full name, without a 0 terminator, and return its actual length, we can + never be sure that's what's happened until a later call with bigger buffer + confirms it by returning less than its buffer size.) + */ + // Full path may be longer than MAX_PATH - expand until we have enough space: + QVarLengthArray<wchar_t, MAX_PATH + 1> space; + DWORD v; + size_t size = 1; do { - ++i; - size = MAX_PATH * i; - b = reinterpret_cast<wchar_t *>(realloc(b, (size + 1) * sizeof(wchar_t))); - if (b) - v = GetModuleFileName(NULL, b, DWORD(size)); - } while (b && v == size); - - if (b) - *(b + size) = 0; - QString res = QString::fromWCharArray(b); - free(b); + size += MAX_PATH; + space.resize(int(size)); + v = GetModuleFileName(NULL, space.data(), DWORD(space.size())); + } while (Q_UNLIKELY(v >= size)); - return res; + return QString::fromWCharArray(space.data(), v); } QString QCoreApplicationPrivate::appName() const diff --git a/src/corelib/kernel/qeventdispatcher_unix_p.h b/src/corelib/kernel/qeventdispatcher_unix_p.h index d728040343..bcf5b10baa 100644 --- a/src/corelib/kernel/qeventdispatcher_unix_p.h +++ b/src/corelib/kernel/qeventdispatcher_unix_p.h @@ -55,7 +55,6 @@ #include "QtCore/qlist.h" #include "private/qabstracteventdispatcher_p.h" #include "private/qcore_unix_p.h" -#include "private/qpodlist_p.h" #include "QtCore/qvarlengtharray.h" #include "private/qtimerinfo_unix_p.h" diff --git a/src/corelib/kernel/qeventdispatcher_win.cpp b/src/corelib/kernel/qeventdispatcher_win.cpp index 7a6149b405..bbd442d570 100644 --- a/src/corelib/kernel/qeventdispatcher_win.cpp +++ b/src/corelib/kernel/qeventdispatcher_win.cpp @@ -53,6 +53,7 @@ #include "qcoreapplication_p.h" #include <private/qthread_p.h> #include <private/qmutexpool_p.h> +#include <private/qwineventnotifier_p.h> QT_BEGIN_NAMESPACE @@ -96,13 +97,14 @@ LRESULT QT_WIN_CALLBACK qt_internal_proc(HWND hwnd, UINT message, WPARAM wp, LPA QEventDispatcherWin32Private::QEventDispatcherWin32Private() : threadId(GetCurrentThreadId()), interrupt(false), closingDown(false), internalHwnd(0), getMessageHook(0), serialNumber(0), lastSerialNumber(0), sendPostedEventsWindowsTimerId(0), - wakeUps(0) - , activateNotifiersPosted(false) + wakeUps(0), activateNotifiersPosted(false), winEventNotifierActivatedEvent(NULL) { } QEventDispatcherWin32Private::~QEventDispatcherWin32Private() { + if (winEventNotifierActivatedEvent) + CloseHandle(winEventNotifierActivatedEvent); if (internalHwnd) DestroyWindow(internalHwnd); } @@ -538,12 +540,14 @@ bool QEventDispatcherWin32::processEvents(QEventLoop::ProcessEventsFlags flags) bool needWM_QT_SENDPOSTEDEVENTS = false; do { DWORD waitRet = 0; - HANDLE pHandles[MAXIMUM_WAIT_OBJECTS - 1]; + DWORD nCount = 0; + HANDLE *pHandles = nullptr; + if (d->winEventNotifierActivatedEvent) { + nCount = 1; + pHandles = &d->winEventNotifierActivatedEvent; + } QVarLengthArray<MSG> processedTimers; while (!d->interrupt) { - DWORD nCount = d->winEventNotifierList.count(); - Q_ASSERT(nCount < MAXIMUM_WAIT_OBJECTS - 1); - MSG msg; bool haveMessage; @@ -585,8 +589,6 @@ bool QEventDispatcherWin32::processEvents(QEventLoop::ProcessEventsFlags flags) } if (!haveMessage) { // no message - check for signalled objects - for (int i=0; i<(int)nCount; i++) - pHandles[i] = d->winEventNotifierList.at(i)->handle(); waitRet = MsgWaitForMultipleObjectsEx(nCount, pHandles, 0, QS_ALLINPUT, MWMO_ALERTABLE); if ((haveMessage = (waitRet == WAIT_OBJECT_0 + nCount))) { // a new message has arrived, process it @@ -627,7 +629,7 @@ bool QEventDispatcherWin32::processEvents(QEventLoop::ProcessEventsFlags flags) DispatchMessage(&msg); } } else if (waitRet - WAIT_OBJECT_0 < nCount) { - d->activateEventNotifier(d->winEventNotifierList.at(waitRet - WAIT_OBJECT_0)); + activateEventNotifiers(); } else { // nothing todo so break break; @@ -640,16 +642,11 @@ bool QEventDispatcherWin32::processEvents(QEventLoop::ProcessEventsFlags flags) && !d->interrupt && (flags & QEventLoop::WaitForMoreEvents)); if (canWait) { - DWORD nCount = d->winEventNotifierList.count(); - Q_ASSERT(nCount < MAXIMUM_WAIT_OBJECTS - 1); - for (int i=0; i<(int)nCount; i++) - pHandles[i] = d->winEventNotifierList.at(i)->handle(); - emit aboutToBlock(); waitRet = MsgWaitForMultipleObjectsEx(nCount, pHandles, INFINITE, QS_ALLINPUT, MWMO_ALERTABLE | MWMO_INPUTAVAILABLE); emit awake(); if (waitRet - WAIT_OBJECT_0 < nCount) { - d->activateEventNotifier(d->winEventNotifierList.at(waitRet - WAIT_OBJECT_0)); + activateEventNotifiers(); retVal = true; } } @@ -907,12 +904,12 @@ bool QEventDispatcherWin32::registerEventNotifier(QWinEventNotifier *notifier) if (d->winEventNotifierList.contains(notifier)) return true; - if (d->winEventNotifierList.count() >= MAXIMUM_WAIT_OBJECTS - 2) { - qWarning("QWinEventNotifier: Cannot have more than %d enabled at one time", MAXIMUM_WAIT_OBJECTS - 2); - return false; - } d->winEventNotifierList.append(notifier); - return true; + + if (!d->winEventNotifierActivatedEvent) + d->winEventNotifierActivatedEvent = CreateEvent(0, TRUE, FALSE, nullptr); + + return QWinEventNotifierPrivate::get(notifier)->registerWaitObject(); } void QEventDispatcherWin32::unregisterEventNotifier(QWinEventNotifier *notifier) @@ -928,17 +925,36 @@ void QEventDispatcherWin32::unregisterEventNotifier(QWinEventNotifier *notifier) Q_D(QEventDispatcherWin32); int i = d->winEventNotifierList.indexOf(notifier); - if (i != -1) - d->winEventNotifierList.takeAt(i); + if (i == -1) + return; + d->winEventNotifierList.takeAt(i); + QWinEventNotifierPrivate *nd = QWinEventNotifierPrivate::get(notifier); + if (nd->waitHandle) + nd->unregisterWaitObject(); } void QEventDispatcherWin32::activateEventNotifiers() { Q_D(QEventDispatcherWin32); - //### this could break if events are removed/added in the activation - for (int i=0; i<d->winEventNotifierList.count(); i++) { - if (WaitForSingleObjectEx(d->winEventNotifierList.at(i)->handle(), 0, TRUE) == WAIT_OBJECT_0) - d->activateEventNotifier(d->winEventNotifierList.at(i)); + ResetEvent(d->winEventNotifierActivatedEvent); + + // Iterate backwards, because the notifier might remove itself on activate(). + for (int i = d->winEventNotifierList.count(); --i >= 0;) { + QWinEventNotifier *notifier = d->winEventNotifierList.at(i); + QWinEventNotifierPrivate *nd = QWinEventNotifierPrivate::get(notifier); + if (nd->signaledCount.load() != 0) { + --nd->signaledCount; + nd->unregisterWaitObject(); + d->activateEventNotifier(notifier); + } + } + + // Re-register the remaining activated notifiers. + for (int i = 0; i < d->winEventNotifierList.count(); ++i) { + QWinEventNotifier *notifier = d->winEventNotifierList.at(i); + QWinEventNotifierPrivate *nd = QWinEventNotifierPrivate::get(notifier); + if (!nd->waitHandle) + nd->registerWaitObject(); } } diff --git a/src/corelib/kernel/qeventdispatcher_win_p.h b/src/corelib/kernel/qeventdispatcher_win_p.h index f6d1bffdf5..683c7f8f36 100644 --- a/src/corelib/kernel/qeventdispatcher_win_p.h +++ b/src/corelib/kernel/qeventdispatcher_win_p.h @@ -161,6 +161,7 @@ class Q_CORE_EXPORT QEventDispatcherWin32Private : public QAbstractEventDispatch public: QEventDispatcherWin32Private(); ~QEventDispatcherWin32Private(); + static QEventDispatcherWin32Private *get(QEventDispatcherWin32 *q) { return q->d_func(); } DWORD threadId; @@ -192,6 +193,7 @@ public: void postActivateSocketNotifiers(); void doWsaAsyncSelect(int socket, long event); + HANDLE winEventNotifierActivatedEvent; QList<QWinEventNotifier *> winEventNotifierList; void activateEventNotifier(QWinEventNotifier * wen); diff --git a/src/corelib/kernel/qfunctions_fake_env_p.h b/src/corelib/kernel/qfunctions_fake_env_p.h index 16d18c4d88..7010d2cf5d 100644 --- a/src/corelib/kernel/qfunctions_fake_env_p.h +++ b/src/corelib/kernel/qfunctions_fake_env_p.h @@ -77,7 +77,9 @@ struct NameEquals { { return qstrcmp(other.name, name) == 0; } }; +#ifndef Q_CLANG_QDOC Q_GLOBAL_STATIC(QVector<Variable>, qt_app_environment) +#endif errno_t qt_fake_getenv_s(size_t *sizeNeeded, char *buffer, size_t bufferSize, const char *varName) { diff --git a/src/corelib/kernel/qjnihelpers.cpp b/src/corelib/kernel/qjnihelpers.cpp index cb4b93905e..0d7c143999 100644 --- a/src/corelib/kernel/qjnihelpers.cpp +++ b/src/corelib/kernel/qjnihelpers.cpp @@ -75,6 +75,11 @@ static jmethodID g_hideSplashScreenMethodID = Q_NULLPTR; Q_GLOBAL_STATIC(std::deque<QtAndroidPrivate::Runnable>, g_pendingRunnables); static QBasicMutex g_pendingRunnablesMutex; +Q_GLOBAL_STATIC_WITH_ARGS(QtAndroidPrivate::OnBindListener*, g_onBindListener, (nullptr)); +Q_GLOBAL_STATIC(QMutex, g_onBindListenerMutex); +Q_GLOBAL_STATIC(QSemaphore, g_waitForServiceSetupSemaphore); +Q_GLOBAL_STATIC(QAtomicInt, g_serviceSetupLockers); + class PermissionsResultClass : public QObject { Q_OBJECT @@ -406,7 +411,7 @@ jint QtAndroidPrivate::initJNI(JavaVM *vm, JNIEnv *env) g_runPendingCppRunnablesMethodID = env->GetStaticMethodID(jQtNative, "runPendingCppRunnablesOnAndroidThread", "()V"); - g_hideSplashScreenMethodID = env->GetStaticMethodID(jQtNative, "hideSplashScreen", "()V"); + g_hideSplashScreenMethodID = env->GetStaticMethodID(jQtNative, "hideSplashScreen", "(I)V"); g_jNativeClass = static_cast<jclass>(env->NewGlobalRef(jQtNative)); env->DeleteLocalRef(jQtNative); @@ -511,7 +516,7 @@ void QtAndroidPrivate::requestPermissions(JNIEnv *env, const QStringList &permis }, env); } -QHash<QString, QtAndroidPrivate::PermissionsResult> QtAndroidPrivate::requestPermissionsSync(JNIEnv *env, const QStringList &permissions, int timeoutMs) +QtAndroidPrivate::PermissionsHash QtAndroidPrivate::requestPermissionsSync(JNIEnv *env, const QStringList &permissions, int timeoutMs) { QSharedPointer<QHash<QString, QtAndroidPrivate::PermissionsResult>> res(new QHash<QString, QtAndroidPrivate::PermissionsResult>()); QSharedPointer<QSemaphore> sem(new QSemaphore); @@ -567,9 +572,36 @@ void QtAndroidPrivate::unregisterKeyEventListener(QtAndroidPrivate::KeyEventList g_keyEventListeners()->listeners.removeOne(listener); } -void QtAndroidPrivate::hideSplashScreen(JNIEnv *env) +void QtAndroidPrivate::hideSplashScreen(JNIEnv *env, int duration) +{ + env->CallStaticVoidMethod(g_jNativeClass, g_hideSplashScreenMethodID, duration); +} + +void QtAndroidPrivate::waitForServiceSetup() +{ + g_waitForServiceSetupSemaphore->acquire(); +} + +int QtAndroidPrivate::acuqireServiceSetup(int flags) +{ + g_serviceSetupLockers->ref(); + return flags; +} + +void QtAndroidPrivate::setOnBindListener(QtAndroidPrivate::OnBindListener *listener) +{ + QMutexLocker lock(g_onBindListenerMutex); + *g_onBindListener = listener; + if (!g_serviceSetupLockers->deref()) + g_waitForServiceSetupSemaphore->release(); +} + +jobject QtAndroidPrivate::callOnBindListener(jobject intent) { - env->CallStaticVoidMethod(g_jNativeClass, g_hideSplashScreenMethodID); + QMutexLocker lock(g_onBindListenerMutex); + if (g_onBindListener) + return (*g_onBindListener)->onBind(intent); + return nullptr; } QT_END_NAMESPACE diff --git a/src/corelib/kernel/qjnihelpers_p.h b/src/corelib/kernel/qjnihelpers_p.h index d88e4fc19e..ea5103c173 100644 --- a/src/corelib/kernel/qjnihelpers_p.h +++ b/src/corelib/kernel/qjnihelpers_p.h @@ -100,6 +100,13 @@ namespace QtAndroidPrivate virtual bool handleKeyEvent(jobject event) = 0; }; + class Q_CORE_EXPORT OnBindListener + { + public: + virtual ~OnBindListener() {} + virtual jobject onBind(jobject intent) = 0; + }; + enum class PermissionsResult { Granted, Denied @@ -142,7 +149,14 @@ namespace QtAndroidPrivate Q_CORE_EXPORT void registerKeyEventListener(KeyEventListener *listener); Q_CORE_EXPORT void unregisterKeyEventListener(KeyEventListener *listener); - Q_CORE_EXPORT void hideSplashScreen(JNIEnv *env); + Q_CORE_EXPORT void hideSplashScreen(JNIEnv *env, int duration = 0); + + + Q_CORE_EXPORT void waitForServiceSetup(); + Q_CORE_EXPORT int acuqireServiceSetup(int flags); + Q_CORE_EXPORT void setOnBindListener(OnBindListener *listener); + Q_CORE_EXPORT jobject callOnBindListener(jobject intent); + } QT_END_NAMESPACE diff --git a/src/corelib/kernel/qmath.h b/src/corelib/kernel/qmath.h index 773884047a..305e9065e9 100644 --- a/src/corelib/kernel/qmath.h +++ b/src/corelib/kernel/qmath.h @@ -149,6 +149,8 @@ inline qreal qPow(qreal x, qreal y) return pow(x, y); } +// TODO: use template variables (e.g. Qt::pi<type>) for these once we have C++14 support: + #ifndef M_E #define M_E (2.7182818284590452354) #endif @@ -242,16 +244,13 @@ Q_DECL_CONSTEXPR inline double qRadiansToDegrees(double radians) } -#if defined(QT_HAS_BUILTIN_CLZ) -inline quint32 qNextPowerOfTwo(quint32 v) +Q_DECL_RELAXED_CONSTEXPR inline quint32 qNextPowerOfTwo(quint32 v) { +#if defined(QT_HAS_BUILTIN_CLZ) if (v == 0) return 1; return 2U << (31 ^ QAlgorithmsPrivate::qt_builtin_clz(v)); -} #else -inline quint32 qNextPowerOfTwo(quint32 v) -{ v |= v >> 1; v |= v >> 2; v |= v >> 4; @@ -259,19 +258,16 @@ inline quint32 qNextPowerOfTwo(quint32 v) v |= v >> 16; ++v; return v; -} #endif +} -#if defined(QT_HAS_BUILTIN_CLZLL) -inline quint64 qNextPowerOfTwo(quint64 v) +Q_DECL_RELAXED_CONSTEXPR inline quint64 qNextPowerOfTwo(quint64 v) { +#if defined(QT_HAS_BUILTIN_CLZLL) if (v == 0) return 1; return Q_UINT64_C(2) << (63 ^ QAlgorithmsPrivate::qt_builtin_clzll(v)); -} #else -inline quint64 qNextPowerOfTwo(quint64 v) -{ v |= v >> 1; v |= v >> 2; v |= v >> 4; @@ -280,15 +276,15 @@ inline quint64 qNextPowerOfTwo(quint64 v) v |= v >> 32; ++v; return v; -} #endif +} -inline quint32 qNextPowerOfTwo(qint32 v) +Q_DECL_RELAXED_CONSTEXPR inline quint32 qNextPowerOfTwo(qint32 v) { return qNextPowerOfTwo(quint32(v)); } -inline quint64 qNextPowerOfTwo(qint64 v) +Q_DECL_RELAXED_CONSTEXPR inline quint64 qNextPowerOfTwo(qint64 v) { return qNextPowerOfTwo(quint64(v)); } diff --git a/src/corelib/kernel/qmath.qdoc b/src/corelib/kernel/qmath.qdoc index 3a692d5935..a2e24e925b 100644 --- a/src/corelib/kernel/qmath.qdoc +++ b/src/corelib/kernel/qmath.qdoc @@ -35,7 +35,24 @@ These functions are partly convenience definitions for basic math operations not available in the C or Standard Template Libraries. - \pagekeywords math trigonometry qmath floor ceiling absolute sine cosine tangent inverse tan exponent power natural logarithm + The header also ensures some constants specified in POSIX, but not present + in C++ standards (so absent from <math.h> on some platforms), are defined: + + \value M_E The base of the natural logarithms, e = exp(1) + \value M_LOG2E The base-two logarithm of e + \value M_LOG10E The base-ten logarithm of e + \value M_LN2 The natural logarithm of two + \value M_LN10 The natural logarithm of ten + \value M_PI The ratio of a circle's circumference to diameter, \unicode{0x3C0} + \value M_PI_2 Half M_PI, \unicode{0x3C0} / 2 + \value M_PI_4 Quarter M_PI, \unicode{0x3C0} / 4 + \value M_1_PI The inverse of M_PI, 1 / \unicode{0x3C0} + \value M_2_PI Twice the inverse of M_PI, 2 / \unicode{0x3C0} + \value M_2_SQRTPI Two divided by the square root of pi, 2 / \unicode{0x221A}\unicode{0x3C0} + \value M_SQRT2 The square root of two, \unicode{0x221A}2 + \value M_SQRT1_2 The square roof of half, 1 / \unicode{0x221A}2 + + \pagekeywords math trigonometry qmath floor ceiling absolute sine cosine tangent inverse tan exponent power natural logarithm pi */ /*! diff --git a/src/corelib/kernel/qmetaobject.cpp b/src/corelib/kernel/qmetaobject.cpp index a8003f7e46..f07b463482 100644 --- a/src/corelib/kernel/qmetaobject.cpp +++ b/src/corelib/kernel/qmetaobject.cpp @@ -1489,6 +1489,51 @@ bool QMetaObject::invokeMethod(QObject *obj, val0, val1, val2, val3, val4, val5, val6, val7, val8, val9); } +bool QMetaObject::invokeMethodImpl(QObject *object, QtPrivate::QSlotObjectBase *slot, Qt::ConnectionType type, void *ret) +{ + if (! object) + return false; + + QThread *currentThread = QThread::currentThread(); + QThread *objectThread = object->thread(); + if (type == Qt::AutoConnection) + type = (currentThread == objectThread) ? Qt::DirectConnection : Qt::QueuedConnection; + + void *argv[] = { ret }; + + if (type == Qt::DirectConnection) { + slot->call(object, argv); + } else if (type == Qt::QueuedConnection) { + if (argv[0]) { + qWarning("QMetaObject::invokeMethod: Unable to invoke methods with return values in " + "queued connections"); + return false; + } + + // args and typesCopy will be deallocated by ~QMetaCallEvent() using free() + void **args = static_cast<void **>(calloc(1, sizeof(void *))); + Q_CHECK_PTR(args); + + int *types = static_cast<int *>(calloc(1, sizeof(int))); + Q_CHECK_PTR(types); + + QCoreApplication::postEvent(object, new QMetaCallEvent(slot, 0, -1, 1, types, args)); + } else if (type == Qt::BlockingQueuedConnection) { +#ifndef QT_NO_THREAD + if (currentThread == objectThread) + qWarning("QMetaObject::invokeMethod: Dead lock detected"); + + QSemaphore semaphore; + QCoreApplication::postEvent(object, new QMetaCallEvent(slot, 0, -1, 0, 0, argv, &semaphore)); + semaphore.acquire(); +#endif // QT_NO_THREAD + } else { + qWarning("QMetaObject::invokeMethod: Unknown connection type"); + return false; + } + return true; +} + /*! \fn bool QMetaObject::invokeMethod(QObject *obj, const char *member, QGenericReturnArgument ret, QGenericArgument val0 = QGenericArgument(0), @@ -1544,6 +1589,44 @@ bool QMetaObject::invokeMethod(QObject *obj, */ /*! + \fn bool QMetaObject::invokeMethod(QObject *receiver, PointerToMemberFunction function, Qt::ConnectionType type = Qt::AutoConnection, MemberFunctionReturnType *ret = Q_NULLPTR) + + \since 5.10 + + \overload +*/ + +/*! + \fn bool QMetaObject::invokeMethod(QObject *receiver, PointerToMemberFunction function, MemberFunctionReturnType *ret) + + \since 5.10 + + \overload + + This overload invokes the member function using the connection type Qt::AutoConnection. +*/ + +/*! + \fn bool QMetaObject::invokeMethod(QObject *context, Functor function, Qt::ConnectionType type = Qt::AutoConnection, FunctorReturnType *ret = Q_NULLPTR) + + \since 5.10 + + \overload + + Call the functor in the event loop of \a context. +*/ + +/*! + \fn bool QMetaObject::invokeMethod(QObject *context, Functor function, FunctorReturnType *ret = Q_NULLPTR) + + \since 5.10 + + \overload + + Call the functor in the event loop of \a context using the connection type Qt::AutoConnection. +*/ + +/*! \fn QMetaObject::Connection::Connection(const Connection &other) Constructs a copy of \a other. @@ -3264,7 +3347,21 @@ int QMetaProperty::notifySignalIndex() const if (hasNotifySignal()) { int offset = priv(mobj->d.data)->propertyData + priv(mobj->d.data)->propertyCount * 3 + idx; - return mobj->d.data[offset] + mobj->methodOffset(); + int methodIndex = mobj->d.data[offset]; + if (methodIndex & IsUnresolvedSignal) { + methodIndex &= ~IsUnresolvedSignal; + const QByteArray signalName = stringData(mobj, methodIndex); + const QMetaObject *m = mobj; + const int idx = indexOfMethodRelative<MethodSignal>(&m, signalName, 0, nullptr); + if (idx >= 0) { + return idx + m->methodOffset(); + } else { + qWarning("QMetaProperty::notifySignal: cannot find the NOTIFY signal %s in class %s for property '%s'", + signalName.constData(), objectClassName(mobj), name()); + return -1; + } + } + return methodIndex + mobj->methodOffset(); } else { return -1; } diff --git a/src/corelib/kernel/qmetaobject_p.h b/src/corelib/kernel/qmetaobject_p.h index e247c48703..434ef84808 100644 --- a/src/corelib/kernel/qmetaobject_p.h +++ b/src/corelib/kernel/qmetaobject_p.h @@ -111,7 +111,8 @@ enum MetaObjectFlags { enum MetaDataFlags { IsUnresolvedType = 0x80000000, - TypeNameIndexMask = 0x7FFFFFFF + TypeNameIndexMask = 0x7FFFFFFF, + IsUnresolvedSignal = 0x70000000 }; enum EnumFlags { diff --git a/src/corelib/kernel/qmetaobjectbuilder.cpp b/src/corelib/kernel/qmetaobjectbuilder.cpp index 56f187a59d..e3b70638c6 100644 --- a/src/corelib/kernel/qmetaobjectbuilder.cpp +++ b/src/corelib/kernel/qmetaobjectbuilder.cpp @@ -190,12 +190,13 @@ class QMetaEnumBuilderPrivate { public: QMetaEnumBuilderPrivate(const QByteArray& _name) - : name(_name), isFlag(false) + : name(_name), isFlag(false), isScoped(false) { } QByteArray name; bool isFlag; + bool isScoped; QList<QByteArray> keys; QVector<int> values; }; @@ -637,6 +638,7 @@ QMetaEnumBuilder QMetaObjectBuilder::addEnumerator(const QMetaEnum& prototype) { QMetaEnumBuilder en = addEnumerator(prototype.name()); en.setIsFlag(prototype.isFlag()); + en.setIsScoped(prototype.isScoped()); int count = prototype.keyCount(); for (int index = 0; index < count; ++index) en.addKey(prototype.key(index), prototype.value(index)); @@ -1408,12 +1410,13 @@ static int buildMetaObject(QMetaObjectBuilderPrivate *d, char *buf, Q_ASSERT(!buf || dataIndex == pmeta->enumeratorData); for (const auto &enumerator : d->enumerators) { int name = strings.enter(enumerator.name); - int isFlag = (int)(enumerator.isFlag); + int isFlag = enumerator.isFlag ? EnumIsFlag : 0; + int isScoped = enumerator.isScoped ? EnumIsScoped : 0; int count = enumerator.keys.size(); int enumOffset = enumIndex; if (buf) { data[dataIndex] = name; - data[dataIndex + 1] = isFlag; + data[dataIndex + 1] = isFlag | isScoped; data[dataIndex + 2] = count; data[dataIndex + 3] = enumOffset; } @@ -1641,6 +1644,7 @@ void QMetaObjectBuilder::serialize(QDataStream& stream) const for (const auto &enumerator : d->enumerators) { stream << enumerator.name; stream << enumerator.isFlag; + stream << enumerator.isScoped; stream << enumerator.keys; stream << enumerator.values; } @@ -1807,6 +1811,7 @@ void QMetaObjectBuilder::deserialize addEnumerator(name); QMetaEnumBuilderPrivate &enumerator = d->enumerators[index]; stream >> enumerator.isFlag; + stream >> enumerator.isScoped; stream >> enumerator.keys; stream >> enumerator.values; if (enumerator.keys.size() != enumerator.values.size()) { @@ -2633,6 +2638,31 @@ void QMetaEnumBuilder::setIsFlag(bool value) } /*! + Return \c true if this enumerator should be considered scoped (C++11 enum class). + + \sa setIsScoped() +*/ +bool QMetaEnumBuilder::isScoped() const +{ + QMetaEnumBuilderPrivate *d = d_func(); + if (d) + return d->isScoped; + return false; +} + +/*! + Sets this enumerator to be a scoped enum if \value is true + + \sa isScoped() +*/ +void QMetaEnumBuilder::setIsScoped(bool value) +{ + QMetaEnumBuilderPrivate *d = d_func(); + if (d) + d->isScoped = value; +} + +/*! Returns the number of keys. \sa key(), addKey() diff --git a/src/corelib/kernel/qmetaobjectbuilder_p.h b/src/corelib/kernel/qmetaobjectbuilder_p.h index 144595330d..03b2afaebc 100644 --- a/src/corelib/kernel/qmetaobjectbuilder_p.h +++ b/src/corelib/kernel/qmetaobjectbuilder_p.h @@ -300,6 +300,9 @@ public: bool isFlag() const; void setIsFlag(bool value); + bool isScoped() const; + void setIsScoped(bool value); + int keyCount() const; QByteArray key(int index) const; int value(int index) const; diff --git a/src/corelib/kernel/qmetatype.cpp b/src/corelib/kernel/qmetatype.cpp index b75f2ad9dc..e48807ea49 100644 --- a/src/corelib/kernel/qmetatype.cpp +++ b/src/corelib/kernel/qmetatype.cpp @@ -308,6 +308,7 @@ struct DefinedTypesFilter { \omitvalue TrackingPointerToQObject \omitvalue WasDeclaredAsMetaType \omitvalue IsGadget This type is a Q_GADGET and it's corresponding QMetaObject can be accessed with QMetaType::metaObject Since 5.5. + \omitvalue PointerToGadget */ /*! diff --git a/src/corelib/kernel/qmetatype.h b/src/corelib/kernel/qmetatype.h index d88f469e0f..f704c5b21a 100644 --- a/src/corelib/kernel/qmetatype.h +++ b/src/corelib/kernel/qmetatype.h @@ -462,7 +462,8 @@ public: WeakPointerToQObject = 0x40, TrackingPointerToQObject = 0x80, WasDeclaredAsMetaType = 0x100, - IsGadget = 0x200 + IsGadget = 0x200, + PointerToGadget = 0x400 }; Q_DECLARE_FLAGS(TypeFlags, TypeFlag) @@ -1388,6 +1389,19 @@ namespace QtPrivate enum { Value = sizeof(checkType(&T::qt_check_for_QGADGET_macro)) == sizeof(void *) }; }; + template<typename T, typename Enable = void> + struct IsPointerToGadgetHelper { enum { Value = false }; }; + + template<typename T> + struct IsPointerToGadgetHelper<T*, typename T::QtGadgetHelper> + { + using BaseType = T; + template <typename X> + static char checkType(void (X::*)()); + static void *checkType(void (T::*)()); + enum { Value = sizeof(checkType(&T::qt_check_for_QGADGET_macro)) == sizeof(void *) }; + }; + template<typename T> char qt_getEnumMetaObject(const T&); @@ -1423,6 +1437,11 @@ namespace QtPrivate static inline const QMetaObject *value() { return &T::staticMetaObject; } }; template<typename T> + struct MetaObjectForType<T, typename QEnableIf<IsPointerToGadgetHelper<T>::Value>::Type> + { + static inline const QMetaObject *value() { return &IsPointerToGadgetHelper<T>::BaseType::staticMetaObject; } + }; + template<typename T> struct MetaObjectForType<T, typename std::enable_if<IsQEnumHelper<T>::Value>::type > { static inline const QMetaObject *value() { return qt_getEnumMetaObject(T()); } @@ -1578,6 +1597,7 @@ namespace QtPrivate template <typename T, int = QtPrivate::IsPointerToTypeDerivedFromQObject<T>::Value ? QMetaType::PointerToQObject : QtPrivate::IsGadgetHelper<T>::Value ? QMetaType::IsGadget : + QtPrivate::IsPointerToGadgetHelper<T>::Value ? QMetaType::PointerToGadget : QtPrivate::IsQEnumHelper<T>::Value ? QMetaType::IsEnumeration : 0> struct QMetaTypeIdQObject { @@ -1631,6 +1651,7 @@ namespace QtPrivate { | (IsTrackingPointerToTypeDerivedFromQObject<T>::Value ? QMetaType::TrackingPointerToQObject : 0) | (std::is_enum<T>::value ? QMetaType::IsEnumeration : 0) | (IsGadgetHelper<T>::Value ? QMetaType::IsGadget : 0) + | (IsPointerToGadgetHelper<T>::Value ? QMetaType::PointerToGadget : 0) }; }; @@ -1798,6 +1819,30 @@ struct QMetaTypeIdQObject<T, QMetaType::IsGadget> }; template <typename T> +struct QMetaTypeIdQObject<T*, QMetaType::PointerToGadget> +{ + enum { + Defined = 1 + }; + + static int qt_metatype_id() + { + static QBasicAtomicInt metatype_id = Q_BASIC_ATOMIC_INITIALIZER(0); + if (const int id = metatype_id.loadAcquire()) + return id; + const char * const cName = T::staticMetaObject.className(); + QByteArray typeName; + typeName.reserve(int(strlen(cName)) + 1); + typeName.append(cName).append('*'); + const int newId = qRegisterNormalizedMetaType<T*>( + typeName, + reinterpret_cast<T**>(quintptr(-1))); + metatype_id.storeRelease(newId); + return newId; + } +}; + +template <typename T> struct QMetaTypeIdQObject<T, QMetaType::IsEnumeration> { enum { diff --git a/src/corelib/kernel/qobject.cpp b/src/corelib/kernel/qobject.cpp index 086b8a51ba..bc1af5d66f 100644 --- a/src/corelib/kernel/qobject.cpp +++ b/src/corelib/kernel/qobject.cpp @@ -2339,7 +2339,7 @@ static void err_info_about_objects(const char * func, a thread different from this object's thread. Do not use this function in this type of scenario. - \sa senderSignalIndex(), QSignalMapper + \sa senderSignalIndex() */ QObject *QObject::sender() const diff --git a/src/corelib/kernel/qobject.h b/src/corelib/kernel/qobject.h index 6941c55896..2e66daa914 100644 --- a/src/corelib/kernel/qobject.h +++ b/src/corelib/kernel/qobject.h @@ -304,7 +304,7 @@ public: static inline typename std::enable_if<QtPrivate::FunctionPointer<Func2>::ArgumentCount == -1, QMetaObject::Connection>::type connect(const typename QtPrivate::FunctionPointer<Func1>::Object *sender, Func1 signal, Func2 slot) { - return connect(sender, signal, sender, slot, Qt::DirectConnection); + return connect(sender, signal, sender, std::move(slot), Qt::DirectConnection); } //connect to a functor, with a "context" object defining in which event loop is going to be executed @@ -334,7 +334,7 @@ public: return connectImpl(sender, reinterpret_cast<void **>(&signal), context, Q_NULLPTR, new QtPrivate::QFunctorSlotObject<Func2, SlotArgumentCount, typename QtPrivate::List_Left<typename SignalType::Arguments, SlotArgumentCount>::Value, - typename SignalType::ReturnType>(slot), + typename SignalType::ReturnType>(std::move(slot)), type, types, &SignalType::Object::staticMetaObject); } #endif //Q_QDOC diff --git a/src/corelib/kernel/qobject_impl.h b/src/corelib/kernel/qobject_impl.h index d7ae63a98c..c775d807b1 100644 --- a/src/corelib/kernel/qobject_impl.h +++ b/src/corelib/kernel/qobject_impl.h @@ -74,60 +74,6 @@ namespace QtPrivate { template <typename... Args> struct ConnectionTypes<List<Args...>, true> { static const int *types() { static const int t[sizeof...(Args) + 1] = { (QtPrivate::QMetaTypeIdHelper<Args>::qt_metatype_id())..., 0 }; return t; } }; - // internal base class (interface) containing functions required to call a slot managed by a pointer to function. - class QSlotObjectBase { - QAtomicInt m_ref; - // don't use virtual functions here; we don't want the - // compiler to create tons of per-polymorphic-class stuff that - // we'll never need. We just use one function pointer. - typedef void (*ImplFn)(int which, QSlotObjectBase* this_, QObject *receiver, void **args, bool *ret); - const ImplFn m_impl; - protected: - enum Operation { - Destroy, - Call, - Compare, - - NumOperations - }; - public: - explicit QSlotObjectBase(ImplFn fn) : m_ref(1), m_impl(fn) {} - - inline int ref() Q_DECL_NOTHROW { return m_ref.ref(); } - inline void destroyIfLastRef() Q_DECL_NOTHROW - { if (!m_ref.deref()) m_impl(Destroy, this, Q_NULLPTR, Q_NULLPTR, Q_NULLPTR); } - - inline bool compare(void **a) { bool ret = false; m_impl(Compare, this, Q_NULLPTR, a, &ret); return ret; } - inline void call(QObject *r, void **a) { m_impl(Call, this, r, a, Q_NULLPTR); } - protected: - ~QSlotObjectBase() {} - private: - Q_DISABLE_COPY(QSlotObjectBase) - }; - // implementation of QSlotObjectBase for which the slot is a pointer to member function of a QObject - // Args and R are the List of arguments and the returntype of the signal to which the slot is connected. - template<typename Func, typename Args, typename R> class QSlotObject : public QSlotObjectBase - { - typedef QtPrivate::FunctionPointer<Func> FuncType; - Func function; - static void impl(int which, QSlotObjectBase *this_, QObject *r, void **a, bool *ret) - { - switch (which) { - case Destroy: - delete static_cast<QSlotObject*>(this_); - break; - case Call: - FuncType::template call<Args, R>(static_cast<QSlotObject*>(this_)->function, static_cast<typename FuncType::Object *>(r), a); - break; - case Compare: - *ret = *reinterpret_cast<Func *>(a) == static_cast<QSlotObject*>(this_)->function; - break; - case NumOperations: ; - } - } - public: - explicit QSlotObject(Func f) : QSlotObjectBase(&impl), function(f) {} - }; // implementation of QSlotObjectBase for which the slot is a static function // Args and R are the List of arguments and the returntype of the signal to which the slot is connected. template<typename Func, typename Args, typename R> class QStaticSlotObject : public QSlotObjectBase @@ -151,30 +97,6 @@ namespace QtPrivate { public: explicit QStaticSlotObject(Func f) : QSlotObjectBase(&impl), function(f) {} }; - // implementation of QSlotObjectBase for which the slot is a functor (or lambda) - // N is the number of arguments - // Args and R are the List of arguments and the returntype of the signal to which the slot is connected. - template<typename Func, int N, typename Args, typename R> class QFunctorSlotObject : public QSlotObjectBase - { - typedef QtPrivate::Functor<Func, N> FuncType; - Func function; - static void impl(int which, QSlotObjectBase *this_, QObject *r, void **a, bool *ret) - { - switch (which) { - case Destroy: - delete static_cast<QFunctorSlotObject*>(this_); - break; - case Call: - FuncType::template call<Args, R>(static_cast<QFunctorSlotObject*>(this_)->function, r, a); - break; - case Compare: // not implemented - case NumOperations: - Q_UNUSED(ret); - } - } - public: - explicit QFunctorSlotObject(const Func &f) : QSlotObjectBase(&impl), function(f) {} - }; } diff --git a/src/corelib/kernel/qobject_p.h b/src/corelib/kernel/qobject_p.h index 7b9253ac64..ad88bcf274 100644 --- a/src/corelib/kernel/qobject_p.h +++ b/src/corelib/kernel/qobject_p.h @@ -235,7 +235,7 @@ public: mutable quint32 connectedSignals[2]; union { - QObject *currentChildBeingDeleted; + QObject *currentChildBeingDeleted; // should only be used when QObjectData::isDeletingChildren is set QAbstractDeclarativeData *declarativeData; //extra data used by the declarative module }; @@ -244,6 +244,7 @@ public: QAtomicPointer<QtSharedPointer::ExternalRefCountData> sharedRefcount; }; +Q_DECLARE_TYPEINFO(QObjectPrivate::ConnectionList, Q_MOVABLE_TYPE); /*! \internal diff --git a/src/corelib/kernel/qobjectdefs.h b/src/corelib/kernel/qobjectdefs.h index cec822ad14..5fc8937f23 100644 --- a/src/corelib/kernel/qobjectdefs.h +++ b/src/corelib/kernel/qobjectdefs.h @@ -57,7 +57,6 @@ struct QArrayData; typedef QArrayData QByteArrayData; class QString; - #ifndef Q_MOC_OUTPUT_REVISION #define Q_MOC_OUTPUT_REVISION 67 #endif @@ -467,6 +466,91 @@ struct Q_CORE_EXPORT QMetaObject val1, val2, val3, val4, val5, val6, val7, val8, val9); } +#ifdef Q_QDOC + template<typename PointerToMemberFunction, typename MemberFunctionReturnType> + static bool invokeMethod(QObject *receiver, PointerToMemberFunction function, Qt::ConnectionType type = Qt::AutoConnection, MemberFunctionReturnType *ret = nullptr); + template<typename PointerToMemberFunction, typename MemberFunctionReturnType> + static bool invokeMethod(QObject *receiver, PointerToMemberFunction function, MemberFunctionReturnType *ret); + template<typename Functor, typename FunctorReturnType> + static bool invokeMethod(QObject *context, Functor function, Qt::ConnectionType type = Qt::AutoConnection, FunctorReturnType *ret = nullptr); + template<typename Functor, typename FunctorReturnType> + static bool invokeMethod(QObject *context, Functor function, FunctorReturnType *ret); +#else + + // invokeMethod() for member function pointer + template <typename Func> + static typename std::enable_if<QtPrivate::FunctionPointer<Func>::IsPointerToMemberFunction + && !std::is_convertible<Func, const char*>::value + && QtPrivate::FunctionPointer<Func>::ArgumentCount == 0, bool>::type + invokeMethod(typename QtPrivate::FunctionPointer<Func>::Object *object, + Func function, + Qt::ConnectionType type = Qt::AutoConnection, + typename QtPrivate::FunctionPointer<Func>::ReturnType *ret = nullptr) + { + return invokeMethodImpl(object, new QtPrivate::QSlotObjectWithNoArgs<Func>(function), type, ret); + } + + template <typename Func> + static typename std::enable_if<QtPrivate::FunctionPointer<Func>::IsPointerToMemberFunction + && !std::is_convertible<Func, const char*>::value + && QtPrivate::FunctionPointer<Func>::ArgumentCount == 0, bool>::type + invokeMethod(typename QtPrivate::FunctionPointer<Func>::Object *object, + Func function, + typename QtPrivate::FunctionPointer<Func>::ReturnType *ret) + { + return invokeMethodImpl(object, new QtPrivate::QSlotObjectWithNoArgs<Func>(function), Qt::AutoConnection, ret); + } + + // invokeMethod() for function pointer (not member) + template <typename Func> + static typename std::enable_if<!QtPrivate::FunctionPointer<Func>::IsPointerToMemberFunction + && !std::is_convertible<Func, const char*>::value + && QtPrivate::FunctionPointer<Func>::ArgumentCount == 0, bool>::type + invokeMethod(QObject *context, Func function, + Qt::ConnectionType type = Qt::AutoConnection, + typename QtPrivate::FunctionPointer<Func>::ReturnType *ret = nullptr) + { + return invokeMethodImpl(context, new QtPrivate::QFunctorSlotObjectWithNoArgsImplicitReturn<Func>(function), type, ret); + } + + template <typename Func> + static typename std::enable_if<!QtPrivate::FunctionPointer<Func>::IsPointerToMemberFunction + && !std::is_convertible<Func, const char*>::value + && QtPrivate::FunctionPointer<Func>::ArgumentCount == 0, bool>::type + invokeMethod(QObject *context, Func function, + typename QtPrivate::FunctionPointer<Func>::ReturnType *ret) + { + return invokeMethodImpl(context, new QtPrivate::QFunctorSlotObjectWithNoArgsImplicitReturn<Func>(function), Qt::AutoConnection, ret); + } + + // invokeMethod() for Functor + template <typename Func> + static typename std::enable_if<!QtPrivate::FunctionPointer<Func>::IsPointerToMemberFunction + && QtPrivate::FunctionPointer<Func>::ArgumentCount == -1 + && !std::is_convertible<Func, const char*>::value, bool>::type + invokeMethod(QObject *context, Func function, + Qt::ConnectionType type = Qt::AutoConnection, decltype(function()) *ret = nullptr) + { + return invokeMethodImpl(context, + new QtPrivate::QFunctorSlotObjectWithNoArgs<Func, decltype(function())>(function), + type, + ret); + } + + template <typename Func> + static typename std::enable_if<!QtPrivate::FunctionPointer<Func>::IsPointerToMemberFunction + && QtPrivate::FunctionPointer<Func>::ArgumentCount == -1 + && !std::is_convertible<Func, const char*>::value, bool>::type + invokeMethod(QObject *context, Func function, typename std::result_of<Func()>::type *ret) + { + return invokeMethodImpl(context, + new QtPrivate::QFunctorSlotObjectWithNoArgs<Func, decltype(function())>(function), + Qt::AutoConnection, + ret); + } + +#endif + QObject *newInstance(QGenericArgument val0 = QGenericArgument(Q_NULLPTR), QGenericArgument val1 = QGenericArgument(), QGenericArgument val2 = QGenericArgument(), @@ -506,6 +590,9 @@ struct Q_CORE_EXPORT QMetaObject const QMetaObject * const *relatedMetaObjects; void *extradata; //reserved for future use } d; + +private: + static bool invokeMethodImpl(QObject *object, QtPrivate::QSlotObjectBase *slot, Qt::ConnectionType type, void *ret); }; class Q_CORE_EXPORT QMetaObject::Connection { diff --git a/src/corelib/kernel/qobjectdefs_impl.h b/src/corelib/kernel/qobjectdefs_impl.h index 29ab77b269..b9f2e47e32 100644 --- a/src/corelib/kernel/qobjectdefs_impl.h +++ b/src/corelib/kernel/qobjectdefs_impl.h @@ -51,7 +51,7 @@ #endif QT_BEGIN_NAMESPACE - +class QObject; namespace QtPrivate { template <typename T> struct RemoveRef { typedef T Type; }; @@ -350,6 +350,98 @@ namespace QtPrivate { template <typename D> static D dummy(); typedef decltype(dummy<Functor>().operator()((dummy<ArgList>())...)) Value; }; + + // internal base class (interface) containing functions required to call a slot managed by a pointer to function. + class QSlotObjectBase { + QAtomicInt m_ref; + // don't use virtual functions here; we don't want the + // compiler to create tons of per-polymorphic-class stuff that + // we'll never need. We just use one function pointer. + typedef void (*ImplFn)(int which, QSlotObjectBase* this_, QObject *receiver, void **args, bool *ret); + const ImplFn m_impl; + protected: + enum Operation { + Destroy, + Call, + Compare, + + NumOperations + }; + public: + explicit QSlotObjectBase(ImplFn fn) : m_ref(1), m_impl(fn) {} + + inline int ref() Q_DECL_NOTHROW { return m_ref.ref(); } + inline void destroyIfLastRef() Q_DECL_NOTHROW + { if (!m_ref.deref()) m_impl(Destroy, this, Q_NULLPTR, Q_NULLPTR, Q_NULLPTR); } + + inline bool compare(void **a) { bool ret = false; m_impl(Compare, this, Q_NULLPTR, a, &ret); return ret; } + inline void call(QObject *r, void **a) { m_impl(Call, this, r, a, Q_NULLPTR); } + protected: + ~QSlotObjectBase() {} + private: + Q_DISABLE_COPY(QSlotObjectBase) + }; + + // implementation of QSlotObjectBase for which the slot is a pointer to member function of a QObject + // Args and R are the List of arguments and the returntype of the signal to which the slot is connected. + template<typename Func, typename Args, typename R> class QSlotObject : public QSlotObjectBase + { + typedef QtPrivate::FunctionPointer<Func> FuncType; + Func function; + static void impl(int which, QSlotObjectBase *this_, QObject *r, void **a, bool *ret) + { + switch (which) { + case Destroy: + delete static_cast<QSlotObject*>(this_); + break; + case Call: + FuncType::template call<Args, R>(static_cast<QSlotObject*>(this_)->function, static_cast<typename FuncType::Object *>(r), a); + break; + case Compare: + *ret = *reinterpret_cast<Func *>(a) == static_cast<QSlotObject*>(this_)->function; + break; + case NumOperations: ; + } + } + public: + explicit QSlotObject(Func f) : QSlotObjectBase(&impl), function(f) {} + }; + // implementation of QSlotObjectBase for which the slot is a functor (or lambda) + // N is the number of arguments + // Args and R are the List of arguments and the returntype of the signal to which the slot is connected. + template<typename Func, int N, typename Args, typename R> class QFunctorSlotObject : public QSlotObjectBase + { + typedef QtPrivate::Functor<Func, N> FuncType; + Func function; + static void impl(int which, QSlotObjectBase *this_, QObject *r, void **a, bool *ret) + { + switch (which) { + case Destroy: + delete static_cast<QFunctorSlotObject*>(this_); + break; + case Call: + FuncType::template call<Args, R>(static_cast<QFunctorSlotObject*>(this_)->function, r, a); + break; + case Compare: // not implemented + case NumOperations: + Q_UNUSED(ret); + } + } + public: + explicit QFunctorSlotObject(Func f) : QSlotObjectBase(&impl), function(std::move(f)) {} + }; + + // typedefs for readability for when there are no parameters + template <typename Func> + using QSlotObjectWithNoArgs = QSlotObject<Func, + QtPrivate::List<>, + typename QtPrivate::FunctionPointer<Func>::ReturnType>; + + template <typename Func, typename R> + using QFunctorSlotObjectWithNoArgs = QFunctorSlotObject<Func, 0, QtPrivate::List<>, R>; + + template <typename Func> + using QFunctorSlotObjectWithNoArgsImplicitReturn = QFunctorSlotObjectWithNoArgs<Func, typename QtPrivate::FunctionPointer<Func>::ReturnType>; } QT_END_NAMESPACE diff --git a/src/corelib/kernel/qsignalmapper.cpp b/src/corelib/kernel/qsignalmapper.cpp index a483717da5..d56965281e 100644 --- a/src/corelib/kernel/qsignalmapper.cpp +++ b/src/corelib/kernel/qsignalmapper.cpp @@ -58,10 +58,10 @@ public: }; - /*! \class QSignalMapper \inmodule QtCore + \obsolete \brief The QSignalMapper class bundles signals from identifiable senders. \ingroup objectmodel @@ -108,6 +108,12 @@ public: widget will emit a single \c clicked() signal whose argument is the text of the button the user clicked. + This class was mostly useful before lambda functions could be used as + slots. The example above can be rewritten simpler without QSignalMapper + by connecting to a lambda function. + + \snippet qsignalmapper/buttonwidget.cpp 3 + \sa QObject, QButtonGroup, QActionGroup */ diff --git a/src/corelib/kernel/qsignalmapper.h b/src/corelib/kernel/qsignalmapper.h index f960c7cabf..6c4cfa9627 100644 --- a/src/corelib/kernel/qsignalmapper.h +++ b/src/corelib/kernel/qsignalmapper.h @@ -42,6 +42,8 @@ #include <QtCore/qobject.h> +#if QT_DEPRECATED_SINCE(5, 10) + QT_BEGIN_NAMESPACE class QSignalMapperPrivate; @@ -51,7 +53,7 @@ class Q_CORE_EXPORT QSignalMapper : public QObject Q_OBJECT Q_DECLARE_PRIVATE(QSignalMapper) public: - explicit QSignalMapper(QObject *parent = Q_NULLPTR); + QT_DEPRECATED explicit QSignalMapper(QObject *parent = nullptr); ~QSignalMapper(); void setMapping(QObject *sender, int id); @@ -82,4 +84,6 @@ private: QT_END_NAMESPACE +#endif + #endif // QSIGNALMAPPER_H diff --git a/src/corelib/kernel/qtimer.h b/src/corelib/kernel/qtimer.h index 6e61ca10cb..44ae04f38d 100644 --- a/src/corelib/kernel/qtimer.h +++ b/src/corelib/kernel/qtimer.h @@ -122,14 +122,14 @@ public: !std::is_same<const char*, Func1>::value, void>::type singleShot(Duration interval, Func1 slot) { - singleShot(interval, defaultTypeFor(interval), nullptr, slot); + singleShot(interval, defaultTypeFor(interval), nullptr, std::move(slot)); } template <typename Duration, typename Func1> static inline typename std::enable_if<!QtPrivate::FunctionPointer<Func1>::IsPointerToMemberFunction && !std::is_same<const char*, Func1>::value, void>::type singleShot(Duration interval, Qt::TimerType timerType, Func1 slot) { - singleShot(interval, timerType, nullptr, slot); + singleShot(interval, timerType, nullptr, std::move(slot)); } // singleShot to a functor or function pointer (with context) template <typename Duration, typename Func1> @@ -137,7 +137,7 @@ public: !std::is_same<const char*, Func1>::value, void>::type singleShot(Duration interval, QObject *context, Func1 slot) { - singleShot(interval, defaultTypeFor(interval), context, slot); + singleShot(interval, defaultTypeFor(interval), context, std::move(slot)); } template <typename Duration, typename Func1> static inline typename std::enable_if<!QtPrivate::FunctionPointer<Func1>::IsPointerToMemberFunction && @@ -150,7 +150,7 @@ public: singleShotImpl(interval, timerType, context, new QtPrivate::QFunctorSlotObject<Func1, 0, - typename QtPrivate::List_Left<void, 0>::Value, void>(slot)); + typename QtPrivate::List_Left<void, 0>::Value, void>(std::move(slot))); } #endif @@ -165,37 +165,31 @@ Q_SIGNALS: public: #if QT_HAS_INCLUDE(<chrono>) || defined(Q_QDOC) - Q_ALWAYS_INLINE void setInterval(std::chrono::milliseconds value) { setInterval(int(value.count())); } - Q_ALWAYS_INLINE std::chrono::milliseconds intervalAsDuration() const { return std::chrono::milliseconds(interval()); } - Q_ALWAYS_INLINE std::chrono::milliseconds remainingTimeAsDuration() const { return std::chrono::milliseconds(remainingTime()); } - Q_ALWAYS_INLINE static void singleShot(std::chrono::milliseconds value, const QObject *receiver, const char *member) { singleShot(int(value.count()), receiver, member); } - Q_ALWAYS_INLINE static void singleShot(std::chrono::milliseconds value, Qt::TimerType timerType, const QObject *receiver, const char *member) { singleShot(int(value.count()), timerType, receiver, member); } - Q_ALWAYS_INLINE void start(std::chrono::milliseconds value) { start(int(value.count())); diff --git a/src/corelib/kernel/qvariant.cpp b/src/corelib/kernel/qvariant.cpp index 36fd4567a7..29429b5e55 100644 --- a/src/corelib/kernel/qvariant.cpp +++ b/src/corelib/kernel/qvariant.cpp @@ -470,6 +470,9 @@ static bool convert(const QVariant::Private *d, int t, void *result, bool *ok) case QVariant::Uuid: *str = v_cast<QUuid>(d)->toString(); break; + case QMetaType::Nullptr: + *str = QString(); + break; default: #ifndef QT_NO_QOBJECT { @@ -652,6 +655,9 @@ static bool convert(const QVariant::Private *d, int t, void *result, bool *ok) case QVariant::Uuid: *ba = v_cast<QUuid>(d)->toByteArray(); break; + case QMetaType::Nullptr: + *ba = QByteArray(); + break; default: #ifndef QT_NO_QOBJECT { @@ -938,6 +944,107 @@ static bool convert(const QVariant::Private *d, int t, void *result, bool *ok) return false; } break; +#ifndef QT_BOOTSTRAPPED + case QMetaType::QJsonValue: + switch (d->type) { + case QMetaType::Nullptr: + *static_cast<QJsonValue *>(result) = QJsonValue(QJsonValue::Null); + break; + case QVariant::Bool: + *static_cast<QJsonValue *>(result) = QJsonValue(d->data.b); + break; + case QMetaType::Int: + case QMetaType::UInt: + case QMetaType::Double: + case QMetaType::Float: + case QMetaType::ULong: + case QMetaType::Long: + case QMetaType::LongLong: + case QMetaType::ULongLong: + case QMetaType::UShort: + case QMetaType::UChar: + case QMetaType::Char: + case QMetaType::SChar: + case QMetaType::Short: + *static_cast<QJsonValue *>(result) = QJsonValue(qConvertToRealNumber(d, ok)); + Q_ASSERT(ok); + break; + case QVariant::String: + *static_cast<QJsonValue *>(result) = QJsonValue(*v_cast<QString>(d)); + break; + case QVariant::StringList: + *static_cast<QJsonValue *>(result) = QJsonValue(QJsonArray::fromStringList(*v_cast<QStringList>(d))); + break; + case QVariant::List: + *static_cast<QJsonValue *>(result) = QJsonValue(QJsonArray::fromVariantList(*v_cast<QVariantList>(d))); + break; + case QVariant::Map: + *static_cast<QJsonValue *>(result) = QJsonValue(QJsonObject::fromVariantMap(*v_cast<QVariantMap>(d))); + break; + case QVariant::Hash: + *static_cast<QJsonValue *>(result) = QJsonValue(QJsonObject::fromVariantHash(*v_cast<QVariantHash>(d))); + break; + case QMetaType::QJsonObject: + *static_cast<QJsonValue *>(result) = *v_cast<QJsonObject>(d); + break; + case QMetaType::QJsonArray: + *static_cast<QJsonValue *>(result) = *v_cast<QJsonArray>(d); + break; + case QMetaType::QJsonDocument: { + QJsonDocument doc = *v_cast<QJsonDocument>(d); + *static_cast<QJsonValue *>(result) = doc.isArray() ? QJsonValue(doc.array()) : QJsonValue(doc.object()); + break; + } + default: + *static_cast<QJsonValue *>(result) = QJsonValue(QJsonValue::Undefined); + return false; + } + break; + case QMetaType::QJsonArray: + switch (d->type) { + case QVariant::StringList: + *static_cast<QJsonArray *>(result) = QJsonArray::fromStringList(*v_cast<QStringList>(d)); + break; + case QVariant::List: + *static_cast<QJsonArray *>(result) = QJsonArray::fromVariantList(*v_cast<QVariantList>(d)); + break; + case QMetaType::QJsonValue: + if (!v_cast<QJsonValue>(d)->isArray()) + return false; + *static_cast<QJsonArray *>(result) = v_cast<QJsonValue>(d)->toArray(); + break; + case QMetaType::QJsonDocument: + if (!v_cast<QJsonDocument>(d)->isArray()) + return false; + *static_cast<QJsonArray *>(result) = v_cast<QJsonDocument>(d)->array(); + break; + default: + return false; + } + break; + case QMetaType::QJsonObject: + switch (d->type) { + case QVariant::Map: + *static_cast<QJsonObject *>(result) = QJsonObject::fromVariantMap(*v_cast<QVariantMap>(d)); + break; + case QVariant::Hash: + *static_cast<QJsonObject *>(result) = QJsonObject::fromVariantHash(*v_cast<QVariantHash>(d)); + break; + case QMetaType::QJsonValue: + if (!v_cast<QJsonValue>(d)->isObject()) + return false; + *static_cast<QJsonObject *>(result) = v_cast<QJsonValue>(d)->toObject(); + break; + case QMetaType::QJsonDocument: + if (v_cast<QJsonDocument>(d)->isArray()) + return false; + *static_cast<QJsonObject *>(result) = v_cast<QJsonDocument>(d)->object(); + break; + default: + return false; + } + break; +#endif default: #ifndef QT_NO_QOBJECT if (d->type == QVariant::String || d->type == QVariant::ByteArray) { @@ -1076,7 +1183,17 @@ static void customClear(QVariant::Private *d) static bool customIsNull(const QVariant::Private *d) { - return d->is_null; + if (d->is_null) + return true; + const char *const typeName = QMetaType::typeName(d->type); + if (Q_UNLIKELY(!typeName) && Q_LIKELY(!QMetaType::isRegistered(d->type))) + qFatal("QVariant::isNull: type %d unknown to QVariant.", d->type); + uint typeNameLen = qstrlen(typeName); + if (typeNameLen > 0 && typeName[typeNameLen - 1] == '*') { + const void *d_ptr = d->is_shared ? d->data.shared->ptr : &(d->data.ptr); + return *static_cast<void *const *>(d_ptr) == nullptr; + } + return false; } static bool customCompare(const QVariant::Private *a, const QVariant::Private *b) @@ -1358,7 +1475,11 @@ Q_CORE_EXPORT void QVariantPrivate::registerHandler(const int /* Modules::Names /*! \fn QVariant::QVariant(Type type) - Constructs a null variant of type \a type. + Constructs an uninitialized variant of type \a type. This will create a + variant in a special null state that if accessed will return a default + constructed value of the \a type. + + \sa isNull() */ @@ -3109,8 +3230,8 @@ bool QVariant::canConvert(int targetTypeId) const } } - if (currentType == QMetaType::QJsonValue) { - switch (targetTypeId) { + if (currentType == QMetaType::QJsonValue || targetTypeId == QMetaType::QJsonValue) { + switch (currentType == QMetaType::QJsonValue ? targetTypeId : currentType) { case QMetaType::Nullptr: case QMetaType::QString: case QMetaType::Bool: @@ -3166,11 +3287,11 @@ bool QVariant::canConvert(int targetTypeId) const case QVariant::Bitmap: return currentType == QVariant::Pixmap || currentType == QVariant::Image; case QVariant::ByteArray: - return currentType == QVariant::Color + return currentType == QVariant::Color || currentType == QMetaType::Nullptr || ((QMetaType::typeFlags(currentType) & QMetaType::IsEnumeration) && QMetaType::metaObjectForType(currentType)); case QVariant::String: return currentType == QVariant::KeySequence || currentType == QVariant::Font - || currentType == QVariant::Color + || currentType == QVariant::Color || currentType == QMetaType::Nullptr || ((QMetaType::typeFlags(currentType) & QMetaType::IsEnumeration) && QMetaType::metaObjectForType(currentType)); case QVariant::KeySequence: return currentType == QVariant::String || currentType == QVariant::Int; @@ -3207,17 +3328,20 @@ bool QVariant::canConvert(int targetTypeId) const /*! Casts the variant to the requested type, \a targetTypeId. If the cast cannot be - done, the variant is cleared. Returns \c true if the current type of - the variant was successfully cast; otherwise returns \c false. + done, the variant is still changed to the requested type, but is left in a cleared + null state similar to that constructed by QVariant(Type). + + Returns \c true if the current type of the variant was successfully cast; + otherwise returns \c false. A QVariant containing a pointer to a type derived from QObject will also convert and return true for this function if a qobject_cast to the type described by \a targetTypeId would succeed. Note that this only works for QObject subclasses which use the Q_OBJECT macro. - \warning For historical reasons, converting a null QVariant results - in a null value of the desired type (e.g., an empty string for - QString) and a result of false. + \note converting QVariants that are null due to not being initialized or having + failed a previous conversion will always fail, changing the type, remaining null, + and returning \c false. \sa canConvert(), clear() */ @@ -3234,7 +3358,8 @@ bool QVariant::convert(int targetTypeId) return false; create(targetTypeId, 0); - if (oldValue.isNull()) + // Fail if the value is not initialized or was forced null by a previous failed convert. + if (oldValue.d.is_null) return false; if ((QMetaType::typeFlags(oldValue.userType()) & QMetaType::PointerToQObject) && (QMetaType::typeFlags(targetTypeId) & QMetaType::PointerToQObject)) { @@ -3643,12 +3768,15 @@ void* QVariant::data() /*! Returns \c true if this is a null variant, false otherwise. A variant is - considered null if it contains a default constructed value or a built-in - type instance that has an isNull method, in which case the result - would be the same as calling isNull on the wrapped object. + considered null if it contains no initialized value, or the contained value + is a null pointer or is an instance of a built-in type that has an isNull + method, in which case the result would be the same as calling isNull on the + wrapped object. + + \warning Null variants is not a single state and two null variants may easily + return \c false on the == operator if they do not contain similar null values. - \warning The result of the function doesn't affect == operator, which means - that two values can be equal even if one of them is null and another is not. + \sa QVariant(Type), convert(int) */ bool QVariant::isNull() const { diff --git a/src/corelib/kernel/qvariant_p.h b/src/corelib/kernel/qvariant_p.h index 487949431c..75c269d710 100644 --- a/src/corelib/kernel/qvariant_p.h +++ b/src/corelib/kernel/qvariant_p.h @@ -187,6 +187,16 @@ public: } }; +template <typename T> +struct PrimitiveIsNull<T*> +{ +public: + static bool isNull(const QVariant::Private *d) + { + return d->is_null || d->data.ptr == nullptr; + } +}; + template <> struct PrimitiveIsNull<std::nullptr_t> { diff --git a/src/corelib/kernel/qwineventnotifier.cpp b/src/corelib/kernel/qwineventnotifier.cpp index 0808374a6a..24de491326 100644 --- a/src/corelib/kernel/qwineventnotifier.cpp +++ b/src/corelib/kernel/qwineventnotifier.cpp @@ -37,7 +37,7 @@ ** ****************************************************************************/ -#include "qwineventnotifier.h" +#include "qwineventnotifier_p.h" #ifdef Q_OS_WINRT #include "qeventdispatcher_winrt_p.h" @@ -50,19 +50,6 @@ QT_BEGIN_NAMESPACE -class QWinEventNotifierPrivate : public QObjectPrivate -{ - Q_DECLARE_PUBLIC(QWinEventNotifier) -public: - QWinEventNotifierPrivate() - : handleToEvent(0), enabled(false) {} - QWinEventNotifierPrivate(HANDLE h, bool e) - : handleToEvent(h), enabled(e) {} - - HANDLE handleToEvent; - bool enabled; -}; - /*! \class QWinEventNotifier \inmodule QtCore @@ -170,6 +157,7 @@ void QWinEventNotifier::setHandle(HANDLE hEvent) Q_D(QWinEventNotifier); setEnabled(false); d->handleToEvent = hEvent; + d->signaledCount = 0; } /*! @@ -211,8 +199,11 @@ void QWinEventNotifier::setEnabled(bool enable) d->enabled = enable; QAbstractEventDispatcher *eventDispatcher = d->threadData->eventDispatcher.load(); - if (!eventDispatcher) // perhaps application is shutting down + if (!eventDispatcher) { // perhaps application is shutting down + if (!enable && d->waitHandle != nullptr) + d->unregisterWaitObject(); return; + } if (Q_UNLIKELY(thread() != QThread::currentThread())) { qWarning("QWinEventNotifier: Event notifiers cannot be enabled or disabled from another thread"); return; @@ -246,4 +237,50 @@ bool QWinEventNotifier::event(QEvent * e) return false; } +#if defined(Q_OS_WINRT) + +bool QWinEventNotifierPrivate::registerWaitObject() +{ + Q_UNIMPLEMENTED(); + return false; +} + +void QWinEventNotifierPrivate::unregisterWaitObject() +{ + Q_UNIMPLEMENTED(); +} + +#else // defined(Q_OS_WINRT) + +static void CALLBACK wfsoCallback(void *context, BOOLEAN /*ignore*/) +{ + QWinEventNotifierPrivate *nd = reinterpret_cast<QWinEventNotifierPrivate *>(context); + QAbstractEventDispatcher *eventDispatcher = nd->threadData->eventDispatcher.load(); + QEventDispatcherWin32Private *edp = QEventDispatcherWin32Private::get( + static_cast<QEventDispatcherWin32 *>(eventDispatcher)); + ++nd->signaledCount; + SetEvent(edp->winEventNotifierActivatedEvent); +} + +bool QWinEventNotifierPrivate::registerWaitObject() +{ + if (RegisterWaitForSingleObject(&waitHandle, handleToEvent, wfsoCallback, this, + INFINITE, WT_EXECUTEONLYONCE) == 0) { + qErrnoWarning("QWinEventNotifier: RegisterWaitForSingleObject failed."); + return false; + } + return true; +} + +void QWinEventNotifierPrivate::unregisterWaitObject() +{ + // Unregister the wait handle and wait for pending callbacks to finish. + if (UnregisterWaitEx(waitHandle, INVALID_HANDLE_VALUE)) + waitHandle = NULL; + else + qErrnoWarning("QWinEventNotifier: UnregisterWaitEx failed."); +} + +#endif // !defined(Q_OS_WINRT) + QT_END_NAMESPACE diff --git a/src/corelib/kernel/qwineventnotifier_p.h b/src/corelib/kernel/qwineventnotifier_p.h new file mode 100644 index 0000000000..8bb2c3159a --- /dev/null +++ b/src/corelib/kernel/qwineventnotifier_p.h @@ -0,0 +1,83 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** 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$ +** +****************************************************************************/ + +#ifndef QWINEVENTNOTIFIER_P_H +#define QWINEVENTNOTIFIER_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "qwineventnotifier.h" + +#include <private/qobject_p.h> +#include <QtCore/qatomic.h> +#include <QtCore/qt_windows.h> + +QT_BEGIN_NAMESPACE + +class QWinEventNotifierPrivate : public QObjectPrivate +{ + Q_DECLARE_PUBLIC(QWinEventNotifier) +public: + QWinEventNotifierPrivate() + : handleToEvent(0), enabled(false) {} + QWinEventNotifierPrivate(HANDLE h, bool e) + : handleToEvent(h), enabled(e) {} + + static QWinEventNotifierPrivate *get(QWinEventNotifier *q) { return q->d_func(); } + bool registerWaitObject(); + void unregisterWaitObject(); + + HANDLE handleToEvent; + HANDLE waitHandle = NULL; + QAtomicInt signaledCount; + bool enabled; +}; + +QT_END_NAMESPACE + +#endif // QWINEVENTNOTIFIER_P_H |