summaryrefslogtreecommitdiffstats
path: root/src/corelib/kernel
diff options
context:
space:
mode:
Diffstat (limited to 'src/corelib/kernel')
-rw-r--r--src/corelib/kernel/qabstracteventdispatcher.cpp283
-rw-r--r--src/corelib/kernel/qabstracteventdispatcher.h60
-rw-r--r--src/corelib/kernel/qabstracteventdispatcher_p.h14
-rw-r--r--src/corelib/kernel/qapplicationstatic.h31
-rw-r--r--src/corelib/kernel/qapplicationstatic.qdoc30
-rw-r--r--src/corelib/kernel/qbasictimer.cpp54
-rw-r--r--src/corelib/kernel/qbasictimer.h22
-rw-r--r--src/corelib/kernel/qchronotimer.cpp452
-rw-r--r--src/corelib/kernel/qchronotimer.h149
-rw-r--r--src/corelib/kernel/qcore_foundation.mm19
-rw-r--r--src/corelib/kernel/qcore_mac.mm220
-rw-r--r--src/corelib/kernel/qcore_mac_p.h72
-rw-r--r--src/corelib/kernel/qcore_unix.cpp65
-rw-r--r--src/corelib/kernel/qcore_unix_p.h137
-rw-r--r--src/corelib/kernel/qcore_wasm.cpp54
-rw-r--r--src/corelib/kernel/qcoreapplication.cpp460
-rw-r--r--src/corelib/kernel/qcoreapplication.h81
-rw-r--r--src/corelib/kernel/qcoreapplication_p.h8
-rw-r--r--src/corelib/kernel/qcoreapplication_platform.h4
-rw-r--r--src/corelib/kernel/qcoreapplication_win.cpp2
-rw-r--r--src/corelib/kernel/qcoreevent.cpp42
-rw-r--r--src/corelib/kernel/qcoreevent.h25
-rw-r--r--src/corelib/kernel/qcoreevent_p.h39
-rw-r--r--src/corelib/kernel/qdeadlinetimer.cpp515
-rw-r--r--src/corelib/kernel/qdeadlinetimer.h99
-rw-r--r--src/corelib/kernel/qdeadlinetimer_p.h34
-rw-r--r--src/corelib/kernel/qelapsedtimer.cpp242
-rw-r--r--src/corelib/kernel/qelapsedtimer.h58
-rw-r--r--src/corelib/kernel/qelapsedtimer_generic.cpp169
-rw-r--r--src/corelib/kernel/qelapsedtimer_mac.cpp130
-rw-r--r--src/corelib/kernel/qelapsedtimer_unix.cpp223
-rw-r--r--src/corelib/kernel/qelapsedtimer_win.cpp123
-rw-r--r--src/corelib/kernel/qeventdispatcher_cf.mm54
-rw-r--r--src/corelib/kernel/qeventdispatcher_cf_p.h33
-rw-r--r--src/corelib/kernel/qeventdispatcher_glib.cpp60
-rw-r--r--src/corelib/kernel/qeventdispatcher_glib_p.h14
-rw-r--r--src/corelib/kernel/qeventdispatcher_unix.cpp113
-rw-r--r--src/corelib/kernel/qeventdispatcher_unix_p.h22
-rw-r--r--src/corelib/kernel/qeventdispatcher_wasm.cpp341
-rw-r--r--src/corelib/kernel/qeventdispatcher_wasm_p.h34
-rw-r--r--src/corelib/kernel/qeventdispatcher_win.cpp22
-rw-r--r--src/corelib/kernel/qeventdispatcher_win_p.h9
-rw-r--r--src/corelib/kernel/qeventloop.cpp180
-rw-r--r--src/corelib/kernel/qeventloop.h40
-rw-r--r--src/corelib/kernel/qfunctions_p.h4
-rw-r--r--src/corelib/kernel/qfunctions_vxworks.cpp172
-rw-r--r--src/corelib/kernel/qfunctions_vxworks.h159
-rw-r--r--src/corelib/kernel/qfunctions_win.cpp2
-rw-r--r--src/corelib/kernel/qfunctions_win_p.h1
-rw-r--r--src/corelib/kernel/qiterable.cpp3
-rw-r--r--src/corelib/kernel/qiterable.h48
-rw-r--r--src/corelib/kernel/qjniarray.h464
-rw-r--r--src/corelib/kernel/qjnienvironment.cpp181
-rw-r--r--src/corelib/kernel/qjnienvironment.h16
-rw-r--r--src/corelib/kernel/qjnihelpers.cpp109
-rw-r--r--src/corelib/kernel/qjnihelpers_p.h29
-rw-r--r--src/corelib/kernel/qjniobject.cpp601
-rw-r--r--src/corelib/kernel/qjniobject.h787
-rw-r--r--src/corelib/kernel/qjnitypes.h467
-rw-r--r--src/corelib/kernel/qjnitypes_impl.h373
-rw-r--r--src/corelib/kernel/qmath.h10
-rw-r--r--src/corelib/kernel/qmath.qdoc2
-rw-r--r--src/corelib/kernel/qmetacontainer.cpp16
-rw-r--r--src/corelib/kernel/qmetacontainer.h29
-rw-r--r--src/corelib/kernel/qmetaobject.cpp558
-rw-r--r--src/corelib/kernel/qmetaobject.h33
-rw-r--r--src/corelib/kernel/qmetaobject_p.h37
-rw-r--r--src/corelib/kernel/qmetaobjectbuilder.cpp42
-rw-r--r--src/corelib/kernel/qmetaobjectbuilder_p.h3
-rw-r--r--src/corelib/kernel/qmetatype.cpp300
-rw-r--r--src/corelib/kernel/qmetatype.h123
-rw-r--r--src/corelib/kernel/qmetatype_p.h21
-rw-r--r--src/corelib/kernel/qmimedata.cpp92
-rw-r--r--src/corelib/kernel/qobject.cpp779
-rw-r--r--src/corelib/kernel/qobject.h209
-rw-r--r--src/corelib/kernel/qobject_impl.h24
-rw-r--r--src/corelib/kernel/qobject_p.h103
-rw-r--r--src/corelib/kernel/qobject_p_p.h62
-rw-r--r--src/corelib/kernel/qobjectcleanuphandler.cpp4
-rw-r--r--src/corelib/kernel/qobjectdefs.h207
-rw-r--r--src/corelib/kernel/qobjectdefs_impl.h418
-rw-r--r--src/corelib/kernel/qpermissions.cpp309
-rw-r--r--src/corelib/kernel/qpermissions.h183
-rw-r--r--src/corelib/kernel/qpermissions_android.cpp78
-rw-r--r--src/corelib/kernel/qpermissions_wasm.cpp15
-rw-r--r--src/corelib/kernel/qpointer.h93
-rw-r--r--src/corelib/kernel/qpointer.qdoc (renamed from src/corelib/kernel/qpointer.cpp)87
-rw-r--r--src/corelib/kernel/qpoll.cpp4
-rw-r--r--src/corelib/kernel/qproperty.cpp200
-rw-r--r--src/corelib/kernel/qproperty.h54
-rw-r--r--src/corelib/kernel/qproperty_p.h130
-rw-r--r--src/corelib/kernel/qpropertyprivate.h60
-rw-r--r--src/corelib/kernel/qsharedmemory.cpp714
-rw-r--r--src/corelib/kernel/qsharedmemory.h84
-rw-r--r--src/corelib/kernel/qsharedmemory_android.cpp65
-rw-r--r--src/corelib/kernel/qsharedmemory_p.h140
-rw-r--r--src/corelib/kernel/qsharedmemory_posix.cpp200
-rw-r--r--src/corelib/kernel/qsharedmemory_systemv.cpp226
-rw-r--r--src/corelib/kernel/qsharedmemory_unix.cpp64
-rw-r--r--src/corelib/kernel/qsharedmemory_win.cpp147
-rw-r--r--src/corelib/kernel/qsignalmapper.cpp11
-rw-r--r--src/corelib/kernel/qsignalmapper.h1
-rw-r--r--src/corelib/kernel/qsingleshottimer_p.h141
-rw-r--r--src/corelib/kernel/qsocketnotifier.cpp1
-rw-r--r--src/corelib/kernel/qsocketnotifier.h16
-rw-r--r--src/corelib/kernel/qsystemerror_p.h7
-rw-r--r--src/corelib/kernel/qsystemsemaphore.cpp330
-rw-r--r--src/corelib/kernel/qsystemsemaphore.h63
-rw-r--r--src/corelib/kernel/qsystemsemaphore_android.cpp41
-rw-r--r--src/corelib/kernel/qsystemsemaphore_p.h82
-rw-r--r--src/corelib/kernel/qsystemsemaphore_posix.cpp152
-rw-r--r--src/corelib/kernel/qsystemsemaphore_systemv.cpp190
-rw-r--r--src/corelib/kernel/qsystemsemaphore_unix.cpp63
-rw-r--r--src/corelib/kernel/qsystemsemaphore_win.cpp97
-rw-r--r--src/corelib/kernel/qt_attribution.json4
-rw-r--r--src/corelib/kernel/qtestsupport_core.cpp117
-rw-r--r--src/corelib/kernel/qtestsupport_core.h34
-rw-r--r--src/corelib/kernel/qtimer.cpp386
-rw-r--r--src/corelib/kernel/qtimer.h156
-rw-r--r--src/corelib/kernel/qtimer_p.h48
-rw-r--r--src/corelib/kernel/qtimerinfo_unix.cpp679
-rw-r--r--src/corelib/kernel/qtimerinfo_unix_p.h89
-rw-r--r--src/corelib/kernel/qtmetamacros.h2
-rw-r--r--src/corelib/kernel/qtmochelpers.h48
-rw-r--r--src/corelib/kernel/qtranslator.cpp11
-rw-r--r--src/corelib/kernel/qvariant.cpp407
-rw-r--r--src/corelib/kernel/qvariant.h261
-rw-r--r--src/corelib/kernel/qvariant_p.h109
-rw-r--r--src/corelib/kernel/qwineventnotifier.cpp2
-rw-r--r--src/corelib/kernel/qwinregistry.cpp4
-rw-r--r--src/corelib/kernel/qwinregistry_p.h2
131 files changed, 9152 insertions, 8581 deletions
diff --git a/src/corelib/kernel/qabstracteventdispatcher.cpp b/src/corelib/kernel/qabstracteventdispatcher.cpp
index 3001e3269b..f3056a399c 100644
--- a/src/corelib/kernel/qabstracteventdispatcher.cpp
+++ b/src/corelib/kernel/qabstracteventdispatcher.cpp
@@ -9,9 +9,12 @@
#include <private/qthread_p.h>
#include <private/qcoreapplication_p.h>
#include <private/qfreelist_p.h>
+#include <private/qnumeric_p.h>
QT_BEGIN_NAMESPACE
+using namespace std::chrono_literals;
+
// we allow for 2^24 = 8^8 = 16777216 simultaneously running timers
struct QtTimerIdFreeListConstants : public QFreeListDefaultConstants
{
@@ -52,6 +55,38 @@ Q_CONSTINIT const int QtTimerIdFreeListConstants::Sizes[QtTimerIdFreeListConstan
typedef QFreeList<void, QtTimerIdFreeListConstants> QtTimerIdFreeList;
Q_GLOBAL_STATIC(QtTimerIdFreeList, timerIdFreeList)
+template <typename T> static T fromDuration(std::chrono::nanoseconds interval)
+{
+ using namespace std::chrono;
+ qint64 value = ceil<milliseconds>(interval).count();
+ return qt_saturate<T>(value);
+}
+
+#if QT_VERSION < QT_VERSION_CHECK(7, 0, 0)
+static inline QAbstractEventDispatcherV2 *v2(QAbstractEventDispatcher *self)
+{
+ if (QAbstractEventDispatcherPrivate::get(self)->isV2)
+ return static_cast<QAbstractEventDispatcherV2 *>(self);
+ return nullptr;
+}
+
+static inline const QAbstractEventDispatcherV2 *v2(const QAbstractEventDispatcher *self)
+{
+ if (QAbstractEventDispatcherPrivate::get(self)->isV2)
+ return static_cast<const QAbstractEventDispatcherV2 *>(self);
+ return nullptr;
+}
+#endif // Qt 7
+
+QAbstractEventDispatcherPrivate::QAbstractEventDispatcherPrivate()
+{
+ // Create the timer ID free list here to make sure that it is destroyed
+ // after any global static thread that may be using it.
+ // See also QTBUG-58732.
+ if (!timerIdFreeList.isDestroyed())
+ (void)timerIdFreeList();
+}
+
QAbstractEventDispatcherPrivate::~QAbstractEventDispatcherPrivate()
= default;
@@ -112,6 +147,15 @@ void QAbstractEventDispatcherPrivate::releaseTimerId(int timerId)
\sa QEventLoop, QCoreApplication, QThread
*/
+/*!
+ \typedef QAbstractEventDispatcher::Duration
+
+ A \c{std::chrono::duration} type that is used in various API in this class.
+ This type exists to facilitate a possible transition to a higher or lower
+ granularity.
+
+ In all current platforms, it is \c nanoseconds.
+*/
/*!
Constructs a new event dispatcher with the given \a parent.
@@ -205,16 +249,39 @@ QAbstractEventDispatcher *QAbstractEventDispatcher::instance(QThread *thread)
*/
/*!
+ \obsolete [6.8] This function will be removed in Qt 7. Use the overload taking \l Duration.
+
Registers a timer with the specified \a interval and \a timerType for the
given \a object and returns the timer id.
*/
int QAbstractEventDispatcher::registerTimer(qint64 interval, Qt::TimerType timerType, QObject *object)
{
- int id = QAbstractEventDispatcherPrivate::allocateTimerId();
+ return int(registerTimer(interval * 1ms, timerType, object));
+}
+
+/*!
+ \since 6.8
+ \overload
+
+ Registers a timer with the specified \a interval and \a timerType for the
+ given \a object and returns the timer id.
+*/
+Qt::TimerId QAbstractEventDispatcher::registerTimer(Duration interval, Qt::TimerType timerType,
+ QObject *object)
+{
+ auto id = Qt::TimerId(QAbstractEventDispatcherPrivate::allocateTimerId());
+#if QT_VERSION < QT_VERSION_CHECK(7, 0, 0)
+ if (QAbstractEventDispatcherV2 *self = v2(this))
+ self->registerTimer(id, interval, timerType, object);
+ else
+ registerTimer(qToUnderlying(id), fromDuration<qint64>(interval), timerType, object);
+#else
registerTimer(id, interval, timerType, object);
+#endif
return id;
}
+#if QT_VERSION < QT_VERSION_CHECK(7, 0, 0)
/*!
\fn void QAbstractEventDispatcher::registerTimer(int timerId, qint64 interval, Qt::TimerType timerType, QObject *object)
@@ -232,15 +299,6 @@ int QAbstractEventDispatcher::registerTimer(qint64 interval, Qt::TimerType timer
*/
/*!
- \fn bool QAbstractEventDispatcher::unregisterTimers(QObject *object)
-
- Unregisters all the timers associated with the given \a object.
- Returns \c true if all timers were successful removed; otherwise returns \c false.
-
- \sa unregisterTimer(), registeredTimers()
-*/
-
-/*!
\fn QList<TimerInfo> QAbstractEventDispatcher::registeredTimers(QObject *object) const
Returns a list of registered timers for \a object. The TimerInfo struct has
@@ -258,6 +316,57 @@ int QAbstractEventDispatcher::registerTimer(qint64 interval, Qt::TimerType timer
\sa Qt::TimerType
*/
+#else // Qt 7
+/*!
+ \fn void QAbstractEventDispatcher::registerTimer(Qt::TimerId timerId, Duration interval, Qt::TimerType timerType, QObject *object)
+ \since 6.8
+
+ Register a timer with the specified \a timerId, \a interval, and \a
+ timerType for the given \a object.
+
+ \sa unregisterTimer(), timersForObject()
+*/
+
+/*!
+ \fn bool QAbstractEventDispatcher::unregisterTimer(Qt::TimerId timerId)
+ \since 6.8
+
+ Unregisters the timer with the given \a timerId.
+ Returns \c true if successful; otherwise returns \c false.
+
+ \sa registerTimer(), unregisterTimers()
+*/
+
+/*!
+ \fn QList<TimerInfoV2> QAbstractEventDispatcher::timersForObject(QObject *object) const
+ \since 6.8
+
+ Returns a list of registered timers for \a object. The TimerInfoV2 struct has
+ \c timerId, \c interval, and \c timerType members.
+
+ \sa Qt::TimerType, registerTimer(), unregisterTimer()
+*/
+
+/*!
+ \fn QAbstractEventDispatcher::remainingTime(Qt::TimerId timerId) const
+
+ Returns the remaining time of the timer with the given \a timerId.
+ If the timer is inactive, the returned value will be negative. If the timer
+ is overdue, the returned value will be 0.
+
+ \sa Qt::TimerType, registerTimer(), unregisterTimer()
+*/
+#endif
+
+/*!
+ \fn bool QAbstractEventDispatcher::unregisterTimers(QObject *object)
+
+ Unregisters all the timers associated with the given \a object. Returns \c
+ true if all timers were successfully removed; otherwise returns \c false.
+
+ \sa unregisterTimer(), registeredTimers()
+*/
+
/*! \fn void QAbstractEventDispatcher::wakeUp()
\threadsafe
@@ -299,6 +408,7 @@ void QAbstractEventDispatcher::closingDown()
/*!
\class QAbstractEventDispatcher::TimerInfo
+ \deprecated [6.8] Use TimerInfoV2
\inmodule QtCore
This struct represents information about a timer:
@@ -306,7 +416,7 @@ void QAbstractEventDispatcher::closingDown()
\l{QAbstractEventDispatcher::TimerInfo::interval}{interval}, and
\l{QAbstractEventDispatcher::TimerInfo::timerType}{timerType}.
- \sa registeredTimers()
+ \sa registeredTimers(), QAbstractEventDispatcher::TimerInfoV2, timersForObject()
*/
/*! \fn QAbstractEventDispatcher::TimerInfo::TimerInfo(int timerId, int interval, Qt::TimerType timerType)
@@ -332,6 +442,37 @@ void QAbstractEventDispatcher::closingDown()
*/
/*!
+ \class QAbstractEventDispatcher::TimerInfoV2
+ \inmodule QtCore
+
+ This struct represents information about a timer:
+ \l{QAbstractEventDispatcher::TimerInfoV2::timerId}{timerId},
+ \l{QAbstractEventDispatcher::TimerInfoV2::interval}{interval}, and
+ \l{QAbstractEventDispatcher::TimerInfoV2::timerType}{timerType}.
+
+ \sa timersForObject()
+*/
+/*!
+ \variable QAbstractEventDispatcher::TimerInfoV2::timerId
+
+ The timer's unique id. This is created by registerTimer() upon creation and
+ uniquely identifies a timer while it is active. It is also used by
+ QTimer::id() and returned by QObject::startTimer().
+*/
+/*!
+ \variable QAbstractEventDispatcher::TimerInfoV2::interval
+
+ The timer's interval.
+*/
+/*!
+ \variable QAbstractEventDispatcher::TimerInfoV2::timerType
+
+ The timer's type
+
+ \sa Qt::TimerType
+*/
+
+/*!
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()}
@@ -443,6 +584,126 @@ bool QAbstractEventDispatcher::filterNativeEvent(const QByteArray &eventType, vo
\sa awake()
*/
+#if QT_VERSION < QT_VERSION_CHECK(7, 0, 0)
+void QAbstractEventDispatcher::registerTimer(Qt::TimerId timerId, Duration interval,
+ Qt::TimerType timerType, QObject *object)
+{
+ if (QAbstractEventDispatcherV2 *self = v2(this))
+ self->registerTimer(timerId, interval, timerType, object);
+ else
+ registerTimer(int(timerId), fromDuration<qint64>(interval), timerType, object);
+}
+
+bool QAbstractEventDispatcher::unregisterTimer(Qt::TimerId timerId)
+{
+ if (QAbstractEventDispatcherV2 *self = v2(this))
+ return self->unregisterTimer(timerId);
+ return unregisterTimer(int(timerId));
+}
+
+QList<QAbstractEventDispatcher::TimerInfoV2>
+QAbstractEventDispatcher::timersForObject(QObject *object) const
+{
+ if (const QAbstractEventDispatcherV2 *self = v2(this))
+ return self->timersForObject(object);
+ QList<TimerInfo> timers = registeredTimers(object);
+ QList<TimerInfoV2> result;
+ result.reserve(timers.size());
+ for (const TimerInfo &t : timers)
+ result.emplaceBack(TimerInfoV2{ t.interval * 1ms, Qt::TimerId(t.timerId), t.timerType });
+ return result;
+}
+
+QAbstractEventDispatcher::Duration
+QAbstractEventDispatcher::remainingTime(Qt::TimerId timerId) const
+{
+ if (const QAbstractEventDispatcherV2 *self = v2(this))
+ return self->remainingTime(timerId);
+ return const_cast<QAbstractEventDispatcher *>(this)->remainingTime(int(timerId)) * 1ms;
+}
+
+/*!
+ \class QAbstractEventDispatcherV2
+ \inmodule QtCore
+
+ This class is a temporary hack to enable transition to an API based on
+ \c{std::chrono} for the Qt event dispatcher. In Qt 7, it will be merged
+ with QAbstractEventDispatcher, replacing the pure virtuals there with the
+ ones defined here.
+
+ It is recommended applications and libraries port to the new API before
+ that future release to simplify work when the time comes.
+*/
+
+/*!
+ Constructs a new event dispatcher with the given \a parent.
+*/
+QAbstractEventDispatcherV2::QAbstractEventDispatcherV2(QObject *parent)
+ : QAbstractEventDispatcherV2(*new QAbstractEventDispatcherPrivate, parent)
+{
+}
+
+/*!
+ \internal
+*/
+QAbstractEventDispatcherV2::QAbstractEventDispatcherV2(QAbstractEventDispatcherPrivate &dd,
+ QObject *parent)
+ : QAbstractEventDispatcher((dd.isV2 = true, dd), parent)
+{
+}
+
+/*!
+ Destroys the event dispatcher.
+*/
+QAbstractEventDispatcherV2::~QAbstractEventDispatcherV2() = default;
+
+/*!
+ \internal
+ Temporary compatibility override.
+*/
+void QAbstractEventDispatcherV2::registerTimer(int timerId, qint64 interval,
+ Qt::TimerType timerType, QObject *object)
+{
+ auto self = static_cast<QAbstractEventDispatcherV2 *>(this);
+ self->registerTimer(Qt::TimerId(timerId), interval * 1ms, timerType, object);
+}
+
+/*!
+ \internal
+ Temporary compatibility override.
+*/
+bool QAbstractEventDispatcherV2::unregisterTimer(int timerId)
+{
+ auto self = static_cast<QAbstractEventDispatcherV2 *>(this);
+ return self->unregisterTimer(Qt::TimerId(timerId));
+}
+
+/*!
+ \internal
+ Temporary compatibility override.
+*/
+auto QAbstractEventDispatcherV2::registeredTimers(QObject *object) const -> QList<TimerInfo>
+{
+ auto self = static_cast<const QAbstractEventDispatcherV2 *>(this);
+ QList<TimerInfoV2> timers = self->timersForObject(object);
+ QList<TimerInfo> result;
+ result.reserve(timers.size());
+ for (const TimerInfoV2 &t : timers)
+ result.emplaceBack(qToUnderlying(t.timerId), fromDuration<int>(t.interval), t.timerType);
+ return result;
+}
+
+/*!
+ \internal
+ Temporary compatibility override.
+*/
+int QAbstractEventDispatcherV2::remainingTime(int timerId)
+{
+ auto self = static_cast<QAbstractEventDispatcherV2 *>(this);
+ return fromDuration<int>(self->remainingTime(Qt::TimerId(timerId)));
+}
+#endif // ! Qt 7
+
QT_END_NAMESPACE
#include "moc_qabstracteventdispatcher.cpp"
diff --git a/src/corelib/kernel/qabstracteventdispatcher.h b/src/corelib/kernel/qabstracteventdispatcher.h
index 4026bde347..ad97a93ba2 100644
--- a/src/corelib/kernel/qabstracteventdispatcher.h
+++ b/src/corelib/kernel/qabstracteventdispatcher.h
@@ -19,6 +19,7 @@ class Q_CORE_EXPORT QAbstractEventDispatcher : public QObject
Q_DECLARE_PRIVATE(QAbstractEventDispatcher)
public:
+ using Duration = std::chrono::nanoseconds;
struct TimerInfo
{
int timerId;
@@ -28,6 +29,12 @@ public:
inline TimerInfo(int id, int i, Qt::TimerType t)
: timerId(id), interval(i), timerType(t) { }
};
+ struct TimerInfoV2
+ {
+ Duration interval;
+ Qt::TimerId timerId;
+ Qt::TimerType timerType;
+ };
explicit QAbstractEventDispatcher(QObject *parent = nullptr);
~QAbstractEventDispatcher();
@@ -39,14 +46,29 @@ public:
virtual void registerSocketNotifier(QSocketNotifier *notifier) = 0;
virtual void unregisterSocketNotifier(QSocketNotifier *notifier) = 0;
+ Qt::TimerId registerTimer(Duration interval, Qt::TimerType timerType, QObject *object);
+
+#if QT_VERSION < QT_VERSION_CHECK(7, 0, 0)
int registerTimer(qint64 interval, Qt::TimerType timerType, QObject *object);
+
+ // old, integer-based API
virtual void registerTimer(int timerId, qint64 interval, Qt::TimerType timerType, QObject *object) = 0;
virtual bool unregisterTimer(int timerId) = 0;
- virtual bool unregisterTimers(QObject *object) = 0;
virtual QList<TimerInfo> registeredTimers(QObject *object) const = 0;
-
virtual int remainingTime(int timerId) = 0;
+ void registerTimer(Qt::TimerId timerId, Duration interval, Qt::TimerType timerType, QObject *object);
+ bool unregisterTimer(Qt::TimerId timerId);
+ QList<TimerInfoV2> timersForObject(QObject *object) const;
+ Duration remainingTime(Qt::TimerId timerId) const;
+#else
+ virtual void registerTimer(Qt::TimerId timerId, Duration interval, Qt::TimerType timerType, QObject *object) = 0;
+ virtual bool unregisterTimer(Qt::TimerId timerId) = 0;
+ virtual QList<TimerInfoV2> timersForObject(QObject *object) const = 0;
+ virtual Duration remainingTime(Qt::TimerId timerId) const = 0;
+#endif
+ virtual bool unregisterTimers(QObject *object) = 0;
+
virtual void wakeUp() = 0;
virtual void interrupt() = 0;
@@ -67,6 +89,40 @@ protected:
};
Q_DECLARE_TYPEINFO(QAbstractEventDispatcher::TimerInfo, Q_PRIMITIVE_TYPE);
+Q_DECLARE_TYPEINFO(QAbstractEventDispatcher::TimerInfoV2, Q_PRIMITIVE_TYPE);
+
+#if QT_VERSION < QT_VERSION_CHECK(7, 0, 0)
+class Q_CORE_EXPORT QAbstractEventDispatcherV2 : public QAbstractEventDispatcher
+{
+ Q_OBJECT
+ Q_DECLARE_PRIVATE(QAbstractEventDispatcher) // not V2
+
+public:
+ explicit QAbstractEventDispatcherV2(QObject *parent = nullptr);
+ ~QAbstractEventDispatcherV2();
+
+ // new virtuals
+ virtual void registerTimer(Qt::TimerId timerId, Duration interval, Qt::TimerType timerType,
+ QObject *object) = 0;
+ virtual bool unregisterTimer(Qt::TimerId timerId) = 0;
+ virtual QList<TimerInfoV2> timersForObject(QObject *object) const = 0;
+ virtual Duration remainingTime(Qt::TimerId timerId) const = 0;
+
+protected:
+ QAbstractEventDispatcherV2(QAbstractEventDispatcherPrivate &, QObject *parent);
+
+private:
+ // final overrides from V1
+ virtual void registerTimer(int timerId, qint64 interval, Qt::TimerType timerType,
+ QObject *object) override final;
+ virtual bool unregisterTimer(int timerId) override final;
+ virtual QList<TimerInfo> registeredTimers(QObject *object) const override final;
+
+ virtual int remainingTime(int timerId) override final;
+};
+#else
+using QAbstractEventDispatcherV2 = QAbstractEventDispatcher;
+#endif // Qt 7
QT_END_NAMESPACE
diff --git a/src/corelib/kernel/qabstracteventdispatcher_p.h b/src/corelib/kernel/qabstracteventdispatcher_p.h
index e7b1ac3b24..2576027d52 100644
--- a/src/corelib/kernel/qabstracteventdispatcher_p.h
+++ b/src/corelib/kernel/qabstracteventdispatcher_p.h
@@ -16,7 +16,9 @@
//
#include "QtCore/qabstracteventdispatcher.h"
+#include "QtCore/qnamespace.h"
#include "private/qobject_p.h"
+#include "QtCore/qttypetraits.h"
QT_BEGIN_NAMESPACE
@@ -26,14 +28,22 @@ class Q_CORE_EXPORT QAbstractEventDispatcherPrivate : public QObjectPrivate
{
Q_DECLARE_PUBLIC(QAbstractEventDispatcher)
public:
- inline QAbstractEventDispatcherPrivate()
- { }
+ QAbstractEventDispatcherPrivate();
~QAbstractEventDispatcherPrivate() override;
QList<QAbstractNativeEventFilter *> eventFilters;
+ bool isV2 = false;
+
static int allocateTimerId();
static void releaseTimerId(int id);
+ static void releaseTimerId(Qt::TimerId id)
+ { releaseTimerId(qToUnderlying(id)); }
+
+ static QAbstractEventDispatcherPrivate *get(QAbstractEventDispatcher *o)
+ { return o->d_func(); }
+ static const QAbstractEventDispatcherPrivate *get(const QAbstractEventDispatcher *o)
+ { return o->d_func(); }
};
QT_END_NAMESPACE
diff --git a/src/corelib/kernel/qapplicationstatic.h b/src/corelib/kernel/qapplicationstatic.h
index f311da5d77..345a880748 100644
--- a/src/corelib/kernel/qapplicationstatic.h
+++ b/src/corelib/kernel/qapplicationstatic.h
@@ -9,6 +9,12 @@
#include <QtCore/qcoreapplication.h>
#include <QtCore/qglobalstatic.h>
+#include <new>
+
+#if 0
+#pragma qt_class(QApplicationStatic)
+#endif
+
QT_BEGIN_NAMESPACE
namespace QtGlobalStatic {
@@ -28,7 +34,8 @@ template <typename QAS> struct ApplicationHolder
Q_DISABLE_COPY_MOVE(ApplicationHolder)
~ApplicationHolder()
{
- if (guard.loadRelaxed() == QtGlobalStatic::Initialized) {
+ if (guard.loadAcquire() == QtGlobalStatic::Initialized) {
+ // No mutex! Up to external code to ensure no race happens.
guard.storeRelease(QtGlobalStatic::Destroyed);
realPointer()->~PlainType();
}
@@ -36,30 +43,32 @@ template <typename QAS> struct ApplicationHolder
static PlainType *realPointer()
{
- return reinterpret_cast<PlainType *>(&storage);
+ return std::launder(reinterpret_cast<PlainType *>(&storage));
}
// called from QGlobalStatic::instance()
PlainType *pointer() noexcept(MutexLockIsNoexcept && ConstructionIsNoexcept)
{
- if (guard.loadRelaxed() == QtGlobalStatic::Initialized)
+ if (guard.loadAcquire() == QtGlobalStatic::Initialized)
return realPointer();
QMutexLocker locker(&mutex);
if (guard.loadRelaxed() == QtGlobalStatic::Uninitialized) {
- QAS::innerFunction(realPointer());
- QObject::connect(QCoreApplication::instance(), &QObject::destroyed, reset);
- guard.storeRelaxed(QtGlobalStatic::Initialized);
+ QAS::innerFunction(&storage);
+ const auto *app = QCoreApplication::instance();
+ Q_ASSERT_X(app, Q_FUNC_INFO,
+ "The application static was used without a QCoreApplication instance");
+ QObject::connect(app, &QObject::destroyed, app, reset, Qt::DirectConnection);
+ guard.storeRelease(QtGlobalStatic::Initialized);
}
return realPointer();
}
static void reset()
{
- if (guard.loadRelaxed() == QtGlobalStatic::Initialized) {
- QMutexLocker locker(&mutex);
- realPointer()->~PlainType();
- guard.storeRelaxed(QtGlobalStatic::Uninitialized);
- }
+ // we only synchronize using the mutex here, not the guard
+ QMutexLocker locker(&mutex);
+ realPointer()->~PlainType();
+ guard.storeRelaxed(QtGlobalStatic::Uninitialized);
}
};
} // namespace QtGlobalStatic
diff --git a/src/corelib/kernel/qapplicationstatic.qdoc b/src/corelib/kernel/qapplicationstatic.qdoc
index 82eb7265ff..5cbac65df9 100644
--- a/src/corelib/kernel/qapplicationstatic.qdoc
+++ b/src/corelib/kernel/qapplicationstatic.qdoc
@@ -20,9 +20,8 @@
the QCoreApplication. This makes it ideal to store semi-static QObjects, which
should also be destroyed once the QCoreApplication is destroyed. This means the
type will get deleted once the QCoreApplication emits the destroyed signal.
- However, as long as the actual holder is still in the initialized state, the
- type will be recreated when it's accessed again once a new QCoreApplication
- has been created.
+ It is permitted for the object to be recreated when it's accessed again, if
+ a new QCoreApplication has also been created.
Since the value is bound to the QCoreApplication, it should only ever be
accessed if there is a valid QCoreApplication::instance(). Accessing this
@@ -45,6 +44,31 @@
this macro behaves identically to Q_GLOBAL_STATIC(). Please see that macro's
documentation for more information.
+ \section1 Threading guarantees
+
+ The Q_APPLICATION_STATIC macro ensures that the object is initialized only
+ once (per lifetime of a QCoreApplication), even if multiple threads try to
+ concurrently access the object. This is done by providing a per-object
+ mutex; application and library developers need to be aware that their
+ object will be constructed with this mutex locked and therefore must not
+ reenter the same object's initialization, or a deadlock will occur.
+
+ There is no thread-safety on the destruction of the object: user code must
+ not access this object once the QCoreApplication destructor starts to run.
+ User code must arrange to ensure this does not happen, such as by not
+ accessing it once the main thread's event loop has exited.
+
+ Like Q_GLOBAL_STATIC, Q_APPLICATION_STATIC provides no thread-safety
+ guarantees for accesses to the object once creation is finished. It is up
+ to user code to ensure that no racy data accesses happen.
+
+ In case the object created by this operation is a QObject, its associated
+ thread will be the one that succeeded in creating it. It will be destroyed
+ by the main thread, so a \l{QObject::}{moveToThread()} to the main thread
+ or to no thread before destruction is adviseable. Doing so from the
+ constructor of the class in question is a sensible solution if one can't
+ guarantee that the main thread will be the one to initialize the object.
+
\omit
\section1 Implementation details
See \l Q_GLOBAL_STATIC implementation details for an introduction.
diff --git a/src/corelib/kernel/qbasictimer.cpp b/src/corelib/kernel/qbasictimer.cpp
index 346154b1aa..17711a355e 100644
--- a/src/corelib/kernel/qbasictimer.cpp
+++ b/src/corelib/kernel/qbasictimer.cpp
@@ -33,10 +33,8 @@ QT_BEGIN_NAMESPACE
can maintain a list of basic timers by holding them in container
that supports move-only types, e.g. std::vector.
- The \l{widgets/wiggly}{Wiggly} example uses QBasicTimer to repaint
- a widget at regular intervals.
-
- \sa QTimer, QTimerEvent, QObject::timerEvent(), Timers, {Wiggly Example}
+ \sa QTimer, QChronoTimer, QTimerEvent, QObject::timerEvent(),
+ Timers, {Affine Transformations}
*/
@@ -86,10 +84,18 @@ QT_BEGIN_NAMESPACE
/*!
\fn QBasicTimer::swap(QBasicTimer &other)
+ \since 5.14
+
+ Swaps the timer \a other with this timer.
+ This operation is very fast and never fails.
+*/
+
+/*!
\fn swap(QBasicTimer &lhs, QBasicTimer &rhs)
+ \relates QBasicTimer
\since 5.14
- Swaps string \a other with this string, or \a lhs with \a rhs.
+ Swaps the timer \a lhs with \a rhs.
This operation is very fast and never fails.
*/
@@ -102,42 +108,50 @@ QT_BEGIN_NAMESPACE
*/
/*!
- \fn void QBasicTimer::start(qint64 msec, QObject *object)
+ \fn void QBasicTimer::start(int msec, QObject *object)
+
+ \obsolete Use chrono overload instead.
+*/
- Starts (or restarts) the timer with a \a msec milliseconds timeout. The
+/*!
+ \since 6.5
+
+ Starts (or restarts) the timer with a \a duration timeout. The
timer will be a Qt::CoarseTimer. See Qt::TimerType for information on the
different timer types.
The given \a object will receive timer events.
- \note In Qt versions prior to 6.5, \a msec was \c{int}, not
- \c{qint64}.
-
\sa stop(), isActive(), QObject::timerEvent(), Qt::CoarseTimer
*/
-void QBasicTimer::start(qint64 msec, QObject *obj)
+void QBasicTimer::start(std::chrono::milliseconds duration, QObject *object)
{
- start(msec, Qt::CoarseTimer, obj);
+ start(duration, Qt::CoarseTimer, object);
}
/*!
+ \fn QBasicTimer::start(int msec, Qt::TimerType timerType, QObject *obj)
\overload
+ \obsolete
+
+ Use chrono overload instead.
+*/
+
+/*!
+ \since 6.5
- Starts (or restarts) the timer with a \a msec milliseconds timeout and the
+ Starts (or restarts) the timer with a \a duration timeout and the
given \a timerType. See Qt::TimerType for information on the different
timer types.
\a obj will receive timer events.
- \note In Qt versions prior to 6.5, \a msec was \c{int}, not
- \c{qint64}.
-
\sa stop(), isActive(), QObject::timerEvent(), Qt::TimerType
*/
-void QBasicTimer::start(qint64 msec, Qt::TimerType timerType, QObject *obj)
+void QBasicTimer::start(std::chrono::milliseconds duration, Qt::TimerType timerType, QObject *obj)
{
QAbstractEventDispatcher *eventDispatcher = QAbstractEventDispatcher::instance();
- if (Q_UNLIKELY(msec < 0)) {
+ if (Q_UNLIKELY(duration.count() < 0)) {
qWarning("QBasicTimer::start: Timers cannot have negative timeouts");
return;
}
@@ -151,7 +165,7 @@ void QBasicTimer::start(qint64 msec, Qt::TimerType timerType, QObject *obj)
}
stop();
if (obj)
- id = eventDispatcher->registerTimer(msec, timerType, obj);
+ id = int(eventDispatcher->registerTimer(duration, timerType, obj));
}
/*!
@@ -163,7 +177,7 @@ void QBasicTimer::stop()
{
if (id) {
QAbstractEventDispatcher *eventDispatcher = QAbstractEventDispatcher::instance();
- if (eventDispatcher && !eventDispatcher->unregisterTimer(id)) {
+ if (eventDispatcher && !eventDispatcher->unregisterTimer(Qt::TimerId(id))) {
qWarning("QBasicTimer::stop: Failed. Possibly trying to stop from a different thread");
return;
}
diff --git a/src/corelib/kernel/qbasictimer.h b/src/corelib/kernel/qbasictimer.h
index b39a78d293..ccc93f6e9b 100644
--- a/src/corelib/kernel/qbasictimer.h
+++ b/src/corelib/kernel/qbasictimer.h
@@ -7,6 +7,8 @@
#include <QtCore/qglobal.h>
#include <QtCore/qnamespace.h>
+#include <chrono>
+
QT_BEGIN_NAMESPACE
@@ -31,16 +33,28 @@ public:
bool isActive() const noexcept { return id != 0; }
int timerId() const noexcept { return id; }
-#if QT_CORE_REMOVED_SINCE(6, 5)
+ QT_CORE_INLINE_SINCE(6, 5)
void start(int msec, QObject *obj);
+ QT_CORE_INLINE_SINCE(6, 5)
void start(int msec, Qt::TimerType timerType, QObject *obj);
-#endif
- void start(qint64 msec, QObject *obj);
- void start(qint64 msec, Qt::TimerType timerType, QObject *obj);
+ void start(std::chrono::milliseconds duration, QObject *obj);
+ void start(std::chrono::milliseconds duration, Qt::TimerType timerType, QObject *obj);
void stop();
};
Q_DECLARE_TYPEINFO(QBasicTimer, Q_RELOCATABLE_TYPE);
+#if QT_CORE_INLINE_IMPL_SINCE(6, 5)
+void QBasicTimer::start(int msec, QObject *obj)
+{
+ start(std::chrono::milliseconds{msec}, obj);
+}
+
+void QBasicTimer::start(int msec, Qt::TimerType t, QObject *obj)
+{
+ start(std::chrono::milliseconds{msec}, t, obj);
+}
+#endif
+
inline void swap(QBasicTimer &lhs, QBasicTimer &rhs) noexcept { lhs.swap(rhs); }
QT_END_NAMESPACE
diff --git a/src/corelib/kernel/qchronotimer.cpp b/src/corelib/kernel/qchronotimer.cpp
new file mode 100644
index 0000000000..a517c4446b
--- /dev/null
+++ b/src/corelib/kernel/qchronotimer.cpp
@@ -0,0 +1,452 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// Copyright (C) 2016 Intel Corporation.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "qchronotimer.h"
+#include "qtimer_p.h"
+#include "qsingleshottimer_p.h"
+
+#include "qabstracteventdispatcher.h"
+#include "qcoreapplication.h"
+#include "qcoreapplication_p.h"
+#include "qdeadlinetimer.h"
+#include "qmetaobject_p.h"
+#include "qobject_p.h"
+#include "qproperty_p.h"
+#include "qthread.h"
+
+using namespace std::chrono_literals;
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \class QChronoTimer
+ \inmodule QtCore
+ \since 6.8
+ \ingroup events
+
+ \brief The QChronoTimer class provides repetitive and single-shot timers.
+
+ The QChronoTimer class provides a high-level programming interface for
+ timers. To use it, create a QChronoTimer, either passing the interval to the
+ constructor, or setting it after construction using setInterval(), connect
+ its timeout() signal to the appropriate slots, and call start(). From then
+ on, it will emit the timeout() signal at constant intervals. For example:
+
+ \snippet timers/timers.cpp timer-interval-in-ctor
+ \snippet timers/timers.cpp timer-setinterval
+
+ You can set a timer to time out only once by calling setSingleShot(true).
+
+ QChronoTimer also has singleShot() static methods:
+
+ \snippet timers/timers.cpp qchronotimer-singleshot
+
+ In multithreaded applications, you can use QChronoTimer in any thread
+ that has an event loop. To start an event loop from a non-GUI
+ thread, use QThread::exec(). Qt uses the timer's
+ \l{QObject::thread()}{thread affinity} to determine which thread
+ will emit the \l{QChronoTimer::}{timeout()} signal. Because of this, you
+ must start and stop the timer in its thread; it is not possible to
+ start a timer from another thread.
+
+ As a special case, a QChronoTimer with a timeout of \c 0ns will time out
+ as soon as possible, though the ordering between zero timers and other
+ sources of events is unspecified. Zero timers can be used to do some
+ work while still providing a responsive user interface:
+
+ \snippet timers/timers.cpp zero-timer
+
+ From then on, \c processOneThing() will be called repeatedly. It should
+ be written in such a way that it always returns quickly (for example,
+ after processing one data item) so that Qt can deliver events to the user
+ interface and stop the timer as soon as it has done all its work. This
+ is the traditional way of implementing heavy work in GUI applications,
+ but as multithreading is becoming available on more platforms, a modern
+ alternative is doing the heavy work in a thread other than the GUI (main)
+ thread. Qt has the QThread class, which can be used to achieve that.
+
+ \section1 Accuracy and Timer Resolution
+
+ The accuracy of timers depends on the underlying operating system and
+ hardware. Most platforms support requesting nano-second precision for
+ timers (for example, libc's \c nanosleep), though the accuracy of the
+ timer will not equal this resolution in many real-world situations.
+
+ You can set the \l{Qt::TimerType}{timer type} to tell QChronoTimer which
+ precision to request from the system.
+
+ For Qt::PreciseTimer, QChronoTimer will try to keep the precision at
+ \c 1ns. Precise timers will never time out earlier than expected.
+
+ For Qt::CoarseTimer and Qt::VeryCoarseTimer types, QChronoTimer may wake
+ up earlier than expected, within the margins for those types:
+ \list
+ \li 5% of the interval for Qt::CoarseTimer
+ \li \c 500ms for Qt::VeryCoarseTimer
+ \endlist
+
+ All timer types may time out later than expected if the system is busy or
+ unable to provide the requested accuracy. In such a case of timeout
+ overrun, Qt will emit timeout() only once, even if multiple timeouts have
+ expired, and then will resume the original interval.
+
+ \section1 Alternatives to QChronoTimer
+
+ An alternative to using QChronoTimer is to call QObject::startTimer()
+ for your object and reimplement the QObject::timerEvent() event handler
+ in your class (which must be a sub-class of QObject). The disadvantage
+ is that timerEvent() does not support such high-level features as
+ single-shot timers or signals.
+
+ Another alternative is QBasicTimer. It is typically less cumbersome
+ than using QObject::startTimer() directly. See \l{Timers} for an
+ overview of all three approaches.
+
+ Some operating systems limit the number of timers that may be used;
+ Qt does its best to work around these limitations.
+
+ \sa QBasicTimer, QTimerEvent, QObject::timerEvent(), Timers,
+ {Analog Clock}
+*/
+
+/*!
+ Constructs a timer with the given \a parent, using the default interval,
+ \c 0ns.
+*/
+QChronoTimer::QChronoTimer(QObject *parent)
+ : QChronoTimer(0ns, parent)
+{
+}
+
+/*!
+ Constructs a timer with the given \a parent, using an interval of \a nsec.
+*/
+QChronoTimer::QChronoTimer(std::chrono::nanoseconds nsec, QObject *parent)
+ : QObject(*new QTimerPrivate(nsec, this), parent)
+{
+ Q_ASSERT(!d_func()->isQTimer);
+}
+
+/*!
+ Destroys the timer.
+*/
+QChronoTimer::~QChronoTimer()
+{
+ if (d_func()->isActive()) // stop running timer
+ stop();
+}
+
+/*!
+ \fn void QChronoTimer::timeout()
+
+ This signal is emitted when the timer times out.
+
+ \sa interval, start(), stop()
+*/
+
+/*!
+ \property QChronoTimer::active
+
+ This boolean property is \c true if the timer is running; otherwise
+ \c false.
+*/
+
+/*!
+ Returns \c true if the timer is running (pending); otherwise returns
+ false.
+*/
+bool QChronoTimer::isActive() const
+{
+ return d_func()->isActiveData.value();
+}
+
+QBindable<bool> QChronoTimer::bindableActive()
+{
+ return QBindable<bool>(&d_func()->isActiveData);
+}
+
+/*!
+ Returns a Qt::TimerId representing the timer ID if the timer is running;
+ otherwise returns \c Qt::TimerId::Invalid.
+
+ \sa Qt::TimerId
+*/
+Qt::TimerId QChronoTimer::id() const
+{
+ return d_func()->id;
+}
+
+/*! \overload start()
+
+ Starts or restarts the timer with the timeout specified in \l interval.
+
+ If the timer is already running, it will be
+ \l{QChronoTimer::stop()}{stopped} and restarted.
+
+ If \l singleShot is true, the timer will be activated only once.
+*/
+void QChronoTimer::start()
+{
+ auto *d = d_func();
+ if (d->isActive()) // stop running timer
+ stop();
+ const auto id = Qt::TimerId{QObject::startTimer(d->intervalDuration, d->type)};
+ if (id > Qt::TimerId::Invalid) {
+ d->id = id;
+ d->isActiveData.notify();
+ }
+}
+
+/*!
+ Stops the timer.
+
+ \sa start()
+*/
+void QChronoTimer::stop()
+{
+ auto *d = d_func();
+ if (d->isActive()) {
+ QObject::killTimer(d->id);
+ d->id = Qt::TimerId::Invalid;
+ d->isActiveData.notify();
+ }
+}
+
+/*!
+ \reimp
+*/
+void QChronoTimer::timerEvent(QTimerEvent *e)
+{
+ auto *d = d_func();
+ if (Qt::TimerId{e->timerId()} == d->id) {
+ if (d->single)
+ stop();
+ Q_EMIT timeout(QPrivateSignal());
+ }
+}
+
+/*!
+ \fn template <typename Functor> QMetaObject::Connection QChronoTimer::callOnTimeout(const QObject *context, Functor &&slot, Qt::ConnectionType connectionType = Qt::AutoConnection)
+ \overload callOnTimeout()
+
+ Creates a connection from the timeout() signal to \a slot to be placed in a
+ specific event loop of \a context, with connection type \a connectionType,
+ and returns a handle to the connection.
+
+ This method is provided as a convenience. It's equivalent to calling:
+ \code
+ QObject::connect(timer, &QChronoTimer::timeout, context, slot, connectionType);
+ \endcode
+
+ \sa QObject::connect(), timeout()
+*/
+
+/*!
+ \property QChronoTimer::singleShot
+ \brief Whether the timer is a single-shot timer
+
+ A single-shot timer fires only once, non-single-shot timers fire every
+ \l interval.
+
+ The default value for this property is \c false.
+
+ \sa interval, singleShot()
+*/
+void QChronoTimer::setSingleShot(bool singleShot)
+{
+ d_func()->single = singleShot;
+}
+
+bool QChronoTimer::isSingleShot() const
+{
+ return d_func()->single;
+}
+
+QBindable<bool> QChronoTimer::bindableSingleShot()
+{
+ return QBindable<bool>(&d_func()->single);
+}
+
+/*!
+ \property QChronoTimer::interval
+ \brief The timeout interval
+
+ The default value for this property is \c 0ns.
+
+ A QChronoTimer with a timeout of \c 0ns will time out as soon as all
+ the events in the window system's event queue have been processed.
+
+ Setting the interval of an active timer changes the interval and acquires
+ a new id(). If the timer is not active, only the interval is changed.
+
+ \sa singleShot
+*/
+void QChronoTimer::setInterval(std::chrono::nanoseconds nsec)
+{
+ auto *d = d_func();
+ d->intervalDuration.removeBindingUnlessInWrapper();
+ const bool intervalChanged = nsec != d->intervalDuration.valueBypassingBindings();
+ d->intervalDuration.setValueBypassingBindings(nsec);
+ if (d->isActive()) { // Create new timer
+ QObject::killTimer(d->id); // Restart timer
+ const auto newId = Qt::TimerId{QObject::startTimer(nsec, d->type)};
+ if (newId > Qt::TimerId::Invalid) {
+ // Restarted successfully. No need to update the active state.
+ d->id = newId;
+ } else {
+ // Failed to start the timer.
+ // Need to notify about active state change.
+ d->id = Qt::TimerId::Invalid;
+ d->isActiveData.notify();
+ }
+ }
+ if (intervalChanged)
+ d->intervalDuration.notify();
+}
+
+std::chrono::nanoseconds QChronoTimer::interval() const
+{
+ return d_func()->intervalDuration.value();
+}
+
+QBindable<std::chrono::nanoseconds> QChronoTimer::bindableInterval()
+{
+ return {&d_func()->intervalDuration};
+}
+
+/*!
+ \property QChronoTimer::remainingTime
+ \brief The remaining time
+
+ Returns the remaining duration until the timeout.
+
+ If the timer is inactive, the returned duration will be negative.
+
+ If the timer is overdue, the returned duration will be \c 0ns.
+
+ \sa interval
+*/
+std::chrono::nanoseconds QChronoTimer::remainingTime() const
+{
+ if (isActive())
+ return QAbstractEventDispatcher::instance()->remainingTime(d_func()->id);
+ return std::chrono::nanoseconds::min();
+}
+
+/*!
+ \property QChronoTimer::timerType
+ \brief Controls the accuracy of the timer
+
+ The default value for this property is \c Qt::CoarseTimer.
+
+ \sa Qt::TimerType
+*/
+void QChronoTimer::setTimerType(Qt::TimerType atype)
+{
+ d_func()->type = atype;
+}
+
+Qt::TimerType QChronoTimer::timerType() const
+{
+ return d_func()->type;
+}
+
+QBindable<Qt::TimerType> QChronoTimer::bindableTimerType()
+{
+ return {&d_func()->type};
+}
+
+/*!
+ \overload
+ \reentrant
+
+ This static function calls the slot \a member, on object \a receiver, after
+ time interval \a interval. \a timerType affects the precision of the timer
+
+ \a member has to be a member function of \a receiver; you need to use the
+ \c SLOT() macro to get this parameter.
+
+ This function is provided as a convenience to save the need to use a
+ \l{QObject::timerEvent()}{timerEvent} or create a local QChronoTimer
+ object.
+
+ \sa start(), Qt::TimerType
+*/
+void QChronoTimer::singleShot(std::chrono::nanoseconds interval, Qt::TimerType timerType,
+ const QObject *receiver, const char *member)
+{
+ if (Q_UNLIKELY(interval < 0ns)) {
+ qWarning("QChronoTimer::singleShot: Timers cannot have negative timeouts");
+ return;
+ }
+ if (receiver && member) {
+ if (interval == 0ns) {
+ // special code shortpath for 0-timers
+ const char* bracketPosition = strchr(member, '(');
+ if (!bracketPosition || !(member[0] >= '0' && member[0] <= '2')) {
+ qWarning("QChronoTimer::singleShot: Invalid slot specification");
+ return;
+ }
+ const auto methodName = QByteArrayView(member + 1, // extract method name
+ bracketPosition - 1 - member).trimmed();
+ QMetaObject::invokeMethod(const_cast<QObject *>(receiver),
+ methodName.toByteArray().constData(),
+ Qt::QueuedConnection);
+ return;
+ }
+ (void) new QSingleShotTimer(interval, timerType, receiver, member);
+ }
+}
+
+/*!
+ \internal
+
+ \list
+ \li \a interval the time interval
+ \li \a timerType the type of the timer; this affects the precision of
+ the timer
+ \li \a receiver the receiver or context object; if this is \c nullptr,
+ this method will figure out a context object to use, see code
+ comments below
+ \li \a slotObj a callable, for example a lambda
+ \endlist
+*/
+void QChronoTimer::singleShotImpl(std::chrono::nanoseconds interval, Qt::TimerType timerType,
+ const QObject *receiver, QtPrivate::QSlotObjectBase *slotObj)
+{
+ if (interval == 0ns) {
+ bool deleteReceiver = false;
+ // Optimize: set a receiver context when none is given, such that we can use
+ // QMetaObject::invokeMethod which is more efficient than going through a timer.
+ // We need a QObject living in the current thread. But the QThread itself lives
+ // in a different thread - with the exception of the main QThread which lives in
+ // itself. And QThread::currentThread() is among the few QObjects we know that will
+ // most certainly be there. Note that one can actually call singleShot before the
+ // QApplication is created!
+ if (!receiver && QThread::currentThread() == QCoreApplicationPrivate::mainThread()) {
+ // reuse main thread as context object
+ receiver = QThread::currentThread();
+ } else if (!receiver) {
+ // Create a receiver context object on-demand. According to the benchmarks,
+ // this is still more efficient than going through a timer.
+ receiver = new QObject;
+ deleteReceiver = true;
+ }
+
+ auto h = QtPrivate::invokeMethodHelper({});
+ QMetaObject::invokeMethodImpl(const_cast<QObject *>(receiver), slotObj,
+ Qt::QueuedConnection, h.parameterCount(), h.parameters.data(), h.typeNames.data(),
+ h.metaTypes.data());
+
+ if (deleteReceiver)
+ const_cast<QObject *>(receiver)->deleteLater();
+ return;
+ }
+
+ new QSingleShotTimer(interval, timerType, receiver, slotObj);
+}
+
+QT_END_NAMESPACE
+
+#include "moc_qchronotimer.cpp"
diff --git a/src/corelib/kernel/qchronotimer.h b/src/corelib/kernel/qchronotimer.h
new file mode 100644
index 0000000000..79c475e93c
--- /dev/null
+++ b/src/corelib/kernel/qchronotimer.h
@@ -0,0 +1,149 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QCHRONOTIMER_H
+#define QCHRONOTIMER_H
+
+#ifndef QT_NO_QOBJECT
+
+#include <QtCore/qcoreevent.h>
+#include <QtCore/qnamespace.h>
+#include <QtCore/qobject.h>
+#include <QtCore/qproperty.h>
+
+#include <chrono>
+
+QT_BEGIN_NAMESPACE
+
+class QTimerPrivate;
+class Q_CORE_EXPORT QChronoTimer : public QObject
+{
+ Q_OBJECT
+ Q_PROPERTY(bool singleShot READ isSingleShot WRITE setSingleShot
+ BINDABLE bindableSingleShot FINAL)
+ Q_PROPERTY(std::chrono::nanoseconds interval READ interval WRITE setInterval
+ BINDABLE bindableInterval FINAL)
+ Q_PROPERTY(std::chrono::nanoseconds remainingTime READ remainingTime FINAL)
+ Q_PROPERTY(Qt::TimerType timerType READ timerType WRITE setTimerType
+ BINDABLE bindableTimerType FINAL)
+ Q_PROPERTY(bool active READ isActive STORED false BINDABLE bindableActive FINAL)
+
+ template <typename Functor>
+ using FunctorContext = typename QtPrivate::ContextTypeForFunctor<Functor>::ContextType;
+
+public:
+ explicit QChronoTimer(std::chrono::nanoseconds nsec, QObject *parent = nullptr);
+ explicit QChronoTimer(QObject *parent = nullptr);
+ ~QChronoTimer() override;
+
+ bool isActive() const;
+ QBindable<bool> bindableActive();
+ Qt::TimerId id() const;
+
+ void setInterval(std::chrono::nanoseconds nsec);
+ std::chrono::nanoseconds interval() const;
+ QBindable<std::chrono::nanoseconds> bindableInterval();
+
+ std::chrono::nanoseconds remainingTime() const;
+
+ void setTimerType(Qt::TimerType atype);
+ Qt::TimerType timerType() const;
+ QBindable<Qt::TimerType> bindableTimerType();
+
+ void setSingleShot(bool singleShot);
+ bool isSingleShot() const;
+ QBindable<bool> bindableSingleShot();
+
+ // singleShot with context
+#ifdef Q_QDOC
+ template <typename Functor>
+ static inline void singleShot(std::chrono::nanoseconds interval,
+ const QObject *receiver, Functor &&slot);
+ template <typename Functor>
+ static inline void singleShot(std::chrono::nanoseconds interval interval,
+ Qt::TimerType timerType,
+ const QObject *receiver, Functor &&slot);
+#else
+ template <typename Functor>
+ static void singleShot(std::chrono::nanoseconds interval,
+ const FunctorContext<Functor> *receiver, Functor &&slot)
+ {
+ singleShot(interval, defaultTimerTypeFor(interval), receiver, std::forward<Functor>(slot));
+ }
+ template <typename Functor>
+ static void singleShot(std::chrono::nanoseconds interval, Qt::TimerType timerType,
+ const FunctorContext<Functor> *receiver, Functor &&slot)
+ {
+ using Prototype = void(*)();
+ auto *slotObj = QtPrivate::makeCallableObject<Prototype>(std::forward<Functor>(slot));
+ singleShotImpl(interval, timerType, receiver, slotObj);
+ }
+#endif
+
+ template <typename Functor>
+ static void singleShot(std::chrono::nanoseconds interval, Qt::TimerType timerType,
+ Functor &&slot)
+ { singleShot(interval, timerType, nullptr, std::forward<Functor>(slot)); }
+
+ template <typename Functor>
+ static void singleShot(std::chrono::nanoseconds interval, Functor &&slot)
+ {
+ singleShot(interval, defaultTimerTypeFor(interval), nullptr, std::forward<Functor>(slot));
+ }
+
+ static void singleShot(std::chrono::nanoseconds interval, Qt::TimerType timerType,
+ const QObject *receiver, const char *member);
+ static void singleShot(std::chrono::nanoseconds interval, const QObject *receiver,
+ const char *member)
+ { singleShot(interval, defaultTimerTypeFor(interval), receiver, member); }
+
+#ifdef Q_QDOC
+ template <typename Functor>
+ QMetaObject::Connection callOnTimeout(const QObject *context, Functor &&slot,
+ Qt::ConnectionType connectionType = Qt::AutoConnection);
+#else
+ template <typename ... Args>
+ QMetaObject::Connection callOnTimeout(Args && ...args)
+ {
+ return QObject::connect(this, &QChronoTimer::timeout, std::forward<Args>(args)... );
+ }
+#endif
+
+public Q_SLOTS:
+ void start();
+ void stop();
+
+Q_SIGNALS:
+ void timeout(QPrivateSignal);
+
+protected:
+ void timerEvent(QTimerEvent *) override;
+
+private:
+ Q_DISABLE_COPY(QChronoTimer)
+
+ // QChronoTimer uses QTimerPrivate
+ inline QTimerPrivate *d_func() noexcept
+ { Q_CAST_IGNORE_ALIGN(return reinterpret_cast<QTimerPrivate *>(qGetPtrHelper(d_ptr));) }
+ inline const QTimerPrivate *d_func() const noexcept
+ { Q_CAST_IGNORE_ALIGN(return reinterpret_cast<const QTimerPrivate *>(qGetPtrHelper(d_ptr));) }
+
+ // These two functions are inherited from QObject
+ int startTimer(std::chrono::nanoseconds) = delete;
+ void killTimer(int) = delete;
+
+ static constexpr Qt::TimerType defaultTimerTypeFor(std::chrono::nanoseconds interval) noexcept
+ {
+ using namespace std::chrono_literals;
+ return interval >= 2s ? Qt::CoarseTimer : Qt::PreciseTimer;
+ }
+
+ static void singleShotImpl(std::chrono::nanoseconds interval, Qt::TimerType timerType,
+ const QObject *receiver, QtPrivate::QSlotObjectBase *slotObj);
+};
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_QOBJECT
+
+#endif // QCHRONOTIMER_H
diff --git a/src/corelib/kernel/qcore_foundation.mm b/src/corelib/kernel/qcore_foundation.mm
index 360b4aebd4..6d2451e078 100644
--- a/src/corelib/kernel/qcore_foundation.mm
+++ b/src/corelib/kernel/qcore_foundation.mm
@@ -10,7 +10,7 @@
#include <QtCore/qbytearray.h>
#include <QtCore/qrect.h>
-#if QT_CONFIG(timezone) && !defined(QT_NO_SYSTEMLOCALE)
+#if QT_CONFIG(timezone)
#include <QtCore/qtimezone.h>
#include <QtCore/private/qtimezoneprivate_p.h>
#include <QtCore/private/qcore_mac_p.h>
@@ -300,8 +300,8 @@ QUuid QUuid::fromCFUUID(CFUUIDRef uuid)
*/
CFUUIDRef QUuid::toCFUUID() const
{
- const QByteArray bytes = toRfc4122();
- return CFUUIDCreateFromUUIDBytes(0, *reinterpret_cast<const CFUUIDBytes *>(bytes.constData()));
+ const auto bytes = toBytes();
+ return CFUUIDCreateFromUUIDBytes(0, *reinterpret_cast<const CFUUIDBytes *>(&bytes));
}
/*!
@@ -333,8 +333,11 @@ QUuid QUuid::fromNSUUID(const NSUUID *uuid)
*/
NSUUID *QUuid::toNSUUID() const
{
- const QByteArray bytes = toRfc4122();
- return [[[NSUUID alloc] initWithUUIDBytes:*reinterpret_cast<const uuid_t *>(bytes.constData())] autorelease];
+ const auto bytes = toBytes();
+ static_assert(sizeof bytes == sizeof(uuid_t));
+ uuid_t u;
+ memcpy(&u, &bytes, sizeof(uuid_t));
+ return [[[NSUUID alloc] initWithUUIDBytes:u] autorelease];
}
// ----------------------------------------------------------------------------
@@ -466,7 +469,7 @@ NSDate *QDateTime::toNSDate() const
// ----------------------------------------------------------------------------
-#if QT_CONFIG(timezone) && !defined(QT_NO_SYSTEMLOCALE)
+#if QT_CONFIG(timezone)
/*!
\brief Constructs a new QTimeZone containing a copy of the CFTimeZone \a timeZone.
@@ -495,9 +498,9 @@ QTimeZone QTimeZone::fromCFTimeZone(CFTimeZoneRef timeZone)
CFTimeZoneRef QTimeZone::toCFTimeZone() const
{
#ifndef QT_NO_DYNAMIC_CAST
- Q_ASSERT(dynamic_cast<const QMacTimeZonePrivate *>(d.data()));
+ Q_ASSERT(dynamic_cast<const QMacTimeZonePrivate *>(d.d));
#endif
- const QMacTimeZonePrivate *p = static_cast<const QMacTimeZonePrivate *>(d.data());
+ const QMacTimeZonePrivate *p = static_cast<const QMacTimeZonePrivate *>(d.d);
return reinterpret_cast<CFTimeZoneRef>([p->nsTimeZone() copy]);
}
diff --git a/src/corelib/kernel/qcore_mac.mm b/src/corelib/kernel/qcore_mac.mm
index 067c1a9ec7..00b0d078d7 100644
--- a/src/corelib/kernel/qcore_mac.mm
+++ b/src/corelib/kernel/qcore_mac.mm
@@ -25,11 +25,14 @@
#include "qendian.h"
#include "qhash.h"
-#include "qpair.h"
#include "qmutex.h"
#include "qvarlengtharray.h"
#include "private/qlocking_p.h"
+#if !defined(QT_BOOTSTRAPPED)
+#include <thread>
+#endif
+
#if !defined(QT_APPLE_NO_PRIVATE_APIS)
extern "C" {
typedef uint32_t csr_config_t;
@@ -49,6 +52,7 @@ QT_BEGIN_NAMESPACE
// --------------------------------------------------------------------------
+#if defined(Q_OS_MACOS)
static void initializeStandardUserDefaults()
{
// The standard user defaults are initialized from an ordered list of domains,
@@ -61,6 +65,7 @@ static void initializeStandardUserDefaults()
Q_UNUSED(NSUserDefaults.standardUserDefaults);
}
Q_CONSTRUCTOR_FUNCTION(initializeStandardUserDefaults);
+#endif
// --------------------------------------------------------------------------
@@ -82,17 +87,35 @@ QCFString::operator CFStringRef() const
#if defined(QT_USE_APPLE_UNIFIED_LOGGING)
-bool AppleUnifiedLogger::willMirrorToStderr()
+bool AppleUnifiedLogger::preventsStderrLogging()
{
- // 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.
+ // os_log will mirror to stderr if OS_ACTIVITY_DT_MODE is set,
+ // regardless of its value. OS_ACTIVITY_MODE then controls whether
+ // to include info and/or debug messages in this mirroring.
+ // For some reason, when launched under lldb (via Xcode or not),
+ // all levels are included.
+
+ // CFLog will normally log to both stderr, and via os_log.
+ // Setting CFLOG_FORCE_DISABLE_STDERR disables the stderr
+ // logging. Setting CFLOG_FORCE_STDERR will both duplicate
+ // CFLog's output to stderr, and trigger OS_ACTIVITY_DT_MODE,
+ // resulting in os_log calls also being mirrored to stderr.
+ // Setting ACTIVITY_LOG_STDERR has the same effect.
+
+ // NSLog is plumbed to CFLog, and will respond to the same
+ // environment variables as CFLog.
+
+ // We want to disable Qt's default stderr log handler when
+ // os_log has already mirrored to stderr.
static bool willMirror = qEnvironmentVariableIsSet("OS_ACTIVITY_DT_MODE")
- || qEnvironmentVariableIsSet("ACTIVITY_LOG_STDERR")
- || qEnvironmentVariableIsSet("CFLOG_FORCE_STDERR");
- return willMirror;
+ || qEnvironmentVariableIsSet("ACTIVITY_LOG_STDERR")
+ || qEnvironmentVariableIsSet("CFLOG_FORCE_STDERR");
+
+ // As well as when we suspect that Xcode is going to present os_log
+ // as structured log messages.
+ static bool disableStderr = qEnvironmentVariableIsSet("CFLOG_FORCE_DISABLE_STDERR");
+
+ return willMirror || disableStderr;
}
QT_MAC_WEAK_IMPORT(_os_log_default);
@@ -133,7 +156,7 @@ bool AppleUnifiedLogger::messageHandler(QtMsgType msgType, const QMessageLogCont
// system from redacting our log message.
os_log_with_type(log, logType, "%{public}s", qPrintable(message));
- return willMirrorToStderr();
+ return preventsStderrLogging();
}
os_log_type_t AppleUnifiedLogger::logTypeForMessageType(QtMsgType msgType)
@@ -210,52 +233,38 @@ QT_FOR_EACH_MUTABLE_CORE_GRAPHICS_TYPE(QT_DECLARE_WEAK_QDEBUG_OPERATOR_FOR_CF_TY
QT_END_NAMESPACE
QT_USE_NAMESPACE
+
+#ifdef QT_DEBUG
@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<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.";
+@implementation QT_MANGLE_NAMESPACE(QMacAutoReleasePoolTracker)
+@end
+QT_NAMESPACE_ALIAS_OBJC_CLASS(QMacAutoReleasePoolTracker);
+#endif // QT_DEBUG
- // Reset the pool so that it's not drained again later on
- *m_pool = nullptr;
- }
+// Use the direct runtime interface to manage autorelease pools, as it
+// has less overhead then allocating NSAutoreleasePools, and allows for
+// a future where we use ARC (where NSAutoreleasePool is not allowed).
+// https://clang.llvm.org/docs/AutomaticReferenceCounting.html#runtime-support
- [super dealloc];
+extern "C" {
+void *objc_autoreleasePoolPush(void);
+void objc_autoreleasePoolPop(void *pool);
}
-@end
-QT_NAMESPACE_ALIAS_OBJC_CLASS(QMacAutoReleasePoolTracker);
QT_BEGIN_NAMESPACE
QMacAutoReleasePool::QMacAutoReleasePool()
- : pool([[NSAutoreleasePool alloc] init])
+ : pool(objc_autoreleasePoolPush())
{
+#ifdef QT_DEBUG
+ static const bool debugAutoReleasePools = qEnvironmentVariableIsSet("QT_DARWIN_DEBUG_AUTORELEASEPOOLS");
+ if (!debugAutoReleasePools)
+ return;
+
Class trackerClass = [QMacAutoReleasePoolTracker class];
-#ifdef QT_DEBUG
void *poolFrame = nullptr;
void *frames[2];
if (backtrace_from_fp(__builtin_frame_address(0), frames, 2))
@@ -285,30 +294,14 @@ QMacAutoReleasePool::QMacAutoReleasePool()
free((char*)symbolName);
}
}
-#endif
- [[[trackerClass alloc] initWithPool:
- reinterpret_cast<NSAutoreleasePool **>(&pool)] autorelease];
+ [[trackerClass new] autorelease];
+#endif // QT_DEBUG
}
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.
- [savedPool drain];
+ objc_autoreleasePoolPop(pool);
}
#ifndef QT_NO_DEBUG_STREAM
@@ -353,7 +346,7 @@ std::optional<uint32_t> qt_mac_sipConfiguration()
return config;
#endif
- QIOType<io_registry_entry_t> nvram = IORegistryEntryFromPath(kIOMasterPortDefault, "IODeviceTree:/options");
+ QIOType<io_registry_entry_t> nvram = IORegistryEntryFromPath(kIOMainPortDefault, "IODeviceTree:/options");
if (!nvram) {
qWarning("Failed to locate NVRAM entry in IO registry");
return {};
@@ -456,36 +449,64 @@ AppleApplication *qt_apple_sharedApplication()
}
#endif
+#if !defined(QT_BOOTSTRAPPED)
+
+#if defined(Q_OS_MACOS)
+namespace {
+struct SandboxChecker
+{
+ SandboxChecker() : m_thread([this]{
+ m_isSandboxed = []{
+ QCFType<SecStaticCodeRef> staticCode = nullptr;
+ NSURL *executableUrl = NSBundle.mainBundle.executableURL;
+ if (SecStaticCodeCreateWithPath((__bridge CFURLRef)executableUrl,
+ kSecCSDefaultFlags, &staticCode) != errSecSuccess)
+ return false;
+
+ QCFType<SecRequirementRef> 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;
+ }();
+ })
+ {}
+ ~SandboxChecker() {
+ std::scoped_lock lock(m_mutex);
+ if (m_thread.joinable())
+ m_thread.detach();
+ }
+ bool isSandboxed() const {
+ std::scoped_lock lock(m_mutex);
+ if (m_thread.joinable())
+ m_thread.join();
+ return m_isSandboxed;
+ }
+private:
+ bool m_isSandboxed;
+ mutable std::thread m_thread;
+ mutable std::mutex m_mutex;
+};
+} // namespace
+static SandboxChecker sandboxChecker;
+#endif // Q_OS_MACOS
+
bool qt_apple_isSandboxed()
{
#if defined(Q_OS_MACOS)
- static bool isSandboxed = []() {
- QCFType<SecStaticCodeRef> staticCode = nullptr;
- NSURL *executableUrl = NSBundle.mainBundle.executableURL;
- if (SecStaticCodeCreateWithPath((__bridge CFURLRef)executableUrl,
- kSecCSDefaultFlags, &staticCode) != errSecSuccess)
- return false;
-
- QCFType<SecRequirementRef> 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;
+ return sandboxChecker.isSandboxed();
#else
return true; // All other Apple platforms
#endif
}
-#if !defined(QT_BOOTSTRAPPED)
QT_END_NAMESPACE
-@implementation NSObject (QtSandboxHelpers)
+@implementation NSObject (QtExtras)
- (id)qt_valueForPrivateKey:(NSString *)key
{
if (qt_apple_isSandboxed())
@@ -495,7 +516,7 @@ QT_END_NAMESPACE
}
@end
QT_BEGIN_NAMESPACE
-#endif
+#endif // !QT_BOOTSTRAPPED
#ifdef Q_OS_MACOS
/*
@@ -520,7 +541,7 @@ QMacRootLevelAutoReleasePool::QMacRootLevelAutoReleasePool()
if (qEnvironmentVariableIsSet(ROOT_LEVEL_POOL_DISABLE_SWITCH))
return;
- pool.reset(new QMacAutoReleasePool);
+ pool.emplace();
[[[ROOT_LEVEL_POOL_MARKER alloc] init] autorelease];
@@ -546,6 +567,9 @@ void qt_apple_check_os_version()
#elif defined(__TV_OS_VERSION_MIN_REQUIRED)
const char *os = "tvOS";
const int version = __TV_OS_VERSION_MIN_REQUIRED;
+#elif defined(__VISION_OS_VERSION_MIN_REQUIRED)
+ const char *os = "visionOS";
+ const int version = __VISION_OS_VERSION_MIN_REQUIRED;
#elif defined(__IPHONE_OS_VERSION_MIN_REQUIRED)
const char *os = "iOS";
const int version = __IPHONE_OS_VERSION_MIN_REQUIRED;
@@ -553,10 +577,18 @@ void qt_apple_check_os_version()
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]) {
+
+ const auto required = QVersionNumber(version / 10000, version / 100 % 100, version % 100);
+ const auto current = QOperatingSystemVersion::current().version();
+
+#if defined(Q_OS_MACOS)
+ // Check for compatibility version, in which case we can't do a
+ // comparison to the deployment target, which might be e.g. 11.0
+ if (current.majorVersion() == 10 && current.minorVersion() >= 16)
+ return;
+#endif
+
+ if (current < required) {
NSDictionary *plist = NSBundle.mainBundle.infoDictionary;
NSString *applicationName = plist[@"CFBundleDisplayName"];
if (!applicationName)
@@ -567,8 +599,8 @@ void qt_apple_check_os_version()
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));
+ os, long(required.majorVersion()), long(required.minorVersion()), long(required.microVersion()),
+ os, long(current.majorVersion()), long(current.minorVersion()), long(current.microVersion()));
exit(1);
}
@@ -687,7 +719,7 @@ QMacVersion::VersionTuple QMacVersion::versionsForImage(const mach_header *machH
};
static auto makeVersionTuple = [](uint32_t dt, uint32_t sdk, QOperatingSystemVersion::OSType osType) {
- return qMakePair(
+ return std::pair(
QOperatingSystemVersion(osType, dt >> 16 & 0xffff, dt >> 8 & 0xff, dt & 0xff),
QOperatingSystemVersion(osType, sdk >> 16 & 0xffff, sdk >> 8 & 0xff, sdk & 0xff)
);
diff --git a/src/corelib/kernel/qcore_mac_p.h b/src/corelib/kernel/qcore_mac_p.h
index aee5fcb604..5ecf8072f4 100644
--- a/src/corelib/kernel/qcore_mac_p.h
+++ b/src/corelib/kernel/qcore_mac_p.h
@@ -19,6 +19,8 @@
#include <QtCore/qoperatingsystemversion.h>
+#include <optional>
+
#ifdef Q_OS_MACOS
#include <mach/port.h>
struct mach_header;
@@ -48,7 +50,6 @@ kern_return_t IOObjectRelease(io_object_t object);
#endif
#include "qstring.h"
-#include "qscopedpointer.h"
#include "qpair.h"
#if defined( __OBJC__) && defined(QT_NAMESPACE)
@@ -85,15 +86,18 @@ template <typename T, typename U, auto RetainFunction, auto ReleaseFunction>
class QAppleRefCounted
{
public:
- QAppleRefCounted() : value() {}
- QAppleRefCounted(const T &t) : value(t) {}
- QAppleRefCounted(T &&t) noexcept(std::is_nothrow_move_constructible<T>::value)
+ Q_NODISCARD_CTOR QAppleRefCounted() : value() {}
+ Q_NODISCARD_CTOR QAppleRefCounted(const T &t) : value(t) {}
+ Q_NODISCARD_CTOR QAppleRefCounted(T &&t)
+ noexcept(std::is_nothrow_move_constructible<T>::value)
: value(std::move(t)) {}
- QAppleRefCounted(QAppleRefCounted &&other)
+ Q_NODISCARD_CTOR QAppleRefCounted(QAppleRefCounted &&other)
noexcept(std::is_nothrow_move_assignable<T>::value &&
std::is_nothrow_move_constructible<T>::value)
: value(std::exchange(other.value, T())) {}
- QAppleRefCounted(const QAppleRefCounted &other) : value(other.value) { if (value) RetainFunction(value); }
+ Q_NODISCARD_CTOR QAppleRefCounted(const QAppleRefCounted &other)
+ : value(other.value)
+ { if (value) RetainFunction(value); }
~QAppleRefCounted() { if (value) ReleaseFunction(value); }
operator T() const { return value; }
void swap(QAppleRefCounted &other) noexcept(noexcept(qSwap(value, other.value)))
@@ -109,11 +113,11 @@ protected:
T value;
};
-class Q_CORE_EXPORT QMacAutoReleasePool
+class QMacAutoReleasePool
{
public:
- QMacAutoReleasePool();
- ~QMacAutoReleasePool();
+ Q_NODISCARD_CTOR Q_CORE_EXPORT QMacAutoReleasePool();
+ Q_CORE_EXPORT ~QMacAutoReleasePool();
private:
Q_DISABLE_COPY(QMacAutoReleasePool)
void *pool;
@@ -123,10 +127,10 @@ private:
class QMacRootLevelAutoReleasePool
{
public:
- QMacRootLevelAutoReleasePool();
+ Q_NODISCARD_CTOR QMacRootLevelAutoReleasePool();
~QMacRootLevelAutoReleasePool();
private:
- QScopedPointer<QMacAutoReleasePool> pool;
+ std::optional<QMacAutoReleasePool> pool = std::nullopt;
};
#endif
@@ -148,7 +152,7 @@ class QCFType : public QAppleRefCounted<T, CFTypeRef, CFRetain, CFRelease>
using Base = QAppleRefCounted<T, CFTypeRef, CFRetain, CFRelease>;
public:
using Base::Base;
- explicit QCFType(CFTypeRef r) : Base(static_cast<T>(r)) {}
+ Q_NODISCARD_CTOR explicit QCFType(CFTypeRef r) : Base(static_cast<T>(r)) {}
template <typename X> X as() const { return reinterpret_cast<X>(this->value); }
static QCFType constructFromGet(const T &t)
{
@@ -166,15 +170,15 @@ class QIOType : public QAppleRefCounted<T, io_object_t, IOObjectRetain, IOObject
};
#endif
-class Q_CORE_EXPORT QCFString : public QCFType<CFStringRef>
+class QCFString : public QCFType<CFStringRef>
{
public:
using QCFType<CFStringRef>::QCFType;
- inline QCFString(const QString &str) : QCFType<CFStringRef>(0), string(str) {}
- inline QCFString(const CFStringRef cfstr = 0) : QCFType<CFStringRef>(cfstr) {}
- inline QCFString(const QCFType<CFStringRef> &other) : QCFType<CFStringRef>(other) {}
- operator QString() const;
- operator CFStringRef() const;
+ Q_NODISCARD_CTOR QCFString(const QString &str) : QCFType<CFStringRef>(0), string(str) {}
+ Q_NODISCARD_CTOR QCFString(const CFStringRef cfstr = 0) : QCFType<CFStringRef>(cfstr) {}
+ Q_NODISCARD_CTOR QCFString(const QCFType<CFStringRef> &other) : QCFType<CFStringRef>(other) {}
+ Q_CORE_EXPORT operator QString() const;
+ Q_CORE_EXPORT operator CFStringRef() const;
private:
QString string;
@@ -195,15 +199,18 @@ Q_CORE_EXPORT QDebug operator<<(QDebug debug, const QCFString &string);
#endif
Q_CORE_EXPORT bool qt_apple_isApplicationExtension();
+
+#if !defined(QT_BOOTSTRAPPED)
Q_CORE_EXPORT bool qt_apple_isSandboxed();
-#if !defined(QT_BOOTSTRAPPED) && defined(__OBJC__)
+#if defined(__OBJC__)
QT_END_NAMESPACE
-@interface NSObject (QtSandboxHelpers)
+@interface NSObject (QtExtras)
- (id)qt_valueForPrivateKey:(NSString *)key;
@end
QT_BEGIN_NAMESPACE
#endif
+#endif // !QT_BOOTSTRAPPED
#if !defined(QT_BOOTSTRAPPED) && !defined(Q_OS_WATCHOS)
QT_END_NAMESPACE
@@ -230,9 +237,12 @@ QT_BEGIN_NAMESPACE
class Q_CORE_EXPORT AppleUnifiedLogger
{
public:
- static bool messageHandler(QtMsgType msgType, const QMessageLogContext &context, const QString &message,
- const QString &subsystem = QString());
- static bool willMirrorToStderr();
+ static bool messageHandler(QtMsgType msgType, const QMessageLogContext &context,
+ const QString &message)
+ { return messageHandler(msgType, context, message, QString()); }
+ static bool messageHandler(QtMsgType msgType, const QMessageLogContext &context,
+ const QString &message, const QString &subsystem);
+ static bool preventsStderrLogging();
private:
static os_log_type_t logTypeForMessageType(QtMsgType msgType);
static os_log_t cachedLog(const QString &subsystem, const QString &category);
@@ -427,7 +437,7 @@ public:
private:
QMacVersion() = default;
- using VersionTuple = QPair<QOperatingSystemVersion, QOperatingSystemVersion>;
+ using VersionTuple = std::pair<QOperatingSystemVersion, QOperatingSystemVersion>;
static VersionTuple versionsForImage(const mach_header *machHeader);
static VersionTuple applicationVersion();
static VersionTuple libraryVersion();
@@ -435,6 +445,20 @@ private:
// -------------------------------------------------------------------------
+#ifdef __OBJC__
+template <typename T>
+typename std::enable_if<std::is_pointer<T>::value, T>::type
+qt_objc_cast(id object)
+{
+ if ([object isKindOfClass:[typename std::remove_pointer<T>::type class]])
+ return static_cast<T>(object);
+
+ return nil;
+}
+#endif
+
+// -------------------------------------------------------------------------
+
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 570e3056ab..6861251bc2 100644
--- a/src/corelib/kernel/qcore_unix.cpp
+++ b/src/corelib/kernel/qcore_unix.cpp
@@ -3,8 +3,8 @@
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include <QtCore/private/qglobal_p.h>
+#include <QtCore/qbasicatomic.h>
#include "qcore_unix_p.h"
-#include "qelapsedtimer.h"
#include <stdlib.h>
@@ -14,12 +14,27 @@
# include <unistd.h>
#endif
-#ifdef Q_OS_MAC
+#ifdef Q_OS_DARWIN
#include <mach/mach_time.h>
#endif
QT_BEGIN_NAMESPACE
+void qt_ignore_sigpipe() noexcept // noexcept: sigaction(2) is not a Posix Cancellation Point
+{
+ // Set to ignore SIGPIPE once only.
+ Q_CONSTINIT static QBasicAtomicInt atom = Q_BASIC_ATOMIC_INITIALIZER(0);
+ if (!atom.loadRelaxed()) {
+ // More than one thread could turn off SIGPIPE at the same time
+ // But that's acceptable because they all would be doing the same
+ // action
+ struct sigaction noaction = {};
+ noaction.sa_handler = SIG_IGN;
+ ::sigaction(SIGPIPE, &noaction, nullptr);
+ atom.storeRelaxed(1);
+ }
+}
+
QByteArray qt_readlink(const char *path)
{
#ifndef PATH_MAX
@@ -69,23 +84,15 @@ int qt_open64(const char *pathname, int flags, mode_t mode)
# define ppoll pollts
#endif
-static inline bool time_update(struct timespec *tv, const struct timespec &start,
- const struct timespec &timeout)
-{
- // clock source is (hopefully) monotonic, so we can recalculate how much timeout is left;
- // if it isn't monotonic, we'll simply hope that it hasn't jumped, because we have no alternative
- struct timespec now = qt_gettime();
- *tv = timeout + start - now;
- return tv->tv_sec >= 0;
-}
-
-#if QT_CONFIG(poll_poll)
+[[maybe_unused]]
static inline int timespecToMillisecs(const struct timespec *ts)
{
- return (ts == NULL) ? -1 :
- (ts->tv_sec * 1000) + (ts->tv_nsec / 1000000);
+ using namespace std::chrono;
+ if (!ts)
+ return -1;
+ auto ms = ceil<milliseconds>(timespecToChrono<nanoseconds>(*ts));
+ return int(ms.count());
}
-#endif
// defined in qpoll.cpp
int qt_poll(struct pollfd *fds, nfds_t nfds, const struct timespec *timeout_ts);
@@ -111,31 +118,27 @@ static inline int qt_ppoll(struct pollfd *fds, nfds_t nfds, const struct timespe
using select(2) where necessary. In that case, returns -1 and sets errno
to EINVAL if passed any descriptor greater than or equal to FD_SETSIZE.
*/
-int qt_safe_poll(struct pollfd *fds, nfds_t nfds, const struct timespec *timeout_ts)
+int qt_safe_poll(struct pollfd *fds, nfds_t nfds, QDeadlineTimer deadline)
{
- if (!timeout_ts) {
+ if (deadline.isForever()) {
// no timeout -> block forever
int ret;
- EINTR_LOOP(ret, qt_ppoll(fds, nfds, nullptr));
+ QT_EINTR_LOOP(ret, qt_ppoll(fds, nfds, nullptr));
return ret;
}
- timespec start = qt_gettime();
- timespec timeout = *timeout_ts;
-
+ using namespace std::chrono;
+ nanoseconds remaining = deadline.remainingTimeAsDuration();
// loop and recalculate the timeout as needed
- forever {
- const int ret = qt_ppoll(fds, nfds, &timeout);
+ do {
+ timespec ts = durationToTimespec(remaining);
+ const int ret = qt_ppoll(fds, nfds, &ts);
if (ret != -1 || errno != EINTR)
return ret;
+ remaining = deadline.remainingTimeAsDuration();
+ } while (remaining > 0ns);
- // recalculate the timeout
- if (!time_update(&timeout, start, *timeout_ts)) {
- // timeout during update
- // or clock reset, fake timeout error
- return 0;
- }
- }
+ return 0;
}
#endif // QT_BOOTSTRAPPED
diff --git a/src/corelib/kernel/qcore_unix_p.h b/src/corelib/kernel/qcore_unix_p.h
index 9ccc9ca10b..fd834cb2d9 100644
--- a/src/corelib/kernel/qcore_unix_p.h
+++ b/src/corelib/kernel/qcore_unix_p.h
@@ -18,8 +18,8 @@
#include "qplatformdefs.h"
#include <QtCore/private/qglobal_p.h>
-#include "qatomic.h"
#include "qbytearray.h"
+#include "qdeadlinetimer.h"
#ifndef Q_OS_UNIX
# error "qcore_unix_p.h included on a non-Unix system"
@@ -39,14 +39,11 @@
# include <selectLib.h>
#endif
+#include <chrono>
#include <sys/wait.h>
#include <errno.h>
#include <fcntl.h>
-#if !defined(QT_POSIX_IPC) && !defined(QT_NO_SHAREDMEMORY) && !defined(Q_OS_ANDROID)
-# include <sys/ipc.h>
-#endif
-
#if defined(Q_OS_VXWORKS)
# include <ioLib.h>
#endif
@@ -59,7 +56,7 @@
struct sockaddr;
-#define EINTR_LOOP(var, cmd) \
+#define QT_EINTR_LOOP(var, cmd) \
do { \
var = cmd; \
} while (var == -1 && errno == EINTR)
@@ -68,16 +65,41 @@ QT_BEGIN_NAMESPACE
Q_DECLARE_TYPEINFO(pollfd, Q_PRIMITIVE_TYPE);
+static constexpr auto OneSecAsNsecs = std::chrono::nanoseconds(std::chrono::seconds{ 1 }).count();
+
+inline timespec durationToTimespec(std::chrono::nanoseconds timeout) noexcept
+{
+ using namespace std::chrono;
+ const seconds secs = duration_cast<seconds>(timeout);
+ const nanoseconds frac = timeout - secs;
+ struct timespec ts;
+ ts.tv_sec = secs.count();
+ ts.tv_nsec = frac.count();
+ return ts;
+}
+
+template <typename Duration>
+inline Duration timespecToChrono(timespec ts) noexcept
+{
+ using namespace std::chrono;
+ return duration_cast<Duration>(seconds{ts.tv_sec} + nanoseconds{ts.tv_nsec});
+}
+
+inline std::chrono::milliseconds timespecToChronoMs(timespec ts) noexcept
+{
+ return timespecToChrono<std::chrono::milliseconds>(ts);
+}
+
// Internal operator functions for timespecs
constexpr inline timespec &normalizedTimespec(timespec &t)
{
- while (t.tv_nsec >= 1000000000) {
+ while (t.tv_nsec >= OneSecAsNsecs) {
++t.tv_sec;
- t.tv_nsec -= 1000000000;
+ t.tv_nsec -= OneSecAsNsecs;
}
while (t.tv_nsec < 0) {
--t.tv_sec;
- t.tv_nsec += 1000000000;
+ t.tv_nsec += OneSecAsNsecs;
}
return t;
}
@@ -104,7 +126,7 @@ constexpr inline timespec operator-(const timespec &t1, const timespec &t2)
{
timespec tmp = {};
tmp.tv_sec = t1.tv_sec - (t2.tv_sec - 1);
- tmp.tv_nsec = t1.tv_nsec - (t2.tv_nsec + 1000000000);
+ tmp.tv_nsec = t1.tv_nsec - (t2.tv_nsec + OneSecAsNsecs);
return normalizedTimespec(tmp);
}
constexpr inline timespec operator*(const timespec &t1, int mul)
@@ -114,7 +136,7 @@ constexpr inline timespec operator*(const timespec &t1, int mul)
tmp.tv_nsec = t1.tv_nsec * mul;
return normalizedTimespec(tmp);
}
-inline timeval timespecToTimeval(const timespec &ts)
+inline timeval timespecToTimeval(timespec ts)
{
timeval tv;
tv.tv_sec = ts.tv_sec;
@@ -122,23 +144,44 @@ inline timeval timespecToTimeval(const timespec &ts)
return tv;
}
+inline timespec &operator+=(timespec &t1, std::chrono::milliseconds msecs)
+{
+ t1 += durationToTimespec(msecs);
+ return t1;
+}
-inline void qt_ignore_sigpipe()
+inline timespec &operator+=(timespec &t1, int ms)
{
- // Set to ignore SIGPIPE once only.
- Q_CONSTINIT static QBasicAtomicInt atom = Q_BASIC_ATOMIC_INITIALIZER(0);
- if (!atom.loadRelaxed()) {
- // More than one thread could turn off SIGPIPE at the same time
- // But that's acceptable because they all would be doing the same
- // action
- struct sigaction noaction;
- memset(&noaction, 0, sizeof(noaction));
- noaction.sa_handler = SIG_IGN;
- ::sigaction(SIGPIPE, &noaction, nullptr);
- atom.storeRelaxed(1);
+ t1 += std::chrono::milliseconds{ms};
+ return t1;
+}
+
+inline timespec operator+(const timespec &t1, std::chrono::milliseconds msecs)
+{
+ timespec tmp = t1;
+ tmp += msecs;
+ return tmp;
+}
+
+inline timespec operator+(const timespec &t1, int ms)
+{
+ return t1 + std::chrono::milliseconds{ms};
+}
+
+inline timespec qAbsTimespec(timespec ts)
+{
+ if (ts.tv_sec < 0) {
+ ts.tv_sec = -ts.tv_sec - 1;
+ ts.tv_nsec -= OneSecAsNsecs;
+ }
+ if (ts.tv_sec == 0 && ts.tv_nsec < 0) {
+ ts.tv_nsec = -ts.tv_nsec;
}
+ return normalizedTimespec(ts);
}
+Q_CORE_EXPORT void qt_ignore_sigpipe() noexcept;
+
#if defined(Q_PROCESSOR_X86_32) && defined(__GLIBC__)
# if !__GLIBC_PREREQ(2, 22)
Q_CORE_EXPORT int qt_open64(const char *pathname, int flags, mode_t);
@@ -147,6 +190,16 @@ Q_CORE_EXPORT int qt_open64(const char *pathname, int flags, mode_t);
# endif
#endif
+#ifdef AT_FDCWD
+static inline int qt_safe_openat(int dfd, const char *pathname, int flags, mode_t mode = 0777)
+{
+ // everyone already has O_CLOEXEC
+ int fd;
+ QT_EINTR_LOOP(fd, openat(dfd, pathname, flags | O_CLOEXEC, mode));
+ return fd;
+}
+#endif
+
// don't call QT_OPEN or ::open
// call qt_safe_open
static inline int qt_safe_open(const char *pathname, int flags, mode_t mode = 0777)
@@ -155,7 +208,7 @@ static inline int qt_safe_open(const char *pathname, int flags, mode_t mode = 07
flags |= O_CLOEXEC;
#endif
int fd;
- EINTR_LOOP(fd, QT_OPEN(pathname, flags, mode));
+ QT_EINTR_LOOP(fd, QT_OPEN(pathname, flags, mode));
#ifndef O_CLOEXEC
if (fd != -1)
@@ -227,10 +280,10 @@ static inline int qt_safe_dup2(int oldfd, int newfd, int flags = FD_CLOEXEC)
int ret;
#ifdef QT_THREADSAFE_CLOEXEC
// use dup3
- EINTR_LOOP(ret, ::dup3(oldfd, newfd, flags ? O_CLOEXEC : 0));
+ QT_EINTR_LOOP(ret, ::dup3(oldfd, newfd, flags ? O_CLOEXEC : 0));
return ret;
#else
- EINTR_LOOP(ret, ::dup2(oldfd, newfd));
+ QT_EINTR_LOOP(ret, ::dup2(oldfd, newfd));
if (ret == -1)
return -1;
@@ -243,7 +296,7 @@ static inline int qt_safe_dup2(int oldfd, int newfd, int flags = FD_CLOEXEC)
static inline qint64 qt_safe_read(int fd, void *data, qint64 maxlen)
{
qint64 ret = 0;
- EINTR_LOOP(ret, QT_READ(fd, data, maxlen));
+ QT_EINTR_LOOP(ret, QT_READ(fd, data, maxlen));
return ret;
}
#undef QT_READ
@@ -252,7 +305,7 @@ static inline qint64 qt_safe_read(int fd, void *data, qint64 maxlen)
static inline qint64 qt_safe_write(int fd, const void *data, qint64 len)
{
qint64 ret = 0;
- EINTR_LOOP(ret, QT_WRITE(fd, data, len));
+ QT_EINTR_LOOP(ret, QT_WRITE(fd, data, len));
return ret;
}
#undef QT_WRITE
@@ -267,7 +320,7 @@ static inline qint64 qt_safe_write_nosignal(int fd, const void *data, qint64 len
static inline int qt_safe_close(int fd)
{
int ret;
- EINTR_LOOP(ret, QT_CLOSE(fd));
+ QT_EINTR_LOOP(ret, QT_CLOSE(fd));
return ret;
}
#undef QT_CLOSE
@@ -279,28 +332,28 @@ static inline int qt_safe_execve(const char *filename, char *const argv[],
char *const envp[])
{
int ret;
- EINTR_LOOP(ret, ::execve(filename, argv, envp));
+ QT_EINTR_LOOP(ret, ::execve(filename, argv, envp));
return ret;
}
static inline int qt_safe_execv(const char *path, char *const argv[])
{
int ret;
- EINTR_LOOP(ret, ::execv(path, argv));
+ QT_EINTR_LOOP(ret, ::execv(path, argv));
return ret;
}
static inline int qt_safe_execvp(const char *file, char *const argv[])
{
int ret;
- EINTR_LOOP(ret, ::execvp(file, argv));
+ QT_EINTR_LOOP(ret, ::execvp(file, argv));
return ret;
}
static inline pid_t qt_safe_waitpid(pid_t pid, int *status, int options)
{
int ret;
- EINTR_LOOP(ret, ::waitpid(pid, status, options));
+ QT_EINTR_LOOP(ret, ::waitpid(pid, status, options));
return ret;
}
#endif // QT_CONFIG(process)
@@ -309,9 +362,6 @@ static inline pid_t qt_safe_waitpid(pid_t pid, int *status, int options)
# define _POSIX_MONOTONIC_CLOCK -1
#endif
-// in qelapsedtimer_mac.cpp or qtimestamp_unix.cpp
-timespec qt_gettime() noexcept;
-void qt_nanosleep(timespec amount);
QByteArray qt_readlink(const char *path);
/* non-static */
@@ -329,20 +379,7 @@ inline bool qt_haveLinuxProcfs()
#endif
}
-Q_CORE_EXPORT int qt_safe_poll(struct pollfd *fds, nfds_t nfds, const struct timespec *timeout_ts);
-
-static inline int qt_poll_msecs(struct pollfd *fds, nfds_t nfds, int timeout)
-{
- timespec ts, *pts = nullptr;
-
- if (timeout >= 0) {
- ts.tv_sec = timeout / 1000;
- ts.tv_nsec = (timeout % 1000) * 1000 * 1000;
- pts = &ts;
- }
-
- return qt_safe_poll(fds, nfds, pts);
-}
+Q_CORE_EXPORT int qt_safe_poll(struct pollfd *fds, nfds_t nfds, QDeadlineTimer deadline);
static inline struct pollfd qt_make_pollfd(int fd, short events)
{
diff --git a/src/corelib/kernel/qcore_wasm.cpp b/src/corelib/kernel/qcore_wasm.cpp
index 5c8fac59b0..fb12ae50c3 100644
--- a/src/corelib/kernel/qcore_wasm.cpp
+++ b/src/corelib/kernel/qcore_wasm.cpp
@@ -6,7 +6,7 @@
#include <emscripten/val.h>
#if !defined(Q_OS_WASM)
-static_assert(false, "This is a wasm-only file.");
+#error This is a wasm-only file.
#endif // !defined(Q_OS_WASM)
QT_BEGIN_NAMESPACE
@@ -42,4 +42,56 @@ emscripten::val QRectF::toDOMRect() const
return emscripten::val::global("DOMRect").new_(left(), top(), width(), height());
}
+/*!
+ Converts the \l {https://262.ecma-international.org/#sec-string-object}{ECMAScript string} \a
+ jsString to QString. Behavior is undefined if the provided parameter is not a string.
+
+ \since 6.6
+ \ingroup platform-type-conversions
+
+ \sa toEcmaString()
+*/
+QString QString::fromEcmaString(emscripten::val jsString)
+{
+ Q_ASSERT_X(jsString.isString(), Q_FUNC_INFO, "Passed object is not a string");
+
+ const double length = jsString["length"].as<double>();
+
+ Q_ASSERT_X((double(uint64_t(length)) != double(uint64_t(length) - 1)
+ && double(uint64_t(length)) != double(uint64_t(length) + 1))
+ || !std::numeric_limits<double>::is_iec559,
+ Q_FUNC_INFO, "The floating-point length cannot precisely represent an integer");
+
+ constexpr int zeroTerminatorLength = 1;
+ const auto lengthOfUtf16 = (length + zeroTerminatorLength) * 2;
+
+ Q_ASSERT_X((double(uint64_t(lengthOfUtf16)) != double(uint64_t(lengthOfUtf16) - 1)
+ && double(uint64_t(lengthOfUtf16)) != double(uint64_t(lengthOfUtf16) + 1))
+ || !std::numeric_limits<double>::is_iec559,
+ Q_FUNC_INFO,
+ "The floating-point lengthOfUtf16 cannot precisely represent an integer");
+
+ const QString result(uint64_t(length), Qt::Uninitialized);
+
+ static const emscripten::val stringToUTF16(emscripten::val::module_property("stringToUTF16"));
+ stringToUTF16(jsString, emscripten::val(quintptr(result.data())),
+ emscripten::val(lengthOfUtf16));
+ return result;
+}
+
+/*!
+ Converts this object to an
+ \l {https://262.ecma-international.org/#sec-string-object}{ECMAScript string}.
+
+ \since 6.6
+ \ingroup platform-type-conversions
+
+ \sa fromEcmaString()
+*/
+emscripten::val QString::toEcmaString() const
+{
+ static const emscripten::val UTF16ToString(emscripten::val::module_property("UTF16ToString"));
+ return UTF16ToString(emscripten::val(quintptr(utf16())));
+}
+
QT_END_NAMESPACE
diff --git a/src/corelib/kernel/qcoreapplication.cpp b/src/corelib/kernel/qcoreapplication.cpp
index 04c7474803..a494369c5d 100644
--- a/src/corelib/kernel/qcoreapplication.cpp
+++ b/src/corelib/kernel/qcoreapplication.cpp
@@ -8,6 +8,7 @@
#ifndef QT_NO_QOBJECT
#include "qabstracteventdispatcher.h"
#include "qcoreevent.h"
+#include "qcoreevent_p.h"
#include "qeventloop.h"
#endif
#include "qmetaobject.h"
@@ -31,10 +32,11 @@
#include <private/qthread_p.h>
#if QT_CONFIG(thread)
#include <qthreadpool.h>
+#include <private/qthreadpool_p.h>
#endif
#endif
-#include <qelapsedtimer.h>
#include <qlibraryinfo.h>
+#include <qpointer.h>
#include <qvarlengtharray.h>
#include <private/qfactoryloader_p.h>
#include <private/qfunctions_p.h>
@@ -55,7 +57,9 @@
# include "qeventdispatcher_glib_p.h"
# endif
# endif
-# include "qeventdispatcher_unix_p.h"
+# if !defined(Q_OS_WASM)
+# include "qeventdispatcher_unix_p.h"
+# endif
#endif
#ifdef Q_OS_WIN
#include "qeventdispatcher_win_p.h"
@@ -66,7 +70,7 @@
#include <QtCore/qjniobject.h>
#endif
-#ifdef Q_OS_MAC
+#ifdef Q_OS_DARWIN
# include "qcore_mac_p.h"
#endif
@@ -103,12 +107,30 @@
#include <algorithm>
#include <memory>
+#include <string>
QT_BEGIN_NAMESPACE
+#ifndef QT_NO_QOBJECT
+Q_LOGGING_CATEGORY(lcDeleteLater, "qt.core.qobject.deletelater")
+#endif
+
using namespace Qt::StringLiterals;
-#if defined(Q_OS_WIN) || defined(Q_OS_MAC)
+Q_TRACE_PREFIX(qtcore,
+ "#include <qcoreevent.h>"
+);
+Q_TRACE_METADATA(qtcore, "ENUM { AUTO, RANGE User ... MaxUser } QEvent::Type;");
+Q_TRACE_POINT(qtcore, QCoreApplication_postEvent_entry, QObject *receiver, QEvent *event, QEvent::Type type);
+Q_TRACE_POINT(qtcore, QCoreApplication_postEvent_exit);
+Q_TRACE_POINT(qtcore, QCoreApplication_postEvent_event_compressed, QObject *receiver, QEvent *event);
+Q_TRACE_POINT(qtcore, QCoreApplication_postEvent_event_posted, QObject *receiver, QEvent *event, QEvent::Type type);
+Q_TRACE_POINT(qtcore, QCoreApplication_sendEvent, QObject *receiver, QEvent *event, QEvent::Type type);
+Q_TRACE_POINT(qtcore, QCoreApplication_sendSpontaneousEvent, QObject *receiver, QEvent *event, QEvent::Type type);
+Q_TRACE_POINT(qtcore, QCoreApplication_notify_entry, QObject *receiver, QEvent *event, QEvent::Type type);
+Q_TRACE_POINT(qtcore, QCoreApplication_notify_exit, bool consumed, bool filtered);
+
+#if defined(Q_OS_WIN) || defined(Q_OS_DARWIN)
extern QString qAppFileName();
#endif
@@ -219,7 +241,11 @@ void QCoreApplicationPrivate::processCommandLineArguments()
// Support for introspection
-extern "C" void Q_DECL_EXPORT_OVERRIDABLE qt_startup_hook()
+extern "C" void
+#ifdef QT_SHARED
+Q_DECL_EXPORT_OVERRIDABLE
+#endif
+qt_startup_hook()
{
}
@@ -279,15 +305,15 @@ static void qt_call_pre_routines()
if (!preRList.exists())
return;
- QVFuncList list;
- {
+ const QStartUpFuncList list = [] {
const auto locker = qt_scoped_lock(globalRoutinesMutex);
// 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 (QtCleanUpFunction f : std::as_const(list))
+ return *preRList;
+ }();
+
+ for (QtStartUpFunction f : list)
f();
}
@@ -451,6 +477,8 @@ QCoreApplicationPrivate::~QCoreApplicationPrivate()
#endif
#if defined(Q_OS_WIN)
delete [] origArgv;
+ if (consoleAllocated)
+ FreeConsole();
#endif
QCoreApplicationPrivate::clearApplicationFilePath();
}
@@ -497,6 +525,7 @@ void QCoreApplicationPrivate::eventDispatcherReady()
}
Q_CONSTINIT QBasicAtomicPointer<QThread> QCoreApplicationPrivate::theMainThread = Q_BASIC_ATOMIC_INITIALIZER(nullptr);
+Q_CONSTINIT QBasicAtomicPointer<void> QCoreApplicationPrivate::theMainThreadId = Q_BASIC_ATOMIC_INITIALIZER(nullptr);
QThread *QCoreApplicationPrivate::mainThread()
{
Q_ASSERT(theMainThread.loadRelaxed() != nullptr);
@@ -549,26 +578,86 @@ QString qAppName()
return QCoreApplication::instance()->d_func()->appName();
}
+void QCoreApplicationPrivate::initConsole()
+{
+#ifdef Q_OS_WINDOWS
+ const QString env = qEnvironmentVariable("QT_WIN_DEBUG_CONSOLE");
+ if (env.isEmpty())
+ return;
+ if (env.compare(u"new"_s, Qt::CaseInsensitive) == 0) {
+ if (AllocConsole() == FALSE)
+ return;
+ consoleAllocated = true;
+ } else if (env.compare(u"attach"_s, Qt::CaseInsensitive) == 0) {
+ // If the calling process is already attached to a console,
+ // the error code returned is ERROR_ACCESS_DENIED.
+ if (!::AttachConsole(ATTACH_PARENT_PROCESS) && ::GetLastError() != ERROR_ACCESS_DENIED)
+ return;
+ } else {
+ // Unknown input, don't make any decision for the user.
+ return;
+ }
+ // The std{in,out,err} handles are read-only, so we need to pass in dummies.
+ FILE *in = nullptr;
+ FILE *out = nullptr;
+ FILE *err = nullptr;
+ freopen_s(&in, "CONIN$", "r", stdin);
+ freopen_s(&out, "CONOUT$", "w", stdout);
+ freopen_s(&err, "CONOUT$", "w", stderr);
+ // However, things wouldn't work if the runtime did not preserve the pointers.
+ Q_ASSERT(in == stdin);
+ Q_ASSERT(out == stdout);
+ Q_ASSERT(err == stderr);
+#endif
+}
+
void QCoreApplicationPrivate::initLocale()
{
-#if defined(Q_OS_UNIX) && !defined(QT_BOOTSTRAPPED)
+#if defined(QT_BOOTSTRAPPED)
+ // Don't try to control bootstrap library locale or encoding.
+#elif defined(Q_OS_UNIX)
Q_CONSTINIT static bool qt_locale_initialized = false;
if (qt_locale_initialized)
return;
qt_locale_initialized = true;
-#ifdef Q_OS_INTEGRITY
+ // By default the portable "C"/POSIX locale is selected and active.
+ // Apply the locale from the environment, via setlocale(), which will
+ // read LC_ALL, LC_<category>, and LANG, in order (for each category).
+ setlocale(LC_ALL, "");
+
+ // Next, let's ensure that LC_CTYPE is UTF-8, since QStringConverter's
+ // QLocal8Bit hard-codes this, and we need to be consistent.
+# if defined(Q_OS_INTEGRITY)
setlocale(LC_CTYPE, "UTF-8");
-#else
- // Android's Bionic didn't get nl_langinfo until NDK 15 (Android 8.0),
- // which is too new for Qt, so we just assume it's always UTF-8.
- auto nl_langinfo = [](int) { return "UTF-8"; };
-
- const char *locale = setlocale(LC_ALL, "");
- const char *codec = nl_langinfo(CODESET);
- if (Q_UNLIKELY(strcmp(codec, "UTF-8") != 0 && strcmp(codec, "utf8") != 0)) {
- QByteArray oldLocale = locale;
- QByteArray newLocale = setlocale(LC_CTYPE, nullptr);
+# elif defined(Q_OS_QNX)
+ // QNX has no nl_langinfo, so we can't check.
+ // FIXME: Shouldn't we still setlocale("UTF-8")?
+# elif defined(Q_OS_ANDROID) && __ANDROID_API__ < __ANDROID_API_O__
+ // Android 6 still lacks nl_langinfo(), so we can't check.
+ // FIXME: Shouldn't we still setlocale("UTF-8")?
+# elif defined(Q_OS_VXWORKS)
+ // VxWorks has no nl_langinfo, so we can't check.
+# else
+ // std::string's SSO usually saves this the need to allocate:
+ const std::string oldEncoding = nl_langinfo(CODESET);
+ if (!Q_LIKELY(qstricmp(oldEncoding.data(), "UTF-8") == 0
+ || qstricmp(oldEncoding.data(), "utf8") == 0)) {
+ const QByteArray oldLocale = setlocale(LC_ALL, nullptr);
+ QByteArray newLocale;
+ bool warnOnOverride = true;
+# if defined(Q_OS_DARWIN)
+ // Don't warn unless the char encoding has been changed from the
+ // default "C" encoding, or the user touched any of the locale
+ // environment variables to force the "C" char encoding.
+ warnOnOverride = qstrcmp(setlocale(LC_CTYPE, nullptr), "C") != 0
+ || getenv("LC_ALL") || getenv("LC_CTYPE") || getenv("LANG");
+
+ // No need to try language or region specific CTYPEs, as they
+ // all point back to the same generic UTF-8 CTYPE.
+ newLocale = setlocale(LC_CTYPE, "UTF-8");
+# else
+ newLocale = setlocale(LC_CTYPE, nullptr);
if (qsizetype dot = newLocale.indexOf('.'); dot != -1)
newLocale.truncate(dot); // remove encoding, if any
if (qsizetype at = newLocale.indexOf('@'); at != -1)
@@ -576,23 +665,30 @@ void QCoreApplicationPrivate::initLocale()
newLocale += ".UTF-8";
newLocale = setlocale(LC_CTYPE, newLocale);
- // if locale doesn't exist, try some fallbacks
-# ifdef Q_OS_DARWIN
- if (newLocale.isEmpty())
- newLocale = setlocale(LC_CTYPE, "UTF-8");
-# endif
+ // If that locale doesn't exist, try some fallbacks:
if (newLocale.isEmpty())
newLocale = setlocale(LC_CTYPE, "C.UTF-8");
if (newLocale.isEmpty())
newLocale = setlocale(LC_CTYPE, "C.utf8");
-
- qWarning("Detected system locale encoding (%s, locale \"%s\") is not UTF-8.\n"
- "Qt shall use a UTF-8 locale (\"%s\") instead. If this causes problems,\n"
- "reconfigure your locale. See the locale(1) manual for more information.",
- codec, oldLocale.constData(), newLocale.constData());
+# endif
+
+ if (newLocale.isEmpty()) {
+ // Failed to set a UTF-8 locale.
+ qWarning("Detected locale \"%s\" with character encoding \"%s\", which is not UTF-8.\n"
+ "Qt depends on a UTF-8 locale, but has failed to switch to one.\n"
+ "If this causes problems, reconfigure your locale. See the locale(1) manual\n"
+ "for more information.", oldLocale.constData(), oldEncoding.data());
+ } else if (warnOnOverride) {
+ // Let the user know we over-rode their configuration.
+ qWarning("Detected locale \"%s\" with character encoding \"%s\", which is not UTF-8.\n"
+ "Qt depends on a UTF-8 locale, and has switched to \"%s\" instead.\n"
+ "If this causes problems, reconfigure your locale. See the locale(1) manual\n"
+ "for more information.",
+ oldLocale.constData(), oldEncoding.data(), newLocale.constData());
+ }
}
-#endif
-#endif
+# endif // Platform choice
+#endif // Unix
}
@@ -668,7 +764,8 @@ void QCoreApplicationPrivate::initLocale()
to reset the locale that is used for number formatting to "C"-locale.
\sa QGuiApplication, QAbstractEventDispatcher, QEventLoop,
- {Semaphores Example}, {Wait Conditions Example}
+ {Producer and Consumer using Semaphores},
+ {Producer and Consumer using Wait Conditions}
*/
/*!
@@ -734,7 +831,7 @@ QCoreApplication::QCoreApplication(int &argc, char **argv
\value ApplicationFlags QT_VERSION
*/
-void QCoreApplicationPrivate::init()
+void Q_TRACE_INSTRUMENT(qtcore) QCoreApplicationPrivate::init()
{
Q_TRACE_SCOPE(QCoreApplicationPrivate_init);
@@ -744,6 +841,8 @@ void QCoreApplicationPrivate::init()
Q_Q(QCoreApplication);
+ initConsole();
+
initLocale();
Q_ASSERT_X(!QCoreApplication::self, "QCoreApplication", "there should be only one application object");
@@ -857,8 +956,10 @@ QCoreApplication::~QCoreApplication()
#if QT_CONFIG(thread)
// Synchronize and stop the global thread pool threads.
QThreadPool *globalThreadPool = nullptr;
+ QThreadPool *guiThreadPool = nullptr;
QT_TRY {
globalThreadPool = QThreadPool::globalInstance();
+ guiThreadPool = QThreadPoolPrivate::qtGuiInstance();
} QT_CATCH (...) {
// swallow the exception, since destructors shouldn't throw
}
@@ -866,6 +967,10 @@ QCoreApplication::~QCoreApplication()
globalThreadPool->waitForDone();
delete globalThreadPool;
}
+ if (guiThreadPool) {
+ guiThreadPool->waitForDone();
+ delete guiThreadPool;
+ }
#endif
#ifndef QT_NO_QOBJECT
@@ -899,7 +1004,10 @@ QCoreApplication::~QCoreApplication()
and must be set before a QCoreApplication instance is created.
\note It is strongly recommended not to enable this option since
- it introduces security risks.
+ it introduces security risks. If this application does enable the flag and
+ starts child processes, it should drop the privileges as early as possible
+ by calling \c{setuid(2)} for itself, or at the latest by using the
+ QProcess::UnixProcessParameters::ResetIds flag.
*/
void QCoreApplication::setSetuidAllowed(bool allow)
{
@@ -919,7 +1027,6 @@ bool QCoreApplication::isSetuidAllowed()
return QCoreApplicationPrivate::setuidAllowed;
}
-
/*!
Sets the attribute \a attribute if \a on is true;
otherwise clears the attribute.
@@ -932,6 +1039,10 @@ bool QCoreApplication::isSetuidAllowed()
*/
void QCoreApplication::setAttribute(Qt::ApplicationAttribute attribute, bool on)
{
+ // Since we bit-shift these values, we can't go higher than 32 on 32 bit operating systems
+ // without changing the storage type of QCoreApplicationPrivate::attribs to quint64.
+ static_assert(Qt::AA_AttributeCount <= sizeof(QCoreApplicationPrivate::attribs) * CHAR_BIT);
+
if (on)
QCoreApplicationPrivate::attribs |= 1 << attribute;
else
@@ -980,6 +1091,14 @@ bool QCoreApplication::testAttribute(Qt::ApplicationAttribute attribute)
\brief Whether the use of the QEventLoopLocker feature can cause the
application to quit.
+ When this property is \c true the release of the last remaining
+ QEventLoopLocker operating on the application will attempt to
+ quit the application.
+
+ Note that attempting a quit may not necessarily result in the
+ application quitting, for example if there still are open windows,
+ or the QEvent::Quit event is ignored.
+
The default is \c true.
\sa QEventLoopLocker
@@ -1007,7 +1126,7 @@ void QCoreApplication::setQuitLockEnabled(bool enabled)
bool QCoreApplication::notifyInternal2(QObject *receiver, QEvent *event)
{
bool selfRequired = QCoreApplicationPrivate::threadRequiresCoreApplication();
- if (!self && selfRequired)
+ if (selfRequired && !self)
return false;
// Make it possible for Qt Script to hook into events even
@@ -1027,6 +1146,11 @@ bool QCoreApplication::notifyInternal2(QObject *receiver, QEvent *event)
QScopedScopeLevelCounter scopeLevelCounter(threadData);
if (!selfRequired)
return doNotify(receiver, event);
+
+#if QT_VERSION >= QT_VERSION_CHECK(7, 0, 0)
+ if (threadData->thread.loadRelaxed() != QCoreApplicationPrivate::mainThread())
+ return false;
+#endif
return self->notify(receiver, event);
}
@@ -1085,7 +1209,7 @@ bool QCoreApplication::forwardEvent(QObject *receiver, QEvent *event, QEvent *or
\endlist
\b{Future direction:} This function will not be called for objects that live
- outside the main thread in Qt 6. Applications that need that functionality
+ outside the main thread in Qt 7. Applications that need that functionality
should find other solutions for their event inspection needs in the meantime.
The change may be extended to the main thread, causing this function to be
deprecated.
@@ -1103,6 +1227,11 @@ bool QCoreApplication::notify(QObject *receiver, QEvent *event)
Q_ASSERT(receiver);
Q_ASSERT(event);
+#if QT_VERSION >= QT_VERSION_CHECK(7, 0, 0)
+ Q_ASSERT(receiver->d_func()->threadData.loadAcquire()->thread.loadRelaxed()
+ == QCoreApplicationPrivate::mainThread());
+#endif
+
// no events are delivered after ~QCoreApplication() has started
if (QCoreApplicationPrivate::is_app_closing)
return true;
@@ -1150,7 +1279,9 @@ bool QCoreApplicationPrivate::sendThroughApplicationEventFilters(QObject *receiv
bool QCoreApplicationPrivate::sendThroughObjectEventFilters(QObject *receiver, QEvent *event)
{
- if (receiver != QCoreApplication::instance() && receiver->d_func()->extraData) {
+ if ((receiver->d_func()->threadData.loadRelaxed()->thread.loadAcquire() != mainThread()
+ || receiver != QCoreApplication::instance())
+ && receiver->d_func()->extraData) {
for (qsizetype i = 0; i < receiver->d_func()->extraData->eventFilters.size(); ++i) {
QObject *obj = receiver->d_func()->extraData->eventFilters.at(i);
if (!obj)
@@ -1181,8 +1312,8 @@ bool QCoreApplicationPrivate::notify_helper(QObject *receiver, QEvent * event)
Q_TRACE_EXIT(QCoreApplication_notify_exit, consumed, filtered);
// send to all application event filters (only does anything in the main thread)
- if (QCoreApplication::self
- && receiver->d_func()->threadData.loadRelaxed()->thread.loadAcquire() == mainThread()
+ if (receiver->d_func()->threadData.loadRelaxed()->thread.loadAcquire() == mainThread()
+ && QCoreApplication::self
&& QCoreApplication::self->d_func()->sendThroughApplicationEventFilters(receiver, event)) {
filtered = true;
return filtered;
@@ -1249,7 +1380,8 @@ bool QCoreApplication::closingDown()
\threadsafe
- \sa exec(), QTimer, QEventLoop::processEvents(), sendPostedEvents()
+ \sa exec(), QTimer, QChronoTimer, QEventLoop::processEvents(),
+ sendPostedEvents()
*/
void QCoreApplication::processEvents(QEventLoop::ProcessEventsFlags flags)
{
@@ -1260,12 +1392,29 @@ void QCoreApplication::processEvents(QEventLoop::ProcessEventsFlags flags)
}
/*!
- \overload processEvents()
+ \overload
Processes pending events for the calling thread for \a ms
milliseconds or until there are no more events to process,
whichever is shorter.
+ This is equivalent to calling:
+ \code
+ QCoreApplication::processEvents(flags, QDeadlineTimer(ms));
+ \endcode
+*/
+void QCoreApplication::processEvents(QEventLoop::ProcessEventsFlags flags, int ms)
+{
+ QCoreApplication::processEvents(flags, QDeadlineTimer(ms));
+}
+
+/*!
+ \since 6.7
+ \overload
+
+ Processes pending events for the calling thread untile \a deadline has expired,
+ or until there are no more events to process, whichever happens first.
+
Use of this function is discouraged. Instead, prefer to move long
operations out of the GUI thread into an auxiliary one and to completely
avoid nested event loop processing. If event processing is really
@@ -1281,9 +1430,9 @@ void QCoreApplication::processEvents(QEventLoop::ProcessEventsFlags flags)
\threadsafe
- \sa exec(), QTimer, QEventLoop::processEvents()
+ \sa exec(), QTimer, QChronoTimer, QEventLoop::processEvents()
*/
-void QCoreApplication::processEvents(QEventLoop::ProcessEventsFlags flags, int ms)
+void QCoreApplication::processEvents(QEventLoop::ProcessEventsFlags flags, QDeadlineTimer deadline)
{
// ### TODO: consider splitting this method into a public and a private
// one, so that a user-invoked processEvents can be detected
@@ -1291,10 +1440,9 @@ void QCoreApplication::processEvents(QEventLoop::ProcessEventsFlags flags, int m
QThreadData *data = QThreadData::current();
if (!data->hasEventDispatcher())
return;
- QElapsedTimer start;
- start.start();
+
while (data->eventDispatcher.loadRelaxed()->processEvents(flags & ~QEventLoop::WaitForMoreEvents)) {
- if (start.elapsed() > ms)
+ if (deadline.hasExpired())
break;
}
}
@@ -1312,10 +1460,10 @@ void QCoreApplication::processEvents(QEventLoop::ProcessEventsFlags flags, int m
main event loop receives events from the window system and
dispatches these to the application widgets.
- To make your application perform idle processing (by executing a
- special function whenever there are no pending events), use a
- QTimer with 0 timeout. More advanced idle processing schemes can
- be achieved using processEvents().
+ To make your application perform idle processing (by executing a special
+ function whenever there are no pending events), use a QChronoTimer
+ with 0ns timeout. More advanced idle processing schemes can be achieved
+ using processEvents().
We recommend that you connect clean-up code to the
\l{QCoreApplication::}{aboutToQuit()} signal, instead of putting it in
@@ -1367,6 +1515,8 @@ void QCoreApplicationPrivate::execCleanup()
{
threadData.loadRelaxed()->quitNow = false;
in_exec = false;
+
+ qCDebug(lcDeleteLater) << "Sending deferred delete events as part of exec cleanup";
QCoreApplication::sendPostedEvents(nullptr, QEvent::DeferredDelete);
}
@@ -1550,31 +1700,6 @@ void QCoreApplication::postEvent(QObject *receiver, QEvent *event, int priority)
return;
}
- if (event->type() == QEvent::DeferredDelete)
- receiver->d_ptr->deleteLaterCalled = true;
-
- if (event->type() == QEvent::DeferredDelete && data == QThreadData::current()) {
- // remember the current running eventloop for DeferredDelete
- // events posted in the receiver's thread.
-
- // Events sent by non-Qt event handlers (such as glib) may not
- // have the scopeLevel set correctly. The scope level makes sure that
- // code like this:
- // foo->deleteLater();
- // qApp->processEvents(); // without passing QEvent::DeferredDelete
- // will not cause "foo" to be deleted before returning to the event loop.
-
- // If the scope level is 0 while loopLevel != 0, we are called from a
- // non-conformant code path, and our best guess is that the scope level
- // should be 1. (Loop level 0 is special: it means that no event loops
- // are running.)
- int loopLevel = data->loopLevel;
- int scopeLevel = data->scopeLevel;
- if (scopeLevel == 0 && loopLevel != 0)
- scopeLevel = 1;
- static_cast<QDeferredDeleteEvent *>(event)->level = loopLevel + scopeLevel;
- }
-
// delete the event on exceptions to protect against memory leaks till the event is
// properly owned in the postEventList
std::unique_ptr<QEvent> eventDeleter(event);
@@ -1601,33 +1726,26 @@ bool QCoreApplication::compressEvent(QEvent *event, QObject *receiver, QPostEven
Q_ASSERT(receiver);
Q_ASSERT(postedEvents);
-#ifdef Q_OS_WIN
+ int receiverPostedEvents = receiver->d_func()->postedEvents.loadRelaxed();
// compress posted timers to this object.
- if (event->type() == QEvent::Timer && receiver->d_func()->postedEvents > 0) {
- int timerId = ((QTimerEvent *) event)->timerId();
- for (const QPostEvent &e : std::as_const(*postedEvents)) {
- if (e.receiver == receiver && e.event && e.event->type() == QEvent::Timer
- && ((QTimerEvent *) e.event)->timerId() == timerId) {
+ if (event->type() == QEvent::Timer && receiverPostedEvents > 0) {
+ int timerId = static_cast<QTimerEvent *>(event)->timerId();
+ auto sameReceiver = [receiver](const QPostEvent &e) { return e.receiver == receiver; };
+ auto it = std::find_if(postedEvents->cbegin(), postedEvents->cend(), sameReceiver);
+ while (receiverPostedEvents > 0 && it != postedEvents->cend()) {
+ if (it->event && it->event->type() == QEvent::Timer
+ && static_cast<QTimerEvent *>(it->event)->timerId() == timerId) {
delete event;
return true;
}
- }
- return false;
- }
-#endif
- if (event->type() == QEvent::DeferredDelete) {
- if (receiver->d_ptr->deleteLaterCalled) {
- // there was a previous DeferredDelete event, so we can drop the new one
- delete event;
- return true;
+ if (--receiverPostedEvents)
+ it = std::find_if(it + 1, postedEvents->cend(), sameReceiver);
}
- // deleteLaterCalled is set to true in postedEvents when queueing the very first
- // deferred deletion event.
return false;
}
- if (event->type() == QEvent::Quit && receiver->d_func()->postedEvents > 0) {
+ if (event->type() == QEvent::Quit && receiverPostedEvents > 0) {
for (const QPostEvent &cur : std::as_const(*postedEvents)) {
if (cur.receiver != receiver
|| cur.event == nullptr
@@ -1761,14 +1879,37 @@ void QCoreApplicationPrivate::sendPostedEvents(QObject *receiver, int event_type
// events posted by the current event loop; or
// 3) if the event was posted before the outermost event loop.
- int eventLevel = static_cast<QDeferredDeleteEvent *>(pe.event)->loopLevel();
- int loopLevel = data->loopLevel + data->scopeLevel;
- const bool allowDeferredDelete =
- (eventLevel > loopLevel
- || (!eventLevel && loopLevel > 0)
- || (event_type == QEvent::DeferredDelete
- && eventLevel == loopLevel));
+ const auto *event = static_cast<QDeferredDeleteEvent *>(pe.event);
+ qCDebug(lcDeleteLater) << "Processing deferred delete event for" << pe.receiver
+ << "with loop level" << event->loopLevel() << "and scope level" << event->scopeLevel();
+
+ qCDebug(lcDeleteLater) << "Checking" << data->thread << "with loop level"
+ << data->loopLevel << "and scope level" << data->scopeLevel;
+
+ bool allowDeferredDelete = false;
+ if (event->loopLevel() == 0 && data->loopLevel > 0) {
+ qCDebug(lcDeleteLater) << "Event was posted outside outermost event loop"
+ << "and current thread has an event loop running.";
+ allowDeferredDelete = true;
+ } else {
+ const int totalEventLevel = event->loopLevel() + event->scopeLevel();
+ const int totalThreadLevel = data->loopLevel + data->scopeLevel;
+
+ if (totalEventLevel > totalThreadLevel) {
+ qCDebug(lcDeleteLater) << "Combined levels of event" << totalEventLevel
+ << "is higher than thread" << totalThreadLevel;
+ allowDeferredDelete = true;
+ } else if (event_type == QEvent::DeferredDelete && totalEventLevel == totalThreadLevel) {
+ qCDebug(lcDeleteLater) << "Explicit send of DeferredDelete and"
+ << "levels of event" << totalEventLevel
+ << "is same as thread" << totalThreadLevel;
+ allowDeferredDelete = true;
+ }
+ }
+
if (!allowDeferredDelete) {
+ qCDebug(lcDeleteLater) << "Failed conditions for deferred delete. Deferring again";
+
// cannot send deferred delete
if (!event_type && !receiver) {
// we must copy it first; we want to re-post the event
@@ -1785,6 +1926,8 @@ void QCoreApplicationPrivate::sendPostedEvents(QObject *receiver, int event_type
data->postEventList.addEvent(pe_copy);
}
continue;
+ } else {
+ qCDebug(lcDeleteLater) << "Sending deferred delete to" << pe.receiver;
}
}
@@ -1959,7 +2102,13 @@ bool QCoreApplicationPrivate::canQuitAutomatically()
if (!in_exec)
return false;
- if (quitLockEnabled && quitLockRef.loadRelaxed())
+ // The automatic quit functionality is triggered by
+ // both QEventLoopLocker and maybeLastWindowClosed.
+ // In either case, we don't want to quit if there
+ // are active QEventLoopLockers, even if quitLockEnabled
+ // is not enabled, as the property signals whether to
+ // trigger the automatic quit, not whether to block it.
+ if (quitLockRef.loadRelaxed())
return false;
return true;
@@ -2047,6 +2196,12 @@ void QCoreApplicationPrivate::quit()
last-second cleanup. Note that no user interaction is possible in
this state.
+ \note At this point the main event loop is still running, but will
+ not process further events on return except QEvent::DeferredDelete
+ events for objects deleted via deleteLater(). If event processing is
+ needed, use a nested event loop or call QCoreApplication::processEvents()
+ manually.
+
\sa quit()
*/
@@ -2070,14 +2225,15 @@ void QCoreApplicationPrivate::quit()
to all toplevel widgets, where a reimplementation of changeEvent can
re-translate the user interface by passing user-visible strings via the
tr() function to the respective property setters. User-interface classes
- generated by Qt Designer provide a \c retranslateUi() function that can be
+ generated by \QD provide a \c retranslateUi() function that can be
called.
The function returns \c true on success and false on failure.
\note QCoreApplication does \e not take ownership of \a translationFile.
- \sa removeTranslator(), translate(), QTranslator::load(), {Dynamic Translation}
+ \sa removeTranslator(), translate(), QTranslator::load(),
+ {Writing Source Code for Translation#Prepare for Dynamic Language Changes}{Prepare for Dynamic Language Changes}
*/
bool QCoreApplication::installTranslator(QTranslator *translationFile)
@@ -2195,7 +2351,8 @@ static void replacePercentN(QString *result, int n)
This function is not virtual. You can use alternative translation
techniques by subclassing \l QTranslator.
- \sa QObject::tr(), installTranslator(), removeTranslator(), translate()
+ \sa QObject::tr(), installTranslator(), removeTranslator(),
+ {Internationalization and Translations}
*/
QString QCoreApplication::translate(const char *context, const char *sourceText,
const char *disambiguation, int n)
@@ -2353,10 +2510,10 @@ QString QCoreApplication::applicationFilePath()
if (d->argc) {
static QByteArray procName = QByteArray(d->argv[0]);
- if (procName != d->argv[0]) {
+ if (procName != QByteArrayView(d->argv[0])) {
// clear the cache if the procname changes, so we reprocess it.
QCoreApplicationPrivate::clearApplicationFilePath();
- procName = QByteArray(d->argv[0]);
+ procName.assign(d->argv[0]);
}
}
@@ -2500,7 +2657,7 @@ QStringList QCoreApplication::arguments()
\brief the name of the organization that wrote this application
The value is used by the QSettings class when it is constructed
- using the empty constructor. This saves having to repeat this
+ using the default constructor. This saves having to repeat this
information each time a QSettings object is created.
On Mac, QSettings uses \l {QCoreApplication::}{organizationDomain()} as the organization
@@ -2540,7 +2697,7 @@ QString QCoreApplication::organizationName()
\brief the Internet domain of the organization that wrote this application
The value is used by the QSettings class when it is constructed
- using the empty constructor. This saves having to repeat this
+ using the default constructor. This saves having to repeat this
information each time a QSettings object is created.
On Mac, QSettings uses organizationDomain() as the organization
@@ -2576,11 +2733,15 @@ QString QCoreApplication::organizationDomain()
\property QCoreApplication::applicationName
\brief the name of this application
- The value is used by the QSettings class when it is constructed
- using the empty constructor. This saves having to repeat this
- information each time a QSettings object is created.
+ The application name is used in various Qt classes and modules,
+ most prominently in \l{QSettings} when it is constructed using the default constructor.
+ Other uses are in formatted logging output (see \l{qSetMessagePattern()}),
+ in output by \l{QCommandLineParser}, in \l{QTemporaryDir} and \l{QTemporaryFile}
+ default paths, and in some file locations of \l{QStandardPaths}.
+ \l{Qt D-Bus}, \l{Accessibility}, and the XCB platform integration make use
+ of the application name, too.
- If not set, the application name defaults to the executable name (since 5.0).
+ If not set, the application name defaults to the executable name.
\sa organizationName, organizationDomain, applicationVersion, applicationFilePath()
*/
@@ -2680,8 +2841,8 @@ Qt::PermissionStatus QCoreApplication::checkPermission(const QPermission &permis
}
/*!
- \fn template<typename Functor> void QCoreApplication::requestPermission(
- const QPermission &permission, Functor functor)
+ \fn template <typename Functor> void QCoreApplication::requestPermission(
+ const QPermission &permission, Functor &&functor)
Requests the given \a permission.
@@ -2734,8 +2895,9 @@ Qt::PermissionStatus QCoreApplication::checkPermission(const QPermission &permis
qApp->requestPermission(QCameraPermission{}, this, &CamerWidget::permissionUpdated);
\endcode
- If \a context is destroyed before the request completes,
- the \a functor will not be called.
+ The \a functor will be called in the thread of the \a context object. If
+ \a context is destroyed before the request completes, the \a functor will
+ not be called.
\include permissions.qdocinc requestPermission-postamble
@@ -2749,34 +2911,63 @@ Qt::PermissionStatus QCoreApplication::checkPermission(const QPermission &permis
Called by the various requestPermission overloads to perform the request.
- Calls the functor encapsulated in the \a slotObj in the given \a context
+ Calls the functor encapsulated in the \a slotObjRaw in the given \a context
(which may be \c nullptr).
*/
void QCoreApplication::requestPermission(const QPermission &requestedPermission,
- QtPrivate::QSlotObjectBase *slotObj, const QObject *context)
+ QtPrivate::QSlotObjectBase *slotObjRaw, const QObject *context)
{
+ QtPrivate::SlotObjUniquePtr slotObj{slotObjRaw}; // adopts
+ Q_ASSERT(slotObj);
+
if (QThread::currentThread() != QCoreApplicationPrivate::mainThread()) {
qWarning(lcPermissions, "Permissions can only be requested from the GUI (main) thread");
return;
}
- Q_ASSERT(slotObj);
+ class PermissionReceiver : public QObject
+ {
+ public:
+ explicit PermissionReceiver(QtPrivate::SlotObjUniquePtr &&slotObject, const QObject *context)
+ : slotObject(std::move(slotObject)), context(context ? context : this)
+ {
+ Q_ASSERT(this->context);
+ moveToThread(this->context->thread());
+ }
+
+ void finalizePermissionRequest(const QPermission &permission)
+ {
+ Q_ASSERT(slotObject);
+ // only execute if context object is still alive
+ if (context) {
+ void *args[] = { nullptr, const_cast<QPermission *>(&permission) };
+ slotObject->call(const_cast<QObject *>(context.data()), args);
+ }
+ deleteLater();
+ }
+
+ private:
+ QtPrivate::SlotObjSharedPtr slotObject;
+ QPointer<const QObject> context;
+ };
+
+ PermissionReceiver *receiver = new PermissionReceiver(std::move(slotObj), context);
QPermissions::Private::requestPermission(requestedPermission, [=](Qt::PermissionStatus status) {
- Q_ASSERT_X(status != Qt::PermissionStatus::Undetermined, "QPermission",
- "QCoreApplication::requestPermission() should never return Undetermined");
- if (status == Qt::PermissionStatus::Undetermined)
+ if (status == Qt::PermissionStatus::Undetermined) {
+ Q_ASSERT_X(false, "QPermission",
+ "Internal error: requestPermission() should never return Undetermined");
status = Qt::PermissionStatus::Denied;
+ }
if (QCoreApplication::self) {
QPermission permission = requestedPermission;
permission.m_status = status;
-
- void *argv[] = { nullptr, &permission };
- slotObj->call(const_cast<QObject*>(context), argv);
+ QMetaObject::invokeMethod(receiver,
+ &PermissionReceiver::finalizePermissionRequest,
+ Qt::QueuedConnection,
+ permission);
}
-
- slotObj->destroyIfLastRef();
});
}
@@ -2812,11 +3003,6 @@ Q_GLOBAL_STATIC(QRecursiveMutex, libraryPathMutex)
directory (and its existence) may change when the directory of
the application executable becomes known.
- If you want to iterate over the list, you can use the \l foreach
- pseudo-keyword:
-
- \snippet code/src_corelib_kernel_qcoreapplication.cpp 2
-
\sa setLibraryPaths(), addLibraryPath(), removeLibraryPath(), QLibrary,
{How to Create Qt Plugins}
*/
@@ -3065,7 +3251,7 @@ void QCoreApplication::installNativeEventFilter(QAbstractNativeEventFilter *filt
*/
void QCoreApplication::removeNativeEventFilter(QAbstractNativeEventFilter *filterObject)
{
- QAbstractEventDispatcher *eventDispatcher = QAbstractEventDispatcher::instance();
+ QAbstractEventDispatcher *eventDispatcher = QAbstractEventDispatcher::instance(QCoreApplicationPrivate::theMainThread.loadAcquire());
if (!filterObject || !eventDispatcher)
return;
eventDispatcher->removeNativeEventFilter(filterObject);
diff --git a/src/corelib/kernel/qcoreapplication.h b/src/corelib/kernel/qcoreapplication.h
index 82580ceb34..0078dc3295 100644
--- a/src/corelib/kernel/qcoreapplication.h
+++ b/src/corelib/kernel/qcoreapplication.h
@@ -8,6 +8,7 @@
#include <QtCore/qstring.h>
#ifndef QT_NO_QOBJECT
#include <QtCore/qcoreevent.h>
+#include <QtCore/qdeadlinetimer.h>
#include <QtCore/qeventloop.h>
#include <QtCore/qobject.h>
#else
@@ -32,6 +33,7 @@ class QTranslator;
class QPostEventList;
class QAbstractEventDispatcher;
class QAbstractNativeEventFilter;
+class QEventLoopLocker;
#if QT_CONFIG(permissions) || defined(Q_QDOC)
class QPermission;
@@ -58,6 +60,11 @@ class Q_CORE_EXPORT QCoreApplication
#endif
Q_DECLARE_PRIVATE(QCoreApplication)
+ friend class QEventLoopLocker;
+#if QT_CONFIG(permissions)
+ using RequestPermissionPrototype = void(*)(QPermission);
+#endif
+
public:
enum { ApplicationFlags = QT_VERSION
};
@@ -87,12 +94,13 @@ public:
static void setSetuidAllowed(bool allow);
static bool isSetuidAllowed();
- static QCoreApplication *instance() { return self; }
+ static QCoreApplication *instance() noexcept { return self; }
#ifndef QT_NO_QOBJECT
static int exec();
static void processEvents(QEventLoop::ProcessEventsFlags flags = QEventLoop::AllEvents);
static void processEvents(QEventLoop::ProcessEventsFlags flags, int maxtime);
+ static void processEvents(QEventLoop::ProcessEventsFlags flags, QDeadlineTimer deadline);
static bool sendEvent(QObject *receiver, QEvent *event);
static void postEvent(QObject *receiver, QEvent *event, int priority = Qt::NormalEventPriority);
@@ -116,66 +124,45 @@ public:
# ifdef Q_QDOC
template <typename Functor>
- void requestPermission(const QPermission &permission, Functor functor);
- template <typename Functor>
void requestPermission(const QPermission &permission, const QObject *context, Functor functor);
# else
- template <typename Slot> // requestPermission to a QObject slot
+ // requestPermission with context or receiver object; need to require here that receiver is the
+ // right type to avoid ambiguity with the private implementation function.
+ template <typename Functor,
+ std::enable_if_t<
+ QtPrivate::AreFunctionsCompatible<RequestPermissionPrototype, Functor>::value,
+ bool> = true>
void requestPermission(const QPermission &permission,
- const typename QtPrivate::FunctionPointer<Slot>::Object *receiver, Slot slot)
+ const typename QtPrivate::ContextTypeForFunctor<Functor>::ContextType *receiver,
+ Functor &&func)
{
- using CallbackSignature = QtPrivate::FunctionPointer<void (*)(QPermission)>;
- using SlotSignature = QtPrivate::FunctionPointer<Slot>;
-
- static_assert(int(SlotSignature::ArgumentCount) <= int(CallbackSignature::ArgumentCount),
- "Slot requires more arguments than what can be provided.");
- static_assert((QtPrivate::CheckCompatibleArguments<typename CallbackSignature::Arguments, typename SlotSignature::Arguments>::value),
- "Slot arguments are not compatible (must be QPermission)");
-
- auto slotObj = new QtPrivate::QSlotObject<Slot, typename SlotSignature::Arguments, void>(slot);
- requestPermission(permission, slotObj, receiver);
- }
-
- // requestPermission to a functor or function pointer (with context)
- template <typename Func, std::enable_if_t<
- !QtPrivate::FunctionPointer<Func>::IsPointerToMemberFunction
- && !std::is_same<const char *, Func>::value, bool> = true>
- void requestPermission(const QPermission &permission, const QObject *context, Func func)
- {
- using CallbackSignature = QtPrivate::FunctionPointer<void (*)(QPermission)>;
- constexpr int MatchingArgumentCount = QtPrivate::ComputeFunctorArgumentCount<
- Func, CallbackSignature::Arguments>::Value;
-
- static_assert(MatchingArgumentCount == 0
- || MatchingArgumentCount == CallbackSignature::ArgumentCount,
- "Functor arguments are not compatible (must be QPermission)");
-
- QtPrivate::QSlotObjectBase *slotObj = nullptr;
- if constexpr (MatchingArgumentCount == CallbackSignature::ArgumentCount) {
- slotObj = new QtPrivate::QFunctorSlotObject<Func, 1,
- typename CallbackSignature::Arguments, void>(std::move(func));
- } else {
- slotObj = new QtPrivate::QFunctorSlotObject<Func, 0,
- typename QtPrivate::List_Left<void, 0>::Value, void>(std::move(func));
- }
-
- requestPermission(permission, slotObj, context);
+ requestPermission(permission,
+ QtPrivate::makeCallableObject<RequestPermissionPrototype>(std::forward<Functor>(func)),
+ receiver);
}
+# endif // Q_QDOC
+#ifndef QT_NO_CONTEXTLESS_CONNECT
+ #ifdef Q_QDOC
+ template <typename Functor>
+ #else
// requestPermission to a functor or function pointer (without context)
- template <typename Func, std::enable_if_t<
- !QtPrivate::FunctionPointer<Func>::IsPointerToMemberFunction
- && !std::is_same<const char *, Func>::value, bool> = true>
- void requestPermission(const QPermission &permission, Func func)
+ template <typename Functor,
+ std::enable_if_t<
+ QtPrivate::AreFunctionsCompatible<RequestPermissionPrototype, Functor>::value,
+ bool> = true>
+ #endif
+ void requestPermission(const QPermission &permission, Functor &&func)
{
- requestPermission(permission, nullptr, std::move(func));
+ requestPermission(permission, nullptr, std::forward<Functor>(func));
}
+#endif // QT_NO_CONTEXTLESS_CONNECT
private:
+ // ### Qt 7: rename to requestPermissionImpl to avoid ambiguity
void requestPermission(const QPermission &permission,
QtPrivate::QSlotObjectBase *slotObj, const QObject *context);
public:
-# endif // Q_QDOC
#endif // QT_CONFIG(permission)
diff --git a/src/corelib/kernel/qcoreapplication_p.h b/src/corelib/kernel/qcoreapplication_p.h
index 94c4b0f1e9..bfd65d2c9a 100644
--- a/src/corelib/kernel/qcoreapplication_p.h
+++ b/src/corelib/kernel/qcoreapplication_p.h
@@ -25,6 +25,7 @@
#include "QtCore/qsettings.h"
#endif
#ifndef QT_NO_QOBJECT
+#include <qloggingcategory.h>
#include "private/qobject_p.h"
#include "private/qlocking_p.h"
#endif
@@ -35,6 +36,10 @@
QT_BEGIN_NAMESPACE
+#ifndef QT_NO_QOBJECT
+Q_DECLARE_LOGGING_CATEGORY(lcDeleteLater)
+#endif
+
typedef QList<QTranslator*> QTranslatorList;
class QAbstractEventDispatcher;
@@ -73,6 +78,7 @@ public:
static QString infoDictionaryStringProperty(const QString &propertyName);
#endif
+ void initConsole();
static void initLocale();
static bool checkInstance(const char *method);
@@ -102,6 +108,7 @@ public:
virtual void quit();
static QBasicAtomicPointer<QThread> theMainThread;
+ static QBasicAtomicPointer<void> theMainThreadId;
static QThread *mainThread();
static bool threadRequiresCoreApplication();
@@ -125,6 +132,7 @@ public:
#if defined(Q_OS_WIN)
int origArgc;
char **origArgv; // store unmodified arguments for QCoreApplication::arguments()
+ bool consoleAllocated = false;
#endif
void appendApplicationPathToLibraryPaths(void);
diff --git a/src/corelib/kernel/qcoreapplication_platform.h b/src/corelib/kernel/qcoreapplication_platform.h
index 5a2543146a..d5f266179e 100644
--- a/src/corelib/kernel/qcoreapplication_platform.h
+++ b/src/corelib/kernel/qcoreapplication_platform.h
@@ -33,7 +33,7 @@ typedef _jobject* jobject;
QT_BEGIN_NAMESPACE
#if defined(Q_OS_ANDROID)
-Q_DECLARE_JNI_TYPE(Context, "Landroid/content/Context;")
+Q_DECLARE_JNI_CLASS(Context, "android/content/Context")
#endif
namespace QNativeInterface
@@ -43,7 +43,7 @@ struct Q_CORE_EXPORT QAndroidApplication
{
QT_DECLARE_NATIVE_INTERFACE(QAndroidApplication, 1, QCoreApplication)
#ifdef Q_QDOC
- static jobject context();
+ static QJniObject context();
#else
static QtJniTypes::Context context();
#endif
diff --git a/src/corelib/kernel/qcoreapplication_win.cpp b/src/corelib/kernel/qcoreapplication_win.cpp
index 0fe14af798..3a69bec25b 100644
--- a/src/corelib/kernel/qcoreapplication_win.cpp
+++ b/src/corelib/kernel/qcoreapplication_win.cpp
@@ -21,7 +21,7 @@ QT_BEGIN_NAMESPACE
using namespace Qt::StringLiterals;
-Q_CORE_EXPORT QString qAppFileName() // get application file name
+QString qAppFileName() // get application file name
{
/*
GetModuleFileName() returns the length of the module name, when it has
diff --git a/src/corelib/kernel/qcoreevent.cpp b/src/corelib/kernel/qcoreevent.cpp
index bf227ecf02..9c99530268 100644
--- a/src/corelib/kernel/qcoreevent.cpp
+++ b/src/corelib/kernel/qcoreevent.cpp
@@ -3,6 +3,7 @@
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qcoreevent.h"
+#include "qcoreevent_p.h"
#include "qcoreapplication.h"
#include "qcoreapplication_p.h"
@@ -14,6 +15,9 @@
QT_BEGIN_NAMESPACE
+Q_TRACE_POINT(qtcore, QEvent_ctor, QEvent *event, QEvent::Type type);
+Q_TRACE_POINT(qtcore, QEvent_dtor, QEvent *event, QEvent::Type type);
+
/*!
\class QEvent
\inmodule QtCore
@@ -75,6 +79,8 @@ QT_BEGIN_NAMESPACE
\value ChildAdded An object gets a child (QChildEvent).
\value ChildPolished A widget child gets polished (QChildEvent).
\value ChildRemoved An object loses a child (QChildEvent).
+ \value [since 6.7] ChildWindowAdded A child window was added to the window.
+ \value [since 6.7] ChildWindowRemoved A child window was removed from the window.
\value Clipboard The clipboard contents have changed.
\value Close Widget was closed (QCloseEvent).
\value CloseSoftwareInputPanel A widget wants to close the software input panel (SIP).
@@ -82,6 +88,8 @@ QT_BEGIN_NAMESPACE
\value ContextMenu Context popup menu (QContextMenuEvent).
\value CursorChange The widget's cursor has changed.
\value DeferredDelete The object will be deleted after it has cleaned up (QDeferredDeleteEvent)
+ \value [since 6.6] DevicePixelRatioChange
+ The devicePixelRatio has changed for this widget's or window's underlying backing store.
\value DragEnter The cursor enters a widget during a drag and drop operation (QDragEnterEvent).
\value DragLeave The cursor leaves a widget during a drag and drop operation (QDragLeaveEvent).
\value DragMove A drag and drop operation is in progress (QDragMoveEvent).
@@ -154,8 +162,12 @@ QT_BEGIN_NAMESPACE
\value OrientationChange The screens orientation has changes (QScreenOrientationChangeEvent).
\value Paint Screen update necessary (QPaintEvent).
\value PaletteChange Palette of the widget changed.
- \value ParentAboutToChange The widget parent is about to change.
- \value ParentChange The widget parent has changed.
+ \value ParentAboutToChange The object parent is about to change.
+ Only sent to some object types, such as QWidget.
+ \value ParentChange The object parent has changed.
+ Only sent to some object types, such as QWidget.
+ \value [since 6.7] ParentWindowAboutToChange The parent window is about to change.
+ \value [since 6.7] ParentWindowChange The parent window has changed.
\value PlatformPanel A platform specific panel has been requested.
\value PlatformSurface A native platform surface has been created or is about to be destroyed (QPlatformSurfaceEvent).
\omitvalue Pointer
@@ -264,7 +276,7 @@ QEvent::QEvent(Type type)
: t(type), m_reserved(0),
m_inputEvent(false), m_pointerEvent(false), m_singlePointEvent(false)
{
- Q_TRACE(QEvent_ctor, this, t);
+ Q_TRACE(QEvent_ctor, this, type);
}
/*!
@@ -428,8 +440,9 @@ struct QBasicAtomicBitField {
QBasicAtomicInteger<uint> &entry = data[which / BitsPerInt];
const uint old = entry.loadRelaxed();
const uint bit = 1U << (which % BitsPerInt);
- return !(old & bit) // wasn't taken
- && entry.testAndSetRelaxed(old, old | bit); // still wasn't taken
+ if (old & bit)
+ return false; // already taken
+ return (entry.fetchAndOrRelaxed(bit) & bit) == 0;
// don't update 'next' here - it's unlikely that it will need
// to be updated, in the general case, and having 'next'
@@ -506,12 +519,12 @@ int QEvent::registerEventType(int hint) noexcept
started one or more timers. Each timer has a unique identifier. A
timer is started with QObject::startTimer().
- The QTimer class provides a high-level programming interface that
+ The QChronoTimer class provides a high-level programming interface that
uses signals instead of events. It also provides single-shot timers.
The event handler QObject::timerEvent() receives timer events.
- \sa QTimer, QObject::timerEvent(), QObject::startTimer(),
+ \sa QChronoTimer, QObject::timerEvent(), QObject::startTimer(),
QObject::killTimer()
*/
@@ -629,23 +642,14 @@ Q_IMPL_EVENT_COMMON(QDynamicPropertyChangeEvent)
*/
/*!
- Constructs a deferred delete event with an initial loopLevel() of zero.
+ Constructs a deferred delete event with the given loop and scope level.
*/
-QDeferredDeleteEvent::QDeferredDeleteEvent()
- : QEvent(QEvent::DeferredDelete)
- , level(0)
+QDeferredDeleteEvent::QDeferredDeleteEvent(int loopLevel, int scopeLevel)
+ : QEvent(QEvent::DeferredDelete), m_loopLevel(loopLevel), m_scopeLevel(scopeLevel)
{ }
Q_IMPL_EVENT_COMMON(QDeferredDeleteEvent)
-/*! \fn int QDeferredDeleteEvent::loopLevel() const
-
- Returns the loop-level in which the event was posted. The
- loop-level is set by QCoreApplication::postEvent().
-
- \sa QObject::deleteLater()
-*/
-
QT_END_NAMESPACE
#include "moc_qcoreevent.cpp"
diff --git a/src/corelib/kernel/qcoreevent.h b/src/corelib/kernel/qcoreevent.h
index 536c5ab558..a65dbee7da 100644
--- a/src/corelib/kernel/qcoreevent.h
+++ b/src/corelib/kernel/qcoreevent.h
@@ -34,7 +34,7 @@ protected: \
Class* Class::clone() const \
{ \
auto c = new Class(*this); \
- QEvent *e = c; \
+ [[maybe_unused]] QEvent *e = c; \
/* check that covariant return is safe to add */ \
Q_ASSERT(reinterpret_cast<quintptr>(c) == reinterpret_cast<quintptr>(e)); \
return c; \
@@ -77,7 +77,7 @@ public:
Hide = 18, // widget is hidden
Close = 19, // request to close widget
Quit = 20, // request to quit application
- ParentChange = 21, // widget has been reparented
+ ParentChange = 21, // object has been reparented
ParentAboutToChange = 131, // sent just before the parent change is done
ThreadChange = 22, // object has changed threads
WindowActivate = 24, // window was activated
@@ -284,6 +284,13 @@ public:
// GraphicsSceneLeave = 220,
WindowAboutToChangeInternal = 221, // internal for QQuickWidget and texture-based widgets
+ DevicePixelRatioChange = 222,
+
+ ChildWindowAdded = 223,
+ ChildWindowRemoved = 224,
+ ParentWindowAboutToChange = 225,
+ ParentWindowChange = 226,
+
// 512 reserved for Qt Jambi's MetaCall event
// 513 reserved for Qt Jambi's DeleteOnMainThread event
@@ -347,6 +354,8 @@ private:
friend class QApplication;
friend class QGraphicsScenePrivate;
// from QtTest:
+ // QtWebEngine event handling requires forwarding events as spontaneous.
+ // Impersonated QSpontaneKeyEvent in QtWebEngine to handle such cases.
friend class QSpontaneKeyEvent;
// needs this:
Q_ALWAYS_INLINE
@@ -393,18 +402,6 @@ private:
QByteArray n;
};
-class Q_CORE_EXPORT QDeferredDeleteEvent : public QEvent
-{
- Q_DECL_EVENT_COMMON(QDeferredDeleteEvent)
-public:
- explicit QDeferredDeleteEvent();
- int loopLevel() const { return level; }
-
-private:
- int level;
- friend class QCoreApplication;
-};
-
QT_END_NAMESPACE
#endif // QCOREEVENT_H
diff --git a/src/corelib/kernel/qcoreevent_p.h b/src/corelib/kernel/qcoreevent_p.h
new file mode 100644
index 0000000000..ac90baad9b
--- /dev/null
+++ b/src/corelib/kernel/qcoreevent_p.h
@@ -0,0 +1,39 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QCOREEVENT_P_H
+#define QCOREEVENT_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 "QtCore/qcoreevent.h"
+
+QT_BEGIN_NAMESPACE
+
+class QCoreApplication;
+
+class QDeferredDeleteEvent : public QEvent
+{
+ Q_DECL_EVENT_COMMON(QDeferredDeleteEvent)
+public:
+ explicit QDeferredDeleteEvent(int loopLevel, int scopeLevel);
+ int loopLevel() const { return m_loopLevel; }
+ int scopeLevel() const { return m_scopeLevel; }
+
+private:
+ int m_loopLevel = 0;
+ int m_scopeLevel = 0;
+};
+
+QT_END_NAMESPACE
+
+#endif // QCOREEVENT_P_H
diff --git a/src/corelib/kernel/qdeadlinetimer.cpp b/src/corelib/kernel/qdeadlinetimer.cpp
index d012390c5b..f99e68f990 100644
--- a/src/corelib/kernel/qdeadlinetimer.cpp
+++ b/src/corelib/kernel/qdeadlinetimer.cpp
@@ -2,298 +2,47 @@
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qdeadlinetimer.h"
-#include "qdeadlinetimer_p.h"
#include "private/qnumeric_p.h"
QT_BEGIN_NAMESPACE
QT_IMPL_METATYPE_EXTERN(QDeadlineTimer)
-namespace {
- class TimeReference
- {
- enum : unsigned {
- umega = 1000 * 1000,
- ugiga = umega * 1000
- };
-
- enum : qint64 {
- kilo = 1000,
- mega = kilo * 1000,
- giga = mega * 1000
- };
-
- public:
- enum RoundingStrategy {
- RoundDown,
- RoundUp,
- RoundDefault = RoundDown
- };
-
- static constexpr qint64 Min = std::numeric_limits<qint64>::min();
- static constexpr qint64 Max = std::numeric_limits<qint64>::max();
-
- inline TimeReference(qint64 = 0, unsigned = 0);
- inline void updateTimer(qint64 &, unsigned &);
-
- inline bool addNanoseconds(qint64);
- inline bool addMilliseconds(qint64);
- bool addSecsAndNSecs(qint64, qint64);
-
- inline bool subtract(const qint64, const unsigned);
-
- inline bool toMilliseconds(qint64 *, RoundingStrategy = RoundDefault) const;
- inline bool toNanoseconds(qint64 *) const;
-
- inline void saturate(bool toMax);
- static bool sign(qint64, qint64);
-
- private:
- bool adjust(const qint64, const unsigned, qint64 = 0);
-
- private:
- qint64 secs;
- unsigned nsecs;
- };
-}
-
-inline TimeReference::TimeReference(qint64 t1, unsigned t2)
- : secs(t1), nsecs(t2)
-{
-}
-
-inline void TimeReference::updateTimer(qint64 &t1, unsigned &t2)
-{
- t1 = secs;
- t2 = nsecs;
-}
-
-inline void TimeReference::saturate(bool toMax)
-{
- secs = toMax ? Max : Min;
-}
-
-/*!
- * \internal
- *
- * Determines the sign of a (seconds, nanoseconds) pair
- * for differentiating overflow from underflow. It doesn't
- * deal with equality as it shouldn't ever be called in that case.
- *
- * Returns true if the pair represents a positive time offset
- * false otherwise.
- */
-bool TimeReference::sign(qint64 secs, qint64 nsecs)
-{
- if (secs > 0) {
- if (nsecs > 0)
- return true;
- } else {
- if (nsecs < 0)
- return false;
- }
+using namespace std::chrono;
- // They are different in sign
- secs += nsecs / giga;
- if (secs > 0)
- return true;
- else if (secs < 0)
- return false;
-
- // We should never get over|underflow out of
- // the case: secs * giga == -nsecs
- // So the sign of nsecs is the deciding factor
- Q_ASSERT(nsecs % giga != 0);
- return nsecs > 0;
-}
-
-#if defined(Q_OS_UNIX) && !defined(Q_OS_DARWIN)
-inline bool TimeReference::addNanoseconds(qint64 arg)
+namespace {
+struct TimeReference : std::numeric_limits<qint64>
{
- return addSecsAndNSecs(arg / giga, arg % giga);
+ static constexpr qint64 Min = min();
+ static constexpr qint64 Max = max();
+};
}
-inline bool TimeReference::addMilliseconds(qint64 arg)
+template <typename Duration1, typename... Durations>
+static qint64 add_saturate(qint64 t1, Duration1 dur, Durations... extra)
{
- return addSecsAndNSecs(arg / kilo, (arg % kilo) * mega);
-}
+ qint64 v = dur.count();
+ qint64 saturated = std::numeric_limits<qint64>::max();
+ if (v < 0)
+ saturated = std::numeric_limits<qint64>::min();
-/*!
- * \internal
- *
- * Adds \a t1 addSecs seconds and \a addNSecs nanoseconds to the
- * time reference. The arguments are normalized to seconds (qint64)
- * and nanoseconds (unsigned) before the actual calculation is
- * delegated to adjust(). If the nanoseconds are negative the
- * owed second used for the normalization is passed on to adjust()
- * as third argument.
- *
- * Returns true if operation was successful, false on over|underflow
- */
-bool TimeReference::addSecsAndNSecs(qint64 addSecs, qint64 addNSecs)
-{
- // Normalize the arguments
- if (qAbs(addNSecs) >= giga) {
- if (add_overflow<qint64>(addSecs, addNSecs / giga, &addSecs))
- return false;
+ // convert to nanoseconds with saturation
+ using Ratio = std::ratio_divide<typename Duration1::period, nanoseconds::period>;
+ static_assert(Ratio::den == 1, "sub-multiples of nanosecond are not supported");
+ if (qMulOverflow<Ratio::num>(v, &v))
+ return saturated;
- addNSecs %= giga;
+ qint64 r;
+ if (qAddOverflow(t1, v, &r))
+ return saturated;
+ if constexpr (sizeof...(Durations)) {
+ // chain more additions
+ return add_saturate(r, extra...);
}
-
- if (addNSecs < 0)
- return adjust(addSecs, ugiga - unsigned(-addNSecs), -1);
-
- return adjust(addSecs, unsigned(addNSecs));
+ return r;
}
/*!
- * \internal
- *
- * Adds \a t1 seconds and \a t2 nanoseconds to the internal members.
- * Takes into account the additional \a carrySeconds we may owe or need to carry over.
- *
- * Returns true if operation was successful, false on over|underflow
- */
-bool TimeReference::adjust(const qint64 t1, const unsigned t2, qint64 carrySeconds)
-{
- static_assert(QDeadlineTimerNanosecondsInT2);
- nsecs += t2;
- if (nsecs >= ugiga) {
- nsecs -= ugiga;
- carrySeconds++;
- }
-
- // We don't worry about the order of addition, because the result returned by
- // callers of this function is unchanged regardless of us over|underflowing.
- // If we do, we do so by no more than a second, thus saturating the timer to
- // Forever has the same effect as if we did the arithmetic exactly and salvaged
- // the overflow.
- return !add_overflow<qint64>(secs, t1, &secs) && !add_overflow<qint64>(secs, carrySeconds, &secs);
-}
-
-/*!
- * \internal
- *
- * Subtracts \a t1 seconds and \a t2 nanoseconds from the time reference.
- * When normalizing the nanoseconds to a positive number the owed seconds is
- * passed as third argument to adjust() as the seconds may over|underflow
- * if we do the calculation directly. There is little sense to check the
- * seconds for over|underflow here in case we are going to need to carry
- * over a second _after_ we add the nanoseconds.
- *
- * Returns true if operation was successful, false on over|underflow
- */
-inline bool TimeReference::subtract(const qint64 t1, const unsigned t2)
-{
- Q_ASSERT(t2 < ugiga);
- return adjust(-t1, ugiga - t2, -1);
-}
-
-/*!
- * \internal
- *
- * Converts the time reference to milliseconds.
- *
- * Checks are done without making use of mul_overflow because it may
- * not be implemented on some 32bit platforms.
- *
- * Returns true if operation was successful, false on over|underflow
- */
-inline bool TimeReference::toMilliseconds(qint64 *result, RoundingStrategy rounding) const
-{
- static constexpr qint64 maxSeconds = Max / kilo;
- static constexpr qint64 minSeconds = Min / kilo;
- if (secs > maxSeconds || secs < minSeconds)
- return false;
-
- unsigned ns = rounding == RoundDown ? nsecs : nsecs + umega - 1;
-
- return !add_overflow<qint64>(secs * kilo, ns / umega, result);
-}
-
-/*!
- * \internal
- *
- * Converts the time reference to nanoseconds.
- *
- * Checks are done without making use of mul_overflow because it may
- * not be implemented on some 32bit platforms.
- *
- * Returns true if operation was successful, false on over|underflow
- */
-inline bool TimeReference::toNanoseconds(qint64 *result) const
-{
- static constexpr qint64 maxSeconds = Max / giga;
- static constexpr qint64 minSeconds = Min / giga;
- if (secs > maxSeconds || secs < minSeconds)
- return false;
-
- return !add_overflow<qint64>(secs * giga, nsecs, result);
-}
-#else
-inline bool TimeReference::addNanoseconds(qint64 arg)
-{
- return adjust(arg, 0);
-}
-
-inline bool TimeReference::addMilliseconds(qint64 arg)
-{
- static constexpr qint64 maxMilliseconds = Max / mega;
- if (qAbs(arg) > maxMilliseconds)
- return false;
-
- return addNanoseconds(arg * mega);
-}
-
-inline bool TimeReference::addSecsAndNSecs(qint64 addSecs, qint64 addNSecs)
-{
- static constexpr qint64 maxSeconds = Max / giga;
- static constexpr qint64 minSeconds = Min / giga;
- if (addSecs > maxSeconds || addSecs < minSeconds || add_overflow<qint64>(addSecs * giga, addNSecs, &addNSecs))
- return false;
-
- return addNanoseconds(addNSecs);
-}
-
-inline bool TimeReference::adjust(const qint64 t1, const unsigned t2, qint64 carrySeconds)
-{
- static_assert(!QDeadlineTimerNanosecondsInT2);
- Q_UNUSED(t2);
- Q_UNUSED(carrySeconds);
-
- return !add_overflow<qint64>(secs, t1, &secs);
-}
-
-inline bool TimeReference::subtract(const qint64 t1, const unsigned t2)
-{
- Q_UNUSED(t2);
-
- return addNanoseconds(-t1);
-}
-
-inline bool TimeReference::toMilliseconds(qint64 *result, RoundingStrategy rounding) const
-{
- // Force QDeadlineTimer to treat the border cases as
- // over|underflow and saturate the results returned to the user.
- // We don't want to get valid milliseconds out of saturated timers.
- if (secs == Max || secs == Min)
- return false;
-
- *result = secs / mega;
- if (rounding == RoundUp && secs > *result * mega)
- (*result)++;
-
- return true;
-}
-
-inline bool TimeReference::toNanoseconds(qint64 *result) const
-{
- *result = secs;
- return true;
-}
-#endif
-
-/*!
\class QDeadlineTimer
\inmodule QtCore
\brief The QDeadlineTimer class marks a deadline in the future.
@@ -302,6 +51,8 @@ inline bool TimeReference::toNanoseconds(qint64 *result) const
\reentrant
\ingroup tools
+ \compares strong
+
The QDeadlineTimer class is usually used to calculate future deadlines and
verify whether the deadline has expired. QDeadlineTimer can also be used
for deadlines without expiration ("forever"). It forms a counterpart to
@@ -335,11 +86,12 @@ inline bool TimeReference::toNanoseconds(qint64 *result) const
\section1 Timer types
- Like QTimer, QDeadlineTimer can select among different levels of coarseness
- on the timers. You can select precise timing by passing Qt::PreciseTimer to
- the functions that set of change the timer, or you can select coarse timing
- by passing Qt::CoarseTimer. Qt::VeryCoarseTimer is currently interpreted
- the same way as Qt::CoarseTimer.
+ Like QTimer and QChronoTimer, QDeadlineTimer can select among
+ different levels of coarseness on the timers. You can select
+ precise timing by passing Qt::PreciseTimer to the functions that
+ set of change the timer, or you can select coarse timing by passing
+ Qt::CoarseTimer. Qt::VeryCoarseTimer is currently interpreted the same
+ way as Qt::CoarseTimer.
This feature is dependent on support from the operating system: if the OS
does not support a coarse timer functionality, then QDeadlineTimer will
@@ -371,7 +123,7 @@ inline bool TimeReference::toNanoseconds(qint64 *result) const
\snippet code/src_corelib_kernel_qdeadlinetimer.cpp 2
- \sa QTime, QTimer, QDeadlineTimer, Qt::TimerType
+ \sa QTime, QChronoTimer, QDeadlineTimer, Qt::TimerType
*/
/*!
@@ -382,10 +134,12 @@ inline bool TimeReference::toNanoseconds(qint64 *result) const
*/
/*!
+ \fn QDeadlineTimer::QDeadlineTimer()
\fn QDeadlineTimer::QDeadlineTimer(Qt::TimerType timerType)
Constructs an expired QDeadlineTimer object. For this object,
- remainingTime() will return 0.
+ remainingTime() will return 0. If \a timerType is not set, then the object
+ will use the \l{Qt::CoarseTimer}{coarse} \l{QDeadlineTimer#Timer types}{timer type}.
The timer type \a timerType may be ignored, since the timer is already
expired. Similarly, for optimization purposes, this function will not
@@ -415,7 +169,7 @@ inline bool TimeReference::toNanoseconds(qint64 *result) const
from the moment of the creation of this object, if msecs is positive. If \a
msecs is zero, this QDeadlineTimer will be marked as expired, causing
remainingTime() to return zero and deadline() to return an indeterminate
- time point in the past. If \a msecs is -1, the timer will be set to never
+ time point in the past. If \a msecs is negative, the timer will be set to never
expire, causing remainingTime() to return -1 and deadline() to return the
maximum value.
@@ -428,10 +182,12 @@ inline bool TimeReference::toNanoseconds(qint64 *result) const
functionality is required, use QDeadlineTimer::current() and add time to
it.
+ \note Prior to Qt 6.6, the only value that caused the timer to never expire
+ was -1.
+
\sa hasExpired(), isForever(), remainingTime(), setRemainingTime()
*/
QDeadlineTimer::QDeadlineTimer(qint64 msecs, Qt::TimerType type) noexcept
- : t2(0)
{
setRemainingTime(msecs, type);
}
@@ -495,51 +251,70 @@ QDeadlineTimer::QDeadlineTimer(qint64 msecs, Qt::TimerType type) noexcept
/*!
Sets the remaining time for this QDeadlineTimer object to \a msecs
milliseconds from now, if \a msecs has a positive value. If \a msecs is
- zero, this QDeadlineTimer object will be marked as expired, whereas a value
- of -1 will set it to never expire.
+ zero, this QDeadlineTimer object will be marked as expired, whereas a
+ negative value will set it to never expire.
+
+ For optimization purposes, if \a msecs is zero, this function may skip
+ obtaining the current time and may instead use a value known to be in the
+ past. If that happens, deadline() may return an unexpected value and this
+ object cannot be used in calculation of how long it is overdue. If that
+ functionality is required, use QDeadlineTimer::current() and add time to
+ it.
The timer type for this QDeadlineTimer object will be set to the specified \a timerType.
+ \note Prior to Qt 6.6, the only value that caused the timer to never expire
+ was -1.
+
\sa setPreciseRemainingTime(), hasExpired(), isForever(), remainingTime()
*/
void QDeadlineTimer::setRemainingTime(qint64 msecs, Qt::TimerType timerType) noexcept
{
- if (msecs == -1) {
+ if (msecs < 0) {
*this = QDeadlineTimer(Forever, timerType);
- return;
+ } else if (msecs == 0) {
+ *this = QDeadlineTimer(timerType);
+ t1 = std::numeric_limits<qint64>::min();
+ } else {
+ *this = current(timerType);
+ milliseconds ms(msecs);
+ t1 = add_saturate(t1, ms);
}
-
- *this = current(timerType);
-
- TimeReference ref(t1, t2);
- if (!ref.addMilliseconds(msecs))
- ref.saturate(msecs > 0);
- ref.updateTimer(t1, t2);
}
/*!
Sets the remaining time for this QDeadlineTimer object to \a secs seconds
plus \a nsecs nanoseconds from now, if \a secs has a positive value. If \a
- secs is -1, this QDeadlineTimer will be set it to never expire. If both
- parameters are zero, this QDeadlineTimer will be marked as expired.
+ secs is negative, this QDeadlineTimer will be set it to never expire (this
+ behavior does not apply to \a nsecs). If both parameters are zero, this
+ QDeadlineTimer will be marked as expired.
+
+ For optimization purposes, if both \a secs and \a nsecs are zero, this
+ function may skip obtaining the current time and may instead use a value
+ known to be in the past. If that happens, deadline() may return an
+ unexpected value and this object cannot be used in calculation of how long
+ it is overdue. If that functionality is required, use
+ QDeadlineTimer::current() and add time to it.
The timer type for this QDeadlineTimer object will be set to the specified
\a timerType.
+ \note Prior to Qt 6.6, the only condition that caused the timer to never
+ expire was when \a secs was -1.
+
\sa setRemainingTime(), hasExpired(), isForever(), remainingTime()
*/
void QDeadlineTimer::setPreciseRemainingTime(qint64 secs, qint64 nsecs, Qt::TimerType timerType) noexcept
{
- if (secs == -1) {
+ if (secs < 0) {
*this = QDeadlineTimer(Forever, timerType);
- return;
+ } else if (secs == 0 && nsecs == 0) {
+ *this = QDeadlineTimer(timerType);
+ t1 = std::numeric_limits<qint64>::min();
+ } else {
+ *this = current(timerType);
+ t1 = add_saturate(t1, seconds{secs}, nanoseconds{nsecs});
}
-
- *this = current(timerType);
- TimeReference ref(t1, t2);
- if (!ref.addSecsAndNSecs(secs, nsecs))
- ref.saturate(TimeReference::sign(secs, nsecs));
- ref.updateTimer(t1, t2);
}
/*!
@@ -589,6 +364,8 @@ bool QDeadlineTimer::hasExpired() const noexcept
{
if (isForever())
return false;
+ if (t1 == std::numeric_limits<qint64>::min())
+ return true;
return *this <= current(timerType());
}
@@ -637,19 +414,8 @@ qint64 QDeadlineTimer::remainingTime() const noexcept
if (isForever())
return -1;
- QDeadlineTimer now = current(timerType());
- TimeReference ref(t1, t2);
-
- qint64 msecs;
- if (!ref.subtract(now.t1, now.t2))
- return 0; // We can only underflow here
-
- // If we fail the conversion, t1 < now.t1 means we underflowed,
- // thus the deadline had long expired
- if (!ref.toMilliseconds(&msecs, TimeReference::RoundUp))
- return t1 < now.t1 ? 0 : -1;
-
- return msecs < 0 ? 0 : msecs;
+ nanoseconds nsecs(remainingTimeNSecs());
+ return ceil<milliseconds>(nsecs).count();
}
/*!
@@ -671,23 +437,19 @@ qint64 QDeadlineTimer::remainingTimeNSecs() const noexcept
/*!
\internal
Same as remainingTimeNSecs, but may return negative remaining times. Does
- not deal with Forever. In case of underflow the result is saturated to
- the minimum possible value, on overflow - the maximum possible value.
+ not deal with Forever. In case of underflow, which is only possible if the
+ timer has expired, an arbitrary negative value is returned.
*/
qint64 QDeadlineTimer::rawRemainingTimeNSecs() const noexcept
{
- QDeadlineTimer now = current(timerType());
- TimeReference ref(t1, t2);
+ if (t1 == std::numeric_limits<qint64>::min())
+ return t1; // we'd saturate to this anyway
- qint64 nsecs;
- if (!ref.subtract(now.t1, now.t2))
- return TimeReference::Min; // We can only underflow here
-
- // If we fail the conversion, t1 < now.t1 means we underflowed,
- // thus the deadline had long expired
- if (!ref.toNanoseconds(&nsecs))
- return t1 < now.t1 ? TimeReference::Min : TimeReference::Max;
- return nsecs;
+ QDeadlineTimer now = current(timerType());
+ qint64 r;
+ if (qSubOverflow(t1, now.t1, &r))
+ return -1; // any negative number is fine
+ return r;
}
/*!
@@ -714,12 +476,11 @@ qint64 QDeadlineTimer::deadline() const noexcept
{
if (isForever())
return TimeReference::Max;
+ if (t1 == TimeReference::Min)
+ return t1;
- qint64 result;
- if (!TimeReference(t1, t2).toMilliseconds(&result))
- return t1 < 0 ? TimeReference::Min : TimeReference::Max;
-
- return result;
+ nanoseconds ns(t1);
+ return duration_cast<milliseconds>(ns).count();
}
/*!
@@ -748,11 +509,7 @@ qint64 QDeadlineTimer::deadlineNSecs() const noexcept
if (isForever())
return TimeReference::Max;
- qint64 result;
- if (!TimeReference(t1, t2).toNanoseconds(&result))
- return t1 < 0 ? TimeReference::Min : TimeReference::Max;
-
- return result;
+ return t1;
}
/*!
@@ -776,11 +533,7 @@ void QDeadlineTimer::setDeadline(qint64 msecs, Qt::TimerType timerType) noexcept
}
type = timerType;
-
- TimeReference ref;
- if (!ref.addMilliseconds(msecs))
- ref.saturate(msecs > 0);
- ref.updateTimer(t1, t2);
+ t1 = add_saturate(0, milliseconds{msecs});
}
/*!
@@ -798,13 +551,7 @@ void QDeadlineTimer::setDeadline(qint64 msecs, Qt::TimerType timerType) noexcept
void QDeadlineTimer::setPreciseDeadline(qint64 secs, qint64 nsecs, Qt::TimerType timerType) noexcept
{
type = timerType;
-
- // We don't pass the seconds to the constructor, because we don't know
- // at this point if t1 holds the seconds or nanoseconds; it's platform specific.
- TimeReference ref;
- if (!ref.addSecsAndNSecs(secs, nsecs))
- ref.saturate(TimeReference::sign(secs, nsecs));
- ref.updateTimer(t1, t2);
+ t1 = add_saturate(0, seconds{secs}, nanoseconds{nsecs});
}
/*!
@@ -820,11 +567,7 @@ QDeadlineTimer QDeadlineTimer::addNSecs(QDeadlineTimer dt, qint64 nsecs) noexcep
if (dt.isForever())
return dt;
- TimeReference ref(dt.t1, dt.t2);
- if (!ref.addNanoseconds(nsecs))
- ref.saturate(nsecs > 0);
- ref.updateTimer(dt.t1, dt.t2);
-
+ dt.t1 = add_saturate(dt.t1, nanoseconds{nsecs});
return dt;
}
@@ -837,11 +580,22 @@ QDeadlineTimer QDeadlineTimer::addNSecs(QDeadlineTimer dt, qint64 nsecs) noexcep
The QDeadlineTimer object will be constructed with the specified \a timerType.
*/
+QDeadlineTimer QDeadlineTimer::current(Qt::TimerType timerType) noexcept
+{
+ // ensure we get nanoseconds; this will work so long as steady_clock's
+ // time_point isn't of finer resolution (picoseconds)
+ std::chrono::nanoseconds ns = std::chrono::steady_clock::now().time_since_epoch();
+
+ QDeadlineTimer result;
+ result.t1 = ns.count();
+ result.type = timerType;
+ return result;
+}
/*!
- \fn bool QDeadlineTimer::operator==(QDeadlineTimer d1, QDeadlineTimer d2)
+ \fn bool QDeadlineTimer::operator==(const QDeadlineTimer &lhs, const QDeadlineTimer &rhs)
- Returns true if the deadline on \a d1 and the deadline in \a d2 are the
+ Returns true if the deadline on \a lhs and the deadline in \a rhs are the
same, false otherwise. The timer type used to create the two deadlines is
ignored. This function is equivalent to:
@@ -852,9 +606,9 @@ QDeadlineTimer QDeadlineTimer::addNSecs(QDeadlineTimer dt, qint64 nsecs) noexcep
*/
/*!
- \fn bool QDeadlineTimer::operator!=(QDeadlineTimer d1, QDeadlineTimer d2)
+ \fn bool QDeadlineTimer::operator!=(const QDeadlineTimer &lhs, const QDeadlineTimer &rhs)
- Returns true if the deadline on \a d1 and the deadline in \a d2 are
+ Returns true if the deadline on \a lhs and the deadline in \a rhs are
different, false otherwise. The timer type used to create the two deadlines
is ignored. This function is equivalent to:
@@ -865,10 +619,10 @@ QDeadlineTimer QDeadlineTimer::addNSecs(QDeadlineTimer dt, qint64 nsecs) noexcep
*/
/*!
- \fn bool QDeadlineTimer::operator<(QDeadlineTimer d1, QDeadlineTimer d2)
+ \fn bool QDeadlineTimer::operator<(const QDeadlineTimer &lhs, const QDeadlineTimer &rhs)
- Returns true if the deadline on \a d1 is earlier than the deadline in \a
- d2, false otherwise. The timer type used to create the two deadlines is
+ Returns true if the deadline on \a lhs is earlier than the deadline in \a
+ rhs, false otherwise. The timer type used to create the two deadlines is
ignored. This function is equivalent to:
\snippet code/src_corelib_kernel_qdeadlinetimer.cpp 10
@@ -878,10 +632,10 @@ QDeadlineTimer QDeadlineTimer::addNSecs(QDeadlineTimer dt, qint64 nsecs) noexcep
*/
/*!
- \fn bool QDeadlineTimer::operator<=(QDeadlineTimer d1, QDeadlineTimer d2)
+ \fn bool QDeadlineTimer::operator<=(const QDeadlineTimer &lhs, const QDeadlineTimer &rhs)
- Returns true if the deadline on \a d1 is earlier than or the same as the
- deadline in \a d2, false otherwise. The timer type used to create the two
+ Returns true if the deadline on \a lhs is earlier than or the same as the
+ deadline in \a rhs, false otherwise. The timer type used to create the two
deadlines is ignored. This function is equivalent to:
\snippet code/src_corelib_kernel_qdeadlinetimer.cpp 11
@@ -891,10 +645,10 @@ QDeadlineTimer QDeadlineTimer::addNSecs(QDeadlineTimer dt, qint64 nsecs) noexcep
*/
/*!
- \fn bool QDeadlineTimer::operator>(QDeadlineTimer d1, QDeadlineTimer d2)
+ \fn bool QDeadlineTimer::operator>(const QDeadlineTimer &lhs, const QDeadlineTimer &rhs)
- Returns true if the deadline on \a d1 is later than the deadline in \a
- d2, false otherwise. The timer type used to create the two deadlines is
+ Returns true if the deadline on \a lhs is later than the deadline in \a
+ rhs, false otherwise. The timer type used to create the two deadlines is
ignored. This function is equivalent to:
\snippet code/src_corelib_kernel_qdeadlinetimer.cpp 12
@@ -904,10 +658,10 @@ QDeadlineTimer QDeadlineTimer::addNSecs(QDeadlineTimer dt, qint64 nsecs) noexcep
*/
/*!
- \fn bool QDeadlineTimer::operator>=(QDeadlineTimer d1, QDeadlineTimer d2)
+ \fn bool QDeadlineTimer::operator>=(const QDeadlineTimer &lhs, const QDeadlineTimer &rhs)
- Returns true if the deadline on \a d1 is later than or the same as the
- deadline in \a d2, false otherwise. The timer type used to create the two
+ Returns true if the deadline on \a lhs is later than or the same as the
+ deadline in \a rhs, false otherwise. The timer type used to create the two
deadlines is ignored. This function is equivalent to:
\snippet code/src_corelib_kernel_qdeadlinetimer.cpp 13
@@ -931,11 +685,7 @@ QDeadlineTimer operator+(QDeadlineTimer dt, qint64 msecs)
if (dt.isForever())
return dt;
- TimeReference ref(dt.t1, dt.t2);
- if (!ref.addMilliseconds(msecs))
- ref.saturate(msecs > 0);
- ref.updateTimer(dt.t1, dt.t2);
-
+ dt.t1 = add_saturate(dt.t1, milliseconds{msecs});
return dt;
}
@@ -1001,11 +751,4 @@ QDeadlineTimer operator+(QDeadlineTimer dt, qint64 msecs)
Returns the time remaining before the deadline.
*/
-/*!
- \fn QPair<qint64, unsigned> QDeadlineTimer::_q_data() const
- \internal
-*/
-
-// the rest of the functions are in qelapsedtimer_xxx.cpp
-
QT_END_NAMESPACE
diff --git a/src/corelib/kernel/qdeadlinetimer.h b/src/corelib/kernel/qdeadlinetimer.h
index 980a2866e0..515cdb5387 100644
--- a/src/corelib/kernel/qdeadlinetimer.h
+++ b/src/corelib/kernel/qdeadlinetimer.h
@@ -7,7 +7,6 @@
#include <QtCore/qelapsedtimer.h>
#include <QtCore/qmetatype.h>
#include <QtCore/qnamespace.h>
-#include <QtCore/qpair.h>
#ifdef max
// un-pollute the namespace. We need std::numeric_limits::max() and std::chrono::duration::max()
@@ -23,16 +22,18 @@ QT_BEGIN_NAMESPACE
class Q_CORE_EXPORT QDeadlineTimer
{
public:
- enum ForeverConstant { Forever };
+ enum class ForeverConstant { Forever };
+ static constexpr ForeverConstant Forever = ForeverConstant::Forever;
- constexpr QDeadlineTimer(Qt::TimerType type_ = Qt::CoarseTimer) noexcept
- : t1(0), t2(0), type(type_) {}
+ constexpr QDeadlineTimer() noexcept = default;
+ constexpr explicit QDeadlineTimer(Qt::TimerType type_) noexcept
+ : type(type_) {}
constexpr QDeadlineTimer(ForeverConstant, Qt::TimerType type_ = Qt::CoarseTimer) noexcept
- : t1((std::numeric_limits<qint64>::max)()), t2(0), type(type_) {}
+ : t1((std::numeric_limits<qint64>::max)()), type(type_) {}
explicit QDeadlineTimer(qint64 msecs, Qt::TimerType type = Qt::CoarseTimer) noexcept;
void swap(QDeadlineTimer &other) noexcept
- { std::swap(t1, other.t1); std::swap(t2, other.t2); std::swap(type, other.type); }
+ { std::swap(t1, other.t1); std::swap(type, other.type); }
constexpr bool isForever() const noexcept
{ return t1 == (std::numeric_limits<qint64>::max)(); }
@@ -57,19 +58,6 @@ public:
static QDeadlineTimer addNSecs(QDeadlineTimer dt, qint64 nsecs) noexcept Q_DECL_PURE_FUNCTION;
static QDeadlineTimer current(Qt::TimerType timerType = Qt::CoarseTimer) noexcept;
- friend bool operator==(QDeadlineTimer d1, QDeadlineTimer d2) noexcept
- { return d1.t1 == d2.t1 && d1.t2 == d2.t2; }
- friend bool operator!=(QDeadlineTimer d1, QDeadlineTimer d2) noexcept
- { return !(d1 == d2); }
- friend bool operator<(QDeadlineTimer d1, QDeadlineTimer d2) noexcept
- { return d1.t1 < d2.t1 || (d1.t1 == d2.t1 && d1.t2 < d2.t2); }
- friend bool operator<=(QDeadlineTimer d1, QDeadlineTimer d2) noexcept
- { return d1 == d2 || d1 < d2; }
- friend bool operator>(QDeadlineTimer d1, QDeadlineTimer d2) noexcept
- { return d2 < d1; }
- friend bool operator>=(QDeadlineTimer d1, QDeadlineTimer d2) noexcept
- { return !(d1 < d2); }
-
friend Q_CORE_EXPORT QDeadlineTimer operator+(QDeadlineTimer dt, qint64 msecs);
friend QDeadlineTimer operator+(qint64 msecs, QDeadlineTimer dt)
{ return dt + msecs; }
@@ -82,25 +70,20 @@ public:
QDeadlineTimer &operator-=(qint64 msecs)
{ *this = *this + (-msecs); return *this; }
- template <class Clock, class Duration>
+ template <class Clock, class Duration = typename Clock::duration>
QDeadlineTimer(std::chrono::time_point<Clock, Duration> deadline_,
Qt::TimerType type_ = Qt::CoarseTimer) : t2(0)
{ setDeadline(deadline_, type_); }
- template <class Clock, class Duration>
+ template <class Clock, class Duration = typename Clock::duration>
QDeadlineTimer &operator=(std::chrono::time_point<Clock, Duration> deadline_)
{ setDeadline(deadline_); return *this; }
- template <class Clock, class Duration>
- void setDeadline(std::chrono::time_point<Clock, Duration> deadline_,
- Qt::TimerType type_ = Qt::CoarseTimer)
- { setRemainingTime(deadline_ == deadline_.max() ? Duration::max() : deadline_ - Clock::now(), type_); }
+ template <class Clock, class Duration = typename Clock::duration>
+ void setDeadline(std::chrono::time_point<Clock, Duration> tp,
+ Qt::TimerType type_ = Qt::CoarseTimer);
template <class Clock, class Duration = typename Clock::duration>
- std::chrono::time_point<Clock, Duration> deadline() const
- {
- auto val = std::chrono::nanoseconds(rawRemainingTimeNSecs()) + Clock::now();
- return std::chrono::time_point_cast<Duration>(val);
- }
+ std::chrono::time_point<Clock, Duration> deadline() const;
template <class Rep, class Period>
QDeadlineTimer(std::chrono::duration<Rep, Period> remaining, Qt::TimerType type_ = Qt::CoarseTimer)
@@ -114,10 +97,11 @@ public:
template <class Rep, class Period>
void setRemainingTime(std::chrono::duration<Rep, Period> remaining, Qt::TimerType type_ = Qt::CoarseTimer)
{
+ using namespace std::chrono;
if (remaining == remaining.max())
*this = QDeadlineTimer(Forever, type_);
else
- setPreciseRemainingTime(0, std::chrono::nanoseconds(remaining).count(), type_);
+ setPreciseRemainingTime(0, ceil<nanoseconds>(remaining).count(), type_);
}
std::chrono::nanoseconds remainingTimeAsDuration() const noexcept
@@ -141,44 +125,55 @@ public:
{ return dt = dt + value; }
private:
- qint64 t1;
- unsigned t2;
- unsigned type;
+ friend bool comparesEqual(const QDeadlineTimer &lhs,
+ const QDeadlineTimer &rhs) noexcept
+ {
+ return lhs.t1 == rhs.t1;
+ }
+ friend Qt::strong_ordering compareThreeWay(const QDeadlineTimer &lhs,
+ const QDeadlineTimer &rhs) noexcept
+ {
+ return Qt::compareThreeWay(lhs.t1, rhs.t1);
+ }
+ Q_DECLARE_STRONGLY_ORDERED(QDeadlineTimer)
- qint64 rawRemainingTimeNSecs() const noexcept;
+ qint64 t1 = 0;
+#if QT_VERSION < QT_VERSION_CHECK(7, 0, 0)
+ unsigned t2 = 0;
+#endif
+ unsigned type = Qt::CoarseTimer;
-public:
- // This is not a public function, it's here only for Qt's internal convenience...
- QPair<qint64, unsigned> _q_data() const { return qMakePair(t1, t2); }
+ qint64 rawRemainingTimeNSecs() const noexcept;
};
-#if defined(Q_OS_DARWIN) || defined(Q_OS_LINUX) || (defined(Q_CC_MSVC) && Q_CC_MSVC >= 1900)
-// We know for these OS/compilers that the std::chrono::steady_clock uses the same
-// reference time as QDeadlineTimer
-
-template <> inline std::chrono::steady_clock::time_point
-QDeadlineTimer::deadline<std::chrono::steady_clock, std::chrono::steady_clock::duration>() const
+template<class Clock, class Duration>
+std::chrono::time_point<Clock, Duration> QDeadlineTimer::deadline() const
{
- return std::chrono::steady_clock::time_point(std::chrono::nanoseconds(deadlineNSecs()));
+ using namespace std::chrono;
+ if constexpr (std::is_same_v<Clock, steady_clock>) {
+ auto val = duration_cast<Duration>(nanoseconds(deadlineNSecs()));
+ return time_point<Clock, Duration>(val);
+ } else {
+ auto val = nanoseconds(rawRemainingTimeNSecs()) + Clock::now();
+ return time_point_cast<Duration>(val);
+ }
}
-template <> inline void
-QDeadlineTimer::setDeadline<std::chrono::steady_clock, std::chrono::steady_clock::duration>(std::chrono::steady_clock::time_point tp, Qt::TimerType type_)
+template<class Clock, class Duration>
+void QDeadlineTimer::setDeadline(std::chrono::time_point<Clock, Duration> tp, Qt::TimerType type_)
{
using namespace std::chrono;
if (tp == tp.max()) {
*this = Forever;
type = type_;
- } else if (type_ != Qt::PreciseTimer) {
- // if we aren't using PreciseTimer, then we need to convert
- setPreciseRemainingTime(0, duration_cast<nanoseconds>(tp - steady_clock::now()).count(), type_);
- } else {
+ } else if constexpr (std::is_same_v<Clock, steady_clock>) {
setPreciseDeadline(0,
duration_cast<nanoseconds>(tp.time_since_epoch()).count(),
type_);
+ } else {
+ setPreciseRemainingTime(0, duration_cast<nanoseconds>(tp - Clock::now()).count(), type_);
}
}
-#endif
Q_DECLARE_SHARED(QDeadlineTimer)
diff --git a/src/corelib/kernel/qdeadlinetimer_p.h b/src/corelib/kernel/qdeadlinetimer_p.h
deleted file mode 100644
index 41054435ba..0000000000
--- a/src/corelib/kernel/qdeadlinetimer_p.h
+++ /dev/null
@@ -1,34 +0,0 @@
-// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
-
-#ifndef QDEADLINETIMER_P_H
-#define QDEADLINETIMER_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 <QtCore/private/qglobal_p.h>
-
-QT_BEGIN_NAMESPACE
-
-enum {
-#if defined(Q_OS_UNIX) && !defined(Q_OS_DARWIN)
- // t1 contains seconds and t2 contains nanoseconds
- QDeadlineTimerNanosecondsInT2 = 1
-#else
- // t1 contains nanoseconds, t2 is always zero
- QDeadlineTimerNanosecondsInT2 = 0
-#endif
-};
-
-QT_END_NAMESPACE
-
-#endif
diff --git a/src/corelib/kernel/qelapsedtimer.cpp b/src/corelib/kernel/qelapsedtimer.cpp
index 39ddb3491c..c4308a0b8f 100644
--- a/src/corelib/kernel/qelapsedtimer.cpp
+++ b/src/corelib/kernel/qelapsedtimer.cpp
@@ -14,6 +14,8 @@ QT_BEGIN_NAMESPACE
\reentrant
\ingroup tools
+ \compares strong
+
The QElapsedTimer class is usually used to quickly calculate how much
time has elapsed between two events. Its API is similar to that of QTime,
so code that was using that can be ported quickly to the new class.
@@ -75,7 +77,7 @@ QT_BEGIN_NAMESPACE
that the clock used is the same as QElapsedTimer (see
QElapsedTimer::clockType()).
- \sa QTime, QTimer, QDeadlineTimer
+ \sa QTime, QChronoTimer, QDeadlineTimer
*/
/*!
@@ -155,8 +157,7 @@ QT_BEGIN_NAMESPACE
Returns \c true if \a lhs and \a rhs contain different times, false otherwise.
*/
/*!
- \fn bool operator<(const QElapsedTimer &lhs, const QElapsedTimer &rhs) noexcept
- \relates QElapsedTimer
+ \fn bool QElapsedTimer::operator<(const QElapsedTimer &lhs, const QElapsedTimer &rhs) noexcept
Returns \c true if \a lhs was started before \a rhs, false otherwise.
@@ -165,9 +166,229 @@ QT_BEGIN_NAMESPACE
function will return false.
*/
+/*!
+ \fn QElapsedTimer::clockType() noexcept
+
+ Returns the clock type that this QElapsedTimer implementation uses.
+
+ Since Qt 6.6, QElapsedTimer uses \c{std::chrono::steady_clock}, so the
+ clock type is always \l MonotonicClock.
+
+ \sa isMonotonic()
+*/
+
+QElapsedTimer::ClockType QElapsedTimer::clockType() noexcept
+{
+ // we use std::chrono::steady_clock
+ return MonotonicClock;
+}
+
+/*!
+ \fn QElapsedTimer::isMonotonic() noexcept
+
+ Returns \c true if this is a monotonic clock, false otherwise. See the
+ information on the different clock types to understand which ones are
+ monotonic.
+
+ Since Qt 6.6, QElapsedTimer uses \c{std::chrono::steady_clock}, so this
+ function now always returns true.
+
+ \sa clockType(), QElapsedTimer::ClockType
+*/
+bool QElapsedTimer::isMonotonic() noexcept
+{
+ // We trust std::chrono::steady_clock to be steady (monotonic); if the
+ // Standard Library is lying to us, users must complain to their vendor.
+ return true;
+}
+
+/*!
+ \typealias QElapsedTimer::Duration
+ Synonym for \c std::chrono::nanoseconds.
+*/
+
+/*!
+ \typealias QElapsedTimer::TimePoint
+ Synonym for \c {std::chrono::time_point<std::chrono::steady_clock, Duration>}.
+*/
+
+/*!
+ Starts this timer. Once started, a timer value can be checked with elapsed() or msecsSinceReference().
+
+ Normally, a timer is started just before a lengthy operation, such as:
+ \snippet qelapsedtimer/main.cpp 0
+
+ Also, starting a timer makes it valid again.
+
+ \sa restart(), invalidate(), elapsed()
+*/
+void QElapsedTimer::start() noexcept
+{
+ static_assert(sizeof(t1) == sizeof(Duration::rep));
+
+ // This assignment will work so long as TimePoint uses the same time
+ // duration or one of finer granularity than steady_clock::time_point. That
+ // means it will work until the first steady_clock using picoseconds.
+ TimePoint now = std::chrono::steady_clock::now();
+ t1 = now.time_since_epoch().count();
+ QT6_ONLY(t2 = 0);
+}
+
+/*!
+ Restarts the timer and returns the number of milliseconds elapsed since
+ the previous start.
+ This function is equivalent to obtaining the elapsed time with elapsed()
+ and then starting the timer again with start(), but it does so in one
+ single operation, avoiding the need to obtain the clock value twice.
+
+ Calling this function on a QElapsedTimer that is invalid
+ results in undefined behavior.
+
+ The following example illustrates how to use this function to calibrate a
+ parameter to a slow operation (for example, an iteration count) so that
+ this operation takes at least 250 milliseconds:
+
+ \snippet qelapsedtimer/main.cpp 3
+
+ \sa start(), invalidate(), elapsed(), isValid()
+*/
+qint64 QElapsedTimer::restart() noexcept
+{
+ QElapsedTimer old = *this;
+ start();
+ return old.msecsTo(*this);
+}
+
+/*!
+ \since 6.6
+
+ Returns a \c{std::chrono::nanoseconds} with the time since this QElapsedTimer was last
+ started.
+
+ Calling this function on a QElapsedTimer that is invalid
+ results in undefined behavior.
+
+ On platforms that do not provide nanosecond resolution, the value returned
+ will be the best estimate available.
+
+ \sa start(), restart(), hasExpired(), invalidate()
+*/
+auto QElapsedTimer::durationElapsed() const noexcept -> Duration
+{
+ TimePoint then{Duration(t1)};
+ return std::chrono::steady_clock::now() - then;
+}
+
+/*!
+ \since 4.8
+
+ Returns the number of nanoseconds since this QElapsedTimer was last
+ started.
+
+ Calling this function on a QElapsedTimer that is invalid
+ results in undefined behavior.
+
+ On platforms that do not provide nanosecond resolution, the value returned
+ will be the best estimate available.
+
+ \sa start(), restart(), hasExpired(), invalidate()
+*/
+qint64 QElapsedTimer::nsecsElapsed() const noexcept
+{
+ return durationElapsed().count();
+}
+
+/*!
+ Returns the number of milliseconds since this QElapsedTimer was last
+ started.
+
+ Calling this function on a QElapsedTimer that is invalid
+ results in undefined behavior.
+
+ \sa start(), restart(), hasExpired(), isValid(), invalidate()
+*/
+qint64 QElapsedTimer::elapsed() const noexcept
+{
+ using namespace std::chrono;
+ return duration_cast<milliseconds>(durationElapsed()).count();
+}
+
+/*!
+ Returns the number of milliseconds between last time this QElapsedTimer
+ object was started and its reference clock's start.
+
+ This number is usually arbitrary for all clocks except the
+ QElapsedTimer::SystemTime clock. For that clock type, this number is the
+ number of milliseconds since January 1st, 1970 at 0:00 UTC (that is, it
+ is the Unix time expressed in milliseconds).
+
+ On Linux, Windows and Apple platforms, this value is usually the time
+ since the system boot, though it usually does not include the time the
+ system has spent in sleep states.
+
+ \sa clockType(), elapsed()
+*/
+qint64 QElapsedTimer::msecsSinceReference() const noexcept
+{
+ using namespace std::chrono;
+ return duration_cast<milliseconds>(Duration(t1)).count();
+}
+
+/*!
+ \since 6.6
+
+ Returns the time difference between this QElapsedTimer and \a other as a
+ \c{std::chrono::nanoseconds}. If \a other was started before this object,
+ the returned value will be negative. If it was started later, the returned
+ value will be positive.
+
+ The return value is undefined if this object or \a other were invalidated.
+
+ \sa secsTo(), elapsed()
+*/
+auto QElapsedTimer::durationTo(const QElapsedTimer &other) const noexcept -> Duration
+{
+ Duration d1(t1);
+ Duration d2(other.t1);
+ return d2 - d1;
+}
+
+/*!
+ Returns the number of milliseconds between this QElapsedTimer and \a
+ other. If \a other was started before this object, the returned value
+ will be negative. If it was started later, the returned value will be
+ positive.
+
+ The return value is undefined if this object or \a other were invalidated.
+
+ \sa secsTo(), elapsed()
+*/
+qint64 QElapsedTimer::msecsTo(const QElapsedTimer &other) const noexcept
+{
+ using namespace std::chrono;
+ return duration_cast<milliseconds>(durationTo(other)).count();
+}
+
+/*!
+ Returns the number of seconds between this QElapsedTimer and \a other. If
+ \a other was started before this object, the returned value will be
+ negative. If it was started later, the returned value will be positive.
+
+ Calling this function on or with a QElapsedTimer that is invalid
+ results in undefined behavior.
+
+ \sa msecsTo(), elapsed()
+*/
+qint64 QElapsedTimer::secsTo(const QElapsedTimer &other) const noexcept
+{
+ using namespace std::chrono;
+ return duration_cast<seconds>(durationTo(other)).count();
+}
+
static const qint64 invalidData = Q_INT64_C(0x8000000000000000);
/*!
+ \fn QElapsedTimer::invalidate() noexcept
Marks this QElapsedTimer object as invalid.
An invalid object can be checked with isValid(). Calculations of timer
@@ -193,10 +414,12 @@ bool QElapsedTimer::isValid() const noexcept
}
/*!
- Returns \c true if this QElapsedTimer has already expired by \a timeout
- milliseconds (that is, more than \a timeout milliseconds have elapsed).
- The value of \a timeout can be -1 to indicate that this timer does not
- expire, in which case this function will always return false.
+ Returns \c true if elapsed() exceeds the given \a timeout, otherwise \c false.
+
+ A negative \a timeout is interpreted as infinite, so \c false is returned in
+ this case. Otherwise, this is equivalent to \c {elapsed() > timeout}. You
+ can do the same for a duration by comparing durationElapsed() to a duration
+ timeout.
\sa elapsed(), QDeadlineTimer
*/
@@ -207,4 +430,9 @@ bool QElapsedTimer::hasExpired(qint64 timeout) const noexcept
return quint64(elapsed()) > quint64(timeout);
}
+bool operator<(const QElapsedTimer &lhs, const QElapsedTimer &rhs) noexcept
+{
+ return lhs.t1 < rhs.t1;
+}
+
QT_END_NAMESPACE
diff --git a/src/corelib/kernel/qelapsedtimer.h b/src/corelib/kernel/qelapsedtimer.h
index a4db43304a..e71573456d 100644
--- a/src/corelib/kernel/qelapsedtimer.h
+++ b/src/corelib/kernel/qelapsedtimer.h
@@ -4,10 +4,12 @@
#ifndef QELAPSEDTIMER_H
#define QELAPSEDTIMER_H
+#include <QtCore/qcompare.h>
#include <QtCore/qglobal.h>
-QT_BEGIN_NAMESPACE
+#include <chrono>
+QT_BEGIN_NAMESPACE
class Q_CORE_EXPORT QElapsedTimer
{
@@ -21,11 +23,11 @@ public:
PerformanceCounter
};
- constexpr QElapsedTimer()
- : t1(Q_INT64_C(0x8000000000000000)),
- t2(Q_INT64_C(0x8000000000000000))
- {
- }
+ // similar to std::chrono::*_clock
+ using Duration = std::chrono::nanoseconds;
+ using TimePoint = std::chrono::time_point<std::chrono::steady_clock, Duration>;
+
+ constexpr QElapsedTimer() = default;
static ClockType clockType() noexcept;
static bool isMonotonic() noexcept;
@@ -35,24 +37,52 @@ public:
void invalidate() noexcept;
bool isValid() const noexcept;
+ Duration durationElapsed() const noexcept;
qint64 nsecsElapsed() const noexcept;
qint64 elapsed() const noexcept;
bool hasExpired(qint64 timeout) const noexcept;
qint64 msecsSinceReference() const noexcept;
+ Duration durationTo(const QElapsedTimer &other) const noexcept;
qint64 msecsTo(const QElapsedTimer &other) const noexcept;
qint64 secsTo(const QElapsedTimer &other) const noexcept;
-
- friend bool operator==(const QElapsedTimer &lhs, const QElapsedTimer &rhs) noexcept
- { return lhs.t1 == rhs.t1 && lhs.t2 == rhs.t2; }
- friend bool operator!=(const QElapsedTimer &lhs, const QElapsedTimer &rhs) noexcept
- { return !(lhs == rhs); }
-
friend bool Q_CORE_EXPORT operator<(const QElapsedTimer &lhs, const QElapsedTimer &rhs) noexcept;
private:
- qint64 t1;
- qint64 t2;
+ friend bool comparesEqual(const QElapsedTimer &lhs, const QElapsedTimer &rhs) noexcept
+ {
+ return lhs.t1 == rhs.t1 && lhs.t2 == rhs.t2;
+ }
+ Q_DECLARE_EQUALITY_COMPARABLE(QElapsedTimer)
+
+ friend Qt::strong_ordering compareThreeWay(const QElapsedTimer &lhs,
+ const QElapsedTimer &rhs) noexcept
+ {
+ return Qt::compareThreeWay(lhs.t1, rhs.t1);
+ }
+
+#if defined(__cpp_lib_three_way_comparison)
+ friend std::strong_ordering
+ operator<=>(const QElapsedTimer &lhs, const QElapsedTimer &rhs) noexcept
+ {
+ return compareThreeWay(lhs, rhs);
+ }
+#else
+ friend bool operator>(const QElapsedTimer &lhs, const QElapsedTimer &rhs) noexcept
+ {
+ return is_gt(compareThreeWay(lhs, rhs));
+ }
+ friend bool operator<=(const QElapsedTimer &lhs, const QElapsedTimer &rhs) noexcept
+ {
+ return is_lteq(compareThreeWay(lhs, rhs));
+ }
+ friend bool operator>=(const QElapsedTimer &lhs, const QElapsedTimer &rhs) noexcept
+ {
+ return is_gteq(compareThreeWay(lhs, rhs));
+ }
+#endif // defined(__cpp_lib_three_way_comparison)
+ qint64 t1 = Q_INT64_C(0x8000000000000000);
+ qint64 t2 = Q_INT64_C(0x8000000000000000);
};
QT_END_NAMESPACE
diff --git a/src/corelib/kernel/qelapsedtimer_generic.cpp b/src/corelib/kernel/qelapsedtimer_generic.cpp
deleted file mode 100644
index 874122f493..0000000000
--- a/src/corelib/kernel/qelapsedtimer_generic.cpp
+++ /dev/null
@@ -1,169 +0,0 @@
-// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
-
-#include "qelapsedtimer.h"
-#include "qdeadlinetimer.h"
-#include "qdatetime.h"
-
-QT_BEGIN_NAMESPACE
-
-/*!
- Returns the clock type that this QElapsedTimer implementation uses.
-
- \sa isMonotonic()
-*/
-QElapsedTimer::ClockType QElapsedTimer::clockType() noexcept
-{
- return SystemTime;
-}
-
-/*!
- Returns \c true if this is a monotonic clock, false otherwise. See the
- information on the different clock types to understand which ones are
- monotonic.
-
- \sa clockType(), QElapsedTimer::ClockType
-*/
-bool QElapsedTimer::isMonotonic() noexcept
-{
- return false;
-}
-
-/*!
- Starts this timer. Once started, a timer value can be checked with elapsed() or msecsSinceReference().
-
- Normally, a timer is started just before a lengthy operation, such as:
- \snippet qelapsedtimer/main.cpp 0
-
- Also, starting a timer makes it valid again.
-
- \sa restart(), invalidate(), elapsed()
-*/
-void QElapsedTimer::start() noexcept
-{
- restart();
-}
-
-/*!
- Restarts the timer and returns the number of milliseconds elapsed since
- the previous start.
- This function is equivalent to obtaining the elapsed time with elapsed()
- and then starting the timer again with start(), but it does so in one
- single operation, avoiding the need to obtain the clock value twice.
-
- Calling this function on a QElapsedTimer that is invalid
- results in undefined behavior.
-
- The following example illustrates how to use this function to calibrate a
- parameter to a slow operation (for example, an iteration count) so that
- this operation takes at least 250 milliseconds:
-
- \snippet qelapsedtimer/main.cpp 3
-
- \sa start(), invalidate(), elapsed(), isValid()
-*/
-qint64 QElapsedTimer::restart() noexcept
-{
- qint64 old = t1;
- t1 = QDateTime::currentMSecsSinceEpoch();
- t2 = 0;
- return t1 - old;
-}
-
-/*! \since 4.8
-
- Returns the number of nanoseconds since this QElapsedTimer was last
- started.
-
- Calling this function on a QElapsedTimer that is invalid
- results in undefined behavior.
-
- On platforms that do not provide nanosecond resolution, the value returned
- will be the best estimate available.
-
- \sa start(), restart(), hasExpired(), invalidate()
-*/
-qint64 QElapsedTimer::nsecsElapsed() const noexcept
-{
- return elapsed() * 1000000;
-}
-
-/*!
- Returns the number of milliseconds since this QElapsedTimer was last
- started.
-
- Calling this function on a QElapsedTimer that is invalid
- results in undefined behavior.
-
- \sa start(), restart(), hasExpired(), isValid(), invalidate()
-*/
-qint64 QElapsedTimer::elapsed() const noexcept
-{
- return QDateTime::currentMSecsSinceEpoch() - t1;
-}
-
-/*!
- Returns the number of milliseconds between last time this QElapsedTimer
- object was started and its reference clock's start.
-
- This number is usually arbitrary for all clocks except the
- QElapsedTimer::SystemTime clock. For that clock type, this number is the
- number of milliseconds since January 1st, 1970 at 0:00 UTC (that is, it
- is the Unix time expressed in milliseconds).
-
- On Linux, Windows and Apple platforms, this value is usually the time
- since the system boot, though it usually does not include the time the
- system has spent in sleep states.
-
- \sa clockType(), elapsed()
-*/
-qint64 QElapsedTimer::msecsSinceReference() const noexcept
-{
- return t1;
-}
-
-/*!
- Returns the number of milliseconds between this QElapsedTimer and \a
- other. If \a other was started before this object, the returned value
- will be negative. If it was started later, the returned value will be
- positive.
-
- The return value is undefined if this object or \a other were invalidated.
-
- \sa secsTo(), elapsed()
-*/
-qint64 QElapsedTimer::msecsTo(const QElapsedTimer &other) const noexcept
-{
- qint64 diff = other.t1 - t1;
- return diff;
-}
-
-/*!
- Returns the number of seconds between this QElapsedTimer and \a other. If
- \a other was started before this object, the returned value will be
- negative. If it was started later, the returned value will be positive.
-
- Calling this function on or with a QElapsedTimer that is invalid
- results in undefined behavior.
-
- \sa msecsTo(), elapsed()
-*/
-qint64 QElapsedTimer::secsTo(const QElapsedTimer &other) const noexcept
-{
- return msecsTo(other) / 1000;
-}
-
-bool operator<(const QElapsedTimer &lhs, const QElapsedTimer &rhs) noexcept
-{
- return lhs.t1 < rhs.t1;
-}
-
-QDeadlineTimer QDeadlineTimer::current(Qt::TimerType timerType) noexcept
-{
- QDeadlineTimer result;
- result.t1 = QDateTime::currentMSecsSinceEpoch() * 1000 * 1000;
- result.type = timerType;
- return result;
-}
-
-QT_END_NAMESPACE
diff --git a/src/corelib/kernel/qelapsedtimer_mac.cpp b/src/corelib/kernel/qelapsedtimer_mac.cpp
deleted file mode 100644
index bc87202d65..0000000000
--- a/src/corelib/kernel/qelapsedtimer_mac.cpp
+++ /dev/null
@@ -1,130 +0,0 @@
-// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
-
-// ask for the latest POSIX, just in case
-#define _POSIX_C_SOURCE 200809L
-
-#include "qelapsedtimer.h"
-#include "qdeadlinetimer.h"
-#include "qdeadlinetimer_p.h"
-#include <sys/time.h>
-#include <time.h>
-#include <unistd.h>
-
-#include <mach/mach_time.h>
-#include <private/qcore_unix_p.h>
-
-QT_BEGIN_NAMESPACE
-
-#ifdef __LP64__
-typedef __int128_t LargeInt;
-#else
-typedef qint64 LargeInt;
-#endif
-
-QElapsedTimer::ClockType QElapsedTimer::clockType() noexcept
-{
- return MachAbsoluteTime;
-}
-
-bool QElapsedTimer::isMonotonic() noexcept
-{
- return true;
-}
-
-static mach_timebase_info_data_t info = { 0, 0 };
-static qint64 absoluteToNSecs(qint64 cpuTime)
-{
- if (info.denom == 0)
- mach_timebase_info(&info);
-
- // don't do multiplication & division if those are equal
- // (mathematically it would be the same, but it's computationally expensive)
- if (info.numer == info.denom)
- return cpuTime;
- qint64 nsecs = LargeInt(cpuTime) * info.numer / info.denom;
- return nsecs;
-}
-
-static qint64 absoluteToMSecs(qint64 cpuTime)
-{
- return absoluteToNSecs(cpuTime) / 1000000;
-}
-
-timespec qt_gettime() noexcept
-{
- timespec tv;
-
- uint64_t cpu_time = mach_absolute_time();
- uint64_t nsecs = absoluteToNSecs(cpu_time);
- tv.tv_sec = nsecs / 1000000000ull;
- tv.tv_nsec = nsecs - (tv.tv_sec * 1000000000ull);
- return tv;
-}
-
-void qt_nanosleep(timespec amount)
-{
- // Mac doesn't have clock_nanosleep, but it does have nanosleep.
- // nanosleep is POSIX.1-1993
-
- int r;
- EINTR_LOOP(r, nanosleep(&amount, &amount));
-}
-
-void QElapsedTimer::start() noexcept
-{
- t1 = mach_absolute_time();
- t2 = 0;
-}
-
-qint64 QElapsedTimer::restart() noexcept
-{
- qint64 old = t1;
- t1 = mach_absolute_time();
- t2 = 0;
-
- return absoluteToMSecs(t1 - old);
-}
-
-qint64 QElapsedTimer::nsecsElapsed() const noexcept
-{
- uint64_t cpu_time = mach_absolute_time();
- return absoluteToNSecs(cpu_time - t1);
-}
-
-qint64 QElapsedTimer::elapsed() const noexcept
-{
- uint64_t cpu_time = mach_absolute_time();
- return absoluteToMSecs(cpu_time - t1);
-}
-
-qint64 QElapsedTimer::msecsSinceReference() const noexcept
-{
- return absoluteToMSecs(t1);
-}
-
-qint64 QElapsedTimer::msecsTo(const QElapsedTimer &other) const noexcept
-{
- return absoluteToMSecs(other.t1 - t1);
-}
-
-qint64 QElapsedTimer::secsTo(const QElapsedTimer &other) const noexcept
-{
- return msecsTo(other) / 1000;
-}
-
-bool operator<(const QElapsedTimer &v1, const QElapsedTimer &v2) noexcept
-{
- return v1.t1 < v2.t1;
-}
-
-QDeadlineTimer QDeadlineTimer::current(Qt::TimerType timerType) noexcept
-{
- static_assert(!QDeadlineTimerNanosecondsInT2);
- QDeadlineTimer result;
- result.type = timerType;
- result.t1 = absoluteToNSecs(mach_absolute_time());
- return result;
-}
-
-QT_END_NAMESPACE
diff --git a/src/corelib/kernel/qelapsedtimer_unix.cpp b/src/corelib/kernel/qelapsedtimer_unix.cpp
deleted file mode 100644
index 9f2d75d0b8..0000000000
--- a/src/corelib/kernel/qelapsedtimer_unix.cpp
+++ /dev/null
@@ -1,223 +0,0 @@
-// Copyright (C) 2016 The Qt Company Ltd.
-// Copyright (C) 2016 Intel Corporation.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
-
-#include "qelapsedtimer.h"
-#include "qdeadlinetimer.h"
-#include "qdeadlinetimer_p.h"
-#if defined(Q_OS_VXWORKS)
-#include "qfunctions_vxworks.h"
-#else
-#include <sys/time.h>
-#include <time.h>
-#endif
-#include <unistd.h>
-
-#include <qatomic.h>
-#include "private/qcore_unix_p.h"
-
-#if defined(QT_NO_CLOCK_MONOTONIC) || defined(QT_BOOTSTRAPPED)
-// turn off the monotonic clock
-# ifdef _POSIX_MONOTONIC_CLOCK
-# undef _POSIX_MONOTONIC_CLOCK
-# endif
-# define _POSIX_MONOTONIC_CLOCK -1
-#endif
-
-QT_BEGIN_NAMESPACE
-
-/*
- * Design:
- *
- * POSIX offers a facility to select the system's monotonic clock when getting
- * the current timestamp. Whereas the functions are mandatory in POSIX.1-2008,
- * the presence of a monotonic clock is a POSIX Option (see the document
- * http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap02.html#tag_02_01_06 )
- *
- * The macro _POSIX_MONOTONIC_CLOCK can therefore assume the following values:
- * -1 monotonic clock is never supported on this system
- * 0 monotonic clock might be supported, runtime check is needed
- * >1 (such as 200809L) monotonic clock is always supported
- *
- * The unixCheckClockType() function will return the clock to use: either
- * CLOCK_MONOTONIC or CLOCK_REALTIME. In the case the POSIX option has a value
- * of zero, then this function stores a static that contains the clock to be
- * used.
- *
- * There's one extra case, which is when CLOCK_REALTIME isn't defined. When
- * that's the case, we'll emulate the clock_gettime function with gettimeofday.
- *
- * Conforming to:
- * POSIX.1b-1993 section "Clocks and Timers"
- * included in UNIX98 (Single Unix Specification v2)
- * included in POSIX.1-2001
- * see http://pubs.opengroup.org/onlinepubs/9699919799/functions/clock_getres.html
- */
-
-#if !defined(CLOCK_REALTIME)
-# define CLOCK_REALTIME 0
-static inline void qt_clock_gettime(int, struct timespec *ts)
-{
- // support clock_gettime with gettimeofday
- struct timeval tv;
- gettimeofday(&tv, 0);
- ts->tv_sec = tv.tv_sec;
- ts->tv_nsec = tv.tv_usec * 1000;
-}
-
-static inline int regularClock()
-{
- return 0;
-}
-#else
-static inline void qt_clock_gettime(clockid_t clock, struct timespec *ts)
-{
- clock_gettime(clock, ts);
-}
-
-static inline clock_t regularClockCheck()
-{
- struct timespec regular_clock_resolution;
- int r = -1;
-
-# ifdef CLOCK_MONOTONIC
- // try the monotonic clock
- r = clock_getres(CLOCK_MONOTONIC, &regular_clock_resolution);
-
-# ifdef Q_OS_LINUX
- // Despite glibc claiming that we should check at runtime, the Linux kernel
- // always supports the monotonic clock
- Q_ASSERT(r == 0);
- return CLOCK_MONOTONIC;
-# endif
-
- if (r == 0)
- return CLOCK_MONOTONIC;
-# endif
-
- // no monotonic, try the realtime clock
- r = clock_getres(CLOCK_REALTIME, &regular_clock_resolution);
- Q_ASSERT(r == 0);
- return CLOCK_REALTIME;
-}
-
-static inline clock_t regularClock()
-{
- static const clock_t clock = regularClockCheck();
- return clock;
-}
-#endif
-
-bool QElapsedTimer::isMonotonic() noexcept
-{
- return clockType() == MonotonicClock;
-}
-
-QElapsedTimer::ClockType QElapsedTimer::clockType() noexcept
-{
- return regularClock() == CLOCK_REALTIME ? SystemTime : MonotonicClock;
-}
-
-static inline void do_gettime(qint64 *sec, qint64 *frac)
-{
- timespec ts;
- qt_clock_gettime(regularClock(), &ts);
- *sec = ts.tv_sec;
- *frac = ts.tv_nsec;
-}
-
-// used in qcore_unix.cpp and qeventdispatcher_unix.cpp
-struct timespec qt_gettime() noexcept
-{
- qint64 sec, frac;
- do_gettime(&sec, &frac);
-
- timespec tv;
- tv.tv_sec = sec;
- tv.tv_nsec = frac;
-
- return tv;
-}
-
-void qt_nanosleep(timespec amount)
-{
- // We'd like to use clock_nanosleep.
- //
- // But clock_nanosleep is from POSIX.1-2001 and both are *not*
- // affected by clock changes when using relative sleeps, even for
- // CLOCK_REALTIME.
- //
- // nanosleep is POSIX.1-1993
-
- int r;
- EINTR_LOOP(r, nanosleep(&amount, &amount));
-}
-
-static qint64 elapsedAndRestart(qint64 sec, qint64 frac,
- qint64 *nowsec, qint64 *nowfrac)
-{
- do_gettime(nowsec, nowfrac);
- sec = *nowsec - sec;
- frac = *nowfrac - frac;
- return (sec * Q_INT64_C(1000000000) + frac) / Q_INT64_C(1000000);
-}
-
-void QElapsedTimer::start() noexcept
-{
- do_gettime(&t1, &t2);
-}
-
-qint64 QElapsedTimer::restart() noexcept
-{
- return elapsedAndRestart(t1, t2, &t1, &t2);
-}
-
-qint64 QElapsedTimer::nsecsElapsed() const noexcept
-{
- qint64 sec, frac;
- do_gettime(&sec, &frac);
- sec = sec - t1;
- frac = frac - t2;
- return sec * Q_INT64_C(1000000000) + frac;
-}
-
-qint64 QElapsedTimer::elapsed() const noexcept
-{
- return nsecsElapsed() / Q_INT64_C(1000000);
-}
-
-qint64 QElapsedTimer::msecsSinceReference() const noexcept
-{
- return t1 * Q_INT64_C(1000) + t2 / Q_INT64_C(1000000);
-}
-
-qint64 QElapsedTimer::msecsTo(const QElapsedTimer &other) const noexcept
-{
- qint64 secs = other.t1 - t1;
- qint64 fraction = other.t2 - t2;
- return (secs * Q_INT64_C(1000000000) + fraction) / Q_INT64_C(1000000);
-}
-
-qint64 QElapsedTimer::secsTo(const QElapsedTimer &other) const noexcept
-{
- return other.t1 - t1;
-}
-
-bool operator<(const QElapsedTimer &v1, const QElapsedTimer &v2) noexcept
-{
- return v1.t1 < v2.t1 || (v1.t1 == v2.t1 && v1.t2 < v2.t2);
-}
-
-QDeadlineTimer QDeadlineTimer::current(Qt::TimerType timerType) noexcept
-{
- static_assert(QDeadlineTimerNanosecondsInT2);
- QDeadlineTimer result;
- qint64 cursec, curnsec;
- do_gettime(&cursec, &curnsec);
- result.t1 = cursec;
- result.t2 = curnsec;
- result.type = timerType;
- return result;
-}
-
-QT_END_NAMESPACE
diff --git a/src/corelib/kernel/qelapsedtimer_win.cpp b/src/corelib/kernel/qelapsedtimer_win.cpp
deleted file mode 100644
index bbd5b220fe..0000000000
--- a/src/corelib/kernel/qelapsedtimer_win.cpp
+++ /dev/null
@@ -1,123 +0,0 @@
-// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
-
-#include "qelapsedtimer.h"
-#include "qdeadlinetimer.h"
-#include "qdeadlinetimer_p.h"
-#include <qt_windows.h>
-
-QT_BEGIN_NAMESPACE
-
-// Result of QueryPerformanceFrequency
-static quint64 counterFrequency = 0;
-
-static void resolveCounterFrequency()
-{
- static bool done = false;
- if (done)
- return;
-
- // Retrieve the number of high-resolution performance counter ticks per second
- LARGE_INTEGER frequency;
- if (!QueryPerformanceFrequency(&frequency) || frequency.QuadPart == 0)
- qFatal("QueryPerformanceFrequency failed, even though Microsoft documentation promises it wouldn't.");
- counterFrequency = frequency.QuadPart;
-
- done = true;
-}
-
-static inline qint64 ticksToNanoseconds(qint64 ticks)
-{
- // QueryPerformanceCounter uses an arbitrary frequency
- qint64 seconds = ticks / counterFrequency;
- qint64 nanoSeconds = (ticks - seconds * counterFrequency) * 1000000000 / counterFrequency;
- return seconds * 1000000000 + nanoSeconds;
-}
-
-
-static quint64 getTickCount()
-{
- resolveCounterFrequency();
-
- LARGE_INTEGER counter;
- bool ok = QueryPerformanceCounter(&counter);
- Q_ASSERT_X(ok, "QElapsedTimer::start()",
- "QueryPerformanceCounter failed, although QueryPerformanceFrequency succeeded.");
- Q_UNUSED(ok);
- return counter.QuadPart;
-}
-
-quint64 qt_msectime()
-{
- return ticksToNanoseconds(getTickCount()) / 1000000;
-}
-
-QElapsedTimer::ClockType QElapsedTimer::clockType() noexcept
-{
- resolveCounterFrequency();
-
- return PerformanceCounter;
-}
-
-bool QElapsedTimer::isMonotonic() noexcept
-{
- return true;
-}
-
-void QElapsedTimer::start() noexcept
-{
- t1 = getTickCount();
- t2 = 0;
-}
-
-qint64 QElapsedTimer::restart() noexcept
-{
- qint64 oldt1 = t1;
- t1 = getTickCount();
- t2 = 0;
- return ticksToNanoseconds(t1 - oldt1) / 1000000;
-}
-
-qint64 QElapsedTimer::nsecsElapsed() const noexcept
-{
- qint64 elapsed = getTickCount() - t1;
- return ticksToNanoseconds(elapsed);
-}
-
-qint64 QElapsedTimer::elapsed() const noexcept
-{
- qint64 elapsed = getTickCount() - t1;
- return ticksToNanoseconds(elapsed) / 1000000;
-}
-
-qint64 QElapsedTimer::msecsSinceReference() const noexcept
-{
- return ticksToNanoseconds(t1) / 1000000;
-}
-
-qint64 QElapsedTimer::msecsTo(const QElapsedTimer &other) const noexcept
-{
- qint64 difference = other.t1 - t1;
- return ticksToNanoseconds(difference) / 1000000;
-}
-
-qint64 QElapsedTimer::secsTo(const QElapsedTimer &other) const noexcept
-{
- return msecsTo(other) / 1000;
-}
-
-bool operator<(const QElapsedTimer &v1, const QElapsedTimer &v2) noexcept
-{
- return (v1.t1 - v2.t1) < 0;
-}
-
-QDeadlineTimer QDeadlineTimer::current(Qt::TimerType timerType) noexcept
-{
- static_assert(!QDeadlineTimerNanosecondsInT2);
- QDeadlineTimer result;
- result.t1 = ticksToNanoseconds(getTickCount());
- result.type = timerType;
- return result;
-}
-
-QT_END_NAMESPACE
diff --git a/src/corelib/kernel/qeventdispatcher_cf.mm b/src/corelib/kernel/qeventdispatcher_cf.mm
index 770cb87e69..042b0651f4 100644
--- a/src/corelib/kernel/qeventdispatcher_cf.mm
+++ b/src/corelib/kernel/qeventdispatcher_cf.mm
@@ -169,7 +169,7 @@ static const CFTimeInterval kCFTimeIntervalDistantFuture = std::numeric_limits<C
#pragma mark - Class definition
QEventDispatcherCoreFoundation::QEventDispatcherCoreFoundation(QObject *parent)
- : QAbstractEventDispatcher(parent)
+ : QAbstractEventDispatcherV2(parent)
, m_processEvents(QEventLoop::EventLoopExec)
, m_postedEventsRunLoopSource(this, &QEventDispatcherCoreFoundation::processPostedEvents)
, m_runLoopActivityObserver(this, &QEventDispatcherCoreFoundation::handleRunLoopActivity, kCFRunLoopAllActivities)
@@ -195,7 +195,7 @@ void QEventDispatcherCoreFoundation::startingUp()
QEventDispatcherCoreFoundation::~QEventDispatcherCoreFoundation()
{
invalidateTimer();
- qDeleteAll(m_timerInfoList);
+ m_timerInfoList.clearTimers();
m_cfSocketNotifier.removeSocketNotifiers();
}
@@ -506,26 +506,28 @@ void QEventDispatcherCoreFoundation::unregisterSocketNotifier(QSocketNotifier *n
#pragma mark - Timers
-void QEventDispatcherCoreFoundation::registerTimer(int timerId, qint64 interval, Qt::TimerType timerType, QObject *object)
+void QEventDispatcherCoreFoundation::registerTimer(Qt::TimerId timerId, Duration interval,
+ Qt::TimerType timerType, QObject *object)
{
- qCDebug(lcEventDispatcherTimers) << "Registering timer with id =" << timerId << "interval =" << interval
+ qCDebug(lcEventDispatcherTimers) << "Registering timer with id =" << int(timerId) << "interval =" << interval
<< "type =" << timerType << "object =" << object;
- Q_ASSERT(timerId > 0 && interval >= 0 && object);
+ Q_ASSERT(qToUnderlying(timerId) > 0 && interval.count() >= 0 && object);
Q_ASSERT(object->thread() == thread() && thread() == QThread::currentThread());
m_timerInfoList.registerTimer(timerId, interval, timerType, object);
updateTimers();
}
-bool QEventDispatcherCoreFoundation::unregisterTimer(int timerId)
+bool QEventDispatcherCoreFoundation::unregisterTimer(Qt::TimerId timerId)
{
- Q_ASSERT(timerId > 0);
+ Q_ASSERT(qToUnderlying(timerId) > 0);
Q_ASSERT(thread() == QThread::currentThread());
bool returnValue = m_timerInfoList.unregisterTimer(timerId);
- qCDebug(lcEventDispatcherTimers) << "Unegistered timer with id =" << timerId << "Timers left:" << m_timerInfoList.size();
+ qCDebug(lcEventDispatcherTimers) << "Unegistered timer with id =" << qToUnderlying(timerId)
+ << "Timers left:" << m_timerInfoList.size();
updateTimers();
return returnValue;
@@ -543,22 +545,18 @@ bool QEventDispatcherCoreFoundation::unregisterTimers(QObject *object)
return returnValue;
}
-QList<QAbstractEventDispatcher::TimerInfo> QEventDispatcherCoreFoundation::registeredTimers(QObject *object) const
+QList<QAbstractEventDispatcher::TimerInfoV2>
+QEventDispatcherCoreFoundation::timersForObject(QObject *object) const
{
Q_ASSERT(object);
return m_timerInfoList.registeredTimers(object);
}
-int QEventDispatcherCoreFoundation::remainingTime(int timerId)
+QEventDispatcherCoreFoundation::Duration
+QEventDispatcherCoreFoundation::remainingTime(Qt::TimerId timerId) const
{
- Q_ASSERT(timerId > 0);
- return m_timerInfoList.timerRemainingTime(timerId);
-}
-
-static double timespecToSeconds(const timespec &spec)
-{
- static double nanosecondsPerSecond = 1.0 * 1000 * 1000 * 1000;
- return spec.tv_sec + (spec.tv_nsec / nanosecondsPerSecond);
+ Q_ASSERT(qToUnderlying(timerId) > 0);
+ return m_timerInfoList.remainingDuration(timerId);
}
void QEventDispatcherCoreFoundation::updateTimers()
@@ -566,12 +564,20 @@ void QEventDispatcherCoreFoundation::updateTimers()
if (m_timerInfoList.size() > 0) {
// We have Qt timers registered, so create or reschedule CF timer to match
- timespec tv = { -1, -1 };
- CFAbsoluteTime timeToFire = m_timerInfoList.timerWait(tv) ?
+ using namespace std::chrono_literals;
+ using DoubleSeconds = std::chrono::duration<double, std::ratio<1>>;
+
+ CFAbsoluteTime timeToFire;
+ auto opt = m_timerInfoList.timerWait();
+ DoubleSeconds secs{};
+ if (opt) {
// We have a timer ready to fire right now, or some time in the future
- CFAbsoluteTimeGetCurrent() + timespecToSeconds(tv)
+ secs = DoubleSeconds{*opt};
+ timeToFire = CFAbsoluteTimeGetCurrent() + secs.count();
+ } else {
// We have timers, but they are all currently blocked by callbacks
- : kCFTimeIntervalDistantFuture;
+ timeToFire = kCFTimeIntervalDistantFuture;
+ }
if (!m_runLoopTimer) {
m_runLoopTimer = CFRunLoopTimerCreateWithHandler(kCFAllocatorDefault,
@@ -587,9 +593,9 @@ void QEventDispatcherCoreFoundation::updateTimers()
qCDebug(lcEventDispatcherTimers) << "Re-scheduled CFRunLoopTimer" << m_runLoopTimer;
}
- m_overdueTimerScheduled = !timespecToSeconds(tv);
+ m_overdueTimerScheduled = secs > 0s;
- qCDebug(lcEventDispatcherTimers) << "Next timeout in" << tv << "seconds";
+ qCDebug(lcEventDispatcherTimers) << "Next timeout in" << secs;
} else {
// No Qt timers are registered, so make sure we're not running any CF timers
diff --git a/src/corelib/kernel/qeventdispatcher_cf_p.h b/src/corelib/kernel/qeventdispatcher_cf_p.h
index c4c0d14027..3575a6fb39 100644
--- a/src/corelib/kernel/qeventdispatcher_cf_p.h
+++ b/src/corelib/kernel/qeventdispatcher_cf_p.h
@@ -168,7 +168,7 @@ private:
CFRunLoopObserverRef m_observer;
};
-class Q_CORE_EXPORT QEventDispatcherCoreFoundation : public QAbstractEventDispatcher
+class Q_CORE_EXPORT QEventDispatcherCoreFoundation : public QAbstractEventDispatcherV2
{
Q_OBJECT
@@ -182,12 +182,12 @@ public:
void registerSocketNotifier(QSocketNotifier *notifier) override;
void unregisterSocketNotifier(QSocketNotifier *notifier) override;
- void registerTimer(int timerId, qint64 interval, Qt::TimerType timerType, QObject *object) override;
- bool unregisterTimer(int timerId) override;
- bool unregisterTimers(QObject *object) override;
- QList<QAbstractEventDispatcher::TimerInfo> registeredTimers(QObject *object) const override;
-
- int remainingTime(int timerId) override;
+ void registerTimer(Qt::TimerId timerId, Duration interval, Qt::TimerType timerType,
+ QObject *object) override final;
+ bool unregisterTimer(Qt::TimerId timerId) override final;
+ bool unregisterTimers(QObject *object) override final;
+ QList<TimerInfoV2> timersForObject(QObject *object) const override final;
+ Duration remainingTime(Qt::TimerId timerId) const override final;
void wakeUp() override;
void interrupt() override;
@@ -204,6 +204,25 @@ protected:
, processedPostedEvents(false), processedTimers(false)
, deferredWakeUp(false), deferredUpdateTimers(false) {}
+ ProcessEventsState(const ProcessEventsState &other)
+ : flags(other.flags.loadAcquire())
+ , wasInterrupted(other.wasInterrupted.loadAcquire())
+ , processedPostedEvents(other.processedPostedEvents.loadAcquire())
+ , processedTimers(other.processedTimers.loadAcquire())
+ , deferredWakeUp(other.deferredWakeUp.loadAcquire())
+ , deferredUpdateTimers(other.deferredUpdateTimers) {}
+
+ ProcessEventsState &operator=(const ProcessEventsState &other)
+ {
+ flags.storeRelease(other.flags.loadAcquire());
+ wasInterrupted.storeRelease(other.wasInterrupted.loadAcquire());
+ processedPostedEvents.storeRelease(other.processedPostedEvents.loadAcquire());
+ processedTimers.storeRelease(other.processedTimers.loadAcquire());
+ deferredWakeUp.storeRelease(other.deferredWakeUp.loadAcquire());
+ deferredUpdateTimers = other.deferredUpdateTimers;
+ return *this;
+ }
+
QAtomicInt flags;
QAtomicInteger<char> wasInterrupted;
QAtomicInteger<char> processedPostedEvents;
diff --git a/src/corelib/kernel/qeventdispatcher_glib.cpp b/src/corelib/kernel/qeventdispatcher_glib.cpp
index c644dd35d8..1e906c4b27 100644
--- a/src/corelib/kernel/qeventdispatcher_glib.cpp
+++ b/src/corelib/kernel/qeventdispatcher_glib.cpp
@@ -4,16 +4,19 @@
#include "qeventdispatcher_glib_p.h"
#include "qeventdispatcher_unix_p.h"
+#include <private/qnumeric_p.h>
#include <private/qthread_p.h>
#include "qcoreapplication.h"
#include "qsocketnotifier.h"
#include <QtCore/qlist.h>
-#include <QtCore/qpair.h>
#include <glib.h>
+using namespace std::chrono;
+using namespace std::chrono_literals;
+
QT_BEGIN_NAMESPACE
struct GPollFDWithQSocketNotifier
@@ -95,11 +98,13 @@ struct GTimerSource
static gboolean timerSourcePrepareHelper(GTimerSource *src, gint *timeout)
{
- timespec tv = { 0l, 0l };
- if (!(src->processEventsFlags & QEventLoop::X11ExcludeTimers) && src->timerList.timerWait(tv))
- *timeout = (tv.tv_sec * 1000) + ((tv.tv_nsec + 999999) / 1000 / 1000);
- else
+ if (src->processEventsFlags & QEventLoop::X11ExcludeTimers) {
*timeout = -1;
+ return true;
+ }
+
+ auto remaining = src->timerList.timerWait().value_or(-1ms);
+ *timeout = qt_saturate<gint>(ceil<milliseconds>(remaining).count());
return (*timeout == 0);
}
@@ -110,10 +115,7 @@ static gboolean timerSourceCheckHelper(GTimerSource *src)
|| (src->processEventsFlags & QEventLoop::X11ExcludeTimers))
return false;
- if (src->timerList.updateCurrentTime() < src->timerList.constFirst()->timeout)
- return false;
-
- return true;
+ return !src->timerList.hasPendingTimers();
}
static gboolean timerSourcePrepare(GSource *source, gint *timeout)
@@ -325,12 +327,12 @@ void QEventDispatcherGlibPrivate::runTimersOnceWithNormalPriority()
}
QEventDispatcherGlib::QEventDispatcherGlib(QObject *parent)
- : QAbstractEventDispatcher(*(new QEventDispatcherGlibPrivate), parent)
+ : QAbstractEventDispatcherV2(*(new QEventDispatcherGlibPrivate), parent)
{
}
QEventDispatcherGlib::QEventDispatcherGlib(GMainContext *mainContext, QObject *parent)
- : QAbstractEventDispatcher(*(new QEventDispatcherGlibPrivate(mainContext)), parent)
+ : QAbstractEventDispatcherV2(*(new QEventDispatcherGlibPrivate(mainContext)), parent)
{ }
QEventDispatcherGlib::~QEventDispatcherGlib()
@@ -338,7 +340,7 @@ QEventDispatcherGlib::~QEventDispatcherGlib()
Q_D(QEventDispatcherGlib);
// destroy all timer sources
- qDeleteAll(d->timerSource->timerList);
+ d->timerSource->timerList.clearTimers();
d->timerSource->timerList.~QTimerInfoList();
g_source_destroy(&d->timerSource->source);
g_source_unref(&d->timerSource->source);
@@ -405,7 +407,7 @@ bool QEventDispatcherGlib::processEvents(QEventLoop::ProcessEventsFlags flags)
void QEventDispatcherGlib::registerSocketNotifier(QSocketNotifier *notifier)
{
Q_ASSERT(notifier);
- int sockfd = notifier->socket();
+ int sockfd = int(notifier->socket());
int type = notifier->type();
#ifndef QT_NO_DEBUG
if (sockfd < 0) {
@@ -445,8 +447,7 @@ void QEventDispatcherGlib::unregisterSocketNotifier(QSocketNotifier *notifier)
{
Q_ASSERT(notifier);
#ifndef QT_NO_DEBUG
- int sockfd = notifier->socket();
- if (sockfd < 0) {
+ if (notifier->socket() < 0) {
qWarning("QSocketNotifier: Internal error");
return;
} else if (notifier->thread() != thread()
@@ -476,10 +477,11 @@ void QEventDispatcherGlib::unregisterSocketNotifier(QSocketNotifier *notifier)
}
}
-void QEventDispatcherGlib::registerTimer(int timerId, qint64 interval, Qt::TimerType timerType, QObject *object)
+void QEventDispatcherGlib::registerTimer(Qt::TimerId timerId, Duration interval,
+ Qt::TimerType timerType, QObject *object)
{
#ifndef QT_NO_DEBUG
- if (timerId < 1 || interval < 0 || !object) {
+ if (qToUnderlying(timerId) < 1 || interval < 0ns || !object) {
qWarning("QEventDispatcherGlib::registerTimer: invalid arguments");
return;
} else if (object->thread() != thread() || thread() != QThread::currentThread()) {
@@ -492,10 +494,10 @@ void QEventDispatcherGlib::registerTimer(int timerId, qint64 interval, Qt::Timer
d->timerSource->timerList.registerTimer(timerId, interval, timerType, object);
}
-bool QEventDispatcherGlib::unregisterTimer(int timerId)
+bool QEventDispatcherGlib::unregisterTimer(Qt::TimerId timerId)
{
#ifndef QT_NO_DEBUG
- if (timerId < 1) {
+ if (qToUnderlying(timerId) < 1) {
qWarning("QEventDispatcherGlib::unregisterTimer: invalid argument");
return false;
} else if (thread() != QThread::currentThread()) {
@@ -524,28 +526,30 @@ bool QEventDispatcherGlib::unregisterTimers(QObject *object)
return d->timerSource->timerList.unregisterTimers(object);
}
-QList<QEventDispatcherGlib::TimerInfo> QEventDispatcherGlib::registeredTimers(QObject *object) const
+QList<QEventDispatcherGlib::TimerInfoV2> QEventDispatcherGlib::timersForObject(QObject *object) const
{
+#ifndef QT_NO_DEBUG
if (!object) {
- qWarning("QEventDispatcherUNIX:registeredTimers: invalid argument");
- return QList<TimerInfo>();
+ qWarning("QEventDispatcherGlib:timersForObject: invalid argument");
+ return {};
}
+#endif
Q_D(const QEventDispatcherGlib);
return d->timerSource->timerList.registeredTimers(object);
}
-int QEventDispatcherGlib::remainingTime(int timerId)
+QEventDispatcherGlib::Duration QEventDispatcherGlib::remainingTime(Qt::TimerId timerId) const
{
#ifndef QT_NO_DEBUG
- if (timerId < 1) {
+ if (qToUnderlying(timerId) < 1) {
qWarning("QEventDispatcherGlib::remainingTimeTime: invalid argument");
- return -1;
+ return Duration::min();
}
#endif
- Q_D(QEventDispatcherGlib);
- return d->timerSource->timerList.timerRemainingTime(timerId);
+ Q_D(const QEventDispatcherGlib);
+ return d->timerSource->timerList.remainingDuration(timerId);
}
void QEventDispatcherGlib::interrupt()
@@ -570,7 +574,7 @@ bool QEventDispatcherGlib::versionSupported()
}
QEventDispatcherGlib::QEventDispatcherGlib(QEventDispatcherGlibPrivate &dd, QObject *parent)
- : QAbstractEventDispatcher(dd, parent)
+ : QAbstractEventDispatcherV2(dd, parent)
{
}
diff --git a/src/corelib/kernel/qeventdispatcher_glib_p.h b/src/corelib/kernel/qeventdispatcher_glib_p.h
index 4881a8543f..30783d3858 100644
--- a/src/corelib/kernel/qeventdispatcher_glib_p.h
+++ b/src/corelib/kernel/qeventdispatcher_glib_p.h
@@ -24,7 +24,7 @@ QT_BEGIN_NAMESPACE
class QEventDispatcherGlibPrivate;
-class Q_CORE_EXPORT QEventDispatcherGlib : public QAbstractEventDispatcher
+class Q_CORE_EXPORT QEventDispatcherGlib : public QAbstractEventDispatcherV2
{
Q_OBJECT
Q_DECLARE_PRIVATE(QEventDispatcherGlib)
@@ -39,12 +39,12 @@ public:
void registerSocketNotifier(QSocketNotifier *socketNotifier) final;
void unregisterSocketNotifier(QSocketNotifier *socketNotifier) final;
- void registerTimer(int timerId, qint64 interval, Qt::TimerType timerType, QObject *object) final;
- bool unregisterTimer(int timerId) final;
- bool unregisterTimers(QObject *object) final;
- QList<TimerInfo> registeredTimers(QObject *object) const final;
-
- int remainingTime(int timerId) final;
+ void registerTimer(Qt::TimerId timerId, Duration interval, Qt::TimerType timerType,
+ QObject *object) override final;
+ bool unregisterTimer(Qt::TimerId timerId) override final;
+ bool unregisterTimers(QObject *object) override final;
+ QList<TimerInfoV2> timersForObject(QObject *object) const override final;
+ Duration remainingTime(Qt::TimerId timerId) const override final;
void wakeUp() final;
void interrupt() final;
diff --git a/src/corelib/kernel/qeventdispatcher_unix.cpp b/src/corelib/kernel/qeventdispatcher_unix.cpp
index d7b2fd6dd7..21bd224415 100644
--- a/src/corelib/kernel/qeventdispatcher_unix.cpp
+++ b/src/corelib/kernel/qeventdispatcher_unix.cpp
@@ -5,7 +5,6 @@
#include "qplatformdefs.h"
#include "qcoreapplication.h"
-#include "qpair.h"
#include "qhash.h"
#include "qsocketnotifier.h"
#include "qthread.h"
@@ -19,23 +18,19 @@
#include <stdio.h>
#include <stdlib.h>
-#ifndef QT_NO_EVENTFD
+#if __has_include(<sys/eventfd.h>)
# include <sys/eventfd.h>
+static constexpr bool UsingEventfd = true;
+#else
+static constexpr bool UsingEventfd = false;
#endif
-// VxWorks doesn't correctly set the _POSIX_... options
#if defined(Q_OS_VXWORKS)
-# if defined(_POSIX_MONOTONIC_CLOCK) && (_POSIX_MONOTONIC_CLOCK <= 0)
-# undef _POSIX_MONOTONIC_CLOCK
-# define _POSIX_MONOTONIC_CLOCK 1
-# endif
# include <pipeDrv.h>
-# include <sys/time.h>
#endif
-#if (_POSIX_MONOTONIC_CLOCK-0 <= 0) || defined(QT_BOOTSTRAPPED)
-# include <sys/times.h>
-#endif
+using namespace std::chrono;
+using namespace std::chrono_literals;
QT_BEGIN_NAMESPACE
@@ -55,11 +50,6 @@ static const char *socketType(QSocketNotifier::Type type)
QThreadPipe::QThreadPipe()
{
- fds[0] = -1;
- fds[1] = -1;
-#if defined(Q_OS_VXWORKS)
- name[0] = '\0';
-#endif
}
QThreadPipe::~QThreadPipe()
@@ -67,7 +57,7 @@ QThreadPipe::~QThreadPipe()
if (fds[0] >= 0)
close(fds[0]);
- if (fds[1] >= 0)
+ if (!UsingEventfd && fds[1] >= 0)
close(fds[1]);
#if defined(Q_OS_VXWORKS)
@@ -104,23 +94,25 @@ bool QThreadPipe::init()
// create the pipe
if (pipeDevCreate(name, 128 /*maxMsg*/, 1 /*maxLength*/) != OK) {
- perror("QThreadPipe: Unable to create thread pipe device %s", name);
+ perror("QThreadPipe: Unable to create thread pipe device");
return false;
}
if ((fds[0] = open(name, O_RDWR, 0)) < 0) {
- perror("QThreadPipe: Unable to open pipe device %s", name);
+ perror("QThreadPipe: Unable to open pipe device");
return false;
}
initThreadPipeFD(fds[0]);
fds[1] = fds[0];
#else
-# ifndef QT_NO_EVENTFD
- if ((fds[0] = eventfd(0, EFD_NONBLOCK | EFD_CLOEXEC)) >= 0)
- return true;
+ int ret;
+# ifdef EFD_CLOEXEC
+ ret = fds[0] = eventfd(0, EFD_NONBLOCK | EFD_CLOEXEC);
# endif
- if (qt_safe_pipe(fds, O_NONBLOCK) == -1) {
+ if (!UsingEventfd)
+ ret = qt_safe_pipe(fds, O_NONBLOCK);
+ if (ret == -1) {
perror("QThreadPipe: Unable to create pipe");
return false;
}
@@ -136,15 +128,10 @@ pollfd QThreadPipe::prepare() const
void QThreadPipe::wakeUp()
{
- if (wakeUps.testAndSetAcquire(0, 1)) {
-#ifndef QT_NO_EVENTFD
- if (fds[1] == -1) {
- // eventfd
- eventfd_t value = 1;
- int ret;
- EINTR_LOOP(ret, eventfd_write(fds[0], value));
- return;
- }
+ if ((wakeUps.fetchAndOrAcquire(1) & 1) == 0) {
+# ifdef EFD_CLOEXEC
+ eventfd_write(fds[0], 1);
+ return;
#endif
char c = 0;
qt_safe_write(fds[1], &c, 1);
@@ -165,14 +152,11 @@ int QThreadPipe::check(const pollfd &pfd)
::read(fds[0], c, sizeof(c));
::ioctl(fds[0], FIOFLUSH, 0);
#else
-# ifndef QT_NO_EVENTFD
- if (fds[1] == -1) {
- // eventfd
- eventfd_t value;
- eventfd_read(fds[0], &value);
- } else
+# ifdef EFD_CLOEXEC
+ eventfd_t value;
+ eventfd_read(fds[0], &value);
# endif
- {
+ if (!UsingEventfd) {
while (::read(fds[0], c, sizeof(c)) > 0) {}
}
#endif
@@ -195,7 +179,7 @@ QEventDispatcherUNIXPrivate::QEventDispatcherUNIXPrivate()
QEventDispatcherUNIXPrivate::~QEventDispatcherUNIXPrivate()
{
// cleanup timers
- qDeleteAll(timerList);
+ timerList.clearTimers();
}
void QEventDispatcherUNIXPrivate::setSocketNotifierPending(QSocketNotifier *notifier)
@@ -273,11 +257,11 @@ int QEventDispatcherUNIXPrivate::activateSocketNotifiers()
}
QEventDispatcherUNIX::QEventDispatcherUNIX(QObject *parent)
- : QAbstractEventDispatcher(*new QEventDispatcherUNIXPrivate, parent)
+ : QAbstractEventDispatcherV2(*new QEventDispatcherUNIXPrivate, parent)
{ }
QEventDispatcherUNIX::QEventDispatcherUNIX(QEventDispatcherUNIXPrivate &dd, QObject *parent)
- : QAbstractEventDispatcher(dd, parent)
+ : QAbstractEventDispatcherV2(dd, parent)
{ }
QEventDispatcherUNIX::~QEventDispatcherUNIX()
@@ -286,10 +270,10 @@ QEventDispatcherUNIX::~QEventDispatcherUNIX()
/*!
\internal
*/
-void QEventDispatcherUNIX::registerTimer(int timerId, qint64 interval, Qt::TimerType timerType, QObject *obj)
+void QEventDispatcherUNIX::registerTimer(Qt::TimerId timerId, Duration interval, Qt::TimerType timerType, QObject *obj)
{
#ifndef QT_NO_DEBUG
- if (timerId < 1 || interval < 0 || !obj) {
+ if (qToUnderlying(timerId) < 1 || interval.count() < 0 || !obj) {
qWarning("QEventDispatcherUNIX::registerTimer: invalid arguments");
return;
} else if (obj->thread() != thread() || thread() != QThread::currentThread()) {
@@ -305,10 +289,10 @@ void QEventDispatcherUNIX::registerTimer(int timerId, qint64 interval, Qt::Timer
/*!
\internal
*/
-bool QEventDispatcherUNIX::unregisterTimer(int timerId)
+bool QEventDispatcherUNIX::unregisterTimer(Qt::TimerId timerId)
{
#ifndef QT_NO_DEBUG
- if (timerId < 1) {
+ if (qToUnderlying(timerId) < 1) {
qWarning("QEventDispatcherUNIX::unregisterTimer: invalid argument");
return false;
} else if (thread() != QThread::currentThread()) {
@@ -340,12 +324,12 @@ bool QEventDispatcherUNIX::unregisterTimers(QObject *object)
return d->timerList.unregisterTimers(object);
}
-QList<QEventDispatcherUNIX::TimerInfo>
-QEventDispatcherUNIX::registeredTimers(QObject *object) const
+QList<QEventDispatcherUNIX::TimerInfoV2>
+QEventDispatcherUNIX::timersForObject(QObject *object) const
{
if (!object) {
qWarning("QEventDispatcherUNIX:registeredTimers: invalid argument");
- return QList<TimerInfo>();
+ return QList<TimerInfoV2>();
}
Q_D(const QEventDispatcherUNIX);
@@ -445,11 +429,19 @@ bool QEventDispatcherUNIX::processEvents(QEventLoop::ProcessEventsFlags flags)
if (d->interrupt.loadRelaxed())
return false;
- timespec *tm = nullptr;
- timespec wait_tm = { 0, 0 };
-
- if (!canWait || (include_timers && d->timerList.timerWait(wait_tm)))
- tm = &wait_tm;
+ QDeadlineTimer deadline;
+ if (canWait) {
+ if (include_timers) {
+ std::optional<nanoseconds> remaining = d->timerList.timerWait();
+ deadline = remaining ? QDeadlineTimer{*remaining}
+ : QDeadlineTimer(QDeadlineTimer::Forever);
+ } else {
+ deadline = QDeadlineTimer(QDeadlineTimer::Forever);
+ }
+ } else {
+ // Using the default-constructed `deadline`, which is already expired,
+ // ensures the code in the do-while loop in qt_safe_poll runs at least once.
+ }
d->pollfds.clear();
d->pollfds.reserve(1 + (include_notifiers ? d->socketNotifiers.size() : 0));
@@ -462,8 +454,7 @@ bool QEventDispatcherUNIX::processEvents(QEventLoop::ProcessEventsFlags flags)
d->pollfds.append(d->threadPipe.prepare());
int nevents = 0;
-
- switch (qt_safe_poll(d->pollfds.data(), d->pollfds.size(), tm)) {
+ switch (qt_safe_poll(d->pollfds.data(), d->pollfds.size(), deadline)) {
case -1:
qErrnoWarning("qt_safe_poll");
if (QT_CONFIG(poll_exit_on_error))
@@ -485,17 +476,17 @@ bool QEventDispatcherUNIX::processEvents(QEventLoop::ProcessEventsFlags flags)
return (nevents > 0);
}
-int QEventDispatcherUNIX::remainingTime(int timerId)
+auto QEventDispatcherUNIX::remainingTime(Qt::TimerId timerId) const -> Duration
{
#ifndef QT_NO_DEBUG
- if (timerId < 1) {
+ if (int(timerId) < 1) {
qWarning("QEventDispatcherUNIX::remainingTime: invalid argument");
- return -1;
+ return Duration::min();
}
#endif
- Q_D(QEventDispatcherUNIX);
- return d->timerList.timerRemainingTime(timerId);
+ Q_D(const QEventDispatcherUNIX);
+ return d->timerList.remainingDuration(timerId);
}
void QEventDispatcherUNIX::wakeUp()
diff --git a/src/corelib/kernel/qeventdispatcher_unix_p.h b/src/corelib/kernel/qeventdispatcher_unix_p.h
index 4e9a360f3e..6596e998c9 100644
--- a/src/corelib/kernel/qeventdispatcher_unix_p.h
+++ b/src/corelib/kernel/qeventdispatcher_unix_p.h
@@ -51,17 +51,17 @@ struct QThreadPipe
int check(const pollfd &pfd);
// note for eventfd(7) support:
- // if fds[1] is -1, then eventfd(7) is in use and is stored in fds[0]
- int fds[2];
+ // fds[0] stores the eventfd, fds[1] is unused
+ int fds[2] = { -1, -1 };
QAtomicInt wakeUps;
#if defined(Q_OS_VXWORKS)
- static const int len_name = 20;
- char name[len_name];
+ static constexpr int len_name = 20;
+ char name[len_name] = {};
#endif
};
-class Q_CORE_EXPORT QEventDispatcherUNIX : public QAbstractEventDispatcher
+class Q_CORE_EXPORT QEventDispatcherUNIX : public QAbstractEventDispatcherV2
{
Q_OBJECT
Q_DECLARE_PRIVATE(QEventDispatcherUNIX)
@@ -75,12 +75,12 @@ public:
void registerSocketNotifier(QSocketNotifier *notifier) final;
void unregisterSocketNotifier(QSocketNotifier *notifier) final;
- void registerTimer(int timerId, qint64 interval, Qt::TimerType timerType, QObject *object) final;
- bool unregisterTimer(int timerId) final;
- bool unregisterTimers(QObject *object) final;
- QList<TimerInfo> registeredTimers(QObject *object) const final;
-
- int remainingTime(int timerId) final;
+ void registerTimer(Qt::TimerId timerId, Duration interval, Qt::TimerType timerType,
+ QObject *object) override final;
+ bool unregisterTimer(Qt::TimerId timerId) override final;
+ bool unregisterTimers(QObject *object) override final;
+ QList<TimerInfoV2> timersForObject(QObject *object) const override final;
+ Duration remainingTime(Qt::TimerId timerId) const override final;
void wakeUp() override;
void interrupt() final;
diff --git a/src/corelib/kernel/qeventdispatcher_wasm.cpp b/src/corelib/kernel/qeventdispatcher_wasm.cpp
index 23f218c4a0..4aa435b64b 100644
--- a/src/corelib/kernel/qeventdispatcher_wasm.cpp
+++ b/src/corelib/kernel/qeventdispatcher_wasm.cpp
@@ -14,6 +14,9 @@
#include <emscripten/threading.h>
#include <emscripten/val.h>
+using namespace std::chrono;
+using namespace std::chrono_literals;
+
QT_BEGIN_NAMESPACE
// using namespace emscripten;
@@ -40,10 +43,57 @@ static bool useAsyncify()
return qstdweb::haveAsyncify();
}
+static bool useJspi()
+{
+ return qstdweb::haveJspi();
+}
+
+// clang-format off
+EM_ASYNC_JS(void, qt_jspi_suspend_js, (), {
+ ++Module.qtJspiSuspensionCounter;
+
+ await new Promise(resolve => {
+ Module.qtAsyncifyWakeUp.push(resolve);
+ });
+});
+
+EM_JS(bool, qt_jspi_resume_js, (), {
+ if (!Module.qtJspiSuspensionCounter)
+ return false;
+
+ --Module.qtJspiSuspensionCounter;
+
+ setTimeout(() => {
+ const wakeUp = (Module.qtAsyncifyWakeUp ?? []).pop();
+ if (wakeUp) wakeUp();
+ });
+ return true;
+});
+
+EM_JS(bool, qt_jspi_can_resume_js, (), {
+ return Module.qtJspiSuspensionCounter > 0;
+});
+
+EM_JS(void, init_jspi_support_js, (), {
+ Module.qtAsyncifyWakeUp = [];
+ Module.qtJspiSuspensionCounter = 0;
+});
+// clang-format on
+
+void initJspiSupport() {
+ init_jspi_support_js();
+}
+
+Q_CONSTRUCTOR_FUNCTION(initJspiSupport);
+
+// clang-format off
EM_JS(void, qt_asyncify_suspend_js, (), {
+ if (Module.qtSuspendId === undefined)
+ Module.qtSuspendId = 0;
let sleepFn = (wakeUp) => {
Module.qtAsyncifyWakeUp = wakeUp;
};
+ ++Module.qtSuspendId;
return Asyncify.handleSleep(sleepFn);
});
@@ -52,11 +102,18 @@ EM_JS(void, qt_asyncify_resume_js, (), {
if (wakeUp == undefined)
return;
Module.qtAsyncifyWakeUp = undefined;
+ const suspendId = Module.qtSuspendId;
// Delayed wakeup with zero-timer. Workaround/fix for
// https://github.com/emscripten-core/emscripten/issues/10515
- setTimeout(wakeUp);
+ setTimeout(() => {
+ // Another suspend occurred while the timeout was in queue.
+ if (Module.qtSuspendId !== suspendId)
+ return;
+ wakeUp();
+ });
});
+// clang-format on
#else
@@ -67,6 +124,28 @@ static bool useAsyncify()
return false;
}
+static bool useJspi()
+{
+ return false;
+}
+
+void qt_jspi_suspend_js()
+{
+ Q_UNREACHABLE();
+}
+
+bool qt_jspi_resume_js()
+{
+ Q_UNREACHABLE();
+ return false;
+}
+
+bool qt_jspi_can_resume_js()
+{
+ Q_UNREACHABLE();
+ return false;
+}
+
void qt_asyncify_suspend_js()
{
Q_UNREACHABLE();
@@ -94,26 +173,27 @@ bool qt_asyncify_suspend()
// Wakes any currently suspended main thread. Returns true if the main
// thread was suspended, in which case it will now be asynchronously woken.
-bool qt_asyncify_resume()
+void qt_asyncify_resume()
{
if (!g_is_asyncify_suspended)
- return false;
+ return;
g_is_asyncify_suspended = false;
qt_asyncify_resume_js();
- return true;
}
+
Q_CONSTINIT QEventDispatcherWasm *QEventDispatcherWasm::g_mainThreadEventDispatcher = nullptr;
#if QT_CONFIG(thread)
Q_CONSTINIT QVector<QEventDispatcherWasm *> QEventDispatcherWasm::g_secondaryThreadEventDispatchers;
Q_CONSTINIT std::mutex QEventDispatcherWasm::g_staticDataMutex;
+emscripten::ProxyingQueue QEventDispatcherWasm::g_proxyingQueue;
+pthread_t QEventDispatcherWasm::g_mainThread;
#endif
// ### dynamic initialization:
std::multimap<int, QSocketNotifier *> QEventDispatcherWasm::g_socketNotifiers;
std::map<int, QEventDispatcherWasm::SocketReadyState> QEventDispatcherWasm::g_socketState;
QEventDispatcherWasm::QEventDispatcherWasm()
- : QAbstractEventDispatcher()
{
// QEventDispatcherWasm operates in two main modes:
// - On the main thread:
@@ -135,6 +215,15 @@ QEventDispatcherWasm::QEventDispatcherWasm()
// dispatchers so we set a global pointer to it.
Q_ASSERT(g_mainThreadEventDispatcher == nullptr);
g_mainThreadEventDispatcher = this;
+#if QT_CONFIG(thread)
+ g_mainThread = pthread_self();
+#endif
+
+ // Call the "onLoaded" JavaScript callback, unless startup tasks
+ // have been registered which should complete first. Run async
+ // to make sure event dispatcher construction (in particular any
+ // subclass construction) has completed first.
+ runAsync(callOnLoadedIfRequired);
} else {
#if QT_CONFIG(thread)
std::lock_guard<std::mutex> lock(g_staticDataMutex);
@@ -189,7 +278,6 @@ bool QEventDispatcherWasm::isValidEventDispatcherPointer(QEventDispatcherWasm *e
if (eventDispatcher == g_mainThreadEventDispatcher)
return true;
#if QT_CONFIG(thread)
- Q_ASSERT(!g_staticDataMutex.try_lock()); // caller must lock mutex
if (g_secondaryThreadEventDispatchers.contains(eventDispatcher))
return true;
#endif
@@ -198,12 +286,9 @@ bool QEventDispatcherWasm::isValidEventDispatcherPointer(QEventDispatcherWasm *e
bool QEventDispatcherWasm::processEvents(QEventLoop::ProcessEventsFlags flags)
{
- emit awake();
-
- bool hasPendingEvents = qGlobalPostedEventsCount() > 0;
+ qCDebug(lcEventDispatcher) << "QEventDispatcherWasm::processEvents flags" << flags;
- qCDebug(lcEventDispatcher) << "QEventDispatcherWasm::processEvents flags" << flags
- << "pending events" << hasPendingEvents;
+ emit awake();
if (isMainThreadEventDispatcher()) {
if (flags & QEventLoop::DialogExec)
@@ -212,30 +297,36 @@ bool QEventDispatcherWasm::processEvents(QEventLoop::ProcessEventsFlags flags)
handleApplicationExec();
}
- hasPendingEvents = qGlobalPostedEventsCount() > 0;
+#if QT_CONFIG(thread)
+ {
+ // Reset wakeUp state: if wakeUp() was called at some point before
+ // this then processPostedEvents() below will service that call.
+ std::unique_lock<std::mutex> lock(m_mutex);
+ m_wakeUpCalled = false;
+ }
+#endif
+
+ processPostedEvents();
- if (!hasPendingEvents && (flags & QEventLoop::WaitForMoreEvents))
- wait();
+ // The processPostedEvents() call above may process an event which deletes the
+ // application object and the event dispatcher; stop event processing in that case.
+ if (!isValidEventDispatcherPointer(this))
+ return false;
if (m_interrupted) {
m_interrupted = false;
return false;
}
+ if (flags & QEventLoop::WaitForMoreEvents)
+ wait();
+
if (m_processTimers) {
m_processTimers = false;
processTimers();
}
- hasPendingEvents = qGlobalPostedEventsCount() > 0;
- QCoreApplication::sendPostedEvents();
- processWindowSystemEvents(flags);
- return hasPendingEvents;
-}
-
-void QEventDispatcherWasm::processWindowSystemEvents(QEventLoop::ProcessEventsFlags flags)
-{
- Q_UNUSED(flags);
+ return false;
}
void QEventDispatcherWasm::registerSocketNotifier(QSocketNotifier *notifier)
@@ -245,7 +336,7 @@ void QEventDispatcherWasm::registerSocketNotifier(QSocketNotifier *notifier)
bool wasEmpty = g_socketNotifiers.empty();
g_socketNotifiers.insert({notifier->socket(), notifier});
if (wasEmpty)
- runOnMainThread([]{ setEmscriptenSocketCallbacks(); });
+ runOnMainThread([] { setEmscriptenSocketCallbacks(); });
}
void QEventDispatcherWasm::unregisterSocketNotifier(QSocketNotifier *notifier)
@@ -261,13 +352,13 @@ void QEventDispatcherWasm::unregisterSocketNotifier(QSocketNotifier *notifier)
}
if (g_socketNotifiers.empty())
- runOnMainThread([]{ clearEmscriptenSocketCallbacks(); });
+ runOnMainThread([] { clearEmscriptenSocketCallbacks(); });
}
-void QEventDispatcherWasm::registerTimer(int timerId, qint64 interval, Qt::TimerType timerType, QObject *object)
+void QEventDispatcherWasm::registerTimer(Qt::TimerId timerId, Duration interval, Qt::TimerType timerType, QObject *object)
{
#ifndef QT_NO_DEBUG
- if (timerId < 1 || interval < 0 || !object) {
+ if (qToUnderlying(timerId) < 1 || interval < 0ns || !object) {
qWarning("QEventDispatcherWasm::registerTimer: invalid arguments");
return;
} else if (object->thread() != thread() || thread() != QThread::currentThread()) {
@@ -276,16 +367,16 @@ void QEventDispatcherWasm::registerTimer(int timerId, qint64 interval, Qt::Timer
return;
}
#endif
- qCDebug(lcEventDispatcherTimers) << "registerTimer" << timerId << interval << timerType << object;
+ qCDebug(lcEventDispatcherTimers) << "registerTimer" << int(timerId) << interval << timerType << object;
m_timerInfo->registerTimer(timerId, interval, timerType, object);
updateNativeTimer();
}
-bool QEventDispatcherWasm::unregisterTimer(int timerId)
+bool QEventDispatcherWasm::unregisterTimer(Qt::TimerId timerId)
{
#ifndef QT_NO_DEBUG
- if (timerId < 1) {
+ if (qToUnderlying(timerId) < 1) {
qWarning("QEventDispatcherWasm::unregisterTimer: invalid argument");
return false;
} else if (thread() != QThread::currentThread()) {
@@ -295,7 +386,7 @@ bool QEventDispatcherWasm::unregisterTimer(int timerId)
}
#endif
- qCDebug(lcEventDispatcherTimers) << "unregisterTimer" << timerId;
+ qCDebug(lcEventDispatcherTimers) << "unregisterTimer" << int(timerId);
bool ans = m_timerInfo->unregisterTimer(timerId);
updateNativeTimer();
@@ -322,22 +413,22 @@ bool QEventDispatcherWasm::unregisterTimers(QObject *object)
return ans;
}
-QList<QAbstractEventDispatcher::TimerInfo>
-QEventDispatcherWasm::registeredTimers(QObject *object) const
+QList<QAbstractEventDispatcher::TimerInfoV2>
+QEventDispatcherWasm::timersForObject(QObject *object) const
{
#ifndef QT_NO_DEBUG
if (!object) {
qWarning("QEventDispatcherWasm:registeredTimers: invalid argument");
- return QList<TimerInfo>();
+ return {};
}
#endif
return m_timerInfo->registeredTimers(object);
}
-int QEventDispatcherWasm::remainingTime(int timerId)
+QEventDispatcherWasm::Duration QEventDispatcherWasm::remainingTime(Qt::TimerId timerId) const
{
- return m_timerInfo->timerRemainingTime(timerId);
+ return m_timerInfo->remainingDuration(timerId);
}
void QEventDispatcherWasm::interrupt()
@@ -353,7 +444,9 @@ void QEventDispatcherWasm::wakeUp()
// event loop. Make sure the thread is unblocked or make it
// process events.
bool wasBlocked = wakeEventDispatcherThread();
- if (!wasBlocked && isMainThreadEventDispatcher()) {
+ // JSPI does not need a scheduled call to processPostedEvents, as the stack is not unwound
+ // at startup.
+ if (!qstdweb::haveJspi() && !wasBlocked && isMainThreadEventDispatcher()) {
{
LOCK_GUARD(m_mutex);
if (m_pendingProcessEvents)
@@ -361,7 +454,7 @@ void QEventDispatcherWasm::wakeUp()
m_pendingProcessEvents = true;
}
runOnMainThreadAsync([this](){
- QEventDispatcherWasm::callProcessEvents(this);
+ QEventDispatcherWasm::callProcessPostedEvents(this);
});
}
}
@@ -376,10 +469,14 @@ void QEventDispatcherWasm::handleApplicationExec()
// Note that we don't use asyncify here: Emscripten supports one level of
// asyncify only and we want to reserve that for dialog exec() instead of
// using it for the one qApp exec().
- const bool simulateInfiniteLoop = true;
- emscripten_set_main_loop([](){
- emscripten_pause_main_loop();
- }, 0, simulateInfiniteLoop);
+ // When JSPI is used, awaited async calls are allowed to be nested, so we
+ // proceed normally.
+ if (!qstdweb::haveJspi()) {
+ const bool simulateInfiniteLoop = true;
+ emscripten_set_main_loop([](){
+ emscripten_pause_main_loop();
+ }, 0, simulateInfiniteLoop);
+ }
}
void QEventDispatcherWasm::handleDialogExec()
@@ -405,7 +502,12 @@ bool QEventDispatcherWasm::wait(int timeout)
if (isSecondaryThreadEventDispatcher()) {
std::unique_lock<std::mutex> lock(m_mutex);
- m_wakeUpCalled = false;
+ // If wakeUp() was called there might be pending events in the event
+ // queue which should be processed. Don't block, instead return
+ // so that the event loop can spin and call processEvents() again.
+ if (m_wakeUpCalled)
+ return true;
+
auto wait_time = timeout > 0 ? timeout * 1ms : std::chrono::duration<int, std::micro>::max();
bool wakeUpCalled = m_moreEvents.wait_for(lock, wait_time, [=] { return m_wakeUpCalled; });
return wakeUpCalled;
@@ -417,10 +519,14 @@ bool QEventDispatcherWasm::wait(int timeout)
if (timeout > 0)
qWarning() << "QEventDispatcherWasm asyncify wait with timeout is not supported; timeout will be ignored"; // FIXME
- bool didSuspend = qt_asyncify_suspend();
- if (!didSuspend) {
- qWarning("QEventDispatcherWasm: current thread is already suspended; could not asyncify wait for events");
- return false;
+ if (useJspi()) {
+ qt_jspi_suspend_js();
+ } else {
+ bool didSuspend = qt_asyncify_suspend();
+ if (!didSuspend) {
+ qWarning("QEventDispatcherWasm: current thread is already suspended; could not asyncify wait for events");
+ return false;
+ }
}
return true;
} else {
@@ -444,16 +550,27 @@ bool QEventDispatcherWasm::wakeEventDispatcherThread()
}
#endif
Q_ASSERT(isMainThreadEventDispatcher());
- if (g_is_asyncify_suspended) {
- runOnMainThread([]{ qt_asyncify_resume(); });
- return true;
+ if (useJspi()) {
+
+#if QT_CONFIG(thread)
+ return qstdweb::runTaskOnMainThread<bool>(
+ []() { return qt_jspi_can_resume_js() && qt_jspi_resume_js(); }, &g_proxyingQueue);
+#else
+ return qstdweb::runTaskOnMainThread<bool>(
+ []() { return qt_jspi_can_resume_js() && qt_jspi_resume_js(); });
+#endif
+
+ } else {
+ if (!g_is_asyncify_suspended)
+ return false;
+ runOnMainThread([]() { qt_asyncify_resume(); });
}
- return false;
+ return true;
}
// Process event activation callbacks for the main thread event dispatcher.
// Must be called on the main thread.
-void QEventDispatcherWasm::callProcessEvents(void *context)
+void QEventDispatcherWasm::callProcessPostedEvents(void *context)
{
Q_ASSERT(emscripten_is_main_runtime_thread());
@@ -461,7 +578,7 @@ void QEventDispatcherWasm::callProcessEvents(void *context)
if (!g_mainThreadEventDispatcher)
return;
- // In the unlikely event that we get a callProcessEvents() call for
+ // In the unlikely event that we get a callProcessPostedEvents() call for
// a previous main thread event dispatcher (i.e. the QApplication
// object was deleted and created again): just ignore it and return.
if (context != g_mainThreadEventDispatcher)
@@ -471,7 +588,14 @@ void QEventDispatcherWasm::callProcessEvents(void *context)
LOCK_GUARD(g_mainThreadEventDispatcher->m_mutex);
g_mainThreadEventDispatcher->m_pendingProcessEvents = false;
}
- g_mainThreadEventDispatcher->processEvents(QEventLoop::AllEvents);
+
+ g_mainThreadEventDispatcher->processPostedEvents();
+}
+
+bool QEventDispatcherWasm::processPostedEvents()
+{
+ QCoreApplication::sendPostedEvents();
+ return false;
}
void QEventDispatcherWasm::processTimers()
@@ -495,34 +619,32 @@ void QEventDispatcherWasm::updateNativeTimer()
// access to m_timerInfo), and then call native API to set the new
// wakeup time on the main thread.
- auto timespecToNanosec = [](timespec ts) -> uint64_t {
- return ts.tv_sec * 1000 + ts.tv_nsec / (1000 * 1000);
- };
- timespec toWait;
- bool hasTimer = m_timerInfo->timerWait(toWait);
- uint64_t currentTime = timespecToNanosec(m_timerInfo->currentTime);
- uint64_t toWaitDuration = timespecToNanosec(toWait);
- uint64_t newTargetTime = currentTime + toWaitDuration;
-
- auto maintainNativeTimer = [this, hasTimer, toWaitDuration, newTargetTime]() {
+ const std::optional<std::chrono::nanoseconds> wait = m_timerInfo->timerWait();
+ const auto toWaitDuration = duration_cast<milliseconds>(wait.value_or(0ms));
+ const auto newTargetTimePoint = m_timerInfo->currentTime + toWaitDuration;
+ auto epochNsecs = newTargetTimePoint.time_since_epoch();
+ auto newTargetTime = std::chrono::duration_cast<std::chrono::milliseconds>(epochNsecs);
+ auto maintainNativeTimer = [this, wait, toWaitDuration, newTargetTime]() {
Q_ASSERT(emscripten_is_main_runtime_thread());
- if (!hasTimer) {
+ if (!wait) {
if (m_timerId > 0) {
emscripten_clear_timeout(m_timerId);
m_timerId = 0;
- m_timerTargetTime = 0;
+ m_timerTargetTime = 0ms;
}
return;
}
- if (m_timerTargetTime != 0 && newTargetTime >= m_timerTargetTime)
+ if (m_timerTargetTime != 0ms && newTargetTime >= m_timerTargetTime)
return; // existing timer is good
qCDebug(lcEventDispatcherTimers)
- << "Created new native timer with wait" << toWaitDuration << "timeout" << newTargetTime;
+ << "Created new native timer with wait" << toWaitDuration.count() << "ms"
+ << "timeout" << newTargetTime.count() << "ms";
emscripten_clear_timeout(m_timerId);
- m_timerId = emscripten_set_timeout(&QEventDispatcherWasm::callProcessTimers, toWaitDuration, this);
+ m_timerId = emscripten_set_timeout(&QEventDispatcherWasm::callProcessTimers,
+ toWaitDuration.count(), this);
m_timerTargetTime = newTargetTime;
};
@@ -536,8 +658,8 @@ void QEventDispatcherWasm::updateNativeTimer()
// and keep the mutex locked while updating the native timer to
// prevent it from being deleted.
LOCK_GUARD(g_staticDataMutex);
- if (isValidEventDispatcherPointer(this))
- maintainNativeTimer();
+ if (isValidEventDispatcherPointer(this))
+ maintainNativeTimer();
});
}
@@ -553,7 +675,7 @@ void QEventDispatcherWasm::callProcessTimers(void *context)
// Process timers on this thread if this is the main event dispatcher
if (reinterpret_cast<QEventDispatcherWasm *>(context) == g_mainThreadEventDispatcher) {
- g_mainThreadEventDispatcher->m_timerTargetTime = 0;
+ g_mainThreadEventDispatcher->m_timerTargetTime = 0ms;
g_mainThreadEventDispatcher->processTimers();
return;
}
@@ -563,7 +685,7 @@ void QEventDispatcherWasm::callProcessTimers(void *context)
std::lock_guard<std::mutex> lock(g_staticDataMutex);
if (g_secondaryThreadEventDispatchers.contains(context)) {
QEventDispatcherWasm *eventDispatcher = reinterpret_cast<QEventDispatcherWasm *>(context);
- eventDispatcher->m_timerTargetTime = 0;
+ eventDispatcher->m_timerTargetTime = 0ms;
eventDispatcher->m_processTimers = true;
eventDispatcher->wakeUp();
}
@@ -780,6 +902,50 @@ void QEventDispatcherWasm::socketSelect(int timeout, int socket, bool waitForRea
}
namespace {
+ int g_startupTasks = 0;
+}
+
+// The following functions manages sending the "qtLoaded" event/callback
+// from qtloader.js on startup, once Qt initialization has been completed
+// and the application is ready to display the first frame. This can be
+// either as soon as the event loop is running, or later, if additional
+// startup tasks (e.g. local font loading) have been registered.
+
+void QEventDispatcherWasm::registerStartupTask()
+{
+ ++g_startupTasks;
+}
+
+void QEventDispatcherWasm::completeStarupTask()
+{
+ --g_startupTasks;
+ callOnLoadedIfRequired();
+}
+
+void QEventDispatcherWasm::callOnLoadedIfRequired()
+{
+ if (g_startupTasks > 0)
+ return;
+
+ static bool qtLoadedCalled = false;
+ if (qtLoadedCalled)
+ return;
+ qtLoadedCalled = true;
+
+ Q_ASSERT(g_mainThreadEventDispatcher);
+ g_mainThreadEventDispatcher->onLoaded();
+}
+
+void QEventDispatcherWasm::onLoaded()
+{
+ // TODO: call qtloader.js onLoaded from here, in order to delay
+ // hiding the "Loading..." message until the app is ready to paint
+ // the first frame. Currently onLoaded must be called early before
+ // main() in order to ensure that the screen/container elements
+ // have valid geometry at startup.
+}
+
+namespace {
void trampoline(void *context) {
auto async_fn = [](void *context){
@@ -798,24 +964,19 @@ void QEventDispatcherWasm::run(std::function<void(void)> fn)
fn();
}
-// Runs a function asynchronously. Main thread only.
-void QEventDispatcherWasm::runAsync(std::function<void(void)> fn)
-{
- trampoline(new std::function<void(void)>(fn));
-}
-
-// Runs a function on the main thread. The function runs synchronusly if
-// the calling thread is then main thread.
void QEventDispatcherWasm::runOnMainThread(std::function<void(void)> fn)
{
#if QT_CONFIG(thread)
- if (!emscripten_is_main_runtime_thread()) {
- void *context = new std::function<void(void)>(fn);
- emscripten_async_run_in_main_runtime_thread_(EM_FUNC_SIG_VI, reinterpret_cast<void *>(trampoline), context);
- return;
- }
+ qstdweb::runTaskOnMainThread<void>(fn, &g_proxyingQueue);
+#else
+ qstdweb::runTaskOnMainThread<void>(fn);
#endif
- fn();
+}
+
+// Runs a function asynchronously. Main thread only.
+void QEventDispatcherWasm::runAsync(std::function<void(void)> fn)
+{
+ trampoline(new std::function<void(void)>(fn));
}
// Runs a function on the main thread. The function always runs asynchronously,
@@ -825,7 +986,9 @@ void QEventDispatcherWasm::runOnMainThreadAsync(std::function<void(void)> fn)
void *context = new std::function<void(void)>(fn);
#if QT_CONFIG(thread)
if (!emscripten_is_main_runtime_thread()) {
- emscripten_async_run_in_main_runtime_thread_(EM_FUNC_SIG_VI, reinterpret_cast<void *>(trampoline), context);
+ g_proxyingQueue.proxyAsync(g_mainThread, [context]{
+ trampoline(context);
+ });
return;
}
#endif
@@ -833,3 +996,5 @@ void QEventDispatcherWasm::runOnMainThreadAsync(std::function<void(void)> fn)
}
QT_END_NAMESPACE
+
+#include "moc_qeventdispatcher_wasm_p.cpp"
diff --git a/src/corelib/kernel/qeventdispatcher_wasm_p.h b/src/corelib/kernel/qeventdispatcher_wasm_p.h
index b6de4187f4..7b257e02ad 100644
--- a/src/corelib/kernel/qeventdispatcher_wasm_p.h
+++ b/src/corelib/kernel/qeventdispatcher_wasm_p.h
@@ -20,16 +20,19 @@
#include <QtCore/qloggingcategory.h>
#include <QtCore/qwaitcondition.h>
+#include <chrono>
#include <mutex>
#include <optional>
#include <tuple>
+#include <emscripten/proxying.h>
+
QT_BEGIN_NAMESPACE
Q_DECLARE_LOGGING_CATEGORY(lcEventDispatcher);
Q_DECLARE_LOGGING_CATEGORY(lcEventDispatcherTimers)
-class Q_CORE_EXPORT QEventDispatcherWasm : public QAbstractEventDispatcher
+class Q_CORE_EXPORT QEventDispatcherWasm : public QAbstractEventDispatcherV2
{
Q_OBJECT
public:
@@ -41,19 +44,27 @@ public:
void registerSocketNotifier(QSocketNotifier *notifier) override;
void unregisterSocketNotifier(QSocketNotifier *notifier) override;
- void registerTimer(int timerId, qint64 interval, Qt::TimerType timerType, QObject *object) override;
- bool unregisterTimer(int timerId) override;
- bool unregisterTimers(QObject *object) override;
- QList<QAbstractEventDispatcher::TimerInfo> registeredTimers(QObject *object) const override;
- int remainingTime(int timerId) override;
+ void registerTimer(Qt::TimerId timerId, Duration interval, Qt::TimerType timerType,
+ QObject *object) override final;
+ bool unregisterTimer(Qt::TimerId timerId) override final;
+ bool unregisterTimers(QObject *object) override final;
+ QList<TimerInfoV2> timersForObject(QObject *object) const override final;
+ Duration remainingTime(Qt::TimerId timerId) const override final;
void interrupt() override;
void wakeUp() override;
+ static void runOnMainThread(std::function<void(void)> fn);
static void socketSelect(int timeout, int socket, bool waitForRead, bool waitForWrite,
bool *selectForRead, bool *selectForWrite, bool *socketDisconnect);
- protected:
- virtual void processWindowSystemEvents(QEventLoop::ProcessEventsFlags flags);
+
+ static void registerStartupTask();
+ static void completeStarupTask();
+ static void callOnLoadedIfRequired();
+ virtual void onLoaded();
+
+protected:
+ virtual bool processPostedEvents();
private:
bool isMainThreadEventDispatcher();
@@ -64,7 +75,7 @@ private:
void handleDialogExec();
bool wait(int timeout = -1);
bool wakeEventDispatcherThread();
- static void callProcessEvents(void *eventDispatcher);
+ static void callProcessPostedEvents(void *eventDispatcher);
void processTimers();
void updateNativeTimer();
@@ -86,7 +97,6 @@ private:
static void run(std::function<void(void)> fn);
static void runAsync(std::function<void(void)> fn);
- static void runOnMainThread(std::function<void(void)> fn);
static void runOnMainThreadAsync(std::function<void(void)> fn);
static QEventDispatcherWasm *g_mainThreadEventDispatcher;
@@ -97,7 +107,7 @@ private:
QTimerInfoList *m_timerInfo = new QTimerInfoList();
long m_timerId = 0;
- uint64_t m_timerTargetTime = 0;
+ std::chrono::milliseconds m_timerTargetTime{};
#if QT_CONFIG(thread)
std::mutex m_mutex;
@@ -106,6 +116,8 @@ private:
static QVector<QEventDispatcherWasm *> g_secondaryThreadEventDispatchers;
static std::mutex g_staticDataMutex;
+ static emscripten::ProxyingQueue g_proxyingQueue;
+ static pthread_t g_mainThread;
// Note on mutex usage: the global g_staticDataMutex protects the global (g_ prefixed) data,
// while the per eventdispatcher m_mutex protects the state accociated with blocking and waking
diff --git a/src/corelib/kernel/qeventdispatcher_win.cpp b/src/corelib/kernel/qeventdispatcher_win.cpp
index 1c54c97514..a7663b2481 100644
--- a/src/corelib/kernel/qeventdispatcher_win.cpp
+++ b/src/corelib/kernel/qeventdispatcher_win.cpp
@@ -7,7 +7,6 @@
#include "qcoreapplication.h"
#include <private/qsystemlibrary_p.h>
#include "qoperatingsystemversion.h"
-#include "qpair.h"
#include "qset.h"
#include "qsocketnotifier.h"
#include "qvarlengtharray.h"
@@ -56,6 +55,13 @@ class QEventDispatcherWin32Private;
LRESULT QT_WIN_CALLBACK qt_internal_proc(HWND hwnd, UINT message, WPARAM wp, LPARAM lp);
+static quint64 qt_msectime()
+{
+ using namespace std::chrono;
+ auto t = duration_cast<milliseconds>(steady_clock::now().time_since_epoch());
+ return t.count();
+}
+
QEventDispatcherWin32Private::QEventDispatcherWin32Private()
: interrupt(false), internalHwnd(0),
sendPostedEventsTimerId(0), wakeUps(0),
@@ -129,7 +135,7 @@ LRESULT QT_WIN_CALLBACK qt_internal_proc(HWND hwnd, UINT message, WPARAM wp, LPA
QSNDict *sn_vec[4] = { &d->sn_read, &d->sn_write, &d->sn_except, &d->sn_read };
QSNDict *dict = sn_vec[type];
- QSockNot *sn = dict ? dict->value(wp) : 0;
+ QSockNot *sn = dict ? dict->value(qintptr(wp)) : 0;
if (sn == nullptr) {
d->postActivateSocketNotifiers();
} else {
@@ -402,7 +408,7 @@ void QEventDispatcherWin32Private::sendTimerEvent(int timerId)
}
}
-void QEventDispatcherWin32Private::doWsaAsyncSelect(int socket, long event)
+void QEventDispatcherWin32Private::doWsaAsyncSelect(qintptr socket, long event)
{
Q_ASSERT(internalHwnd);
// BoundsChecker may emit a warning for WSAAsyncSelect when event == 0
@@ -551,7 +557,7 @@ bool QEventDispatcherWin32::processEvents(QEventLoop::ProcessEventsFlags flags)
void QEventDispatcherWin32::registerSocketNotifier(QSocketNotifier *notifier)
{
Q_ASSERT(notifier);
- int sockfd = notifier->socket();
+ qintptr sockfd = notifier->socket();
int type = notifier->type();
#ifndef QT_NO_DEBUG
if (sockfd < 0) {
@@ -575,7 +581,7 @@ void QEventDispatcherWin32::registerSocketNotifier(QSocketNotifier *notifier)
const char *t[] = { "Read", "Write", "Exception" };
/* Variable "socket" below is a function pointer. */
qWarning("QSocketNotifier: Multiple socket notifiers for "
- "same socket %d and type %s", sockfd, t[type]);
+ "same socket %" PRIdQINTPTR " and type %s", sockfd, t[type]);
}
QSockNot *sn = new QSockNot;
@@ -619,7 +625,7 @@ void QEventDispatcherWin32::unregisterSocketNotifier(QSocketNotifier *notifier)
{
Q_ASSERT(notifier);
#ifndef QT_NO_DEBUG
- int sockfd = notifier->socket();
+ qintptr sockfd = notifier->socket();
if (sockfd < 0) {
qWarning("QEventDispatcherWin32::unregisterSocketNotifier: invalid socket identifier");
return;
@@ -636,7 +642,7 @@ void QEventDispatcherWin32::doUnregisterSocketNotifier(QSocketNotifier *notifier
{
Q_D(QEventDispatcherWin32);
int type = notifier->type();
- int sockfd = notifier->socket();
+ qintptr sockfd = notifier->socket();
Q_ASSERT(sockfd >= 0);
QSFDict::iterator it = d->active_fd.find(sockfd);
@@ -899,3 +905,5 @@ HWND QEventDispatcherWin32::internalHwnd()
}
QT_END_NAMESPACE
+
+#include "moc_qeventdispatcher_win_p.cpp"
diff --git a/src/corelib/kernel/qeventdispatcher_win_p.h b/src/corelib/kernel/qeventdispatcher_win_p.h
index ecd4bcb27b..558490a85e 100644
--- a/src/corelib/kernel/qeventdispatcher_win_p.h
+++ b/src/corelib/kernel/qeventdispatcher_win_p.h
@@ -28,7 +28,6 @@ class QEventDispatcherWin32Private;
// forward declaration
LRESULT QT_WIN_CALLBACK qt_internal_proc(HWND hwnd, UINT message, WPARAM wp, LPARAM lp);
-quint64 qt_msectime();
class Q_CORE_EXPORT QEventDispatcherWin32 : public QAbstractEventDispatcher
{
@@ -72,9 +71,9 @@ private:
struct QSockNot {
QSocketNotifier *obj;
- int fd;
+ qintptr fd;
};
-typedef QHash<int, QSockNot *> QSNDict;
+typedef QHash<qintptr, QSockNot *> QSNDict;
struct QSockFd {
long event;
@@ -83,7 +82,7 @@ struct QSockFd {
explicit inline QSockFd(long ev = 0, long ma = 0) : event(ev), mask(ma), selected(false) { }
};
-typedef QHash<int, QSockFd> QSFDict;
+typedef QHash<qintptr, QSockFd> QSFDict;
struct WinTimerInfo { // internal timer info
QObject *dispatcher;
@@ -136,7 +135,7 @@ public:
QSFDict active_fd;
bool activateNotifiersPosted;
void postActivateSocketNotifiers();
- void doWsaAsyncSelect(int socket, long event);
+ void doWsaAsyncSelect(qintptr socket, long event);
bool closingDown = false;
diff --git a/src/corelib/kernel/qeventloop.cpp b/src/corelib/kernel/qeventloop.cpp
index 7001bfa5bd..e314a17ff8 100644
--- a/src/corelib/kernel/qeventloop.cpp
+++ b/src/corelib/kernel/qeventloop.cpp
@@ -6,7 +6,7 @@
#include "qabstracteventdispatcher.h"
#include "qcoreapplication.h"
#include "qcoreapplication_p.h"
-#include "qelapsedtimer.h"
+#include "qdeadlinetimer.h"
#include "qobject_p.h"
#include "qeventloop_p.h"
@@ -116,10 +116,10 @@ bool QEventLoop::processEvents(ProcessEventsFlags flags)
can be used before calling exec(), because modal widgets
use their own local event loop.
- To make your application perform idle processing (i.e. executing a
- special function whenever there are no pending events), use a
- QTimer with 0 timeout. More sophisticated idle processing schemes
- can be achieved using processEvents().
+ To make your application perform idle processing (i.e. executing a special
+ function whenever there are no pending events), use a QChronoTimer with
+ 0ns timeout. More sophisticated idle processing schemes can be achieved
+ using processEvents().
\sa QCoreApplication::quit(), exit(), processEvents()
*/
@@ -151,6 +151,9 @@ int QEventLoop::exec(ProcessEventsFlags flags)
auto threadData = d->threadData.loadRelaxed();
++threadData->loopLevel;
threadData->eventLoops.push(d->q_func());
+ qCDebug(lcDeleteLater) << "Increased" << threadData->thread
+ << "loop level to" << threadData->loopLevel
+ << "with leaf loop now" << threadData->eventLoops.last();
locker.unlock();
}
@@ -169,6 +172,12 @@ int QEventLoop::exec(ProcessEventsFlags flags)
Q_UNUSED(eventLoop); // --release warning
d->inExec = false;
--threadData->loopLevel;
+
+ qCDebug(lcDeleteLater) << "Decreased" << threadData->thread
+ << "loop level to" << threadData->loopLevel
+ << "with leaf loop now" << (threadData->eventLoops.isEmpty()
+ ? nullptr : threadData->eventLoops.last());
+
}
};
LoopReference ref(d, locker);
@@ -186,9 +195,27 @@ int QEventLoop::exec(ProcessEventsFlags flags)
}
/*!
+ \overload
+
Process pending events that match \a flags for a maximum of \a
maxTime milliseconds, or until there are no more events to
process, whichever is shorter.
+
+ Equivalent to calling:
+ \code
+ processEvents(flags, QDeadlineTimer(maxTime));
+ \endcode
+*/
+void QEventLoop::processEvents(ProcessEventsFlags flags, int maxTime)
+{
+ processEvents(flags, QDeadlineTimer(maxTime));
+}
+
+/*!
+ \since 6.7
+
+ Process pending events that match \a flags until \a deadline has expired,
+ or until there are no more events to process, whichever happens first.
This function is especially useful if you have a long running
operation and want to show its progress without allowing user
input, i.e. by using the \l ExcludeUserInputEvents flag.
@@ -201,16 +228,14 @@ int QEventLoop::exec(ProcessEventsFlags flags)
and will be ignored.
\endlist
*/
-void QEventLoop::processEvents(ProcessEventsFlags flags, int maxTime)
+void QEventLoop::processEvents(ProcessEventsFlags flags, QDeadlineTimer deadline)
{
Q_D(QEventLoop);
if (!d->threadData.loadRelaxed()->hasEventDispatcher())
return;
- QElapsedTimer start;
- start.start();
while (processEvents(flags & ~WaitForMoreEvents)) {
- if (start.elapsed() > maxTime)
+ if (deadline.hasExpired())
break;
}
}
@@ -293,57 +318,10 @@ bool QEventLoop::event(QEvent *event)
void QEventLoop::quit()
{ exit(0); }
-
-class QEventLoopLockerPrivate
-{
-public:
- explicit QEventLoopLockerPrivate(QEventLoopPrivate *loop)
- : loop(loop), type(EventLoop)
- {
- loop->ref();
- }
-
- explicit QEventLoopLockerPrivate(QThreadPrivate *thread)
- : thread(thread), type(Thread)
- {
- thread->ref();
- }
-
- explicit QEventLoopLockerPrivate(QCoreApplicationPrivate *app)
- : app(app), type(Application)
- {
- app->ref();
- }
-
- ~QEventLoopLockerPrivate()
- {
- switch (type)
- {
- case EventLoop:
- loop->deref();
- break;
- case Thread:
- thread->deref();
- break;
- default:
- app->deref();
- break;
- }
- }
-
-private:
- union {
- QEventLoopPrivate * loop;
- QThreadPrivate * thread;
- QCoreApplicationPrivate * app;
- };
- enum Type {
- EventLoop,
- Thread,
- Application
- };
- const Type type;
-};
+// If any of these trigger, the Type bits will interfere with the pointer values:
+static_assert(alignof(QEventLoop) >= 4);
+static_assert(alignof(QThread) >= 4);
+static_assert(alignof(QCoreApplication) >= 4);
/*!
\class QEventLoopLocker
@@ -368,12 +346,16 @@ private:
/*!
Creates an event locker operating on the QCoreApplication.
- The application will quit when there are no more QEventLoopLockers operating on it.
+ The application will attempt to quit when there are no more QEventLoopLockers
+ operating on it, as long as QCoreApplication::isQuitLockEnabled() is \c true.
+
+ Note that attempting a quit may not necessarily result in the application quitting,
+ if there for example are open windows, or the QEvent::Quit event is ignored.
\sa QCoreApplication::quit(), QCoreApplication::isQuitLockEnabled()
*/
-QEventLoopLocker::QEventLoopLocker()
- : d_ptr(new QEventLoopLockerPrivate(static_cast<QCoreApplicationPrivate*>(QObjectPrivate::get(QCoreApplication::instance()))))
+QEventLoopLocker::QEventLoopLocker() noexcept
+ : QEventLoopLocker{QCoreApplication::instance(), Type::Application}
{
}
@@ -385,8 +367,8 @@ QEventLoopLocker::QEventLoopLocker()
\sa QEventLoop::quit()
*/
-QEventLoopLocker::QEventLoopLocker(QEventLoop *loop)
- : d_ptr(new QEventLoopLockerPrivate(static_cast<QEventLoopPrivate*>(QObjectPrivate::get(loop))))
+QEventLoopLocker::QEventLoopLocker(QEventLoop *loop) noexcept
+ : QEventLoopLocker{loop, Type::EventLoop}
{
}
@@ -398,18 +380,80 @@ QEventLoopLocker::QEventLoopLocker(QEventLoop *loop)
\sa QThread::quit()
*/
-QEventLoopLocker::QEventLoopLocker(QThread *thread)
- : d_ptr(new QEventLoopLockerPrivate(static_cast<QThreadPrivate*>(QObjectPrivate::get(thread))))
+QEventLoopLocker::QEventLoopLocker(QThread *thread) noexcept
+ : QEventLoopLocker{thread, Type::Thread}
{
}
/*!
+ \fn QEventLoopLocker::QEventLoopLocker(QEventLoopLocker &&other)
+ \since 6.7
+
+ Move-constructs an event-loop locker from \a other. \a other will have a
+ no-op destructor, while responsibility for preventing the
+ QEventLoop/QThread/QCoreApplication from quitting is transferred to the new
+ object.
+*/
+
+/*!
+ \fn QEventLoopLocker &QEventLoopLocker::operator=(QEventLoopLocker &&other)
+ \since 6.7
+
+ Move-assigns this event-loop locker from \a other. \a other will have a
+ no-op destructor, while responsibility for preventing the
+ QEventLoop/QThread/QCoreApplication from quitting is transferred to this
+ object.
+*/
+
+/*!
+ \fn QEventLoopLocker::swap(QEventLoopLocker &other)
+ \since 6.7
+
+ Swaps the object and the state of this QEventLoopLocker with \a other.
+ This operation is very fast and never fails.
+*/
+
+/*!
+ \fn QEventLoopLocker::swap(QEventLoopLocker &lhs, QEventLoopLocker &rhs)
+ \since 6.7
+
+ Swaps the object and the state of \a lhs with \a rhs.
+ This operation is very fast and never fails.
+*/
+
+/*!
Destroys this event loop locker object
*/
QEventLoopLocker::~QEventLoopLocker()
{
- delete d_ptr;
+ visit([](auto p) { p->d_func()->deref(); });
+}
+
+/*!
+ \internal
+*/
+QEventLoopLocker::QEventLoopLocker(void *ptr, Type t) noexcept
+ : p{quintptr(ptr) | quintptr(t)}
+{
+ visit([](auto p) { p->d_func()->ref(); });
+}
+
+/*!
+ \internal
+*/
+template <typename Func>
+void QEventLoopLocker::visit(Func f) const
+{
+ const auto ptr = pointer();
+ if (!ptr)
+ return;
+ switch (type()) {
+ case Type::EventLoop: return f(static_cast<QEventLoop *>(ptr));
+ case Type::Thread: return f(static_cast<QThread *>(ptr));
+ case Type::Application: return f(static_cast<QCoreApplication *>(ptr));
+ }
+ Q_UNREACHABLE();
}
QT_END_NAMESPACE
diff --git a/src/corelib/kernel/qeventloop.h b/src/corelib/kernel/qeventloop.h
index 1da1d2336e..ec79c07cd7 100644
--- a/src/corelib/kernel/qeventloop.h
+++ b/src/corelib/kernel/qeventloop.h
@@ -5,15 +5,18 @@
#define QEVENTLOOP_H
#include <QtCore/qobject.h>
+#include <QtCore/qdeadlinetimer.h>
QT_BEGIN_NAMESPACE
+class QEventLoopLocker;
class QEventLoopPrivate;
class Q_CORE_EXPORT QEventLoop : public QObject
{
Q_OBJECT
Q_DECLARE_PRIVATE(QEventLoop)
+ friend class QEventLoopLocker;
public:
explicit QEventLoop(QObject *parent = nullptr);
@@ -34,6 +37,7 @@ public:
bool processEvents(ProcessEventsFlags flags = AllEvents);
void processEvents(ProcessEventsFlags flags, int maximumTime);
+ void processEvents(ProcessEventsFlags flags, QDeadlineTimer deadline);
int exec(ProcessEventsFlags flags = AllEvents);
bool isRunning() const;
@@ -51,17 +55,41 @@ Q_DECLARE_OPERATORS_FOR_FLAGS(QEventLoop::ProcessEventsFlags)
class QEventLoopLockerPrivate;
-class Q_CORE_EXPORT QEventLoopLocker
+class QEventLoopLocker
{
public:
- QEventLoopLocker();
- explicit QEventLoopLocker(QEventLoop *loop);
- explicit QEventLoopLocker(QThread *thread);
- ~QEventLoopLocker();
+ Q_NODISCARD_CTOR Q_CORE_EXPORT QEventLoopLocker() noexcept;
+ Q_NODISCARD_CTOR Q_CORE_EXPORT explicit QEventLoopLocker(QEventLoop *loop) noexcept;
+ Q_NODISCARD_CTOR Q_CORE_EXPORT explicit QEventLoopLocker(QThread *thread) noexcept;
+ Q_CORE_EXPORT ~QEventLoopLocker();
+
+ Q_NODISCARD_CTOR QEventLoopLocker(QEventLoopLocker &&other) noexcept
+ : p{std::exchange(other.p, 0)} {}
+ QT_MOVE_ASSIGNMENT_OPERATOR_IMPL_VIA_MOVE_AND_SWAP(QEventLoopLocker)
+
+ void swap(QEventLoopLocker &other) noexcept { std::swap(p, other.p); }
+ friend void swap(QEventLoopLocker &lhs, QEventLoopLocker &rhs) noexcept { lhs.swap(rhs); }
private:
Q_DISABLE_COPY(QEventLoopLocker)
- QEventLoopLockerPrivate *d_ptr;
+ friend class QEventLoopLockerPrivate;
+
+ //
+ // Private implementation details.
+ // Do not call from public inline API!
+ //
+ enum class Type : quintptr {
+ EventLoop,
+ Thread,
+ Application,
+ };
+ explicit QEventLoopLocker(void *ptr, Type t) noexcept;
+ quintptr p;
+ static constexpr quintptr TypeMask = 0x3;
+ Type type() const { return Type(p & TypeMask); }
+ void *pointer() const { return reinterpret_cast<void *>(p & ~TypeMask); }
+ template <typename Func>
+ void visit(Func func) const;
};
QT_END_NAMESPACE
diff --git a/src/corelib/kernel/qfunctions_p.h b/src/corelib/kernel/qfunctions_p.h
index f1ae1f3c9c..58afc207e6 100644
--- a/src/corelib/kernel/qfunctions_p.h
+++ b/src/corelib/kernel/qfunctions_p.h
@@ -17,9 +17,5 @@
#include <QtCore/private/qglobal_p.h>
-#if defined(Q_OS_VXWORKS)
-# include "QtCore/qfunctions_vxworks.h"
-#endif
-
#endif
diff --git a/src/corelib/kernel/qfunctions_vxworks.cpp b/src/corelib/kernel/qfunctions_vxworks.cpp
deleted file mode 100644
index 0ada99701c..0000000000
--- a/src/corelib/kernel/qfunctions_vxworks.cpp
+++ /dev/null
@@ -1,172 +0,0 @@
-// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
-
-#include "qglobal.h"
-
-#ifdef Q_OS_VXWORKS
-
-#include "qplatformdefs.h"
-#include "qfunctions_vxworks.h"
-
-#if defined(_WRS_KERNEL)
-#include <vmLib.h>
-#endif
-#include <selectLib.h>
-#include <ioLib.h>
-
-QT_USE_NAMESPACE
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-// no lfind() - used by the TIF image format
-void *lfind(const void* key, const void* base, size_t* elements, size_t size,
- int (*compare)(const void*, const void*))
-{
- const char* current = (char*) base;
- const char* const end = (char*) (current + (*elements) * size);
- while (current != end) {
- if (compare(current, key) == 0)
- return (void*)current;
- current += size;
- }
- return 0;
-}
-
-
-// no rand_r(), but rand()
-// NOTE: this implementation is wrong for multi threaded applications,
-// but there is no way to get it right on VxWorks (in kernel mode)
-#if defined(_WRS_KERNEL)
-int rand_r(unsigned int * /*seedp*/)
-{
- return rand();
-}
-#endif
-
-// no usleep() support
-int usleep(unsigned int usec)
-{
- div_t dt = div(usec, 1000000);
- struct timespec ts = { dt.quot, dt.rem * 1000 };
-
- return nanosleep(&ts, 0);
-}
-
-
-// gettimeofday() is declared, but is missing from the library
-// It IS however defined in the Curtis-Wright X11 libraries, so
-// we have to make the symbol 'weak'
-#if defined(Q_CC_DIAB) && !defined(VXWORKS_DKM) && !defined(VXWORKS_RTP)
-# pragma weak gettimeofday
-#endif
-int gettimeofday(struct timeval *tv, void /*struct timezone*/ *)
-{
- // the compiler will optimize this and will only use one code path
- if (sizeof(struct timeval) == sizeof(struct timespec)) {
- int res = clock_gettime(CLOCK_REALTIME, (struct timespec *) tv);
- if (!res)
- tv->tv_usec /= 1000;
- return res;
- } else {
- struct timespec ts;
-
- int res = clock_gettime(CLOCK_REALTIME, &ts);
- if (!res) {
- tv->tv_sec = ts.tv_sec;
- tv->tv_usec = ts.tv_nsec / 1000;
- }
- return res;
- }
-}
-
-// neither getpagesize() or sysconf(_SC_PAGESIZE) are available
-int getpagesize()
-{
-#if defined(_WRS_KERNEL)
- return vmPageSizeGet();
-#else
- return sysconf(_SC_PAGESIZE);
-#endif
-}
-
-// symlinks are not supported (lstat is now just a call to stat - see qplatformdefs.h)
-int symlink(const char *, const char *)
-{
- errno = EIO;
- return -1;
-}
-
-ssize_t readlink(const char *, char *, size_t)
-{
- errno = EIO;
- return -1;
-}
-
-// there's no truncate(), but ftruncate() support...
-int truncate(const char *path, off_t length)
-{
- int fd = open(path, O_WRONLY, 00777);
- if (fd >= 0) {
- int res = ftruncate(fd, length);
- int en = errno;
- close(fd);
- errno = en;
- return res;
- }
- // errno is already set by open
- return -1;
-}
-
-
-
-// VxWorks doesn't know about passwd & friends.
-// in order to avoid patching the unix fs path everywhere
-// we introduce some dummy functions that simulate a single
-// 'root' user on the system.
-
-uid_t getuid()
-{
- return 0;
-}
-
-gid_t getgid()
-{
- return 0;
-}
-
-uid_t geteuid()
-{
- return 0;
-}
-
-struct passwd *getpwuid(uid_t uid)
-{
- static struct passwd pwbuf = { "root", 0, 0, 0, 0, 0, 0 };
-
- if (uid == 0) {
- return &pwbuf;
- } else {
- errno = ENOENT;
- return 0;
- }
-}
-
-struct group *getgrgid(gid_t gid)
-{
- static struct group grbuf = { "root", 0, 0, 0 };
-
- if (gid == 0) {
- return &grbuf;
- } else {
- errno = ENOENT;
- return 0;
- }
-}
-
-#ifdef __cplusplus
-} // extern "C"
-#endif
-
-#endif // Q_OS_VXWORKS
diff --git a/src/corelib/kernel/qfunctions_vxworks.h b/src/corelib/kernel/qfunctions_vxworks.h
deleted file mode 100644
index 26006bd564..0000000000
--- a/src/corelib/kernel/qfunctions_vxworks.h
+++ /dev/null
@@ -1,159 +0,0 @@
-// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
-
-#ifndef QFUNCTIONS_VXWORKS_H
-#define QFUNCTIONS_VXWORKS_H
-
-#include <QtCore/qglobal.h>
-
-#ifdef Q_OS_VXWORKS
-
-#include <unistd.h>
-#include <pthread.h>
-#include <dirent.h>
-#include <signal.h>
-#include <string.h>
-#include <strings.h>
-#include <errno.h>
-#include <sys/types.h>
-#include <sys/ioctl.h>
-#if defined(_WRS_KERNEL)
-#include <sys/times.h>
-#else
-#include <sys/time.h>
-#endif
-#include <sys/socket.h>
-#include <sys/stat.h>
-#include <sys/wait.h>
-#include <netinet/in.h>
-
-// VxWorks has public header mbuf.h which defines following variables for DKM.
-// Let's undef those to because they overlap with Qt variable names-
-// File mbuf.h is included in headers <netinet/in.h> <net/if.h>, so make sure
-// that those are included before undef's.
-#if defined(mbuf)
-# undef mbuf
-#endif
-#if defined(m_data)
-# undef m_data
-#endif
-#if defined(m_type)
-# undef m_type
-#endif
-#if defined(m_next)
-# undef m_next
-#endif
-#if defined(m_len)
-# undef m_len
-#endif
-#if defined(m_flags)
-# undef m_flags
-#endif
-#if defined(m_hdr)
-# undef m_hdr
-#endif
-#if defined(m_ext)
-# undef m_ext
-#endif
-#if defined(m_act)
-# undef m_act
-#endif
-#if defined(m_nextpkt)
-# undef m_nextpkt
-#endif
-#if defined(m_pkthdr)
-# undef m_pkthdr
-#endif
-
-QT_BEGIN_NAMESPACE
-
-#ifdef QT_BUILD_CORE_LIB
-#endif
-
-QT_END_NAMESPACE
-
-#ifndef RTLD_LOCAL
-#define RTLD_LOCAL 0
-#endif
-
-#ifndef NSIG
-#define NSIG _NSIGS
-#endif
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-// isascii is missing (sometimes!!)
-#ifndef isascii
-inline int isascii(int c) { return (c & 0x7f); }
-#endif
-
-// no lfind() - used by the TIF image format
-void *lfind(const void* key, const void* base, size_t* elements, size_t size,
- int (*compare)(const void*, const void*));
-
-// no rand_r(), but rand()
-// NOTE: this implementation is wrong for multi threaded applications,
-// but there is no way to get it right on VxWorks (in kernel mode)
-#if defined(_WRS_KERNEL)
-int rand_r(unsigned int * /*seedp*/);
-#endif
-
-// no usleep() support
-int usleep(unsigned int);
-
-#if defined(VXWORKS_DKM) || defined(VXWORKS_RTP)
-int gettimeofday(struct timeval *, void *);
-#else
-// gettimeofday() is declared, but is missing from the library.
-// It IS however defined in the Curtis-Wright X11 libraries, so
-// we have to make the symbol 'weak'
-int gettimeofday(struct timeval *tv, void /*struct timezone*/ *) __attribute__((weak));
-#endif
-
-// getpagesize() not available
-int getpagesize();
-
-// symlinks are not supported (lstat is now just a call to stat - see qplatformdefs.h)
-int symlink(const char *, const char *);
-ssize_t readlink(const char *, char *, size_t);
-
-// there's no truncate(), but ftruncate() support...
-int truncate(const char *path, off_t length);
-
-// VxWorks doesn't know about passwd & friends.
-// in order to avoid patching the unix fs path everywhere
-// we introduce some dummy functions that simulate a single
-// 'root' user on the system.
-
-uid_t getuid();
-gid_t getgid();
-uid_t geteuid();
-
-struct passwd {
- char *pw_name; /* user name */
- char *pw_passwd; /* user password */
- uid_t pw_uid; /* user ID */
- gid_t pw_gid; /* group ID */
- char *pw_gecos; /* real name */
- char *pw_dir; /* home directory */
- char *pw_shell; /* shell program */
-};
-
-struct group {
- char *gr_name; /* group name */
- char *gr_passwd; /* group password */
- gid_t gr_gid; /* group ID */
- char **gr_mem; /* group members */
-};
-
-struct passwd *getpwuid(uid_t uid);
-struct group *getgrgid(gid_t gid);
-
-#ifdef __cplusplus
-} // extern "C"
-#endif
-
-#endif // Q_OS_VXWORKS
-#endif // QFUNCTIONS_VXWORKS_H
diff --git a/src/corelib/kernel/qfunctions_win.cpp b/src/corelib/kernel/qfunctions_win.cpp
index ed0e25467c..048fdbc934 100644
--- a/src/corelib/kernel/qfunctions_win.cpp
+++ b/src/corelib/kernel/qfunctions_win.cpp
@@ -28,11 +28,13 @@ QComHelper::QComHelper(COINIT concurrencyModel)
QComHelper::~QComHelper()
{
+ Q_ASSERT(m_threadId == GetCurrentThreadId());
if (SUCCEEDED(m_initResult))
CoUninitialize();
}
/*!
+ \internal
Checks if the application has a \e{package identity}
Having a \e{package identity} is required to use many modern
diff --git a/src/corelib/kernel/qfunctions_win_p.h b/src/corelib/kernel/qfunctions_win_p.h
index 540405bd2f..ab5417f8a2 100644
--- a/src/corelib/kernel/qfunctions_win_p.h
+++ b/src/corelib/kernel/qfunctions_win_p.h
@@ -39,6 +39,7 @@ public:
private:
HRESULT m_initResult = E_FAIL;
+ DWORD m_threadId{ GetCurrentThreadId() };
};
Q_CORE_EXPORT bool qt_win_hasPackageIdentity();
diff --git a/src/corelib/kernel/qiterable.cpp b/src/corelib/kernel/qiterable.cpp
index 80715b2347..a8c93fbc1c 100644
--- a/src/corelib/kernel/qiterable.cpp
+++ b/src/corelib/kernel/qiterable.cpp
@@ -240,6 +240,7 @@ QT_BEGIN_NAMESPACE
/*!
\fn template<class Container> qsizetype QIterator<Container>::operator-(const QIterator<Container> &j) const
+ \overload
Returns the distance between the two iterators.
@@ -377,6 +378,8 @@ QT_BEGIN_NAMESPACE
/*!
\fn template <class Container> qsizetype QConstIterator<Container>::operator-(const QConstIterator<Container> &j) const
+ \overload
+
Returns the distance between the two iterators.
\sa operator+(), operator-=(), QIterable::canReverseIterate()
diff --git a/src/corelib/kernel/qiterable.h b/src/corelib/kernel/qiterable.h
index 11bf82dc04..4adcdfd76f 100644
--- a/src/corelib/kernel/qiterable.h
+++ b/src/corelib/kernel/qiterable.h
@@ -19,16 +19,16 @@ namespace QtPrivate {
QTaggedPointer<Storage, Tag> m_pointer;
public:
- QConstPreservingPointer(std::nullptr_t) : m_pointer(nullptr, Const) {}
+ Q_NODISCARD_CTOR QConstPreservingPointer(std::nullptr_t) : m_pointer(nullptr, Const) {}
- QConstPreservingPointer(const void *pointer, qsizetype alignment)
+ Q_NODISCARD_CTOR QConstPreservingPointer(const void *pointer, qsizetype alignment)
: m_pointer(reinterpret_cast<Storage *>(const_cast<void *>(pointer)), Const)
{
Q_UNUSED(alignment);
Q_ASSERT(alignment > qsizetype(alignof(Storage)));
}
- QConstPreservingPointer(void *pointer, qsizetype alignment)
+ Q_NODISCARD_CTOR QConstPreservingPointer(void *pointer, qsizetype alignment)
: m_pointer(reinterpret_cast<Storage *>(pointer), Mutable)
{
Q_UNUSED(alignment);
@@ -36,20 +36,20 @@ namespace QtPrivate {
}
template<typename InputType>
- QConstPreservingPointer(const InputType *pointer)
+ Q_NODISCARD_CTOR QConstPreservingPointer(const InputType *pointer)
: m_pointer(reinterpret_cast<Storage *>(const_cast<InputType *>(pointer)), Const)
{
static_assert(alignof(InputType) >= alignof(Storage));
}
template<typename InputType>
- QConstPreservingPointer(InputType *pointer)
+ Q_NODISCARD_CTOR QConstPreservingPointer(InputType *pointer)
: m_pointer(reinterpret_cast<Storage *>(pointer), Mutable)
{
static_assert(alignof(InputType) >= alignof(Storage));
}
- QConstPreservingPointer() = default;
+ Q_NODISCARD_CTOR QConstPreservingPointer() = default;
const Type *constPointer() const
{
@@ -71,28 +71,32 @@ public:
QTaggedIterator(Iterator &&it) : Iterator(std::move(it))
{
const QMetaContainer metaContainer = this->metaContainer();
- if (std::is_base_of_v<std::random_access_iterator_tag, IteratorCategory>
- && !metaContainer.hasRandomAccessIterator()) {
- qFatal("You cannot use this iterator as a random access iterator");
- this->clearIterator();
+ if constexpr (std::is_base_of_v<std::random_access_iterator_tag, IteratorCategory>) {
+ if (!metaContainer.hasRandomAccessIterator()) {
+ qFatal("You cannot use this iterator as a random access iterator");
+ this->clearIterator();
+ }
}
- if (std::is_base_of_v<std::bidirectional_iterator_tag, IteratorCategory>
- && !metaContainer.hasBidirectionalIterator()) {
- qFatal("You cannot use this iterator as a bidirectional iterator");
- this->clearIterator();
+ if constexpr (std::is_base_of_v<std::bidirectional_iterator_tag, IteratorCategory>) {
+ if (!metaContainer.hasBidirectionalIterator()) {
+ qFatal("You cannot use this iterator as a bidirectional iterator");
+ this->clearIterator();
+ }
}
- if (std::is_base_of_v<std::forward_iterator_tag, IteratorCategory>
- && !metaContainer.hasForwardIterator()) {
- qFatal("You cannot use this iterator as a forward iterator");
- this->clearIterator();
+ if constexpr (std::is_base_of_v<std::forward_iterator_tag, IteratorCategory>) {
+ if (!metaContainer.hasForwardIterator()) {
+ qFatal("You cannot use this iterator as a forward iterator");
+ this->clearIterator();
+ }
}
- if (std::is_base_of_v<std::input_iterator_tag, IteratorCategory>
- && !metaContainer.hasInputIterator()) {
- qFatal("You cannot use this iterator as an input iterator");
- this->clearIterator();
+ if constexpr (std::is_base_of_v<std::input_iterator_tag, IteratorCategory>) {
+ if (!metaContainer.hasInputIterator()) {
+ qFatal("You cannot use this iterator as an input iterator");
+ this->clearIterator();
+ }
}
}
diff --git a/src/corelib/kernel/qjniarray.h b/src/corelib/kernel/qjniarray.h
new file mode 100644
index 0000000000..2ea82e39db
--- /dev/null
+++ b/src/corelib/kernel/qjniarray.h
@@ -0,0 +1,464 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QJNIARRAY_H
+#define QJNIARRAY_H
+
+#include <QtCore/qlist.h>
+
+#if defined(Q_QDOC) || defined(Q_OS_ANDROID)
+#include <QtCore/qbytearray.h>
+#include <QtCore/qjniobject.h>
+
+#include <iterator>
+#include <utility>
+#include <QtCore/q20type_traits.h>
+
+QT_BEGIN_NAMESPACE
+
+template <typename T> class QJniArray;
+template <typename T>
+struct QT_TECH_PREVIEW_API QJniArrayIterator
+{
+ QJniArrayIterator() = default;
+
+ constexpr QJniArrayIterator(const QJniArrayIterator &other) noexcept = default;
+ constexpr QJniArrayIterator(QJniArrayIterator &&other) noexcept = default;
+ constexpr QJniArrayIterator &operator=(const QJniArrayIterator &other) noexcept = default;
+ constexpr QJniArrayIterator &operator=(QJniArrayIterator &&other) noexcept = default;
+
+ using difference_type = jsize;
+ using value_type = T;
+ using pointer = T *;
+ using reference = T; // difference to container requirements
+ using const_reference = reference;
+ using iterator_category = std::bidirectional_iterator_tag;
+
+ friend bool operator==(const QJniArrayIterator &lhs, const QJniArrayIterator &rhs) noexcept
+ {
+ return lhs.m_array == rhs.m_array && lhs.m_index == rhs.m_index;
+ }
+ friend bool operator!=(const QJniArrayIterator &lhs, const QJniArrayIterator &rhs) noexcept
+ {
+ return !(lhs == rhs);
+ }
+ const_reference operator*() const
+ {
+ return m_array->at(m_index);
+ }
+ friend QJniArrayIterator &operator++(QJniArrayIterator &that) noexcept
+ {
+ ++that.m_index;
+ return that;
+ }
+ friend QJniArrayIterator operator++(QJniArrayIterator &that, int) noexcept
+ {
+ auto copy = that;
+ ++that;
+ return copy;
+ }
+ friend QJniArrayIterator &operator--(QJniArrayIterator &that) noexcept
+ {
+ --that.m_index;
+ return that;
+ }
+ friend QJniArrayIterator operator--(QJniArrayIterator &that, int) noexcept
+ {
+ auto copy = that;
+ --that;
+ return copy;
+ }
+ void swap(QJniArrayIterator &other) noexcept
+ {
+ std::swap(m_index, other.m_index);
+ qt_ptr_swap(m_array, other.m_array);
+ }
+
+private:
+ using VT = std::remove_const_t<T>;
+ friend class QJniArray<VT>;
+
+ qsizetype m_index = 0;
+ const QJniArray<VT> *m_array = nullptr;
+
+ QJniArrayIterator(qsizetype index, const QJniArray<VT> *array)
+ : m_index(index), m_array(array)
+ {}
+};
+
+class QT_TECH_PREVIEW_API QJniArrayBase
+{
+ // for SFINAE'ing out the fromContainer named constructor
+ template <typename Container, typename = void> struct CanConvertHelper : std::false_type {};
+ template <typename Container>
+ struct CanConvertHelper<Container, std::void_t<decltype(std::data(std::declval<Container>())),
+ decltype(std::size(std::declval<Container>())),
+ typename Container::value_type
+ >
+ > : std::true_type {};
+
+public:
+ using size_type = jsize;
+ using difference_type = size_type;
+
+ operator QJniObject() const { return m_object; }
+
+ template <typename T = jobject>
+ T object() const { return m_object.object<T>(); }
+ bool isValid() const { return m_object.isValid(); }
+
+ size_type size() const
+ {
+ if (jarray array = m_object.object<jarray>())
+ return jniEnv()->GetArrayLength(array);
+ return 0;
+ }
+
+ template <typename Container>
+ static constexpr bool canConvert = CanConvertHelper<q20::remove_cvref_t<Container>>::value;
+ template <typename Container>
+ using IfCanConvert = std::enable_if_t<canConvert<Container>, bool>;
+ template <typename Container
+ , IfCanConvert<Container> = true
+ >
+ static auto fromContainer(Container &&container)
+ {
+ Q_ASSERT_X(size_t(std::size(container)) <= size_t((std::numeric_limits<size_type>::max)()),
+ "QJniArray::fromContainer", "Container is too large for a Java array");
+
+ using ElementType = typename std::remove_reference_t<Container>::value_type;
+ if constexpr (std::disjunction_v<std::is_same<ElementType, jobject>,
+ std::is_same<ElementType, QJniObject>,
+ std::is_same<ElementType, QString>,
+ std::is_base_of<QtJniTypes::JObjectBase, ElementType>
+ >) {
+ return makeObjectArray(std::forward<Container>(container));
+ } else if constexpr (std::is_same_v<ElementType, jfloat>) {
+ return makeArray<jfloat>(std::forward<Container>(container), &JNIEnv::NewFloatArray,
+ &JNIEnv::SetFloatArrayRegion);
+ } else if constexpr (std::is_same_v<ElementType, jdouble>) {
+ return makeArray<jdouble>(std::forward<Container>(container), &JNIEnv::NewDoubleArray,
+ &JNIEnv::SetDoubleArrayRegion);
+ } else if constexpr (std::disjunction_v<std::is_same<ElementType, jboolean>,
+ std::is_same<ElementType, bool>>) {
+ return makeArray<jboolean>(std::forward<Container>(container), &JNIEnv::NewBooleanArray,
+ &JNIEnv::SetBooleanArrayRegion);
+ } else if constexpr (std::disjunction_v<std::is_same<ElementType, jbyte>,
+ std::is_same<ElementType, char>>) {
+ return makeArray<jbyte>(std::forward<Container>(container), &JNIEnv::NewByteArray,
+ &JNIEnv::SetByteArrayRegion);
+ } else if constexpr (std::disjunction_v<std::is_same<ElementType, jchar>,
+ std::is_same<ElementType, QChar>>) {
+ return makeArray<jchar>(std::forward<Container>(container), &JNIEnv::NewCharArray,
+ &JNIEnv::SetCharArrayRegion);
+ } else if constexpr (std::is_same_v<ElementType, jshort>
+ || sizeof(ElementType) == sizeof(jshort)) {
+ return makeArray<jshort>(std::forward<Container>(container), &JNIEnv::NewShortArray,
+ &JNIEnv::SetShortArrayRegion);
+ } else if constexpr (std::is_same_v<ElementType, jint>
+ || sizeof(ElementType) == sizeof(jint)) {
+ return makeArray<jint>(std::forward<Container>(container), &JNIEnv::NewIntArray,
+ &JNIEnv::SetIntArrayRegion);
+ } else if constexpr (std::is_same_v<ElementType, jlong>
+ || sizeof(ElementType) == sizeof(jlong)) {
+ return makeArray<jlong>(std::forward<Container>(container), &JNIEnv::NewLongArray,
+ &JNIEnv::SetLongArrayRegion);
+ }
+ }
+
+protected:
+ QJniArrayBase() = default;
+ ~QJniArrayBase() = default;
+
+ explicit QJniArrayBase(jarray array)
+ : m_object(static_cast<jobject>(array))
+ {
+ }
+ explicit QJniArrayBase(const QJniObject &object)
+ : m_object(object)
+ {}
+ explicit QJniArrayBase(QJniObject &&object) noexcept
+ : m_object(std::move(object))
+ {}
+
+ JNIEnv *jniEnv() const noexcept { return QJniEnvironment::getJniEnv(); }
+
+ template <typename ElementType, typename List, typename NewFn, typename SetFn>
+ static auto makeArray(List &&list, NewFn &&newArray, SetFn &&setRegion);
+ template <typename List>
+ static auto makeObjectArray(List &&list);
+
+private:
+ QJniObject m_object;
+};
+
+template <typename T>
+class QT_TECH_PREVIEW_API QJniArray : public QJniArrayBase
+{
+ friend struct QJniArrayIterator<T>;
+public:
+ using Type = T;
+
+ using value_type = T;
+ using reference = T;
+ using const_reference = const reference;
+
+ // read-only container, so no iterator typedef
+ using const_iterator = QJniArrayIterator<const T>;
+ using const_reverse_iterator = std::reverse_iterator<const_iterator>;
+
+ QJniArray() = default;
+ explicit QJniArray(jarray array) : QJniArrayBase(array) {}
+ explicit QJniArray(const QJniObject &object) : QJniArrayBase(object) {}
+ explicit QJniArray(QJniObject &&object) noexcept : QJniArrayBase(std::move(object)) {}
+
+ // base class destructor is protected, so need to provide all SMFs
+ QJniArray(const QJniArray &other) = default;
+ QJniArray(QJniArray &&other) noexcept = default;
+ QJniArray &operator=(const QJniArray &other) = default;
+ QJniArray &operator=(QJniArray &&other) noexcept = default;
+
+ template <typename Container
+ , IfCanConvert<Container> = true
+ >
+ explicit QJniArray(Container &&container)
+ : QJniArrayBase(QJniArrayBase::fromContainer(std::forward<Container>(container)))
+ {
+ }
+
+ template <typename E = T
+ , IfCanConvert<std::initializer_list<E>> = true
+ >
+ Q_IMPLICIT inline QJniArray(std::initializer_list<T> list)
+ : QJniArrayBase(QJniArrayBase::fromContainer(list))
+ {
+ }
+
+ template <typename Other, std::enable_if_t<std::is_convertible_v<Other, Type>, bool> = true>
+ QJniArray(QJniArray<Other> &&other)
+ : QJniArrayBase(std::forward<QJniArray<Other>>(other))
+ {
+ }
+ ~QJniArray() = default;
+
+ auto arrayObject() const
+ {
+ if constexpr (std::is_convertible_v<jobject, T>)
+ return object<jobjectArray>();
+ else if constexpr (std::is_same_v<T, jbyte>)
+ return object<jbyteArray>();
+ else if constexpr (std::is_same_v<T, jchar>)
+ return object<jcharArray>();
+ else if constexpr (std::is_same_v<T, jboolean>)
+ return object<jbooleanArray>();
+ else if constexpr (std::is_same_v<T, jshort>)
+ return object<jshortArray>();
+ else if constexpr (std::is_same_v<T, jint>)
+ return object<jintArray>();
+ else if constexpr (std::is_same_v<T, jlong>)
+ return object<jlongArray>();
+ else if constexpr (std::is_same_v<T, jfloat>)
+ return object<jfloatArray>();
+ else if constexpr (std::is_same_v<T, jdouble>)
+ return object<jdoubleArray>();
+ else
+ return object<jarray>();
+ }
+
+ const_iterator begin() const noexcept { return {0, this}; }
+ const_iterator constBegin() const noexcept { return begin(); }
+ const_iterator cbegin() const noexcept { return begin(); }
+ const_iterator end() const noexcept { return {size(), this}; }
+ const_iterator constEnd() const noexcept { return {end()}; }
+ const_iterator cend() const noexcept { return {end()}; }
+
+ const_reverse_iterator rbegin() const noexcept { return const_reverse_iterator(end()); }
+ const_reverse_iterator rend() const noexcept { return const_reverse_iterator(begin()); }
+ const_reverse_iterator crbegin() const noexcept { return const_reverse_iterator(end()); }
+ const_reverse_iterator crend() const noexcept { return const_reverse_iterator(begin()); }
+
+ const_reference operator[](size_type i) const { return at(i); }
+ const_reference at(size_type i) const
+ {
+ JNIEnv *env = jniEnv();
+ if constexpr (std::is_convertible_v<jobject, T>) {
+ jobject element = env->GetObjectArrayElement(object<jobjectArray>(), i);
+ if constexpr (std::is_base_of_v<QJniObject, T>)
+ return QJniObject::fromLocalRef(element);
+ else if constexpr (std::is_base_of_v<QtJniTypes::JObjectBase, T>)
+ return T::fromLocalRef(element);
+ else
+ return T{element};
+ } else if constexpr (std::is_base_of_v<std::remove_pointer_t<jobject>, std::remove_pointer_t<T>>) {
+ // jstring, jclass etc
+ return static_cast<T>(env->GetObjectArrayElement(object<jobjectArray>(), i));
+ } else {
+ T res = {};
+ if constexpr (std::is_same_v<T, jbyte>)
+ env->GetByteArrayRegion(object<jbyteArray>(), i, 1, &res);
+ else if constexpr (std::is_same_v<T, jchar>)
+ env->GetCharArrayRegion(object<jcharArray>(), i, 1, &res);
+ else if constexpr (std::is_same_v<T, jboolean>)
+ env->GetBooleanArrayRegion(object<jbooleanArray>(), i, 1, &res);
+ else if constexpr (std::is_same_v<T, jshort>)
+ env->GetShortArrayRegion(object<jshortArray>(), i, 1, &res);
+ else if constexpr (std::is_same_v<T, jint>)
+ env->GetIntArrayRegion(object<jbyteArray>(), i, 1, &res);
+ else if constexpr (std::is_same_v<T, jlong>)
+ env->GetLongArrayRegion(object<jlongArray>(), i, 1, &res);
+ else if constexpr (std::is_same_v<T, jfloat>)
+ env->GetFloatArrayRegion(object<jfloatArray>(), i, 1, &res);
+ else if constexpr (std::is_same_v<T, jdouble>)
+ env->GetDoubleArrayRegion(object<jdoubleArray>(), i, 1, &res);
+ return res;
+ }
+ }
+ auto toContainer() const
+ {
+ JNIEnv *env = jniEnv();
+ if constexpr (std::is_same_v<T, jobject>) {
+ QList<jobject> res;
+ res.reserve(size());
+ for (auto element : *this)
+ res.append(element);
+ return res;
+ } else if constexpr (std::is_same_v<T, jstring>) {
+ QStringList res;
+ res.reserve(size());
+ for (auto element : *this)
+ res.append(QJniObject(element).toString());
+ return res;
+ } else if constexpr (std::is_same_v<T, jbyte>) {
+ const qsizetype bytecount = size();
+ QByteArray res(bytecount, Qt::Initialization::Uninitialized);
+ env->GetByteArrayRegion(object<jbyteArray>(),
+ 0, bytecount, reinterpret_cast<jbyte *>(res.data()));
+ return res;
+ } else {
+ QList<T> res;
+ res.resize(size());
+ if constexpr (std::is_same_v<T, jchar>) {
+ env->GetCharArrayRegion(object<jcharArray>(),
+ 0, res.size(), res.data());
+ } else if constexpr (std::is_same_v<T, jboolean>) {
+ env->GetBooleanArrayRegion(object<jbooleanArray>(),
+ 0, res.size(), res.data());
+ } else if constexpr (std::is_same_v<T, jshort>) {
+ env->GetShortArrayRegion(object<jshortArray>(),
+ 0, res.size(), res.data());
+ } else if constexpr (std::is_same_v<T, jint>) {
+ env->GetIntArrayRegion(object<jintArray>(),
+ 0, res.size(), res.data());
+ } else if constexpr (std::is_same_v<T, jlong>) {
+ env->GetLongArrayRegion(object<jlongArray>(),
+ 0, res.size(), res.data());
+ } else if constexpr (std::is_same_v<T, jfloat>) {
+ env->GetFloatArrayRegion(object<jfloatArray>(),
+ 0, res.size(), res.data());
+ } else if constexpr (std::is_same_v<T, jdouble>) {
+ env->GetDoubleArrayRegion(object<jdoubleArray>(),
+ 0, res.size(), res.data());
+ } else {
+ res.clear();
+ }
+ return res;
+ }
+ }
+};
+
+template <typename ElementType, typename List, typename NewFn, typename SetFn>
+auto QJniArrayBase::makeArray(List &&list, NewFn &&newArray, SetFn &&setRegion)
+{
+ const size_type length = size_type(std::size(list));
+ JNIEnv *env = QJniEnvironment::getJniEnv();
+ auto localArray = (env->*newArray)(length);
+ if (QJniEnvironment::checkAndClearExceptions(env))
+ return QJniArray<ElementType>();
+
+ // can't use static_cast here because we have signed/unsigned mismatches
+ if (length) {
+ (env->*setRegion)(localArray, 0, length,
+ reinterpret_cast<const ElementType *>(std::data(std::as_const(list))));
+ }
+ return QJniArray<ElementType>(localArray);
+};
+
+template <typename List>
+auto QJniArrayBase::makeObjectArray(List &&list)
+{
+ using ElementType = typename q20::remove_cvref_t<List>::value_type;
+ using ResultType = QJniArray<decltype(std::declval<QJniObject::LocalFrame<>>().convertToJni(
+ std::declval<ElementType>()))
+ >;
+
+ if (std::size(list) == 0)
+ return ResultType();
+
+ JNIEnv *env = QJniEnvironment::getJniEnv();
+ const size_type length = size_type(std::size(list));
+
+ // this assumes that all objects in the list have the same class
+ jclass elementClass = nullptr;
+ if constexpr (std::disjunction_v<std::is_same<ElementType, QJniObject>,
+ std::is_base_of<QtJniTypes::JObjectBase, ElementType>>) {
+ elementClass = std::begin(list)->objectClass();
+ } else if constexpr (std::is_same_v<ElementType, QString>) {
+ elementClass = env->FindClass("java/lang/String");
+ } else {
+ elementClass = env->GetObjectClass(*std::begin(list));
+ }
+ auto localArray = env->NewObjectArray(length, elementClass, nullptr);
+ if (QJniEnvironment::checkAndClearExceptions(env))
+ return ResultType();
+
+ // explicitly manage the frame for local references in chunks of 100
+ QJniObject::LocalFrame frame(env);
+ constexpr jint frameCapacity = 100;
+ qsizetype i = 0;
+ for (const auto &element : std::as_const(list)) {
+ if (i % frameCapacity == 0) {
+ if (i)
+ env->PopLocalFrame(nullptr);
+ if (env->PushLocalFrame(frameCapacity) != 0)
+ return ResultType{};
+ }
+ jobject object = frame.convertToJni(element);
+ env->SetObjectArrayElement(localArray, i, object);
+ ++i;
+ }
+ if (i)
+ env->PopLocalFrame(nullptr);
+ return ResultType(localArray);
+}
+
+namespace QtJniTypes
+{
+template <typename T> struct IsJniArray: std::false_type {};
+template <typename T> struct IsJniArray<QJniArray<T>> : std::true_type {};
+template <typename T> struct Traits<QJniArray<T>> {
+ template <IfValidFieldType<T> = true>
+ static constexpr auto signature()
+ {
+ return CTString("[") + Traits<T>::signature();
+ }
+};
+template <typename T> struct Traits<QList<T>> {
+ template <IfValidFieldType<T> = true>
+ static constexpr auto signature()
+ {
+ return CTString("[") + Traits<T>::signature();
+ }
+};
+template <> struct Traits<QByteArray> {
+ static constexpr auto signature()
+ {
+ return CTString("[B");
+ }
+};
+}
+
+QT_END_NAMESPACE
+
+#endif
+
+#endif // QJNIARRAY_H
diff --git a/src/corelib/kernel/qjnienvironment.cpp b/src/corelib/kernel/qjnienvironment.cpp
index 88ad68178e..e890b16f42 100644
--- a/src/corelib/kernel/qjnienvironment.cpp
+++ b/src/corelib/kernel/qjnienvironment.cpp
@@ -2,7 +2,6 @@
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qjnienvironment.h"
-#include "qjniobject.h"
#include "qjnihelpers_p.h"
#include <QtCore/QThread>
@@ -30,8 +29,6 @@ QT_BEGIN_NAMESPACE
It has not been tested for other platforms.
*/
-static const char qJniThreadName[] = "QtThread";
-
class QJniEnvironmentPrivate
{
public:
@@ -47,47 +44,42 @@ public:
}
};
-struct QJniLocalRefDeleterPrivate
-{
- static void cleanup(jobject obj)
- {
- if (!obj)
- return;
-
- QJniEnvironment env;
- env->DeleteLocalRef(obj);
- }
-};
-
-// To simplify this we only define it for jobjects.
-typedef QScopedPointer<_jobject, QJniLocalRefDeleterPrivate> QJniScopedLocalRefPrivate;
-
-
Q_GLOBAL_STATIC(QThreadStorage<QJniEnvironmentPrivateTLS *>, jniEnvTLS)
-
/*!
- \fn QJniEnvironment::QJniEnvironment()
-
Constructs a new JNI Environment object and attaches the current thread to the Java VM.
*/
QJniEnvironment::QJniEnvironment()
: d(new QJniEnvironmentPrivate{})
{
+ d->jniEnv = getJniEnv();
+}
+
+/*!
+ Returns the JNIEnv pointer for the current thread.
+
+ The current thread will be attached to the Java VM.
+*/
+JNIEnv *QJniEnvironment::getJniEnv()
+{
+ JNIEnv *jniEnv = nullptr;
+
JavaVM *vm = QtAndroidPrivate::javaVM();
- const jint ret = vm->GetEnv((void**)&d->jniEnv, JNI_VERSION_1_6);
- if (ret == JNI_OK) // Already attached
- return;
+ const jint ret = vm->GetEnv((void**)&jniEnv, JNI_VERSION_1_6);
if (ret == JNI_EDETACHED) { // We need to (re-)attach
- JavaVMAttachArgs args = { JNI_VERSION_1_6, qJniThreadName, nullptr };
- if (vm->AttachCurrentThread(&d->jniEnv, &args) != JNI_OK)
- return;
-
- if (!jniEnvTLS->hasLocalData()) // If we attached the thread we own it.
- jniEnvTLS->setLocalData(new QJniEnvironmentPrivateTLS);
+ const QByteArray threadName = QThread::currentThread()->objectName().toUtf8();
+ JavaVMAttachArgs args = { JNI_VERSION_1_6,
+ threadName.isEmpty() ? "QtThread" : threadName.constData(),
+ nullptr
+ };
+ if (vm->AttachCurrentThread(&jniEnv, &args) == JNI_OK) {
+ if (!jniEnvTLS->hasLocalData()) // If we attached the thread we own it.
+ jniEnvTLS->setLocalData(new QJniEnvironmentPrivateTLS);
+ }
}
+ return jniEnv;
}
/*!
@@ -112,8 +104,6 @@ bool QJniEnvironment::isValid() const
}
/*!
- \fn JNIEnv *QJniEnvironment::operator->() const
-
Provides access to the JNI Environment's \c JNIEnv pointer.
*/
JNIEnv *QJniEnvironment::operator->() const
@@ -122,8 +112,6 @@ JNIEnv *QJniEnvironment::operator->() const
}
/*!
- \fn JNIEnv &QJniEnvironment::operator*() const
-
Returns the JNI Environment's \c JNIEnv object.
*/
JNIEnv &QJniEnvironment::operator*() const
@@ -132,8 +120,6 @@ JNIEnv &QJniEnvironment::operator*() const
}
/*!
- \fn JNIEnv *QJniEnvironment::jniEnv() const
-
Returns the JNI Environment's \c JNIEnv pointer.
*/
JNIEnv *QJniEnvironment::jniEnv() const
@@ -324,8 +310,6 @@ jfieldID QJniEnvironment::findStaticField(jclass clazz, const char *fieldName, c
*/
/*!
- \fn JavaVM *QJniEnvironment::javaVM()
-
Returns the Java VM interface for the current process. Although it might
be possible to have multiple Java VMs per process, Android allows only one.
@@ -336,6 +320,30 @@ JavaVM *QJniEnvironment::javaVM()
}
/*!
+ \fn template <typename Class> bool QJniEnvironment::registerNativeMethods(std::initializer_list<JNINativeMethods> methods)
+ \overload
+
+ Registers the Java methods in \a methods with the Java class represented by
+ \c Class, and returns whether the registration was successful.
+
+ The \c Class type has to be declared within the QtJniTypes namespace using
+ the Q_DECLARE_JNI_CLASS macro. Functions that are implemented as free C or
+ C++ functions have to be declared using one of the
+ Q_DECLARE_JNI_NATIVE_METHOD macros, and passed into the registration using
+ the Q_JNI_NATIVE_METHOD macro.
+
+ \include jni.qdoc register-free-function
+
+ For functions that are implemented as static class member functions, use
+ the \l{Q_DECLARE_JNI_NATIVE_METHOD_IN_CURRENT_SCOPE}{macros for scoped
+ functions} instead.
+
+ \include jni.qdoc register-scoped-function
+*/
+
+/*!
+ \overload
+
Registers the Java methods in the array \a methods of size \a size, each of
which can call native C++ functions from class \a className. These methods
must be registered before any attempt to call them.
@@ -367,6 +375,15 @@ bool QJniEnvironment::registerNativeMethods(const char *className, const JNINati
return registerNativeMethods(clazz, methods, size);
}
+
+/*!
+ \fn bool QJniEnvironment::registerNativeMethods(const char *className, std::initializer_list<JNINativeMethod> methods)
+ \overload
+
+ Registers the native functions methods in \a methods for the Java class \a className.
+ Returns \c true if the registration is successful, otherwise \c false.
+*/
+
#if QT_DEPRECATED_SINCE(6, 2)
/*!
\overload
@@ -422,6 +439,14 @@ bool QJniEnvironment::registerNativeMethods(jclass clazz, const JNINativeMethod
}
/*!
+ \fn bool QJniEnvironment::registerNativeMethods(jclass clazz, std::initializer_list<JNINativeMethod> methods)
+ \overload
+
+ Registers the native functions methods in \a methods for the Java class \a clazz.
+ Returns \c true if the registration is successful, otherwise \c false.
+*/
+
+/*!
\enum QJniEnvironment::OutputMode
\value Silent The exceptions are cleaned silently
@@ -444,20 +469,59 @@ bool QJniEnvironment::registerNativeMethods(jclass clazz, const JNINativeMethod
*/
bool QJniEnvironment::checkAndClearExceptions(QJniEnvironment::OutputMode outputMode)
{
- if (Q_UNLIKELY(d->jniEnv->ExceptionCheck())) {
- if (outputMode != OutputMode::Silent)
- d->jniEnv->ExceptionDescribe();
- d->jniEnv->ExceptionClear();
+ return checkAndClearExceptions(d->jniEnv, outputMode);
+}
- return true;
+namespace {
+ // Any pending exception need to be cleared before calling this
+ QString exceptionMessage(JNIEnv *env, jthrowable exception)
+ {
+ if (!exception)
+ return {};
+
+ auto logError = []() {
+ qWarning() << "QJniEnvironment: a null object returned or an exception occurred while "
+ "fetching a prior exception message";
+ };
+
+ auto checkAndClear = [env]() {
+ if (Q_UNLIKELY(env->ExceptionCheck())) {
+ env->ExceptionClear();
+ return true;
+ }
+ return false;
+ };
+
+ const jclass logClazz = env->FindClass("android/util/Log");
+ if (checkAndClear() || !logClazz) {
+ logError();
+ return {};
+ }
+
+ const jmethodID methodId = env->GetStaticMethodID(logClazz, "getStackTraceString",
+ "(Ljava/lang/Throwable;)Ljava/lang/String;");
+ if (checkAndClear() || !methodId) {
+ logError();
+ return {};
+ }
+
+ jvalue value;
+ value.l = static_cast<jobject>(exception);
+ const jobject messageObj = env->CallStaticObjectMethodA(logClazz, methodId, &value);
+ const jstring jmessage = static_cast<jstring>(messageObj);
+ if (checkAndClear())
+ return {};
+
+ char const *utfMessage = env->GetStringUTFChars(jmessage, 0);
+ const QString message = QString::fromUtf8(utfMessage);
+
+ env->ReleaseStringUTFChars(jmessage, utfMessage);
+
+ return message;
}
-
- return false;
-}
+} // end namespace
/*!
- \fn QJniEnvironment::checkAndClearExceptions(JNIEnv *env, OutputMode outputMode = OutputMode::Verbose)
-
Cleans any pending exceptions for \a env, either silently or reporting
stack backtrace, depending on the \a outputMode. This is useful when you
already have a \c JNIEnv pointer such as in a native function implementation.
@@ -472,9 +536,22 @@ bool QJniEnvironment::checkAndClearExceptions(QJniEnvironment::OutputMode output
bool QJniEnvironment::checkAndClearExceptions(JNIEnv *env, QJniEnvironment::OutputMode outputMode)
{
if (Q_UNLIKELY(env->ExceptionCheck())) {
- if (outputMode != OutputMode::Silent)
- env->ExceptionDescribe();
- env->ExceptionClear();
+ if (outputMode == OutputMode::Verbose) {
+ if (jthrowable exception = env->ExceptionOccurred()) {
+ env->ExceptionClear();
+ const QString message = exceptionMessage(env, exception);
+ // Print to QWARN since env->ExceptionDescribe() does the same
+ if (!message.isEmpty())
+ qWarning().noquote() << message;
+ env->DeleteLocalRef(exception);
+ } else {
+ // if the exception object is null for some reason just
+ env->ExceptionDescribe();
+ env->ExceptionClear();
+ }
+ } else {
+ env->ExceptionClear();
+ }
return true;
}
diff --git a/src/corelib/kernel/qjnienvironment.h b/src/corelib/kernel/qjnienvironment.h
index f7ffa836c2..dda8dc0950 100644
--- a/src/corelib/kernel/qjnienvironment.h
+++ b/src/corelib/kernel/qjnienvironment.h
@@ -8,7 +8,7 @@
#if defined(Q_QDOC) || defined(Q_OS_ANDROID)
#include <jni.h>
-#include <QtCore/qjnitypes.h>
+#include <QtCore/qjnitypes_impl.h>
QT_BEGIN_NAMESPACE
@@ -25,7 +25,7 @@ public:
JNIEnv *jniEnv() const;
jclass findClass(const char *className);
template<typename Class>
- jclass findClass() { return findClass(QtJniTypes::className<Class>().data()); }
+ jclass findClass() { return findClass(QtJniTypes::Traits<Class>::className().data()); }
jmethodID findMethod(jclass clazz, const char *methodName, const char *signature);
template<typename ...Args>
jmethodID findMethod(jclass clazz, const char *methodName) {
@@ -64,6 +64,16 @@ public:
return registerNativeMethods(clazz, std::data(methods), methods.size());
}
+ template<typename Class
+#ifndef Q_QDOC
+ , std::enable_if_t<QtJniTypes::isObjectType<Class>(), bool> = true
+#endif
+ >
+ bool registerNativeMethods(std::initializer_list<JNINativeMethod> methods)
+ {
+ return registerNativeMethods(QtJniTypes::Traits<Class>::className().data(), methods);
+ }
+
#if QT_DEPRECATED_SINCE(6, 2)
// ### Qt 7: remove
QT_DEPRECATED_VERSION_X_6_2("Use the overload with a const JNINativeMethod[] instead.")
@@ -78,6 +88,8 @@ public:
bool checkAndClearExceptions(OutputMode outputMode = OutputMode::Verbose);
static bool checkAndClearExceptions(JNIEnv *env, OutputMode outputMode = OutputMode::Verbose);
+ static JNIEnv *getJniEnv();
+
private:
Q_DISABLE_COPY_MOVE(QJniEnvironment)
QScopedPointer<QJniEnvironmentPrivate> d;
diff --git a/src/corelib/kernel/qjnihelpers.cpp b/src/corelib/kernel/qjnihelpers.cpp
index 5981a08552..d900b74d37 100644
--- a/src/corelib/kernel/qjnihelpers.cpp
+++ b/src/corelib/kernel/qjnihelpers.cpp
@@ -10,6 +10,7 @@
#include "qsemaphore.h"
#include "qreadwritelock.h"
#include <QtCore/private/qcoreapplication_p.h>
+#include <QtCore/private/qlocking_p.h>
#include <android/log.h>
#include <deque>
@@ -25,8 +26,6 @@ namespace QtAndroidPrivate {
ResumePauseListener::~ResumePauseListener() {}
void ResumePauseListener::handlePause() {}
void ResumePauseListener::handleResume() {}
- GenericMotionEventListener::~GenericMotionEventListener() {}
- KeyEventListener::~KeyEventListener() {}
}
static JavaVM *g_javaVM = nullptr;
@@ -34,47 +33,13 @@ static jobject g_jActivity = nullptr;
static jobject g_jService = nullptr;
static jobject g_jClassLoader = nullptr;
-Q_GLOBAL_STATIC(QtAndroidPrivate::OnBindListener *, g_onBindListener, nullptr);
-Q_GLOBAL_STATIC(QMutex, g_onBindListenerMutex);
+Q_CONSTINIT static QtAndroidPrivate::OnBindListener *g_onBindListener;
+Q_CONSTINIT static QBasicMutex g_onBindListenerMutex;
Q_GLOBAL_STATIC(QSemaphore, g_waitForServiceSetupSemaphore);
-Q_GLOBAL_STATIC(QAtomicInt, g_serviceSetupLockers);
+Q_CONSTINIT static QBasicAtomicInt g_serviceSetupLockers = Q_BASIC_ATOMIC_INITIALIZER(0);
Q_GLOBAL_STATIC(QReadWriteLock, g_updateMutex);
-namespace {
- struct GenericMotionEventListeners {
- QMutex mutex;
- QList<QtAndroidPrivate::GenericMotionEventListener *> listeners;
- };
-}
-Q_GLOBAL_STATIC(GenericMotionEventListeners, g_genericMotionEventListeners)
-
-static jboolean dispatchGenericMotionEvent(JNIEnv *, jclass, jobject event)
-{
- jboolean ret = JNI_FALSE;
- QMutexLocker locker(&g_genericMotionEventListeners()->mutex);
- for (auto *listener : std::as_const(g_genericMotionEventListeners()->listeners))
- ret |= listener->handleGenericMotionEvent(event);
- return ret;
-}
-
-namespace {
- struct KeyEventListeners {
- QMutex mutex;
- QList<QtAndroidPrivate::KeyEventListener *> listeners;
- };
-}
-Q_GLOBAL_STATIC(KeyEventListeners, g_keyEventListeners)
-
-static jboolean dispatchKeyEvent(JNIEnv *, jclass, jobject event)
-{
- jboolean ret = JNI_FALSE;
- QMutexLocker locker(&g_keyEventListeners()->mutex);
- for (auto *listener : std::as_const(g_keyEventListeners()->listeners))
- ret |= listener->handleKeyEvent(event);
- return ret;
-}
-
static jboolean updateNativeActivity(JNIEnv *env, jclass = nullptr)
{
@@ -271,8 +236,6 @@ jint QtAndroidPrivate::initJNI(JavaVM *vm, JNIEnv *env)
}
static const JNINativeMethod methods[] = {
- {"dispatchGenericMotionEvent", "(Landroid/view/MotionEvent;)Z", reinterpret_cast<void *>(dispatchGenericMotionEvent)},
- {"dispatchKeyEvent", "(Landroid/view/KeyEvent;)Z", reinterpret_cast<void *>(dispatchKeyEvent)},
{"updateNativeActivity", "()Z", reinterpret_cast<void *>(updateNativeActivity) },
};
@@ -281,21 +244,37 @@ jint QtAndroidPrivate::initJNI(JavaVM *vm, JNIEnv *env)
if (!regOk && QJniEnvironment::checkAndClearExceptions(env))
return JNI_ERR;
- if (!registerPermissionNatives())
+ QJniEnvironment qJniEnv;
+ if (!registerPermissionNatives(qJniEnv))
+ return JNI_ERR;
+
+ if (!registerNativeInterfaceNatives(qJniEnv))
return JNI_ERR;
- if (!registerNativeInterfaceNatives())
+ if (!registerExtrasNatives(qJniEnv))
return JNI_ERR;
return JNI_OK;
}
+Q_CORE_EXPORT jobject qt_androidActivity()
+{
+ QReadLocker locker(g_updateMutex());
+ return g_jActivity;
+}
+
+
QtJniTypes::Activity QtAndroidPrivate::activity()
{
QReadLocker locker(g_updateMutex());
return g_jActivity;
}
+Q_CORE_EXPORT jobject qt_androidService()
+{
+ return g_jService;
+}
+
QtJniTypes::Service QtAndroidPrivate::service()
{
return g_jService;
@@ -330,30 +309,6 @@ jint QtAndroidPrivate::androidSdkVersion()
return sdkVersion;
}
-void QtAndroidPrivate::registerGenericMotionEventListener(QtAndroidPrivate::GenericMotionEventListener *listener)
-{
- QMutexLocker locker(&g_genericMotionEventListeners()->mutex);
- g_genericMotionEventListeners()->listeners.push_back(listener);
-}
-
-void QtAndroidPrivate::unregisterGenericMotionEventListener(QtAndroidPrivate::GenericMotionEventListener *listener)
-{
- QMutexLocker locker(&g_genericMotionEventListeners()->mutex);
- g_genericMotionEventListeners()->listeners.removeOne(listener);
-}
-
-void QtAndroidPrivate::registerKeyEventListener(QtAndroidPrivate::KeyEventListener *listener)
-{
- QMutexLocker locker(&g_keyEventListeners()->mutex);
- g_keyEventListeners()->listeners.push_back(listener);
-}
-
-void QtAndroidPrivate::unregisterKeyEventListener(QtAndroidPrivate::KeyEventListener *listener)
-{
- QMutexLocker locker(&g_keyEventListeners()->mutex);
- g_keyEventListeners()->listeners.removeOne(listener);
-}
-
void QtAndroidPrivate::waitForServiceSetup()
{
g_waitForServiceSetupSemaphore->acquire();
@@ -361,36 +316,36 @@ void QtAndroidPrivate::waitForServiceSetup()
int QtAndroidPrivate::acuqireServiceSetup(int flags)
{
- g_serviceSetupLockers->ref();
+ g_serviceSetupLockers.ref();
return flags;
}
void QtAndroidPrivate::setOnBindListener(QtAndroidPrivate::OnBindListener *listener)
{
- QMutexLocker lock(g_onBindListenerMutex());
- *g_onBindListener = listener;
- if (!g_serviceSetupLockers->deref())
+ const auto lock = qt_scoped_lock(g_onBindListenerMutex);
+ g_onBindListener = listener;
+ if (!g_serviceSetupLockers.deref())
g_waitForServiceSetupSemaphore->release();
}
jobject QtAndroidPrivate::callOnBindListener(jobject intent)
{
- QMutexLocker lock(g_onBindListenerMutex());
- if (*g_onBindListener)
- return (*g_onBindListener)->onBind(intent);
+ const auto lock = qt_scoped_lock(g_onBindListenerMutex);
+ if (g_onBindListener)
+ return g_onBindListener->onBind(intent);
return nullptr;
}
-Q_GLOBAL_STATIC(QAtomicInt, g_androidDeadlockProtector);
+Q_CONSTINIT static QBasicAtomicInt g_androidDeadlockProtector = Q_BASIC_ATOMIC_INITIALIZER(0);
bool QtAndroidPrivate::acquireAndroidDeadlockProtector()
{
- return g_androidDeadlockProtector->testAndSetAcquire(0, 1);
+ return g_androidDeadlockProtector.testAndSetAcquire(0, 1);
}
void QtAndroidPrivate::releaseAndroidDeadlockProtector()
{
- g_androidDeadlockProtector->storeRelease(0);
+ g_androidDeadlockProtector.storeRelease(0);
}
QT_END_NAMESPACE
diff --git a/src/corelib/kernel/qjnihelpers_p.h b/src/corelib/kernel/qjnihelpers_p.h
index bce2b782de..b5e05fcaf1 100644
--- a/src/corelib/kernel/qjnihelpers_p.h
+++ b/src/corelib/kernel/qjnihelpers_p.h
@@ -22,8 +22,8 @@
QT_BEGIN_NAMESPACE
-Q_DECLARE_JNI_TYPE(Activity, "Landroid/app/Activity;")
-Q_DECLARE_JNI_TYPE(Service, "Landroid/app/Service;")
+Q_DECLARE_JNI_CLASS(Activity, "android/app/Activity")
+Q_DECLARE_JNI_CLASS(Service, "android/app/Service")
namespace QtAndroidPrivate
{
@@ -49,20 +49,6 @@ namespace QtAndroidPrivate
virtual void handleResume();
};
- class Q_CORE_EXPORT GenericMotionEventListener
- {
- public:
- virtual ~GenericMotionEventListener();
- virtual bool handleGenericMotionEvent(jobject event) = 0;
- };
-
- class Q_CORE_EXPORT KeyEventListener
- {
- public:
- virtual ~KeyEventListener();
- virtual bool handleKeyEvent(jobject event) = 0;
- };
-
class Q_CORE_EXPORT OnBindListener
{
public:
@@ -79,8 +65,9 @@ namespace QtAndroidPrivate
jobject classLoader();
Q_CORE_EXPORT jint androidSdkVersion();
- bool registerPermissionNatives();
- bool registerNativeInterfaceNatives();
+ bool registerPermissionNatives(QJniEnvironment &env);
+ bool registerNativeInterfaceNatives(QJniEnvironment &env);
+ bool registerExtrasNatives(QJniEnvironment &env);
Q_CORE_EXPORT void handleActivityResult(jint requestCode, jint resultCode, jobject data);
Q_CORE_EXPORT void registerActivityResultListener(ActivityResultListener *listener);
@@ -95,12 +82,6 @@ namespace QtAndroidPrivate
Q_CORE_EXPORT void registerResumePauseListener(ResumePauseListener *listener);
Q_CORE_EXPORT void unregisterResumePauseListener(ResumePauseListener *listener);
- Q_CORE_EXPORT void registerGenericMotionEventListener(GenericMotionEventListener *listener);
- Q_CORE_EXPORT void unregisterGenericMotionEventListener(GenericMotionEventListener *listener);
-
- Q_CORE_EXPORT void registerKeyEventListener(KeyEventListener *listener);
- Q_CORE_EXPORT void unregisterKeyEventListener(KeyEventListener *listener);
-
Q_CORE_EXPORT void waitForServiceSetup();
Q_CORE_EXPORT int acuqireServiceSetup(int flags);
Q_CORE_EXPORT void setOnBindListener(OnBindListener *listener);
diff --git a/src/corelib/kernel/qjniobject.cpp b/src/corelib/kernel/qjniobject.cpp
index 62a5993559..d5b0cd5663 100644
--- a/src/corelib/kernel/qjniobject.cpp
+++ b/src/corelib/kernel/qjniobject.cpp
@@ -8,9 +8,13 @@
#include <QtCore/qbytearray.h>
#include <QtCore/qhash.h>
#include <QtCore/qreadwritelock.h>
+#include <QtCore/qloggingcategory.h>
+
QT_BEGIN_NAMESPACE
+Q_LOGGING_CATEGORY(lcJniThreadCheck, "qt.core.jni.threadcheck")
+
using namespace Qt::StringLiterals;
/*!
@@ -20,7 +24,7 @@ using namespace Qt::StringLiterals;
\brief A convenience wrapper around the Java Native Interface (JNI).
The QJniObject class wraps a reference to a Java object, ensuring it isn't
- gargage-collected and providing access to most \c JNIEnv method calls
+ garbage-collected and providing access to most \c JNIEnv method calls
(member, static) and fields (setter, getter). It eliminates much
boiler-plate that would normally be needed, with direct JNI access, for
every operation, including exception-handling.
@@ -30,19 +34,10 @@ using namespace Qt::StringLiterals;
\sa QJniEnvironment
- \section1 General Notes
-
- \list
- \li Class names need to be fully-qualified, for example: \c "java/lang/String".
- \li Method signatures are written as \c "(ArgumentsTypes)ReturnType", see \l {JNI Types}.
- \li All object types are returned as a QJniObject.
- \endlist
-
\section1 Method Signatures
QJniObject provides convenience functions that will use the correct signature based on the
- provided template types. For functions that only return and take \l {JNI types}, the
- signature can be generate at compile time:
+ provided or deduced template arguments.
\code
jint x = QJniObject::callMethod<jint>("getSize");
@@ -50,13 +45,11 @@ using namespace Qt::StringLiterals;
jint ret = jString1.callMethod<jint>("compareToIgnoreCase", jString2.object<jstring>());
\endcode
- These functions are variadic templates, and the compiler will deduce the template arguments
- from the actual argument types. In many situations, only the return type needs to be provided
- explicitly.
-
- For functions that take other argument types, you need to supply the signature yourself. It is
- important that the signature matches the function you want to call. The example below
- demonstrates how to call different static functions:
+ These functions are variadic templates, and the compiler will deduce the
+ signature from the actual argument types. Only the return type needs to be
+ provided explicitly. QJniObject can deduce the signature string for
+ functions that take \l {JNI types}, and for types that have been declared
+ with the QtJniTypes type mapping.
\code
// Java class
@@ -69,6 +62,31 @@ using namespace Qt::StringLiterals;
}
\endcode
+ \code
+ // C++ code
+ Q_DECLARE_JNI_CLASS(TestClass, "org/qtproject/qt/TestClass")
+
+ // ...
+ using namespace QtJniTypes;
+ TestClass testClass = TestClass::callStaticMethod<TestClass>("create");
+ \endcode
+
+ This allows working with arbitrary Java and Android types in C++ code, without having to
+ create JNI signature strings explicitly.
+
+ \section2 Explicit JNI Signatures
+
+ It is possible to supply the signature yourself. In that case, it is important
+ that the signature matches the function you want to call.
+
+ \list
+ \li Class names need to be fully-qualified, for example: \c "java/lang/String".
+ \li Method signatures are written as \c "(ArgumentsTypes)ReturnType", see \l {JNI Types}.
+ \li All object types are returned as a QJniObject.
+ \endlist
+
+ The example below demonstrates how to call different static functions:
+
The signature structure is \c "(ArgumentsTypes)ReturnType". Array types in the signature
must have the \c {[} prefix, and the fully-qualified \c Object type names must have the
\c L prefix and the \c ; suffix. The signature for the \c create function is
@@ -101,14 +119,14 @@ using namespace Qt::StringLiterals;
// C++ code
QJniObject string1 = QJniObject::fromString("String1");
QJniObject string2 = QJniObject::fromString("String2");
- QJniObject stringArray = QJniObject::callStaticObjectMethod<jstringArray>(
+ QJniObject stringArray = QJniObject::callStaticObjectMethod<jobjectArray>(
"org/qtproject/qt/TestClass",
- "stringArray"
+ "stringArray",
string1.object<jstring>(),
string2.object<jstring>());
\endcode
- Note that while he first template parameter specifies the return type of the Java
+ Note that while the first template parameter specifies the return type of the Java
function, the method will still return a QJniObject.
\section1 Handling Java Exception
@@ -277,100 +295,151 @@ using namespace Qt::StringLiterals;
class QJniObjectPrivate
{
public:
- QJniObjectPrivate() = default;
+ QJniObjectPrivate()
+ {
+ }
~QJniObjectPrivate() {
- QJniEnvironment env;
+ JNIEnv *env = QJniEnvironment::getJniEnv();
if (m_jobject)
env->DeleteGlobalRef(m_jobject);
if (m_jclass && m_own_jclass)
env->DeleteGlobalRef(m_jclass);
}
- friend jclass QtAndroidPrivate::findClass(const char *className, JNIEnv *env);
- static jclass loadClass(const QByteArray &className, JNIEnv *env, bool binEncoded = false)
+ template <typename ...Args>
+ void construct(const char *signature = nullptr, Args &&...args)
{
- return QJniObject::loadClass(className, env, binEncoded);
- }
-
- static QByteArray toBinaryEncClassName(const QByteArray &className)
- {
- return QJniObject::toBinaryEncClassName(className);
+ if (m_jclass) {
+ JNIEnv *env = QJniEnvironment::getJniEnv();
+ // get default constructor
+ jmethodID constructorId = QJniObject::getCachedMethodID(env, m_jclass, m_className, "<init>",
+ signature ? signature : "()V");
+ if (constructorId) {
+ jobject obj = nullptr;
+ if constexpr (sizeof...(Args) == 0)
+ obj = env->NewObject(m_jclass, constructorId);
+ else
+ obj = env->NewObjectV(m_jclass, constructorId, std::forward<Args>(args)...);
+ if (obj) {
+ m_jobject = env->NewGlobalRef(obj);
+ env->DeleteLocalRef(obj);
+ }
+ }
+ }
}
+ QByteArray m_className;
jobject m_jobject = nullptr;
jclass m_jclass = nullptr;
bool m_own_jclass = true;
- QByteArray m_className;
};
-static inline QLatin1StringView keyBase()
+template <typename ...Args>
+static inline QByteArray cacheKey(Args &&...args)
{
- return "%1%2:%3"_L1;
+ return (QByteArrayView(":") + ... + QByteArrayView(args));
}
-static QString qt_convertJString(jstring string)
-{
- QJniEnvironment env;
- int strLength = env->GetStringLength(string);
- QString res(strLength, Qt::Uninitialized);
- env->GetStringRegion(string, 0, strLength, reinterpret_cast<jchar *>(res.data()));
- return res;
-}
-
-typedef QHash<QString, jclass> JClassHash;
+typedef QHash<QByteArray, jclass> JClassHash;
Q_GLOBAL_STATIC(JClassHash, cachedClasses)
Q_GLOBAL_STATIC(QReadWriteLock, cachedClassesLock)
-static jclass getCachedClass(const QByteArray &classBinEnc, bool *isCached = nullptr)
+static jclass getCachedClass(const QByteArray &className)
{
QReadLocker locker(cachedClassesLock);
- const QHash<QString, jclass>::const_iterator &it = cachedClasses->constFind(QString::fromLatin1(classBinEnc));
- const bool found = (it != cachedClasses->constEnd());
-
- if (isCached)
- *isCached = found;
-
- return found ? it.value() : 0;
+ const auto &it = cachedClasses->constFind(className);
+ return it != cachedClasses->constEnd() ? it.value() : nullptr;
}
-QByteArray QJniObject::toBinaryEncClassName(const QByteArray &className)
+/*!
+ \internal
+
+ Get a JNI object from a jobject variant and do the necessary
+ exception clearing and delete the local reference before returning.
+ The JNI object can be null if there was an exception.
+*/
+static QJniObject getCleanJniObject(jobject object, JNIEnv *env)
{
- return QByteArray(className).replace('/', '.');
+ if (QJniEnvironment::checkAndClearExceptions(env) || !object) {
+ if (object)
+ env->DeleteLocalRef(object);
+ return QJniObject();
+ }
+
+ QJniObject res(object);
+ env->DeleteLocalRef(object);
+ return res;
}
-jclass QJniObject::loadClass(const QByteArray &className, JNIEnv *env, bool binEncoded)
+/*!
+ \internal
+ \a className must be slash-encoded
+*/
+jclass QtAndroidPrivate::findClass(const char *className, JNIEnv *env)
{
- const QByteArray &binEncClassName = binEncoded ? className : QJniObject::toBinaryEncClassName(className);
-
- bool isCached = false;
- jclass clazz = getCachedClass(binEncClassName, &isCached);
- if (clazz || isCached)
+ Q_ASSERT(env);
+ QByteArray classNameArray(className);
+#ifdef QT_DEBUG
+ if (classNameArray.contains('.')) {
+ qWarning("QtAndroidPrivate::findClass: className '%s' should use slash separators!",
+ className);
+ }
+#endif
+ classNameArray.replace('.', '/');
+ jclass clazz = getCachedClass(classNameArray);
+ if (clazz)
return clazz;
- QJniObject classLoader(QtAndroidPrivate::classLoader());
- if (!classLoader.isValid())
- return nullptr;
-
QWriteLocker locker(cachedClassesLock);
- // did we lose the race?
- const QLatin1StringView key(binEncClassName);
- const QHash<QString, jclass>::const_iterator &it = cachedClasses->constFind(key);
+ // Check again; another thread might have added the class to the cache after
+ // our call to getCachedClass and before we acquired the lock.
+ const auto &it = cachedClasses->constFind(classNameArray);
if (it != cachedClasses->constEnd())
return it.value();
- QJniObject stringName = QJniObject::fromString(key);
- QJniObject classObject = classLoader.callObjectMethod("loadClass",
- "(Ljava/lang/String;)Ljava/lang/Class;",
- stringName.object());
+ // JNIEnv::FindClass wants "a fully-qualified class name or an array type signature"
+ // which is a slash-separated class name.
+ jclass localClazz = env->FindClass(classNameArray.constData());
+ if (localClazz) {
+ clazz = static_cast<jclass>(env->NewGlobalRef(localClazz));
+ env->DeleteLocalRef(localClazz);
+ } else {
+ // Clear the exception silently; we are going to try the ClassLoader next,
+ // so no need for warning unless that fails as well.
+ env->ExceptionClear();
+ }
- if (!QJniEnvironment::checkAndClearExceptions(env) && classObject.isValid())
- clazz = static_cast<jclass>(env->NewGlobalRef(classObject.object()));
+ if (!clazz) {
+ // Wrong class loader, try our own
+ QJniObject classLoader(QtAndroidPrivate::classLoader());
+ if (!classLoader.isValid())
+ return nullptr;
+
+ // ClassLoader::loadClass on the other hand wants the binary name of the class,
+ // e.g. dot-separated. In testing it works also with /, but better to stick to
+ // the specification.
+ const QString binaryClassName = QString::fromLatin1(className).replace(u'/', u'.');
+ jstring classNameObject = env->NewString(reinterpret_cast<const jchar*>(binaryClassName.constData()),
+ binaryClassName.length());
+ QJniObject classObject = classLoader.callMethod<jclass>("loadClass", classNameObject);
+ env->DeleteLocalRef(classNameObject);
+
+ if (!QJniEnvironment::checkAndClearExceptions(env) && classObject.isValid())
+ clazz = static_cast<jclass>(env->NewGlobalRef(classObject.object()));
+ }
+
+ if (clazz)
+ cachedClasses->insert(classNameArray, clazz);
- cachedClasses->insert(key, clazz);
return clazz;
}
-typedef QHash<QString, jmethodID> JMethodIDHash;
+jclass QJniObject::loadClass(const QByteArray &className, JNIEnv *env)
+{
+ return QtAndroidPrivate::findClass(className, env);
+}
+
+typedef QHash<QByteArray, jmethodID> JMethodIDHash;
Q_GLOBAL_STATIC(JMethodIDHash, cachedMethodID)
Q_GLOBAL_STATIC(QReadWriteLock, cachedMethodIDLock)
@@ -407,10 +476,8 @@ jmethodID QJniObject::getCachedMethodID(JNIEnv *env,
if (className.isEmpty())
return getMethodID(env, clazz, name, signature, isStatic);
- const QString key = keyBase().arg(QLatin1StringView(className),
- QLatin1StringView(name),
- QLatin1StringView(signature));
- QHash<QString, jmethodID>::const_iterator it;
+ const QByteArray key = cacheKey(className, name, signature);
+ QHash<QByteArray, jmethodID>::const_iterator it;
{
QReadLocker locker(cachedMethodIDLock);
@@ -438,7 +505,7 @@ jmethodID QJniObject::getCachedMethodID(JNIEnv *env, const char *name,
return QJniObject::getCachedMethodID(env, d->m_jclass, d->m_className, name, signature, isStatic);
}
-typedef QHash<QString, jfieldID> JFieldIDHash;
+typedef QHash<QByteArray, jfieldID> JFieldIDHash;
Q_GLOBAL_STATIC(JFieldIDHash, cachedFieldID)
Q_GLOBAL_STATIC(QReadWriteLock, cachedFieldIDLock)
@@ -467,10 +534,8 @@ jfieldID QJniObject::getCachedFieldID(JNIEnv *env,
if (className.isNull())
return getFieldID(env, clazz, name, signature, isStatic);
- const QString key = keyBase().arg(QLatin1StringView(className),
- QLatin1StringView(name),
- QLatin1StringView(signature));
- QHash<QString, jfieldID>::const_iterator it;
+ const QByteArray key = cacheKey(className, name, signature);
+ QHash<QByteArray, jfieldID>::const_iterator it;
{
QReadLocker locker(cachedFieldIDLock);
@@ -500,39 +565,6 @@ jfieldID QJniObject::getCachedFieldID(JNIEnv *env,
return QJniObject::getCachedFieldID(env, d->m_jclass, d->m_className, name, signature, isStatic);
}
-jclass QtAndroidPrivate::findClass(const char *className, JNIEnv *env)
-{
- const QByteArray &classDotEnc = QJniObjectPrivate::toBinaryEncClassName(className);
- bool isCached = false;
- jclass clazz = getCachedClass(classDotEnc, &isCached);
-
- if (clazz || isCached)
- return clazz;
-
- const QLatin1StringView key(classDotEnc);
- if (env) { // We got an env. pointer (We expect this to be the right env. and call FindClass())
- QWriteLocker locker(cachedClassesLock);
- const QHash<QString, jclass>::const_iterator &it = cachedClasses->constFind(key);
- // Did we lose the race?
- if (it != cachedClasses->constEnd())
- return it.value();
-
- jclass fclazz = env->FindClass(className);
- if (!QJniEnvironment::checkAndClearExceptions(env)) {
- clazz = static_cast<jclass>(env->NewGlobalRef(fclazz));
- env->DeleteLocalRef(fclazz);
- }
-
- if (clazz)
- cachedClasses->insert(key, clazz);
- }
-
- if (!clazz) // We didn't get an env. pointer or we got one with the WRONG class loader...
- clazz = QJniObjectPrivate::loadClass(classDotEnc, QJniEnvironment().jniEnv(), true);
-
- return clazz;
-}
-
/*!
\fn QJniObject::QJniObject()
@@ -557,21 +589,11 @@ QJniObject::QJniObject()
QJniObject::QJniObject(const char *className)
: d(new QJniObjectPrivate())
{
- QJniEnvironment env;
- d->m_className = toBinaryEncClassName(className);
- d->m_jclass = loadClass(d->m_className, env.jniEnv(), true);
+ d->m_className = className;
+ d->m_jclass = loadClass(d->m_className, jniEnv());
d->m_own_jclass = false;
- if (d->m_jclass) {
- // get default constructor
- jmethodID constructorId = getCachedMethodID(env.jniEnv(), "<init>", "()V");
- if (constructorId) {
- jobject obj = env->NewObject(d->m_jclass, constructorId);
- if (obj) {
- d->m_jobject = env->NewGlobalRef(obj);
- env->DeleteLocalRef(obj);
- }
- }
- }
+
+ d->construct();
}
/*!
@@ -590,23 +612,14 @@ QJniObject::QJniObject(const char *className)
QJniObject::QJniObject(const char *className, const char *signature, ...)
: d(new QJniObjectPrivate())
{
- QJniEnvironment env;
- d->m_className = toBinaryEncClassName(className);
- d->m_jclass = loadClass(d->m_className, env.jniEnv(), true);
+ d->m_className = className;
+ d->m_jclass = loadClass(d->m_className, jniEnv());
d->m_own_jclass = false;
- if (d->m_jclass) {
- jmethodID constructorId = getCachedMethodID(env.jniEnv(), "<init>", signature);
- if (constructorId) {
- va_list args;
- va_start(args, signature);
- jobject obj = env->NewObjectV(d->m_jclass, constructorId, args);
- va_end(args);
- if (obj) {
- d->m_jobject = env->NewGlobalRef(obj);
- env->DeleteLocalRef(obj);
- }
- }
- }
+
+ va_list args;
+ va_start(args, signature);
+ d->construct(signature, args);
+ va_end(args);
}
/*!
@@ -625,25 +638,6 @@ QJniObject::QJniObject(const char *className, const char *signature, ...)
\endcode
*/
-QJniObject::QJniObject(const char *className, const char *signature, const QVaListPrivate &args)
- : d(new QJniObjectPrivate())
-{
- QJniEnvironment env;
- d->m_className = toBinaryEncClassName(className);
- d->m_jclass = loadClass(d->m_className, env.jniEnv(), true);
- d->m_own_jclass = false;
- if (d->m_jclass) {
- jmethodID constructorId = getCachedMethodID(env.jniEnv(), "<init>", signature);
- if (constructorId) {
- jobject obj = env->NewObjectV(d->m_jclass, constructorId, args);
- if (obj) {
- d->m_jobject = env->NewGlobalRef(obj);
- env->DeleteLocalRef(obj);
- }
- }
- }
-}
-
/*!
Constructs a new JNI object from \a clazz by calling the constructor with
\a signature specifying the types of any subsequent arguments.
@@ -657,22 +651,12 @@ QJniObject::QJniObject(const char *className, const char *signature, const QVaLi
QJniObject::QJniObject(jclass clazz, const char *signature, ...)
: d(new QJniObjectPrivate())
{
- QJniEnvironment env;
if (clazz) {
- d->m_jclass = static_cast<jclass>(env->NewGlobalRef(clazz));
- if (d->m_jclass) {
- jmethodID constructorId = getMethodID(env.jniEnv(), d->m_jclass, "<init>", signature);
- if (constructorId) {
- va_list args;
- va_start(args, signature);
- jobject obj = env->NewObjectV(d->m_jclass, constructorId, args);
- va_end(args);
- if (obj) {
- d->m_jobject = env->NewGlobalRef(obj);
- env->DeleteLocalRef(obj);
- }
- }
- }
+ d->m_jclass = static_cast<jclass>(jniEnv()->NewGlobalRef(clazz));
+ va_list args;
+ va_start(args, signature);
+ d->construct(signature, args);
+ va_end(args);
}
}
@@ -700,40 +684,8 @@ QJniObject::QJniObject(jclass clazz, const char *signature, ...)
*/
QJniObject::QJniObject(jclass clazz)
- : d(new QJniObjectPrivate())
+ : QJniObject(clazz, "()V")
{
- QJniEnvironment env;
- d->m_jclass = static_cast<jclass>(env->NewGlobalRef(clazz));
- if (d->m_jclass) {
- // get default constructor
- jmethodID constructorId = getMethodID(env.jniEnv(), d->m_jclass, "<init>", "()V");
- if (constructorId) {
- jobject obj = env->NewObject(d->m_jclass, constructorId);
- if (obj) {
- d->m_jobject = env->NewGlobalRef(obj);
- env->DeleteLocalRef(obj);
- }
- }
- }
-}
-
-QJniObject::QJniObject(jclass clazz, const char *signature, const QVaListPrivate &args)
- : d(new QJniObjectPrivate())
-{
- QJniEnvironment env;
- if (clazz) {
- d->m_jclass = static_cast<jclass>(env->NewGlobalRef(clazz));
- if (d->m_jclass) {
- jmethodID constructorId = getMethodID(env.jniEnv(), d->m_jclass, "<init>", signature);
- if (constructorId) {
- jobject obj = env->NewObjectV(d->m_jclass, constructorId, args);
- if (obj) {
- d->m_jobject = env->NewGlobalRef(obj);
- env->DeleteLocalRef(obj);
- }
- }
- }
- }
}
/*!
@@ -754,7 +706,7 @@ QJniObject::QJniObject(jobject object)
if (!object)
return;
- QJniEnvironment env;
+ JNIEnv *env = QJniEnvironment::getJniEnv();
d->m_jobject = env->NewGlobalRef(object);
jclass cls = env->GetObjectClass(object);
d->m_jclass = static_cast<jclass>(env->NewGlobalRef(cls));
@@ -777,27 +729,6 @@ QJniObject::QJniObject(jobject object)
*/
/*!
- \brief Get a JNI object from a jobject variant and do the necessary
- exception clearing and delete the local reference before returning.
- The JNI object can be null if there was an exception.
-*/
-QJniObject QJniObject::getCleanJniObject(jobject object)
-{
- if (!object)
- return QJniObject();
-
- QJniEnvironment env;
- if (env.checkAndClearExceptions()) {
- env->DeleteLocalRef(object);
- return QJniObject();
- }
-
- QJniObject res(object);
- env->DeleteLocalRef(object);
- return res;
-}
-
-/*!
\fn QJniObject::~QJniObject()
Destroys the JNI object and releases any references held by the JNI object.
@@ -805,7 +736,37 @@ QJniObject QJniObject::getCleanJniObject(jobject object)
QJniObject::~QJniObject()
{}
+namespace {
+QByteArray getClassNameHelper(JNIEnv *env, const QJniObjectPrivate *d)
+{
+ if (env->PushLocalFrame(3) != JNI_OK) // JVM out of memory
+ return QByteArray();
+
+ jmethodID mid = env->GetMethodID(d->m_jclass, "getClass", "()Ljava/lang/Class;");
+ jobject classObject = env->CallObjectMethod(d->m_jobject, mid);
+ jclass classObjectClass = env->GetObjectClass(classObject);
+ mid = env->GetMethodID(classObjectClass, "getName", "()Ljava/lang/String;");
+ jstring stringObject = static_cast<jstring>(env->CallObjectMethod(classObject, mid));
+ const jsize length = env->GetStringUTFLength(stringObject);
+ const char* nameString = env->GetStringUTFChars(stringObject, NULL);
+ const QByteArray result = QByteArray::fromRawData(nameString, length).replace('.', '/');
+ env->ReleaseStringUTFChars(stringObject, nameString);
+ env->PopLocalFrame(nullptr);
+ return result;
+}
+}
+
+/*! \internal
+
+ Returns the JNIEnv of the calling thread.
+*/
+JNIEnv *QJniObject::jniEnv() const noexcept
+{
+ return QJniEnvironment::getJniEnv();
+}
+
/*!
+ \fn jobject QJniObject::object() const
\fn template <typename T> T QJniObject::object() const
Returns the object held by the QJniObject either as jobject or as type T.
@@ -856,65 +817,11 @@ jclass QJniObject::objectClass() const
*/
QByteArray QJniObject::className() const
{
- return d->m_className;
-}
-
-QJniObject QJniObject::callObjectMethodV(const char *methodName,
- const char *signature,
- va_list args) const
-{
- QJniEnvironment env;
- jobject res = nullptr;
- jmethodID id = getCachedMethodID(env.jniEnv(), methodName, signature);
- if (id) {
- res = env->CallObjectMethodV(d->m_jobject, id, args);
- if (env.checkAndClearExceptions()) {
- env->DeleteLocalRef(res);
- res = nullptr;
- }
+ if (d->m_className.isEmpty() && d->m_jclass && d->m_jobject) {
+ JNIEnv *env = jniEnv();
+ d->m_className = getClassNameHelper(env, d.get());
}
-
- QJniObject obj(res);
- env->DeleteLocalRef(res);
- return obj;
-}
-
-QJniObject QJniObject::callStaticObjectMethodV(const char *className,
- const char *methodName,
- const char *signature,
- va_list args)
-{
- QJniEnvironment env;
- jobject res = nullptr;
- jclass clazz = loadClass(className, env.jniEnv());
- if (clazz) {
- jmethodID id = QJniObject::getCachedMethodID(env.jniEnv(), clazz, toBinaryEncClassName(className),
- methodName, signature, true);
- if (id) {
- res = env->CallStaticObjectMethodV(clazz, id, args);
- if (env.checkAndClearExceptions()) {
- env->DeleteLocalRef(res);
- res = nullptr;
- }
- }
- }
-
- QJniObject obj(res);
- env->DeleteLocalRef(res);
- return obj;
-}
-
-QJniObject QJniObject::callStaticObjectMethodV(jclass clazz,
- const char *methodName,
- const char *signature,
- va_list args)
-{
- QJniEnvironment env;
- jmethodID id = getMethodID(env.jniEnv(), clazz, methodName, signature, true);
- if (!id)
- return QJniObject();
-
- return getCleanJniObject(env->CallStaticObjectMethodV(clazz, id, args));
+ return d->m_className;
}
/*!
@@ -1023,7 +930,7 @@ QJniObject QJniObject::callStaticObjectMethodV(jclass clazz,
\since 6.4
Calls the static method \a methodName on \a clazz and returns the value of type \c Ret
- (unless c Ret is \c void). If \c Ret if a jobject type, then the returned value will
+ (unless \c Ret is \c void). If \c Ret is a jobject type, then the returned value will
be a QJniObject.
\code
@@ -1036,6 +943,18 @@ QJniObject QJniObject::callStaticObjectMethodV(jclass clazz,
*/
/*!
+ \fn template <typename Klass, typename Ret, typename ...Args> auto QJniObject::callStaticMethod(const char *methodName, Args &&...args)
+ \since 6.7
+
+ Calls the static method \a methodName on the class \c Klass and returns the value of type
+ \c Ret (unless \c Ret is \c void). If \c Ret is a jobject type, then the returned value will
+ be a QJniObject.
+
+ The method signature is deduced at compile time from \c Ret and the types of \a args.
+ \c Klass needs to be a C++ type with a registered type mapping to a Java type.
+*/
+
+/*!
\fn QJniObject QJniObject::callObjectMethod(const char *methodName, const char *signature, ...) const
Calls the Java object's method \a methodName with \a signature specifying
@@ -1049,12 +968,11 @@ QJniObject QJniObject::callStaticObjectMethodV(jclass clazz,
*/
QJniObject QJniObject::callObjectMethod(const char *methodName, const char *signature, ...) const
{
- QJniEnvironment env;
- jmethodID id = getCachedMethodID(env.jniEnv(), methodName, signature);
+ jmethodID id = getCachedMethodID(jniEnv(), methodName, signature);
if (id) {
va_list args;
va_start(args, signature);
- QJniObject res = getCleanJniObject(env->CallObjectMethodV(d->m_jobject, id, args));
+ QJniObject res = getCleanJniObject(jniEnv()->CallObjectMethodV(d->m_jobject, id, args), jniEnv());
va_end(args);
return res;
}
@@ -1078,16 +996,16 @@ QJniObject QJniObject::callObjectMethod(const char *methodName, const char *sign
QJniObject QJniObject::callStaticObjectMethod(const char *className, const char *methodName,
const char *signature, ...)
{
- QJniEnvironment env;
- jclass clazz = QJniObject::loadClass(className, env.jniEnv());
+ JNIEnv *env = QJniEnvironment::getJniEnv();
+ jclass clazz = QJniObject::loadClass(className, env);
if (clazz) {
- jmethodID id = QJniObject::getCachedMethodID(env.jniEnv(), clazz,
- QJniObject::toBinaryEncClassName(className),
+ jmethodID id = QJniObject::getCachedMethodID(env, clazz,
+ className,
methodName, signature, true);
if (id) {
va_list args;
va_start(args, signature);
- QJniObject res = getCleanJniObject(env->CallStaticObjectMethodV(clazz, id, args));
+ QJniObject res = getCleanJniObject(env->CallStaticObjectMethodV(clazz, id, args), env);
va_end(args);
return res;
}
@@ -1105,13 +1023,13 @@ QJniObject QJniObject::callStaticObjectMethod(const char *className, const char
QJniObject QJniObject::callStaticObjectMethod(jclass clazz, const char *methodName,
const char *signature, ...)
{
- QJniEnvironment env;
if (clazz) {
+ QJniEnvironment env;
jmethodID id = getMethodID(env.jniEnv(), clazz, methodName, signature, true);
if (id) {
va_list args;
va_start(args, signature);
- QJniObject res = getCleanJniObject(env->CallStaticObjectMethodV(clazz, id, args));
+ QJniObject res = getCleanJniObject(env->CallStaticObjectMethodV(clazz, id, args), env.jniEnv());
va_end(args);
return res;
}
@@ -1137,11 +1055,11 @@ QJniObject QJniObject::callStaticObjectMethod(jclass clazz, const char *methodNa
*/
QJniObject QJniObject::callStaticObjectMethod(jclass clazz, jmethodID methodId, ...)
{
- QJniEnvironment env;
if (clazz && methodId) {
+ QJniEnvironment env;
va_list args;
va_start(args, methodId);
- QJniObject res = getCleanJniObject(env->CallStaticObjectMethodV(clazz, methodId, args));
+ QJniObject res = getCleanJniObject(env->CallStaticObjectMethodV(clazz, methodId, args), env.jniEnv());
va_end(args);
return res;
}
@@ -1187,7 +1105,7 @@ QJniObject QJniObject::callStaticObjectMethod(jclass clazz, jmethodID methodId,
*/
/*!
- \fn template <typename T> QJniObject &QJniObject::operator=(T object)
+ \fn template <typename T, std::enable_if_t<std::is_convertible_v<T, jobject>, bool> = true> QJniObject &QJniObject::operator=(T object)
Replace the current object with \a object. The old Java object will be released.
*/
@@ -1208,7 +1126,7 @@ QJniObject QJniObject::callStaticObjectMethod(jclass clazz, jmethodID methodId,
*/
/*!
- \fn T QJniObject::getField(const char *fieldName) const
+ \fn template<typename T> T QJniObject::getField(const char *fieldName) const
Retrieves the value of the field \a fieldName.
@@ -1219,13 +1137,13 @@ QJniObject QJniObject::callStaticObjectMethod(jclass clazz, jmethodID methodId,
*/
/*!
- \fn T QJniObject::getStaticField(const char *className, const char *fieldName)
+ \fn template<typename T> T QJniObject::getStaticField(const char *className, const char *fieldName)
Retrieves the value from the static field \a fieldName on the class \a className.
*/
/*!
- \fn T QJniObject::getStaticField(jclass clazz, const char *fieldName)
+ \fn template<typename T> T QJniObject::getStaticField(jclass clazz, const char *fieldName)
Retrieves the value from the static field \a fieldName on \a clazz.
*/
@@ -1275,18 +1193,18 @@ QJniObject QJniObject::getStaticObjectField(const char *className,
const char *fieldName,
const char *signature)
{
- QJniEnvironment env;
- jclass clazz = QJniObject::loadClass(className, env.jniEnv());
+ JNIEnv *env = QJniEnvironment::getJniEnv();
+ jclass clazz = QJniObject::loadClass(className, env);
if (!clazz)
return QJniObject();
- jfieldID id = QJniObject::getCachedFieldID(env.jniEnv(), clazz,
- QJniObject::toBinaryEncClassName(className),
+ jfieldID id = QJniObject::getCachedFieldID(env, clazz,
+ className,
fieldName,
signature, true);
if (!id)
return QJniObject();
- return getCleanJniObject(env->GetStaticObjectField(clazz, id));
+ return getCleanJniObject(env->GetStaticObjectField(clazz, id), env);
}
/*!
@@ -1304,12 +1222,9 @@ QJniObject QJniObject::getStaticObjectField(const char *className,
QJniObject QJniObject::getStaticObjectField(jclass clazz, const char *fieldName,
const char *signature)
{
- QJniEnvironment env;
- jfieldID id = getFieldID(env.jniEnv(), clazz, fieldName, signature, true);
- if (!id)
- return QJniObject();
-
- return getCleanJniObject(env->GetStaticObjectField(clazz, id));
+ JNIEnv *env = QJniEnvironment::getJniEnv();
+ jfieldID id = getFieldID(env, clazz, fieldName, signature, true);
+ return getCleanJniObject(env->GetStaticObjectField(clazz, id), env);
}
/*!
@@ -1326,7 +1241,7 @@ QJniObject QJniObject::getStaticObjectField(jclass clazz, const char *fieldName,
*/
/*!
- \fn QJniObject QJniObject::getObjectField(const char *fieldName) const
+ \fn template<typename T> QJniObject QJniObject::getObjectField(const char *fieldName) const
Retrieves a JNI object from the field \a fieldName.
@@ -1348,12 +1263,11 @@ QJniObject QJniObject::getStaticObjectField(jclass clazz, const char *fieldName,
*/
QJniObject QJniObject::getObjectField(const char *fieldName, const char *signature) const
{
- QJniEnvironment env;
- jfieldID id = getCachedFieldID(env.jniEnv(), fieldName, signature);
+ jfieldID id = getCachedFieldID(jniEnv(), fieldName, signature);
if (!id)
return QJniObject();
- return getCleanJniObject(env->GetObjectField(d->m_jobject, id));
+ return getCleanJniObject(jniEnv()->GetObjectField(d->m_jobject, id), jniEnv());
}
/*!
@@ -1370,7 +1284,7 @@ QJniObject QJniObject::getObjectField(const char *fieldName, const char *signatu
*/
/*!
- \fn QJniObject QJniObject::getStaticObjectField(const char *className, const char *fieldName)
+ \fn template<typename T> QJniObject QJniObject::getStaticObjectField(const char *className, const char *fieldName)
Retrieves the object from the field \a fieldName on the class \a className.
@@ -1380,7 +1294,7 @@ QJniObject QJniObject::getObjectField(const char *fieldName, const char *signatu
*/
/*!
- \fn QJniObject QJniObject::getStaticObjectField(jclass clazz, const char *fieldName)
+ \fn template<typename T> QJniObject QJniObject::getStaticObjectField(jclass clazz, const char *fieldName)
Retrieves the object from the field \a fieldName on \a clazz.
@@ -1404,8 +1318,11 @@ QJniObject QJniObject::getObjectField(const char *fieldName, const char *signatu
QJniObject QJniObject::fromString(const QString &string)
{
QJniEnvironment env;
- return getCleanJniObject(env->NewString(reinterpret_cast<const jchar*>(string.constData()),
- string.length()));
+ jstring stringRef = env->NewString(reinterpret_cast<const jchar*>(string.constData()),
+ string.length());
+ QJniObject stringObject = getCleanJniObject(stringRef, env.jniEnv());
+ stringObject.d->m_className = "java/lang/String";
+ return stringObject;
}
/*!
@@ -1428,7 +1345,10 @@ QString QJniObject::toString() const
return QString();
QJniObject string = callObjectMethod<jstring>("toString");
- return qt_convertJString(static_cast<jstring>(string.object()));
+ const int strLength = string.jniEnv()->GetStringLength(string.object<jstring>());
+ QString res(strLength, Qt::Uninitialized);
+ string.jniEnv()->GetStringRegion(string.object<jstring>(), 0, strLength, reinterpret_cast<jchar *>(res.data()));
+ return res;
}
/*!
@@ -1449,7 +1369,7 @@ bool QJniObject::isClassAvailable(const char *className)
if (!env.jniEnv())
return false;
- return loadClass(className, env.jniEnv());;
+ return loadClass(className, env.jniEnv());
}
/*!
@@ -1485,13 +1405,17 @@ bool QJniObject::isValid() const
QJniObject QJniObject::fromLocalRef(jobject lref)
{
QJniObject obj(lref);
- QJniEnvironment()->DeleteLocalRef(lref);
+ obj.jniEnv()->DeleteLocalRef(lref);
return obj;
}
bool QJniObject::isSameObject(jobject obj) const
{
- return QJniEnvironment()->IsSameObject(d->m_jobject, obj);
+ if (d->m_jobject == obj)
+ return true;
+ if (!d->m_jobject || !obj)
+ return false;
+ return jniEnv()->IsSameObject(d->m_jobject, obj);
}
bool QJniObject::isSameObject(const QJniObject &other) const
@@ -1501,15 +1425,14 @@ bool QJniObject::isSameObject(const QJniObject &other) const
void QJniObject::assign(jobject obj)
{
- if (isSameObject(obj))
+ if (d && isSameObject(obj))
return;
- jobject jobj = static_cast<jobject>(obj);
d = QSharedPointer<QJniObjectPrivate>::create();
if (obj) {
- QJniEnvironment env;
- d->m_jobject = env->NewGlobalRef(jobj);
- jclass objectClass = env->GetObjectClass(jobj);
+ JNIEnv *env = QJniEnvironment::getJniEnv();
+ d->m_jobject = env->NewGlobalRef(obj);
+ jclass objectClass = env->GetObjectClass(obj);
d->m_jclass = static_cast<jclass>(env->NewGlobalRef(objectClass));
env->DeleteLocalRef(objectClass);
}
diff --git a/src/corelib/kernel/qjniobject.h b/src/corelib/kernel/qjniobject.h
index 2ae4c03dca..707d1ae28a 100644
--- a/src/corelib/kernel/qjniobject.h
+++ b/src/corelib/kernel/qjniobject.h
@@ -9,7 +9,6 @@
#if defined(Q_QDOC) || defined(Q_OS_ANDROID)
#include <jni.h>
#include <QtCore/qjnienvironment.h>
-#include <QtCore/qjnitypes.h>
QT_BEGIN_NAMESPACE
@@ -17,6 +16,51 @@ class QJniObjectPrivate;
class Q_CORE_EXPORT QJniObject
{
+ friend class QJniArrayBase;
+
+ template <typename ...Args>
+ struct LocalFrame {
+ mutable JNIEnv *env;
+ bool hasFrame = false;
+ explicit LocalFrame(JNIEnv *env = nullptr) noexcept
+ : env(env)
+ {
+ }
+ ~LocalFrame()
+ {
+ if (hasFrame)
+ env->PopLocalFrame(nullptr);
+ }
+ template <typename T>
+ auto newLocalRef(jobject object)
+ {
+ if (!hasFrame) {
+ if (jniEnv()->PushLocalFrame(sizeof...(Args)) < 0)
+ return T{}; // JVM is out of memory, avoid making matters worse
+ hasFrame = true;
+ }
+ return static_cast<T>(jniEnv()->NewLocalRef(object));
+ }
+ template <typename T>
+ auto newLocalRef(const QJniObject &object)
+ {
+ return newLocalRef<T>(object.template object<T>());
+ }
+ JNIEnv *jniEnv() const
+ {
+ if (!env)
+ env = QJniEnvironment::getJniEnv();
+ return env;
+ }
+ bool checkAndClearExceptions()
+ {
+ return env ? QJniEnvironment::checkAndClearExceptions(env) : false;
+ }
+ template <typename T>
+ auto convertToJni(T &&value);
+ template <typename T>
+ auto convertFromJni(QJniObject &&object);
+ };
public:
QJniObject();
explicit QJniObject(const char *className);
@@ -27,9 +71,17 @@ public:
#endif
>
explicit QJniObject(const char *className, Args &&...args)
+ : QJniObject(LocalFrame<Args...>{}, className, std::forward<Args>(args)...)
+ {
+ }
+private:
+ template<typename ...Args>
+ explicit QJniObject(LocalFrame<Args...> localFrame, const char *className, Args &&...args)
: QJniObject(className, QtJniTypes::constructorSignature<Args...>().data(),
- std::forward<Args>(args)...)
- {}
+ localFrame.convertToJni(std::forward<Args>(args))...)
+ {
+ }
+public:
explicit QJniObject(jclass clazz);
explicit QJniObject(jclass clazz, const char *signature, ...);
template<typename ...Args
@@ -42,15 +94,21 @@ public:
std::forward<Args>(args)...)
{}
QJniObject(jobject globalRef);
- inline QJniObject(QtJniTypes::Object wrapper) noexcept : QJniObject(jobject(wrapper)) {}
+
+ QJniObject(const QJniObject &other) noexcept = default;
+ QJniObject(QJniObject &&other) noexcept = default;
+ QJniObject &operator=(const QJniObject &other) noexcept = default;
+ QJniObject &operator=(QJniObject &&other) noexcept = default;
+
~QJniObject();
template<typename Class, typename ...Args>
static inline QJniObject construct(Args &&...args)
{
- return QJniObject(QtJniTypes::className<Class>().data(),
+ LocalFrame<Args...> frame;
+ return QJniObject(QtJniTypes::Traits<Class>::className().data(),
QtJniTypes::constructorSignature<Args...>().data(),
- std::forward<Args>(args)...);
+ frame.convertToJni(std::forward<Args>(args))...);
}
jobject object() const;
@@ -63,49 +121,61 @@ public:
jclass objectClass() const;
QByteArray className() const;
- template <typename Ret, typename ...Args>
+ template <typename Ret, typename ...Args
+#ifndef Q_QDOC
+ , QtJniTypes::IfValidFieldType<Ret> = true
+#endif
+ >
auto callMethod(const char *methodName, const char *signature, Args &&...args) const
{
+ LocalFrame<Args...> frame(jniEnv());
if constexpr (QtJniTypes::isObjectType<Ret>()) {
- return callObjectMethod(methodName, signature, std::forward<Args>(args)...);
+ return frame.template convertFromJni<Ret>(callObjectMethod(methodName, signature,
+ frame.convertToJni(std::forward<Args>(args))...));
} else {
- QtJniTypes::assertPrimitiveType<Ret>();
- QJniEnvironment env;
- jmethodID id = getCachedMethodID(env.jniEnv(), methodName, signature);
+ jmethodID id = getCachedMethodID(frame.jniEnv(), methodName, signature);
if (id) {
- if constexpr (std::is_same<Ret, void>::value) {
- callVoidMethodV(env.jniEnv(), id, std::forward<Args>(args)...);
- env.checkAndClearExceptions();
+ if constexpr (std::is_same_v<Ret, void>) {
+ callVoidMethodV(frame.jniEnv(), id,
+ frame.convertToJni(std::forward<Args>(args))...);
+ frame.checkAndClearExceptions();
} else {
Ret res{};
- callMethodForType<Ret>(env.jniEnv(), res, object(), id, std::forward<Args>(args)...);
- if (env.checkAndClearExceptions())
+ callMethodForType<Ret>(frame.jniEnv(), res, object(), id,
+ frame.convertToJni(std::forward<Args>(args))...);
+ if (frame.checkAndClearExceptions())
res = {};
return res;
}
}
- if constexpr (!std::is_same<Ret, void>::value)
+ if constexpr (!std::is_same_v<Ret, void>)
return Ret{};
}
}
- template <typename Ret, typename ...Args>
+ template <typename Ret, typename ...Args
+#ifndef Q_QDOC
+ , QtJniTypes::IfValidSignatureTypes<Ret, Args...> = true
+#endif
+ >
auto callMethod(const char *methodName, Args &&...args) const
{
constexpr auto signature = QtJniTypes::methodSignature<Ret, Args...>();
- if constexpr (std::is_same<Ret, void>::value) {
- callMethod<void>(methodName, signature.data(), std::forward<Args>(args)...);
- } else {
- return callMethod<Ret>(methodName, signature.data(), std::forward<Args>(args)...);
- }
+ return callMethod<Ret>(methodName, signature.data(), std::forward<Args>(args)...);
}
- template <typename Ret, typename ...Args>
+ template <typename Ret, typename ...Args
+#ifndef Q_QDOC
+ , QtJniTypes::IfValidSignatureTypes<Ret, Args...> = true
+#endif
+ >
QJniObject callObjectMethod(const char *methodName, Args &&...args) const
{
QtJniTypes::assertObjectType<Ret>();
constexpr auto signature = QtJniTypes::methodSignature<Ret, Args...>();
- return callObjectMethod(methodName, signature.data(), std::forward<Args>(args)...);
+ LocalFrame<Args...> frame(jniEnv());
+ return frame.template convertFromJni<Ret>(callObjectMethod(methodName, signature,
+ frame.convertToJni(std::forward<Args>(args))...));
}
QJniObject callObjectMethod(const char *methodName, const char *signature, ...) const;
@@ -113,58 +183,91 @@ public:
template <typename Ret, typename ...Args>
static auto callStaticMethod(const char *className, const char *methodName, const char *signature, Args &&...args)
{
- QJniEnvironment env;
- jclass clazz = QJniObject::loadClass(className, env.jniEnv());
+ JNIEnv *env = QJniEnvironment::getJniEnv();
+ jclass clazz = QJniObject::loadClass(className, env);
return callStaticMethod<Ret>(clazz, methodName, signature, std::forward<Args>(args)...);
}
template <typename Ret, typename ...Args>
static auto callStaticMethod(jclass clazz, const char *methodName, const char *signature, Args &&...args)
{
- QJniEnvironment env;
- jmethodID id = getMethodID(env.jniEnv(), clazz, methodName, signature, true);
- return callStaticMethod<Ret, Args...>(clazz, id, std::forward<Args>(args)...);
+ JNIEnv *env = QJniEnvironment::getJniEnv();
+ jmethodID id = clazz ? getMethodID(env, clazz, methodName, signature, true)
+ : 0;
+ return callStaticMethod<Ret>(clazz, id, std::forward<Args>(args)...);
}
- template <typename Ret, typename ...Args>
+ template <typename Ret, typename ...Args
+#ifndef Q_QDOC
+ , QtJniTypes::IfValidFieldType<Ret> = true
+#endif
+ >
static auto callStaticMethod(jclass clazz, jmethodID methodId, Args &&...args)
{
+ LocalFrame<Args...> frame;
if constexpr (QtJniTypes::isObjectType<Ret>()) {
- return callStaticObjectMethod(clazz, methodId, std::forward<Args>(args)...);
+ return frame.template convertFromJni<Ret>(callStaticObjectMethod(clazz, methodId,
+ frame.convertToJni(std::forward<Args>(args))...));
} else {
- QtJniTypes::assertPrimitiveType<Ret>();
- QJniEnvironment env;
if (clazz && methodId) {
- if constexpr (std::is_same<Ret, void>::value) {
- callStaticMethodForVoid(env.jniEnv(), clazz, methodId, std::forward<Args>(args)...);
- env.checkAndClearExceptions();
+ if constexpr (std::is_same_v<Ret, void>) {
+ callStaticMethodForVoid(frame.jniEnv(), clazz, methodId,
+ frame.convertToJni(std::forward<Args>(args))...);
+ frame.checkAndClearExceptions();
} else {
Ret res{};
- callStaticMethodForType<Ret>(env.jniEnv(), res, clazz, methodId, std::forward<Args>(args)...);
- if (env.checkAndClearExceptions())
+ callStaticMethodForType<Ret>(frame.jniEnv(), res, clazz, methodId,
+ frame.convertToJni(std::forward<Args>(args))...);
+ if (frame.checkAndClearExceptions())
res = {};
return res;
}
}
- if constexpr (!std::is_same<Ret, void>::value)
+ if constexpr (!std::is_same_v<Ret, void>)
return Ret{};
}
}
- template <typename Ret, typename ...Args>
+ template <typename Ret, typename ...Args
+#ifndef Q_QDOC
+ , QtJniTypes::IfValidSignatureTypes<Ret, Args...> = true
+#endif
+ >
static auto callStaticMethod(const char *className, const char *methodName, Args &&...args)
{
- QJniEnvironment env;
- jclass clazz = QJniObject::loadClass(className, env.jniEnv());
- return callStaticMethod<Ret, Args...>(clazz, methodName, std::forward<Args>(args)...);
+ JNIEnv *env = QJniEnvironment::getJniEnv();
+ jclass clazz = QJniObject::loadClass(className, env);
+ const jmethodID id = clazz ? getMethodID(env, clazz, methodName,
+ QtJniTypes::methodSignature<Ret, Args...>().data(), true)
+ : 0;
+ return callStaticMethod<Ret>(clazz, id, std::forward<Args>(args)...);
}
- template <typename Ret, typename ...Args>
+ template <typename Ret, typename ...Args
+#ifndef Q_QDOC
+ , QtJniTypes::IfValidSignatureTypes<Ret, Args...> = true
+#endif
+ >
static auto callStaticMethod(jclass clazz, const char *methodName, Args &&...args)
{
constexpr auto signature = QtJniTypes::methodSignature<Ret, Args...>();
return callStaticMethod<Ret>(clazz, methodName, signature.data(), std::forward<Args>(args)...);
}
+ template <typename Klass, typename Ret, typename ...Args
+#ifndef Q_QDOC
+ , QtJniTypes::IfValidSignatureTypes<Ret, Args...> = true
+#endif
+ >
+ static auto callStaticMethod(const char *methodName, Args &&...args)
+ {
+ JNIEnv *env = QJniEnvironment::getJniEnv();
+ const jclass clazz = QJniObject::loadClass(QtJniTypes::Traits<Klass>::className().data(),
+ env);
+ const jmethodID id = clazz ? getMethodID(env, clazz, methodName,
+ QtJniTypes::methodSignature<Ret, Args...>().data(), true)
+ : 0;
+ return callStaticMethod<Ret>(clazz, id, std::forward<Args>(args)...);
+ }
static QJniObject callStaticObjectMethod(const char *className, const char *methodName,
const char *signature, ...);
@@ -175,109 +278,128 @@ public:
static QJniObject callStaticObjectMethod(jclass clazz, jmethodID methodId, ...);
- template <typename Ret, typename ...Args>
+ template <typename Ret, typename ...Args
+#ifndef Q_QDOC
+ , QtJniTypes::IfValidSignatureTypes<Ret, Args...> = true
+#endif
+ >
static QJniObject callStaticObjectMethod(const char *className, const char *methodName, Args &&...args)
{
QtJniTypes::assertObjectType<Ret>();
constexpr auto signature = QtJniTypes::methodSignature<Ret, Args...>();
- return callStaticObjectMethod(className, methodName, signature.data(), std::forward<Args>(args)...);
+ LocalFrame<Args...> frame;
+ return frame.template convertFromJni<Ret>(callStaticObjectMethod(className, methodName, signature.data(),
+ frame.convertToJni(std::forward<Args>(args))...));
}
- template <typename Ret, typename ...Args>
+ template <typename Ret, typename ...Args
+#ifndef Q_QDOC
+ , QtJniTypes::IfValidSignatureTypes<Ret, Args...> = true
+#endif
+ >
static QJniObject callStaticObjectMethod(jclass clazz, const char *methodName, Args &&...args)
{
QtJniTypes::assertObjectType<Ret>();
constexpr auto signature = QtJniTypes::methodSignature<Ret, Args...>();
- return callStaticObjectMethod(clazz, methodName, signature.data(), std::forward<Args>(args)...);
+ LocalFrame<Args...> frame;
+ return frame.template convertFromJni<Ret>(callStaticObjectMethod(clazz, methodName, signature.data(),
+ frame.convertToJni(std::forward<Args>(args))...));
}
- template <typename T> auto getField(const char *fieldName) const
+ template <typename T
+#ifndef Q_QDOC
+ , QtJniTypes::IfValidFieldType<T> = true
+#endif
+ >
+ auto getField(const char *fieldName) const
{
+ LocalFrame<T> frame(jniEnv());
if constexpr (QtJniTypes::isObjectType<T>()) {
- return getObjectField<T>(fieldName);
+ return frame.template convertFromJni<T>(getObjectField<T>(fieldName));
} else {
- QtJniTypes::assertPrimitiveType<T>();
- QJniEnvironment env;
T res{};
constexpr auto signature = QtJniTypes::fieldSignature<T>();
- jfieldID id = getCachedFieldID(env.jniEnv(), fieldName, signature);
+ jfieldID id = getCachedFieldID(frame.jniEnv(), fieldName, signature);
if (id) {
- getFieldForType<T>(env.jniEnv(), res, object(), id);
- if (env.checkAndClearExceptions())
+ getFieldForType<T>(frame.jniEnv(), res, object(), id);
+ if (frame.checkAndClearExceptions())
res = {};
}
return res;
}
}
- template <typename T>
+ template <typename T
+#ifndef Q_QDOC
+ , QtJniTypes::IfValidFieldType<T> = true
+#endif
+ >
static auto getStaticField(const char *className, const char *fieldName)
{
+ LocalFrame<T> frame;
if constexpr (QtJniTypes::isObjectType<T>()) {
- return getStaticObjectField<T>(className, fieldName);
+ return frame.template convertFromJni<T>(getStaticObjectField<T>(className, fieldName));
} else {
- QtJniTypes::assertPrimitiveType<T>();
- QJniEnvironment env;
- jclass clazz = QJniObject::loadClass(className, env.jniEnv());
- T res{};
+ jclass clazz = QJniObject::loadClass(className, frame.jniEnv());
if (!clazz)
- return res;
-
- constexpr auto signature = QtJniTypes::fieldSignature<T>();
- jfieldID id = getCachedFieldID(env.jniEnv(), clazz,
- QJniObject::toBinaryEncClassName(className),
- fieldName,
- signature, true);
- if (!id)
- return res;
-
- getStaticFieldForType<T>(env.jniEnv(), res, clazz, id);
- if (env.checkAndClearExceptions())
- res = {};
- return res;
+ return T{};
+ return getStaticField<T>(clazz, fieldName);
}
}
- template <typename T>
+ template <typename T
+#ifndef Q_QDOC
+ , QtJniTypes::IfValidFieldType<T> = true
+#endif
+ >
static auto getStaticField(jclass clazz, const char *fieldName)
{
+ LocalFrame<T> frame;
if constexpr (QtJniTypes::isObjectType<T>()) {
- return getStaticObjectField<T>(clazz, fieldName);
+ return frame.template convertFromJni<T>(getStaticObjectField<T>(clazz, fieldName));
} else {
- QtJniTypes::assertPrimitiveType<T>();
- QJniEnvironment env;
T res{};
constexpr auto signature = QtJniTypes::fieldSignature<T>();
- jfieldID id = getFieldID(env.jniEnv(), clazz, fieldName, signature, true);
+ jfieldID id = getFieldID(frame.jniEnv(), clazz, fieldName, signature, true);
if (id) {
- getStaticFieldForType<T>(env.jniEnv(), res, clazz, id);
- if (env.checkAndClearExceptions())
+ getStaticFieldForType<T>(frame.jniEnv(), res, clazz, id);
+ if (frame.checkAndClearExceptions())
res = {};
}
return res;
}
}
- template <typename Klass, typename T>
+ template <typename Klass, typename T
+#ifndef Q_QDOC
+ , QtJniTypes::IfValidFieldType<T> = true
+#endif
+ >
static auto getStaticField(const char *fieldName)
{
- return getStaticField<T>(QtJniTypes::className<Klass>(), fieldName);
+ return getStaticField<T>(QtJniTypes::Traits<Klass>::className(), fieldName);
}
- template <typename T>
+ template <typename T
+#ifndef Q_QDOC
+ , std::enable_if_t<QtJniTypes::isObjectType<T>(), bool> = true
+#endif
+ >
QJniObject getObjectField(const char *fieldName) const
{
- QtJniTypes::assertObjectType<T>();
constexpr auto signature = QtJniTypes::fieldSignature<T>();
return getObjectField(fieldName, signature);
}
QJniObject getObjectField(const char *fieldName, const char *signature) const;
- template <typename T>
+ template <typename T
+#ifndef Q_QDOC
+ , std::enable_if_t<QtJniTypes::isObjectType<T>(), bool> = true
+#endif
+ >
static QJniObject getStaticObjectField(const char *className, const char *fieldName)
{
- QtJniTypes::assertObjectType<T>();
constexpr auto signature = QtJniTypes::fieldSignature<T>();
return getStaticObjectField(className, fieldName, signature);
}
@@ -286,10 +408,13 @@ public:
const char *fieldName,
const char *signature);
- template <typename T>
+ template <typename T
+#ifndef Q_QDOC
+ , std::enable_if_t<QtJniTypes::isObjectType<T>(), bool> = true
+#endif
+ >
static QJniObject getStaticObjectField(jclass clazz, const char *fieldName)
{
- QtJniTypes::assertObjectType<T>();
constexpr auto signature = QtJniTypes::fieldSignature<T>();
return getStaticObjectField(clazz, fieldName, signature);
}
@@ -297,99 +422,114 @@ public:
static QJniObject getStaticObjectField(jclass clazz, const char *fieldName,
const char *signature);
- template <typename T> void setField(const char *fieldName, T value)
+ template <typename T
+#ifndef Q_QDOC
+ , QtJniTypes::IfValidFieldType<T> = true
+#endif
+ >
+ void setField(const char *fieldName, T value)
{
- QtJniTypes::assertType<T>();
- QJniEnvironment env;
constexpr auto signature = QtJniTypes::fieldSignature<T>();
- jfieldID id = getCachedFieldID(env.jniEnv(), fieldName, signature);
+ jfieldID id = getCachedFieldID(jniEnv(), fieldName, signature);
if (id) {
- setFieldForType<T>(env.jniEnv(), object(), id, value);
- env.checkAndClearExceptions();
+ setFieldForType<T>(jniEnv(), object(), id, value);
+ QJniEnvironment::checkAndClearExceptions(jniEnv());
}
}
- template <typename T>
+ template <typename T
+#ifndef Q_QDOC
+ , QtJniTypes::IfValidFieldType<T> = true
+#endif
+ >
void setField(const char *fieldName, const char *signature, T value)
{
- QtJniTypes::assertType<T>();
- QJniEnvironment env;
- jfieldID id = getCachedFieldID(env.jniEnv(), fieldName, signature);
+ jfieldID id = getCachedFieldID(jniEnv(), fieldName, signature);
if (id) {
- setFieldForType<T>(env.jniEnv(), object(), id, value);
- env.checkAndClearExceptions();
+ setFieldForType<T>(jniEnv(), object(), id, value);
+ QJniEnvironment::checkAndClearExceptions(jniEnv());
}
}
- template <typename T>
+ template <typename T
+#ifndef Q_QDOC
+ , QtJniTypes::IfValidFieldType<T> = true
+#endif
+ >
static void setStaticField(const char *className, const char *fieldName, T value)
{
- QtJniTypes::assertType<T>();
- QJniEnvironment env;
- jclass clazz = QJniObject::loadClass(className, env.jniEnv());
+ LocalFrame<T> frame;
+ jclass clazz = QJniObject::loadClass(className, frame.jniEnv());
if (!clazz)
return;
constexpr auto signature = QtJniTypes::fieldSignature<T>();
- jfieldID id = getCachedFieldID(env.jniEnv(), clazz, className, fieldName,
+ jfieldID id = getCachedFieldID(frame.jniEnv(), clazz, className, fieldName,
signature, true);
if (!id)
return;
- setStaticFieldForType<T>(env.jniEnv(), clazz, id, value);
- env.checkAndClearExceptions();
+ setStaticFieldForType<T>(frame.jniEnv(), clazz, id, value);
+ frame.checkAndClearExceptions();
}
- template <typename T>
+ template <typename T
+#ifndef Q_QDOC
+ , QtJniTypes::IfValidFieldType<T> = true
+#endif
+ >
static void setStaticField(const char *className, const char *fieldName,
const char *signature, T value)
{
- QtJniTypes::assertType<T>();
- QJniEnvironment env;
- jclass clazz = QJniObject::loadClass(className, env.jniEnv());
+ JNIEnv *env = QJniEnvironment::getJniEnv();
+ jclass clazz = QJniObject::loadClass(className, env);
if (!clazz)
return;
- jfieldID id = getCachedFieldID(env.jniEnv(), clazz, className, fieldName,
+ jfieldID id = getCachedFieldID(env, clazz, className, fieldName,
signature, true);
if (id) {
- setStaticFieldForType<T>(env.jniEnv(), clazz, id, value);
- env.checkAndClearExceptions();
+ setStaticFieldForType<T>(env, clazz, id, value);
+ QJniEnvironment::checkAndClearExceptions(env);
}
}
- template <typename T>
+ template <typename T
+#ifndef Q_QDOC
+ , QtJniTypes::IfValidFieldType<T> = true
+#endif
+ >
static void setStaticField(jclass clazz, const char *fieldName,
const char *signature, T value)
{
- QtJniTypes::assertType<T>();
- QJniEnvironment env;
- jfieldID id = getFieldID(env.jniEnv(), clazz, fieldName, signature, true);
+ JNIEnv *env = QJniEnvironment::getJniEnv();
+ jfieldID id = getFieldID(env, clazz, fieldName, signature, true);
if (id) {
- setStaticFieldForType<T>(env.jniEnv(), clazz, id, value);
- env.checkAndClearExceptions();
+ setStaticFieldForType<T>(env, clazz, id, value);
+ QJniEnvironment::checkAndClearExceptions(env);
}
}
- template <typename T>
+ template <typename T
+#ifndef Q_QDOC
+ , QtJniTypes::IfValidFieldType<T> = true
+#endif
+ >
static void setStaticField(jclass clazz, const char *fieldName, T value)
{
- QtJniTypes::assertType<T>();
- QJniEnvironment env;
- constexpr auto signature = QtJniTypes::fieldSignature<T>();
- jfieldID id = getFieldID(env.jniEnv(), clazz, fieldName, signature, true);
- if (id) {
- setStaticFieldForType<T>(env.jniEnv(), clazz, id, value);
- env.checkAndClearExceptions();
- }
+ setStaticField(clazz, fieldName, QtJniTypes::fieldSignature<T>(), value);
}
- template <typename Klass, typename T>
+ template <typename Klass, typename T
+#ifndef Q_QDOC
+ , QtJniTypes::IfValidFieldType<T> = true
+#endif
+ >
static void setStaticField(const char *fieldName, T value)
{
- setStaticField(QtJniTypes::className<Klass>(), fieldName, value);
+ setStaticField(QtJniTypes::Traits<Klass>::className(), fieldName, value);
}
static QJniObject fromString(const QString &string);
@@ -401,21 +541,27 @@ public:
// This function takes ownership of the jobject and releases the local ref. before returning.
static QJniObject fromLocalRef(jobject lref);
- template <typename T> QJniObject &operator=(T obj)
+ template <typename T,
+ std::enable_if_t<std::is_convertible_v<T, jobject>, bool> = true>
+ QJniObject &operator=(T obj)
{
- QtJniTypes::assertType<T>();
assign(static_cast<T>(obj));
return *this;
}
+protected:
+ QJniObject(Qt::Initialization) {}
+ JNIEnv *jniEnv() const noexcept;
+
private:
- struct QVaListPrivate { operator va_list &() const { return m_args; } va_list &m_args; };
- QJniObject(const char *className, const char *signature, const QVaListPrivate &args);
- QJniObject(jclass clazz, const char *signature, const QVaListPrivate &args);
+ static jclass loadClass(const QByteArray &className, JNIEnv *env);
- static jclass loadClass(const QByteArray &className, JNIEnv *env, bool binEncoded = false);
+#if QT_CORE_REMOVED_SINCE(6, 7)
+ // these need to stay in the ABI as they were used in inline methods before 6.7
+ static jclass loadClass(const QByteArray &className, JNIEnv *env, bool binEncoded);
static QByteArray toBinaryEncClassName(const QByteArray &className);
- static QJniObject getCleanJniObject(jobject obj);
+ void callVoidMethodV(JNIEnv *env, jmethodID id, va_list args) const;
+#endif
static jfieldID getCachedFieldID(JNIEnv *env, jclass clazz, const QByteArray &className,
const char *name, const char *signature,
@@ -434,14 +580,6 @@ private:
const char *signature, bool isStatic = false);
void callVoidMethodV(JNIEnv *env, jmethodID id, ...) const;
- QJniObject callObjectMethodV(const char *methodName, const char *signature,
- va_list args) const;
-
- static QJniObject callStaticObjectMethodV(const char *className, const char *methodName,
- const char *signature, va_list args);
-
- static QJniObject callStaticObjectMethodV(jclass clazz, const char *methodName,
- const char *signature, va_list args);
bool isSameObject(jobject obj) const;
bool isSameObject(const QJniObject &other) const;
@@ -452,30 +590,11 @@ private:
friend bool operator!=(const QJniObject&, const QJniObject&);
template<typename T>
- static constexpr void callMethodForType(JNIEnv *env, T &res, jobject obj,
- jmethodID id, ...)
+ static constexpr void callMethodForType(JNIEnv *env, T &res, jobject obj, jmethodID id, ...)
{
va_list args = {};
va_start(args, id);
-
- if constexpr(std::is_same<T, jboolean>::value)
- res = env->CallBooleanMethodV(obj, id, args);
- else if constexpr(std::is_same<T, jbyte>::value)
- res = env->CallByteMethodV(obj, id, args);
- else if constexpr(std::is_same<T, jchar>::value)
- res = env->CallCharMethodV(obj, id, args);
- else if constexpr(std::is_same<T, jshort>::value)
- res = env->CallShortMethodV(obj, id, args);
- else if constexpr(std::is_same<T, jint>::value)
- res = env->CallIntMethodV(obj, id, args);
- else if constexpr(std::is_same<T, jlong>::value)
- res = env->CallLongMethodV(obj, id, args);
- else if constexpr(std::is_same<T, jfloat>::value)
- res = env->CallFloatMethodV(obj, id, args);
- else if constexpr(std::is_same<T, jdouble>::value)
- res = env->CallDoubleMethodV(obj, id, args);
- else
- QtJniTypes::staticAssertTypeMismatch();
+ QtJniTypes::Caller<T>::callMethodForType(env, res, obj, id, args);
va_end(args);
}
@@ -483,31 +602,18 @@ private:
static constexpr void callStaticMethodForType(JNIEnv *env, T &res, jclass clazz,
jmethodID id, ...)
{
+ if (!clazz || !id)
+ return;
va_list args = {};
va_start(args, id);
- if constexpr(std::is_same<T, jboolean>::value)
- res = env->CallStaticBooleanMethodV(clazz, id, args);
- else if constexpr(std::is_same<T, jbyte>::value)
- res = env->CallStaticByteMethodV(clazz, id, args);
- else if constexpr(std::is_same<T, jchar>::value)
- res = env->CallStaticCharMethodV(clazz, id, args);
- else if constexpr(std::is_same<T, jshort>::value)
- res = env->CallStaticShortMethodV(clazz, id, args);
- else if constexpr(std::is_same<T, jint>::value)
- res = env->CallStaticIntMethodV(clazz, id, args);
- else if constexpr(std::is_same<T, jlong>::value)
- res = env->CallStaticLongMethodV(clazz, id, args);
- else if constexpr(std::is_same<T, jfloat>::value)
- res = env->CallStaticFloatMethodV(clazz, id, args);
- else if constexpr(std::is_same<T, jdouble>::value)
- res = env->CallStaticDoubleMethodV(clazz, id, args);
- else
- QtJniTypes::staticAssertTypeMismatch();
+ QtJniTypes::Caller<T>::callStaticMethodForType(env, res, clazz, id, args);
va_end(args);
}
static void callStaticMethodForVoid(JNIEnv *env, jclass clazz, jmethodID id, ...)
{
+ if (!clazz || !id)
+ return;
va_list args;
va_start(args, id);
env->CallStaticVoidMethodV(clazz, id, args);
@@ -516,103 +622,37 @@ private:
template<typename T>
- static constexpr void getFieldForType(JNIEnv *env, T &res, jobject obj,
- jfieldID id)
- {
- if constexpr(std::is_same<T, jboolean>::value)
- res = env->GetBooleanField(obj, id);
- else if constexpr(std::is_same<T, jbyte>::value)
- res = env->GetByteField(obj, id);
- else if constexpr(std::is_same<T, jchar>::value)
- res = env->GetCharField(obj, id);
- else if constexpr(std::is_same<T, jshort>::value)
- res = env->GetShortField(obj, id);
- else if constexpr(std::is_same<T, jint>::value)
- res = env->GetIntField(obj, id);
- else if constexpr(std::is_same<T, jlong>::value)
- res = env->GetLongField(obj, id);
- else if constexpr(std::is_same<T, jfloat>::value)
- res = env->GetFloatField(obj, id);
- else if constexpr(std::is_same<T, jdouble>::value)
- res = env->GetDoubleField(obj, id);
- else
- QtJniTypes::staticAssertTypeMismatch();
+ static constexpr void getFieldForType(JNIEnv *env, T &res, jobject obj, jfieldID id)
+ {
+ QtJniTypes::Caller<T>::getFieldForType(env, res, obj, id);
}
template<typename T>
- static constexpr void getStaticFieldForType(JNIEnv *env, T &res, jclass clazz,
- jfieldID id)
- {
- if constexpr(std::is_same<T, jboolean>::value)
- res = env->GetStaticBooleanField(clazz, id);
- else if constexpr(std::is_same<T, jbyte>::value)
- res = env->GetStaticByteField(clazz, id);
- else if constexpr(std::is_same<T, jchar>::value)
- res = env->GetStaticCharField(clazz, id);
- else if constexpr(std::is_same<T, jshort>::value)
- res = env->GetStaticShortField(clazz, id);
- else if constexpr(std::is_same<T, jint>::value)
- res = env->GetStaticIntField(clazz, id);
- else if constexpr(std::is_same<T, jlong>::value)
- res = env->GetStaticLongField(clazz, id);
- else if constexpr(std::is_same<T, jfloat>::value)
- res = env->GetStaticFloatField(clazz, id);
- else if constexpr(std::is_same<T, jdouble>::value)
- res = env->GetStaticDoubleField(clazz, id);
- else
- QtJniTypes::staticAssertTypeMismatch();
+ static constexpr void getStaticFieldForType(JNIEnv *env, T &res, jclass clazz, jfieldID id)
+ {
+ QtJniTypes::Caller<T>::getStaticFieldForType(env, res, clazz, id);
}
template<typename T>
- static constexpr void setFieldForType(JNIEnv *env, jobject obj,
- jfieldID id, T value)
- {
- if constexpr(std::is_same<T, jboolean>::value)
- env->SetBooleanField(obj, id, value);
- else if constexpr(std::is_same<T, jbyte>::value)
- env->SetByteField(obj, id, value);
- else if constexpr(std::is_same<T, jchar>::value)
- env->SetCharField(obj, id, value);
- else if constexpr(std::is_same<T, jshort>::value)
- env->SetShortField(obj, id, value);
- else if constexpr(std::is_same<T, jint>::value)
- env->SetIntField(obj, id, value);
- else if constexpr(std::is_same<T, jlong>::value)
- env->SetLongField(obj, id, value);
- else if constexpr(std::is_same<T, jfloat>::value)
- env->SetFloatField(obj, id, value);
- else if constexpr(std::is_same<T, jdouble>::value)
- env->SetDoubleField(obj, id, value);
- else if constexpr(std::is_convertible<T, jobject>::value)
- env->SetObjectField(obj, id, value);
- else
- QtJniTypes::staticAssertTypeMismatch();
+ static constexpr void setFieldForType(JNIEnv *env, jobject obj, jfieldID id, T value)
+ {
+ if constexpr (QtJniTypes::isObjectType<T>()) {
+ LocalFrame<T> frame(env);
+ env->SetObjectField(obj, id, static_cast<jobject>(frame.convertToJni(value)));
+ } else {
+ QtJniTypes::Caller<T>::setFieldForType(env, obj, id, value);
+ }
}
template<typename T>
- static constexpr void setStaticFieldForType(JNIEnv *env, jclass clazz,
- jfieldID id, T value)
- {
- if constexpr(std::is_same<T, jboolean>::value)
- env->SetStaticBooleanField(clazz, id, value);
- else if constexpr(std::is_same<T, jbyte>::value)
- env->SetStaticByteField(clazz, id, value);
- else if constexpr(std::is_same<T, jchar>::value)
- env->SetStaticCharField(clazz, id, value);
- else if constexpr(std::is_same<T, jshort>::value)
- env->SetStaticShortField(clazz, id, value);
- else if constexpr(std::is_same<T, jint>::value)
- env->SetStaticIntField(clazz, id, value);
- else if constexpr(std::is_same<T, jlong>::value)
- env->SetStaticLongField(clazz, id, value);
- else if constexpr(std::is_same<T, jfloat>::value)
- env->SetStaticFloatField(clazz, id, value);
- else if constexpr(std::is_same<T, jdouble>::value)
- env->SetStaticDoubleField(clazz, id, value);
- else if constexpr(std::is_convertible<T, jobject>::value)
- env->SetStaticObjectField(clazz, id, value);
- else
- QtJniTypes::staticAssertTypeMismatch();
+ static constexpr void setStaticFieldForType(JNIEnv *env, jclass clazz, jfieldID id, T value)
+ {
+ if constexpr (QtJniTypes::isObjectType<T>()) {
+ LocalFrame<T> frame(env);
+ env->SetStaticObjectField(clazz, id, static_cast<jobject>(frame.convertToJni(value)));
+ } else {
+ QtJniTypes::Caller<T>::setStaticFieldForType(env, clazz, id, value);
+ }
}
friend QJniObjectPrivate;
@@ -629,6 +669,201 @@ inline bool operator!=(const QJniObject &obj1, const QJniObject &obj2)
return !obj1.isSameObject(obj2);
}
+namespace QtJniTypes {
+struct QT_TECH_PREVIEW_API JObjectBase
+{
+ operator QJniObject() const { return m_object; }
+
+ bool isValid() const { return m_object.isValid(); }
+ jclass objectClass() const { return m_object.objectClass(); }
+ QString toString() const { return m_object.toString(); }
+
+ template <typename T = jobject>
+ T object() const {
+ return m_object.object<T>();
+ }
+
+protected:
+ JObjectBase() = default;
+ ~JObjectBase() = default;
+
+ Q_IMPLICIT JObjectBase(jobject object) : m_object(object) {}
+ Q_IMPLICIT JObjectBase(const QJniObject &object) : m_object(object) {}
+ Q_IMPLICIT JObjectBase(QJniObject &&object) noexcept : m_object(std::move(object)) {}
+
+ QJniObject m_object;
+};
+
+template<typename Type>
+class QT_TECH_PREVIEW_API JObject : public JObjectBase
+{
+public:
+ using Class = Type;
+
+ JObject()
+ : JObjectBase{QJniObject(QtJniTypes::Traits<Class>::className())}
+ {}
+ Q_IMPLICIT JObject(jobject object) : JObjectBase(object) {}
+ Q_IMPLICIT JObject(const QJniObject &object) : JObjectBase(object) {}
+ Q_IMPLICIT JObject(QJniObject &&object) noexcept : JObjectBase(std::move(object)) {}
+
+ // base class destructor is protected, so need to provide all SMFs
+ JObject(const JObject &other) = default;
+ JObject(JObject &&other) noexcept = default;
+ JObject &operator=(const JObject &other) = default;
+ JObject &operator=(JObject &&other) noexcept = default;
+
+ ~JObject() = default;
+
+ template<typename Arg, typename ...Args
+ , std::enable_if_t<!std::is_same_v<Arg, JObject>, bool> = true
+ , IfValidSignatureTypes<Arg, Args...> = true
+ >
+ explicit JObject(Arg && arg, Args &&...args)
+ : JObjectBase{QJniObject(QtJniTypes::Traits<Class>::className(),
+ std::forward<Arg>(arg), std::forward<Args>(args)...)}
+ {}
+
+ // named constructors avoid ambiguities
+ static Type fromJObject(jobject object) { return Type(object); }
+ template <typename ...Args>
+ static Type construct(Args &&...args) { return Type(std::forward<Args>(args)...); }
+ static Type fromLocalRef(jobject lref) { return Type(QJniObject::fromLocalRef(lref)); }
+
+ static bool registerNativeMethods(std::initializer_list<JNINativeMethod> methods)
+ {
+ QJniEnvironment env;
+ return env.registerNativeMethods<Class>(methods);
+ }
+
+ // public API forwarding to QJniObject, with the implicit Class template parameter
+ template <typename Ret, typename ...Args
+#ifndef Q_QDOC
+ , QtJniTypes::IfValidSignatureTypes<Ret, Args...> = true
+#endif
+ >
+ static auto callStaticMethod(const char *name, Args &&...args)
+ {
+ return QJniObject::callStaticMethod<Class, Ret, Args...>(name,
+ std::forward<Args>(args)...);
+ }
+ template <typename T
+#ifndef Q_QDOC
+ , QtJniTypes::IfValidFieldType<T> = true
+#endif
+ >
+ static auto getStaticField(const char *field)
+ {
+ return QJniObject::getStaticField<Class, T>(field);
+ }
+ template <typename T
+#ifndef Q_QDOC
+ , QtJniTypes::IfValidFieldType<T> = true
+#endif
+ >
+ static void setStaticField(const char *field, T &&value)
+ {
+ QJniObject::setStaticField<Class, T>(field, std::forward<T>(value));
+ }
+
+ // keep only these overloads, the rest is made private
+ template <typename Ret, typename ...Args
+#ifndef Q_QDOC
+ , QtJniTypes::IfValidSignatureTypes<Ret, Args...> = true
+#endif
+ >
+ auto callMethod(const char *method, Args &&...args) const
+ {
+ return m_object.callMethod<Ret>(method, std::forward<Args>(args)...);
+ }
+ template <typename T
+#ifndef Q_QDOC
+ , QtJniTypes::IfValidFieldType<T> = true
+#endif
+ >
+ auto getField(const char *fieldName) const
+ {
+ return m_object.getField<T>(fieldName);
+ }
+
+ template <typename T
+#ifndef Q_QDOC
+ , QtJniTypes::IfValidFieldType<T> = true
+#endif
+ >
+ void setField(const char *fieldName, T &&value)
+ {
+ m_object.setField(fieldName, std::forward<T>(value));
+ }
+
+ QByteArray className() const {
+ return QtJniTypes::Traits<Class>::className().data();
+ }
+
+private:
+ friend bool comparesEqual(const JObject &lhs, const JObject &rhs) noexcept
+ { return lhs.m_object == rhs.m_object; }
+ Q_DECLARE_EQUALITY_COMPARABLE_LITERAL_TYPE(JObject);
+};
+}
+
+// This cannot be included earlier as QJniArray is a QJniObject subclass, but it
+// must be included so that we can implement QJniObject::LocalFrame conversion.
+QT_END_NAMESPACE
+#include <QtCore/qjniarray.h>
+QT_BEGIN_NAMESPACE
+
+template <typename ...Args>
+template <typename T>
+auto QJniObject::LocalFrame<Args...>::convertToJni(T &&value)
+{
+ using Type = q20::remove_cvref_t<T>;
+ if constexpr (std::is_same_v<Type, QString>) {
+ return newLocalRef<jstring>(QJniObject::fromString(value));
+ } else if constexpr (QtJniTypes::IsJniArray<Type>::value) {
+ return value.arrayObject();
+ } else if constexpr (QJniArrayBase::canConvert<T>) {
+ using QJniArrayType = decltype(QJniArrayBase::fromContainer(std::forward<T>(value)));
+ using ArrayType = decltype(std::declval<QJniArrayType>().arrayObject());
+ return newLocalRef<ArrayType>(QJniArrayBase::fromContainer(std::forward<T>(value)).template object<jobject>());
+ } else if constexpr (std::is_base_of_v<QJniObject, Type>
+ || std::is_base_of_v<QtJniTypes::JObjectBase, Type>) {
+ return value.object();
+ } else {
+ return std::forward<T>(value);
+ }
+}
+
+template <typename ...Args>
+template <typename T>
+auto QJniObject::LocalFrame<Args...>::convertFromJni(QJniObject &&object)
+{
+ using Type = q20::remove_cvref_t<T>;
+ if constexpr (std::is_same_v<Type, QString>) {
+ return object.toString();
+ } else if constexpr (QtJniTypes::IsJniArray<Type>::value) {
+ return T(std::move(object));
+ } else if constexpr (QJniArrayBase::canConvert<Type>) {
+ // if we were to create a QJniArray from Type...
+ using QJniArrayType = decltype(QJniArrayBase::fromContainer(std::declval<Type>()));
+ // then that QJniArray would have elements of type
+ using ElementType = typename QJniArrayType::Type;
+ // construct a QJniArray from a jobject pointer of that type
+ return QJniArray<ElementType>(object.template object<jarray>()).toContainer();
+ } else if constexpr (std::is_array_v<Type>) {
+ using ElementType = std::remove_extent_t<Type>;
+ return QJniArray<ElementType>(std::move(object));
+ } else if constexpr (std::is_base_of_v<QJniObject, Type>
+ && !std::is_same_v<QJniObject, Type>) {
+ return T{std::move(object)};
+ } else if constexpr (std::is_base_of_v<QtJniTypes::JObjectBase, Type>) {
+ return T{std::move(object)};
+ } else {
+ return std::move(object);
+ }
+}
+
+
QT_END_NAMESPACE
#endif
diff --git a/src/corelib/kernel/qjnitypes.h b/src/corelib/kernel/qjnitypes.h
index 9c2e1efb77..033445be6a 100644
--- a/src/corelib/kernel/qjnitypes.h
+++ b/src/corelib/kernel/qjnitypes.h
@@ -4,374 +4,190 @@
#ifndef QJNITYPES_H
#define QJNITYPES_H
-#include <QtCore/qglobal.h>
-
#if defined(Q_QDOC) || defined(Q_OS_ANDROID)
-#include <jni.h>
-
-QT_BEGIN_NAMESPACE
-namespace QtJniTypes
-{
+#include <QtCore/qjnitypes_impl.h>
+#include <QtCore/qjniobject.h>
-// a constexpr type for string literals of any character width, aware of the length
-// of the string.
-template<size_t N_WITH_NULL, typename BaseType = char>
-struct String
-{
- BaseType m_data[N_WITH_NULL] = {};
-
- constexpr String() noexcept {}
- // Can be instantiated (only) with a string literal
- constexpr explicit String(const BaseType (&data)[N_WITH_NULL]) noexcept
- {
- for (size_t i = 0; i < N_WITH_NULL - 1; ++i)
- m_data[i] = data[i];
- }
+QT_BEGIN_NAMESPACE
- constexpr BaseType at(size_t i) const { return m_data[i]; }
- constexpr BaseType operator[](size_t i) const { return at(i); }
- static constexpr size_t size() noexcept { return N_WITH_NULL; }
- constexpr operator const BaseType *() const noexcept { return m_data; }
- constexpr const BaseType *data() const noexcept { return m_data; }
- template<size_t N2_WITH_NULL>
- constexpr bool startsWith(const BaseType (&lit)[N2_WITH_NULL]) const noexcept
- {
- if constexpr (N2_WITH_NULL > N_WITH_NULL) {
- return false;
- } else {
- for (size_t i = 0; i < N2_WITH_NULL - 1; ++i) {
- if (m_data[i] != lit[i])
- return false;
- }
- }
- return true;
- }
- constexpr bool startsWith(BaseType c) const noexcept
- {
- return N_WITH_NULL > 1 && m_data[0] == c;
- }
- template<size_t N2_WITH_NULL>
- constexpr bool endsWith(const BaseType (&lit)[N2_WITH_NULL]) const noexcept
- {
- if constexpr (N2_WITH_NULL > N_WITH_NULL) {
- return false;
- } else {
- for (size_t i = 0; i < N2_WITH_NULL; ++i) {
- if (m_data[N_WITH_NULL - i - 1] != lit[N2_WITH_NULL - i - 1])
- return false;
- }
- }
- return true;
- }
- constexpr bool endsWith(BaseType c) const noexcept
- {
- return N_WITH_NULL > 1 && m_data[N_WITH_NULL - 2] == c;
- }
+// QT_TECH_PREVIEW_API
+#define Q_DECLARE_JNI_TYPE_HELPER(Type) \
+namespace QtJniTypes { \
+struct Type : JObject<Type> \
+{ \
+ using JObject::JObject; \
+}; \
+} \
- template<size_t N2_WITH_NULL>
- friend inline constexpr bool operator==(const String<N_WITH_NULL> &lhs,
- const String<N2_WITH_NULL> &rhs) noexcept
- {
- if constexpr (N_WITH_NULL != N2_WITH_NULL) {
- return false;
- } else {
- for (size_t i = 0; i < N_WITH_NULL - 1; ++i) {
- if (lhs.at(i) != rhs.at(i))
- return false;
- }
- }
- return true;
- }
+// QT_TECH_PREVIEW_API
+#define Q_DECLARE_JNI_TYPE(Type, Signature) \
+Q_DECLARE_JNI_TYPE_HELPER(Type) \
+template<> \
+struct QtJniTypes::Traits<QtJniTypes::Type> { \
+ static constexpr auto signature() \
+ { \
+ constexpr QtJniTypes::CTString sig(Signature); \
+ static_assert((sig.startsWith('L') || sig.startsWith("[L")) \
+ && sig.endsWith(';'), \
+ "Type signature needs to start with 'L' or" \
+ " '[L', and end with ';'"); \
+ return sig; \
+ } \
+}; \
- template<size_t N2_WITH_NULL>
- friend inline constexpr bool operator!=(const String<N_WITH_NULL> &lhs,
- const String<N2_WITH_NULL> &rhs) noexcept
- {
- return !operator==(lhs, rhs);
- }
+#define Q_DECLARE_JNI_CLASS(Type, Signature) \
+Q_DECLARE_JNI_TYPE_HELPER(Type) \
+template<> \
+struct QtJniTypes::Traits<QtJniTypes::Type> { \
+ static constexpr auto className() \
+ { \
+ return QtJniTypes::CTString(Signature); \
+ } \
+ static constexpr auto signature() \
+ { \
+ return QtJniTypes::CTString("L") \
+ + className() \
+ + QtJniTypes::CTString(";"); \
+ } \
+}; \
- template<size_t N2_WITH_NULL>
- friend inline constexpr bool operator==(const String<N_WITH_NULL> &lhs,
- const BaseType (&rhs)[N2_WITH_NULL]) noexcept
- {
- return operator==(lhs, String<N2_WITH_NULL>(rhs));
- }
- template<size_t N2_WITH_NULL>
- friend inline constexpr bool operator==(const BaseType (&lhs)[N2_WITH_NULL],
- const String<N_WITH_NULL> &rhs) noexcept
+// Macros for native methods
+
+namespace QtJniMethods {
+namespace Detail {
+// Various helpers to forward a call from a variadic argument function to
+// the real function with proper type conversion. This is needed because we
+// want to write functions that take QJniObjects (subclasses), while Java
+// can only call functions that take jobjects.
+
+// In Var-arg functions, any argument narrower than (unsigned) int or double
+// is promoted to (unsigned) int or double.
+template <typename Arg> struct PromotedType { using Type = Arg; };
+template <> struct PromotedType<bool> { using Type = int; };
+template <> struct PromotedType<char> { using Type = int; };
+template <> struct PromotedType<signed char> { using Type = int; };
+template <> struct PromotedType<unsigned char> { using Type = unsigned int; };
+template <> struct PromotedType<short> { using Type = int; };
+template <> struct PromotedType<unsigned short> { using Type = unsigned int; };
+template <> struct PromotedType<float> { using Type = double; };
+
+// Map any QJniObject type to jobject; that's what's on the va_list
+template <typename Arg>
+struct JNITypeForArgImpl
+{
+ using Type = std::conditional_t<std::disjunction_v<std::is_base_of<QJniObject, Arg>,
+ std::is_base_of<QtJniTypes::JObjectBase, Arg>>,
+ jobject, typename PromotedType<Arg>::Type>;
+ static Arg fromVarArg(Type t)
{
- return operator==(String<N2_WITH_NULL>(lhs), rhs);
+ return static_cast<Arg>(t);
}
+};
- template<size_t N2_WITH_NULL>
- friend inline constexpr bool operator!=(const String<N_WITH_NULL> &lhs,
- const BaseType (&rhs)[N2_WITH_NULL]) noexcept
- {
- return operator!=(lhs, String<N2_WITH_NULL>(rhs));
- }
- template<size_t N2_WITH_NULL>
- friend inline constexpr bool operator!=(const BaseType (&lhs)[N2_WITH_NULL],
- const String<N_WITH_NULL> &rhs) noexcept
- {
- return operator!=(String<N2_WITH_NULL>(lhs), rhs);
- }
+template <>
+struct JNITypeForArgImpl<QString>
+{
+ using Type = jstring;
- template<size_t N2_WITH_NULL>
- friend inline constexpr auto operator+(const String<N_WITH_NULL> &lhs,
- const String<N2_WITH_NULL> &rhs) noexcept
+ static QString fromVarArg(Type t)
{
- char data[N_WITH_NULL + N2_WITH_NULL - 1] = {};
- for (size_t i = 0; i < N_WITH_NULL - 1; ++i)
- data[i] = lhs[i];
- for (size_t i = 0; i < N2_WITH_NULL - 1; ++i)
- data[N_WITH_NULL - 1 + i] = rhs[i];
- return String<N_WITH_NULL + N2_WITH_NULL - 1>(data);
+ return QJniObject(t).toString();
}
};
-
-// Helper types that allow us to disable variadic overloads that would conflict
-// with overloads that take a const char*.
-template<typename T, size_t N = 0> struct IsStringType : std::false_type {};
-template<> struct IsStringType<const char*, 0> : std::true_type {};
-template<size_t N> struct IsStringType<String<N>> : std::true_type {};
-template<size_t N> struct IsStringType<const char[N]> : std::true_type {};
-
-template<bool flag = false>
-static void staticAssertTypeMismatch()
+template <typename T>
+struct JNITypeForArgImpl<QJniArray<T>>
{
- static_assert(flag, "The used type is not supported by this template call. "
- "Use a JNI based type instead.");
-}
+ using Type = jobject;
-template<typename T>
-constexpr auto typeSignature()
-{
- if constexpr(std::is_array_v<T>) {
- using UnderlyingType = typename std::remove_extent<T>::type;
- static_assert(!std::is_array_v<UnderlyingType>,
- "typeSignature() does not handle multi-dimensional arrays");
- return String("[") + typeSignature<UnderlyingType>();
- } else if constexpr(std::is_same_v<T, jobject>) {
- return String("Ljava/lang/Object;");
- } else if constexpr(std::is_same_v<T, jclass>) {
- return String("Ljava/lang/Class;");
- } else if constexpr(std::is_same_v<T, jstring>) {
- return String("Ljava/lang/String;");
- } else if constexpr(std::is_same_v<T, jobjectArray>) {
- return String("[Ljava/lang/Object;");
- } else if constexpr(std::is_same_v<T, jthrowable>) {
- return String("Ljava/lang/Throwable;");
- } else if constexpr(std::is_same_v<T, jbooleanArray>) {
- return String("[Z");
- } else if constexpr(std::is_same_v<T, jbyteArray>) {
- return String("[B");
- } else if constexpr(std::is_same_v<T, jshortArray>) {
- return String("[S");
- } else if constexpr(std::is_same_v<T, jintArray>) {
- return String("[I");
- } else if constexpr(std::is_same_v<T, jlongArray>) {
- return String("[J");
- } else if constexpr(std::is_same_v<T, jfloatArray>) {
- return String("[F");
- } else if constexpr(std::is_same_v<T, jdoubleArray>) {
- return String("[D");
- } else if constexpr(std::is_same_v<T, jcharArray>) {
- return String("[C");
- } else if constexpr(std::is_same_v<T, jboolean>) {
- return String("Z");
- } else if constexpr(std::is_same_v<T, bool>) {
- return String("Z");
- } else if constexpr(std::is_same_v<T, jbyte>) {
- return String("B");
- } else if constexpr(std::is_same_v<T, jchar>) {
- return String("C");
- } else if constexpr(std::is_same_v<T, char>) {
- return String("C");
- } else if constexpr(std::is_same_v<T, jshort>) {
- return String("S");
- } else if constexpr(std::is_same_v<T, short>) {
- return String("S");
- } else if constexpr(std::is_same_v<T, jint>) {
- return String("I");
- } else if constexpr(std::is_same_v<T, int>) {
- return String("I");
- } else if constexpr(std::is_same_v<T, uint>) {
- return String("I");
- } else if constexpr(std::is_same_v<T, jlong>) {
- return String("J");
- } else if constexpr(std::is_same_v<T, long>) {
- return String("J");
- } else if constexpr(std::is_same_v<T, jfloat>) {
- return String("F");
- } else if constexpr(std::is_same_v<T, float>) {
- return String("F");
- } else if constexpr(std::is_same_v<T, jdouble>) {
- return String("D");
- } else if constexpr(std::is_same_v<T, double>) {
- return String("D");
- } else if constexpr(std::is_same_v<T, void>) {
- return String("V");
- } else if constexpr(IsStringType<T>::value) {
- static_assert(!IsStringType<T>::value, "Don't use a literal type, call data!");
- } else {
- staticAssertTypeMismatch();
+ static QJniArray<T> fromVarArg(Type t)
+ {
+ return QJniArray<T>(t);
}
-}
-
-template<bool flag = false>
-static void staticAssertClassNotRegistered()
-{
- static_assert(flag, "Class not registered, use Q_DECLARE_JNI_CLASS");
-}
-
-template<typename T>
-constexpr auto className()
-{
- if constexpr(std::is_same<T, jstring>::value)
- return String("java/lang/String");
- else
- staticAssertClassNotRegistered();
-}
+};
-template<typename T>
-static constexpr bool isPrimitiveType()
+template <typename T>
+struct JNITypeForArgImpl<QList<T>>
{
- return typeSignature<T>().size() == 2;
-}
+private:
+ using ArrayType = decltype(QJniArrayBase::fromContainer(std::declval<QList<T>>()));
+ using ArrayObjectType = decltype(std::declval<ArrayType>().arrayObject());
+ using ElementType = typename ArrayType::value_type;
+public:
+ using Type = ArrayObjectType;
-template<typename T>
-static constexpr bool isObjectType()
-{
- if constexpr(std::is_convertible<T, jobject>::value) {
- return true;
- } else {
- constexpr auto signature = typeSignature<T>();
- return (signature.startsWith('L') || signature.startsWith('['))
- && signature.endsWith(';');
+ static QList<T> fromVarArg(Type t)
+ {
+ return QJniArray<ElementType>(t).toContainer();
}
-}
-
-template<typename T>
-static constexpr bool isArrayType()
-{
- constexpr auto signature = typeSignature<T>();
- return signature.startsWith('[');
-}
-
-template<typename T>
-static constexpr void assertPrimitiveType()
-{
- static_assert(isPrimitiveType<T>(), "Type needs to be a primitive JNI type!");
-}
-
-template<typename T>
-static constexpr void assertObjectType()
-{
- static_assert(isObjectType<T>(),
- "Type needs to be a JNI object type (convertible to jobject, or with "
- "an object type signature registered)!");
-}
-
-template<typename T>
-static constexpr void assertType()
-{
- static_assert(isPrimitiveType<T>() || isObjectType<T>(),
- "Type needs to be a JNI type!");
-}
+};
-template<typename R, typename ...Args>
-static constexpr auto methodSignature()
+template <typename Arg>
+using JNITypeForArg = typename JNITypeForArgImpl<std::decay_t<Arg>>::Type;
+template <typename Arg, typename Type>
+static inline auto methodArgFromVarArg(Type t) // Type comes from a va_arg, so is always POD
{
- return (String("(") +
- ... + typeSignature<std::decay_t<Args>>())
- + String(")")
- + typeSignature<R>();
+ return JNITypeForArgImpl<std::decay_t<Arg>>::fromVarArg(t);
}
-template<typename T>
-static constexpr auto fieldSignature()
+// Turn a va_list into a tuple of typed arguments
+template <typename ...Args>
+static constexpr auto makeTupleFromArgsHelper(va_list args)
{
- return QtJniTypes::typeSignature<T>();
+ return std::tuple(methodArgFromVarArg<Args>(va_arg(args, JNITypeForArg<Args>))...);
}
-template<typename ...Args>
-static constexpr auto constructorSignature()
+template <typename Ret, typename ...Args>
+static constexpr auto makeTupleFromArgs(Ret (*)(JNIEnv *, jobject, Args...), va_list args)
{
- return methodSignature<void, Args...>();
+ return makeTupleFromArgsHelper<Args...>(args);
}
-
-template<typename Ret, typename ...Args>
-static constexpr auto nativeMethodSignature(Ret (*)(JNIEnv *, jobject, Args...))
+template <typename Ret, typename ...Args>
+static constexpr auto makeTupleFromArgs(Ret (*)(JNIEnv *, jclass, Args...), va_list args)
{
- return methodSignature<Ret, Args...>();
+ return makeTupleFromArgsHelper<Args...>(args);
}
-template<typename Ret, typename ...Args>
-static constexpr auto nativeMethodSignature(Ret (*)(JNIEnv *, jclass, Args...))
+// Get the return type of a function point
+template <typename Ret, typename ...Args>
+auto nativeFunctionReturnType(Ret(*function)(Args...))
{
- return methodSignature<Ret, Args...>();
+ return function(std::declval<Args>()...);
}
-// A generic thin wrapper around jobject, convertible to jobject.
-// We need this as a baseclass so that QJniObject can be implicitly
-// constructed from the various subclasses - we can't provide an
-// operator QJniObject() here as the class is not declared.
-struct Object
-{
- jobject _object;
- constexpr operator jobject() const { return _object; }
-};
-
-} // namespace QtJniTypes
-
-#define Q_DECLARE_JNI_TYPE_HELPER(Type) \
-namespace QtJniTypes { \
-struct Type : Object \
-{ \
- constexpr Type(jobject o) noexcept : Object{o} {} \
-}; \
-} \
-
-
-#define Q_DECLARE_JNI_TYPE(Type, Signature) \
-Q_DECLARE_JNI_TYPE_HELPER(Type) \
-template<> \
-constexpr auto QtJniTypes::typeSignature<QtJniTypes::Type>() \
-{ \
- static_assert((Signature[0] == 'L' || Signature[0] == '[') \
- && Signature[sizeof(Signature) - 2] == ';', \
- "Type signature needs to start with 'L' or '['" \
- " and end with ';'"); \
- return QtJniTypes::String(Signature); \
-} \
-
-#define Q_DECLARE_JNI_CLASS(Type, Signature) \
-Q_DECLARE_JNI_TYPE_HELPER(Type) \
-template<> \
-constexpr auto QtJniTypes::className<QtJniTypes::Type>() \
-{ \
- return QtJniTypes::String(Signature); \
-} \
-template<> \
-constexpr auto QtJniTypes::typeSignature<QtJniTypes::Type>() \
-{ \
- return QtJniTypes::String("L") \
- + QtJniTypes::String(Signature) \
- + QtJniTypes::String(";"); \
-} \
-
-#define Q_DECLARE_JNI_NATIVE_METHOD(...) \
+} // namespace Detail
+} // namespace QtJniMethods
+
+// A va_ variadic arguments function that we register with JNI as a proxy
+// for the function we have. This function uses the helpers to unpack the
+// variadic arguments into a tuple of typed arguments, which we then call
+// the actual function with. This then takes care of implicit conversions,
+// e.g. a jobject becomes a QJniObject.
+#define Q_DECLARE_JNI_NATIVE_METHOD_HELPER(Method) \
+static decltype(QtJniMethods::Detail::nativeFunctionReturnType(Method)) \
+va_##Method(JNIEnv *env, jclass thiz, ...) \
+{ \
+ va_list args; \
+ va_start(args, thiz); \
+ auto va_cleanup = qScopeGuard([&args]{ va_end(args); }); \
+ auto argTuple = QtJniMethods::Detail::makeTupleFromArgs(Method, args); \
+ return std::apply([env, thiz](auto &&... args) { \
+ return Method(env, thiz, args...); \
+ }, argTuple); \
+} \
+
+#define Q_DECLARE_JNI_NATIVE_METHOD(...) \
QT_OVERLOADED_MACRO(QT_DECLARE_JNI_NATIVE_METHOD, __VA_ARGS__) \
#define QT_DECLARE_JNI_NATIVE_METHOD_2(Method, Name) \
namespace QtJniMethods { \
+Q_DECLARE_JNI_NATIVE_METHOD_HELPER(Method) \
static constexpr auto Method##_signature = \
QtJniTypes::nativeMethodSignature(Method); \
static const JNINativeMethod Method##_method = { \
#Name, Method##_signature.data(), \
- reinterpret_cast<void *>(Method) \
+ reinterpret_cast<void *>(va_##Method) \
}; \
} \
@@ -384,9 +200,10 @@ static const JNINativeMethod Method##_method = { \
QT_OVERLOADED_MACRO(QT_DECLARE_JNI_NATIVE_METHOD_IN_CURRENT_SCOPE, __VA_ARGS__) \
#define QT_DECLARE_JNI_NATIVE_METHOD_IN_CURRENT_SCOPE_2(Method, Name) \
+ Q_DECLARE_JNI_NATIVE_METHOD_HELPER(Method) \
static inline constexpr auto Method##_signature = QtJniTypes::nativeMethodSignature(Method); \
static inline const JNINativeMethod Method##_method = { \
- #Name, Method##_signature.data(), reinterpret_cast<void *>(Method) \
+ #Name, Method##_signature.data(), reinterpret_cast<void *>(va_##Method) \
};
#define QT_DECLARE_JNI_NATIVE_METHOD_IN_CURRENT_SCOPE_1(Method) \
@@ -396,6 +213,6 @@ static const JNINativeMethod Method##_method = { \
QT_END_NAMESPACE
-#endif
+#endif // defined(Q_QDOC) || defined(Q_OS_ANDROID)
#endif // QJNITYPES_H
diff --git a/src/corelib/kernel/qjnitypes_impl.h b/src/corelib/kernel/qjnitypes_impl.h
new file mode 100644
index 0000000000..d963509023
--- /dev/null
+++ b/src/corelib/kernel/qjnitypes_impl.h
@@ -0,0 +1,373 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QJNITYPES_IMPL_H
+#define QJNITYPES_IMPL_H
+
+#include <QtCore/qglobal.h>
+#include <QtCore/q20type_traits.h>
+
+#if defined(Q_QDOC) || defined(Q_OS_ANDROID)
+#include <jni.h>
+
+QT_BEGIN_NAMESPACE
+
+namespace QtJniTypes
+{
+
+// a constexpr type for string literals of any character width, aware of the length
+// of the string.
+template<size_t N_WITH_NULL, typename BaseType = char>
+struct CTString
+{
+ BaseType m_data[N_WITH_NULL] = {};
+
+ constexpr CTString() noexcept {}
+ // Can be instantiated (only) with a string literal
+ constexpr explicit CTString(const BaseType (&data)[N_WITH_NULL]) noexcept
+ {
+ for (size_t i = 0; i < N_WITH_NULL - 1; ++i)
+ m_data[i] = data[i];
+ }
+
+ constexpr BaseType at(size_t i) const { return m_data[i]; }
+ constexpr BaseType operator[](size_t i) const { return at(i); }
+ static constexpr size_t size() noexcept { return N_WITH_NULL; }
+ constexpr operator const BaseType *() const noexcept { return m_data; }
+ constexpr const BaseType *data() const noexcept { return m_data; }
+ template<size_t N2_WITH_NULL>
+ constexpr bool startsWith(const BaseType (&lit)[N2_WITH_NULL]) const noexcept
+ {
+ if constexpr (N2_WITH_NULL > N_WITH_NULL) {
+ return false;
+ } else {
+ for (size_t i = 0; i < N2_WITH_NULL - 1; ++i) {
+ if (m_data[i] != lit[i])
+ return false;
+ }
+ }
+ return true;
+ }
+ constexpr bool startsWith(BaseType c) const noexcept
+ {
+ return N_WITH_NULL > 1 && m_data[0] == c;
+ }
+ template<size_t N2_WITH_NULL>
+ constexpr bool endsWith(const BaseType (&lit)[N2_WITH_NULL]) const noexcept
+ {
+ if constexpr (N2_WITH_NULL > N_WITH_NULL) {
+ return false;
+ } else {
+ for (size_t i = 0; i < N2_WITH_NULL; ++i) {
+ if (m_data[N_WITH_NULL - i - 1] != lit[N2_WITH_NULL - i - 1])
+ return false;
+ }
+ }
+ return true;
+ }
+ constexpr bool endsWith(BaseType c) const noexcept
+ {
+ return N_WITH_NULL > 1 && m_data[N_WITH_NULL - 2] == c;
+ }
+
+ template<size_t N2_WITH_NULL>
+ friend inline constexpr bool operator==(const CTString<N_WITH_NULL> &lhs,
+ const CTString<N2_WITH_NULL> &rhs) noexcept
+ {
+ if constexpr (N_WITH_NULL != N2_WITH_NULL) {
+ return false;
+ } else {
+ for (size_t i = 0; i < N_WITH_NULL - 1; ++i) {
+ if (lhs.at(i) != rhs.at(i))
+ return false;
+ }
+ }
+ return true;
+ }
+
+ template<size_t N2_WITH_NULL>
+ friend inline constexpr bool operator!=(const CTString<N_WITH_NULL> &lhs,
+ const CTString<N2_WITH_NULL> &rhs) noexcept
+ {
+ return !operator==(lhs, rhs);
+ }
+
+ template<size_t N2_WITH_NULL>
+ friend inline constexpr bool operator==(const CTString<N_WITH_NULL> &lhs,
+ const BaseType (&rhs)[N2_WITH_NULL]) noexcept
+ {
+ return operator==(lhs, CTString<N2_WITH_NULL>(rhs));
+ }
+ template<size_t N2_WITH_NULL>
+ friend inline constexpr bool operator==(const BaseType (&lhs)[N2_WITH_NULL],
+ const CTString<N_WITH_NULL> &rhs) noexcept
+ {
+ return operator==(CTString<N2_WITH_NULL>(lhs), rhs);
+ }
+
+ template<size_t N2_WITH_NULL>
+ friend inline constexpr bool operator!=(const CTString<N_WITH_NULL> &lhs,
+ const BaseType (&rhs)[N2_WITH_NULL]) noexcept
+ {
+ return operator!=(lhs, CTString<N2_WITH_NULL>(rhs));
+ }
+ template<size_t N2_WITH_NULL>
+ friend inline constexpr bool operator!=(const BaseType (&lhs)[N2_WITH_NULL],
+ const CTString<N_WITH_NULL> &rhs) noexcept
+ {
+ return operator!=(CTString<N2_WITH_NULL>(lhs), rhs);
+ }
+
+ template<size_t N2_WITH_NULL>
+ friend inline constexpr auto operator+(const CTString<N_WITH_NULL> &lhs,
+ const CTString<N2_WITH_NULL> &rhs) noexcept
+ {
+ char data[N_WITH_NULL + N2_WITH_NULL - 1] = {};
+ for (size_t i = 0; i < N_WITH_NULL - 1; ++i)
+ data[i] = lhs[i];
+ for (size_t i = 0; i < N2_WITH_NULL - 1; ++i)
+ data[N_WITH_NULL - 1 + i] = rhs[i];
+ return CTString<N_WITH_NULL + N2_WITH_NULL - 1>(data);
+ }
+};
+
+// Helper types that allow us to disable variadic overloads that would conflict
+// with overloads that take a const char*.
+template<typename T, size_t N = 0> struct IsStringType : std::false_type {};
+template<> struct IsStringType<const char *, 0> : std::true_type {};
+template<> struct IsStringType<const char *&, 0> : std::true_type {};
+template<size_t N> struct IsStringType<CTString<N>> : std::true_type {};
+template<size_t N> struct IsStringType<const char[N]> : std::true_type {};
+template<size_t N> struct IsStringType<const char(&)[N]> : std::true_type {};
+template<size_t N> struct IsStringType<char[N]> : std::true_type {};
+
+template <typename T>
+struct Traits {
+ // The return type of className/signature becomes void for any type
+ // not handled here. This indicates that the Traits type is not specialized
+ // for the respective type, which we use to detect invalid types in the
+ // IfValidSignatureTypes and IfValidFieldType predicates below.
+
+ static constexpr auto className()
+ {
+ if constexpr (std::is_same_v<T, jstring>)
+ return CTString("java/lang/String");
+ else if constexpr (std::is_same_v<T, jobject>)
+ return CTString("java/lang/Object");
+ else if constexpr (std::is_same_v<T, jclass>)
+ return CTString("java/lang/Class");
+ else if constexpr (std::is_same_v<T, jthrowable>)
+ return CTString("java/lang/Throwable");
+ // else: return void -> not implemented
+ }
+
+ static constexpr auto signature()
+ {
+ if constexpr (!std::is_same_v<decltype(className()), void>) {
+ // the type signature of any object class is L<className>;
+ return CTString("L") + className() + CTString(";");
+ } else if constexpr (std::is_array_v<T>) {
+ using UnderlyingType = typename std::remove_extent_t<T>;
+ static_assert(!std::is_array_v<UnderlyingType>,
+ "Traits::signature() does not handle multi-dimensional arrays");
+ return CTString("[") + Traits<UnderlyingType>::signature();
+ } else if constexpr (std::is_same_v<T, jobjectArray>) {
+ return CTString("[Ljava/lang/Object;");
+ } else if constexpr (std::is_same_v<T, jbooleanArray>) {
+ return CTString("[Z");
+ } else if constexpr (std::is_same_v<T, jbyteArray>) {
+ return CTString("[B");
+ } else if constexpr (std::is_same_v<T, jshortArray>) {
+ return CTString("[S");
+ } else if constexpr (std::is_same_v<T, jintArray>) {
+ return CTString("[I");
+ } else if constexpr (std::is_same_v<T, jlongArray>) {
+ return CTString("[J");
+ } else if constexpr (std::is_same_v<T, jfloatArray>) {
+ return CTString("[F");
+ } else if constexpr (std::is_same_v<T, jdoubleArray>) {
+ return CTString("[D");
+ } else if constexpr (std::is_same_v<T, jcharArray>) {
+ return CTString("[C");
+ } else if constexpr (std::is_same_v<T, jboolean>) {
+ return CTString("Z");
+ } else if constexpr (std::is_same_v<T, bool>) {
+ return CTString("Z");
+ } else if constexpr (std::is_same_v<T, jbyte>) {
+ return CTString("B");
+ } else if constexpr (std::is_same_v<T, jchar>) {
+ return CTString("C");
+ } else if constexpr (std::is_same_v<T, char>) {
+ return CTString("C");
+ } else if constexpr (std::is_same_v<T, jshort>) {
+ return CTString("S");
+ } else if constexpr (std::is_same_v<T, short>) {
+ return CTString("S");
+ } else if constexpr (std::is_same_v<T, jint>) {
+ return CTString("I");
+ } else if constexpr (std::is_same_v<T, int>) {
+ return CTString("I");
+ } else if constexpr (std::is_same_v<T, uint>) {
+ return CTString("I");
+ } else if constexpr (std::is_same_v<T, jlong>) {
+ return CTString("J");
+ } else if constexpr (std::is_same_v<T, quint64>) {
+ return CTString("J");
+ } else if constexpr (std::is_same_v<T, jfloat>) {
+ return CTString("F");
+ } else if constexpr (std::is_same_v<T, float>) {
+ return CTString("F");
+ } else if constexpr (std::is_same_v<T, jdouble>) {
+ return CTString("D");
+ } else if constexpr (std::is_same_v<T, double>) {
+ return CTString("D");
+ } else if constexpr (std::is_same_v<T, void>) {
+ return CTString("V");
+ } else if constexpr (std::is_enum_v<T>) {
+ return Traits<std::underlying_type_t<T>>::signature();
+ } else if constexpr (std::is_same_v<T, QString>) {
+ return CTString("Ljava/lang/String;");
+ }
+ // else: return void -> not implemented
+ }
+};
+
+template <typename Have, typename Want>
+static constexpr bool sameTypeForJni = (QtJniTypes::Traits<Have>::signature()
+ == QtJniTypes::Traits<Want>::signature())
+ && (sizeof(Have) == sizeof(Want));
+
+template <typename, typename = void>
+struct Caller
+{};
+
+#define MAKE_CALLER(Type, Method) \
+template <typename T> \
+struct Caller<T, std::enable_if_t<sameTypeForJni<T, Type>>> \
+{ \
+ static constexpr void callMethodForType(JNIEnv *env, T &res, jobject obj, jmethodID id, va_list args) \
+ { \
+ res = T(env->Call##Method##MethodV(obj, id, args)); \
+ } \
+ static constexpr void callStaticMethodForType(JNIEnv *env, T &res, jclass clazz, jmethodID id, va_list args) \
+ { \
+ res = T(env->CallStatic##Method##MethodV(clazz, id, args)); \
+ } \
+ static constexpr void getFieldForType(JNIEnv *env, T &res, jobject obj, jfieldID id) \
+ { \
+ res = T(env->Get##Method##Field(obj, id)); \
+ } \
+ static constexpr void getStaticFieldForType(JNIEnv *env, T &res, jclass clazz, jfieldID id) \
+ { \
+ res = T(env->GetStatic##Method##Field(clazz, id)); \
+ } \
+ static constexpr void setFieldForType(JNIEnv *env, jobject obj, jfieldID id, T value) \
+ { \
+ env->Set##Method##Field(obj, id, static_cast<Type>(value)); \
+ } \
+ static constexpr void setStaticFieldForType(JNIEnv *env, jclass clazz, jfieldID id, T value) \
+ { \
+ env->SetStatic##Method##Field(clazz, id, static_cast<Type>(value)); \
+ } \
+}
+
+MAKE_CALLER(jboolean, Boolean);
+MAKE_CALLER(jbyte, Byte);
+MAKE_CALLER(jchar, Char);
+MAKE_CALLER(jshort, Short);
+MAKE_CALLER(jint, Int);
+MAKE_CALLER(jlong, Long);
+MAKE_CALLER(jfloat, Float);
+MAKE_CALLER(jdouble, Double);
+
+#undef MAKE_CALLER
+
+template<typename T>
+static constexpr bool isPrimitiveType()
+{
+ return Traits<T>::signature().size() == 2;
+}
+
+template<typename T>
+static constexpr bool isArrayType()
+{
+ constexpr auto signature = Traits<T>::signature();
+ return signature.startsWith('[') && signature.size() > 2;
+}
+
+template<typename T>
+static constexpr bool isObjectType()
+{
+ if constexpr (std::is_convertible_v<T, jobject>) {
+ return true;
+ } else {
+ constexpr auto signature = Traits<T>::signature();
+ return (signature.startsWith('L') && signature.endsWith(';')) || isArrayType<T>();
+ }
+}
+
+template<typename T>
+static constexpr void assertObjectType()
+{
+ static_assert(isObjectType<T>(),
+ "Type needs to be a JNI object type (convertible to jobject, or with "
+ "an object type signature registered)!");
+}
+
+// A set of types is valid if Traits::signature is implemented for all of them
+template<typename ...Types>
+constexpr bool ValidSignatureTypesDetail = !std::disjunction<std::is_same<
+ decltype(Traits<Types>::signature()),
+ void>...,
+ IsStringType<Types>...>::value;
+template<typename ...Types>
+using IfValidSignatureTypes = std::enable_if_t<
+ ValidSignatureTypesDetail<q20::remove_cvref_t<Types>...>, bool>;
+
+template<typename Type>
+constexpr bool ValidFieldTypeDetail = isObjectType<Type>() || isPrimitiveType<Type>();
+template<typename Type>
+using IfValidFieldType = std::enable_if_t<
+ ValidFieldTypeDetail<q20::remove_cvref_t<Type>>, bool>;
+
+
+template<typename R, typename ...Args, IfValidSignatureTypes<R, Args...> = true>
+static constexpr auto methodSignature()
+{
+ return (CTString("(") +
+ ... + Traits<q20::remove_cvref_t<Args>>::signature())
+ + CTString(")")
+ + Traits<R>::signature();
+}
+
+template<typename T, IfValidSignatureTypes<T> = true>
+static constexpr auto fieldSignature()
+{
+ return QtJniTypes::Traits<T>::signature();
+}
+
+template<typename ...Args, IfValidSignatureTypes<Args...> = true>
+static constexpr auto constructorSignature()
+{
+ return methodSignature<void, Args...>();
+}
+
+template<typename Ret, typename ...Args, IfValidSignatureTypes<Ret, Args...> = true>
+static constexpr auto nativeMethodSignature(Ret (*)(JNIEnv *, jobject, Args...))
+{
+ return methodSignature<Ret, Args...>();
+}
+
+template<typename Ret, typename ...Args, IfValidSignatureTypes<Ret, Args...> = true>
+static constexpr auto nativeMethodSignature(Ret (*)(JNIEnv *, jclass, Args...))
+{
+ return methodSignature<Ret, Args...>();
+}
+
+} // namespace QtJniTypes
+
+QT_END_NAMESPACE
+
+#endif
+
+#endif // QJNITYPES_IMPL_H
diff --git a/src/corelib/kernel/qmath.h b/src/corelib/kernel/qmath.h
index b1e5b4f17e..72057ee16d 100644
--- a/src/corelib/kernel/qmath.h
+++ b/src/corelib/kernel/qmath.h
@@ -370,6 +370,16 @@ constexpr inline quint64 qNextPowerOfTwo(qint64 v)
return qNextPowerOfTwo(quint64(v));
}
+constexpr inline unsigned long qNextPowerOfTwo(unsigned long v)
+{
+ return qNextPowerOfTwo(QIntegerForSizeof<long>::Unsigned(v));
+}
+
+constexpr inline unsigned long qNextPowerOfTwo(long v)
+{
+ return qNextPowerOfTwo(QIntegerForSizeof<long>::Unsigned(v));
+}
+
QT_END_NAMESPACE
#endif // QMATH_H
diff --git a/src/corelib/kernel/qmath.qdoc b/src/corelib/kernel/qmath.qdoc
index c15a20d94f..bc365f26fa 100644
--- a/src/corelib/kernel/qmath.qdoc
+++ b/src/corelib/kernel/qmath.qdoc
@@ -132,7 +132,7 @@
\since 6.1
\overload
\fn template <typename Tx, typename Ty> auto qHypot(Tx x, Ty y)
- Returns the distance of a point (x, y) from the origin (0, 0).
+ Returns the distance of a point (\a x, \a y) from the origin (0, 0).
This is qSqrt(x * x + y * y), optimized.
In particular, underflow and overflow may be avoided.
diff --git a/src/corelib/kernel/qmetacontainer.cpp b/src/corelib/kernel/qmetacontainer.cpp
index 200724c9f4..5f68f8fe74 100644
--- a/src/corelib/kernel/qmetacontainer.cpp
+++ b/src/corelib/kernel/qmetacontainer.cpp
@@ -14,6 +14,8 @@ QT_BEGIN_NAMESPACE
\ingroup objectmodel
+ \compares equality
+
The class provides a number of primitive container operations, using void*
as operands. This way, you can manipulate a generic container retrieved from
a Variant without knowing its type.
@@ -790,21 +792,19 @@ void QMetaSequence::valueAtConstIterator(const void *iterator, void *result) con
}
/*!
- \fn bool operator==(QMetaSequence a, QMetaSequence b)
+ \fn bool QMetaSequence::operator==(const QMetaSequence &lhs, const QMetaSequence &rhs)
\since 6.0
- \relates QMetaSequence
- Returns \c true if the QMetaSequence \a a represents the same container type
- as the QMetaSequence \a b, otherwise returns \c false.
+ Returns \c true if the QMetaSequence \a lhs represents the same container type
+ as the QMetaSequence \a rhs, otherwise returns \c false.
*/
/*!
- \fn bool operator!=(QMetaSequence a, QMetaSequence b)
+ \fn bool QMetaSequence::operator!=(const QMetaSequence &lhs, const QMetaSequence &rhs)
\since 6.0
- \relates QMetaSequence
- Returns \c true if the QMetaSequence \a a represents a different container
- type than the QMetaSequence \a b, otherwise returns \c false.
+ Returns \c true if the QMetaSequence \a lhs represents a different container
+ type than the QMetaSequence \a rhs, otherwise returns \c false.
*/
diff --git a/src/corelib/kernel/qmetacontainer.h b/src/corelib/kernel/qmetacontainer.h
index 68d62bd2ea..1bed7f9f7b 100644
--- a/src/corelib/kernel/qmetacontainer.h
+++ b/src/corelib/kernel/qmetacontainer.h
@@ -5,9 +5,12 @@
#define QMETACONTAINER_H
#include <QtCore/qcontainerinfo.h>
+#include <QtCore/qcompare.h>
#include <QtCore/qflags.h>
#include <QtCore/qglobal.h>
+#include <iterator>
+
QT_BEGIN_NAMESPACE
class QMetaType;
@@ -973,16 +976,15 @@ public:
bool canGetValueAtConstIterator() const;
void valueAtConstIterator(const void *iterator, void *result) const;
- friend bool operator==(const QMetaSequence &a, const QMetaSequence &b)
- {
- return a.d() == b.d();
- }
- friend bool operator!=(const QMetaSequence &a, const QMetaSequence &b)
+ const QtMetaContainerPrivate::QMetaSequenceInterface *iface() const { return d(); }
+
+private:
+ friend bool comparesEqual(const QMetaSequence &lhs, const QMetaSequence &rhs) noexcept
{
- return a.d() != b.d();
+ return lhs.d() == rhs.d();
}
+ Q_DECLARE_EQUALITY_COMPARABLE(QMetaSequence)
-private:
template<typename T>
struct MetaSequence
{
@@ -1167,16 +1169,15 @@ public:
return nullptr;
}
- friend bool operator==(const QMetaAssociation &a, const QMetaAssociation &b)
- {
- return a.d() == b.d();
- }
- friend bool operator!=(const QMetaAssociation &a, const QMetaAssociation &b)
+ const QtMetaContainerPrivate::QMetaAssociationInterface *iface() const { return d(); }
+
+private:
+ friend bool comparesEqual(const QMetaAssociation &lhs, const QMetaAssociation &rhs) noexcept
{
- return a.d() != b.d();
+ return lhs.d() == rhs.d();
}
+ Q_DECLARE_EQUALITY_COMPARABLE(QMetaAssociation)
-private:
template<typename T>
struct MetaAssociation
{
diff --git a/src/corelib/kernel/qmetaobject.cpp b/src/corelib/kernel/qmetaobject.cpp
index acce8572ad..05662b385a 100644
--- a/src/corelib/kernel/qmetaobject.cpp
+++ b/src/corelib/kernel/qmetaobject.cpp
@@ -3,26 +3,23 @@
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qmetaobject.h"
-#include "qmetatype.h"
-#include "qobject.h"
#include "qmetaobject_p.h"
+#include "qmetatype.h"
#include "qmetatype_p.h"
+#include "qobject.h"
+#include "qobject_p.h"
#include <qcoreapplication.h>
-#include <qcoreevent.h>
-#include <qdatastream.h>
-#include <qstringlist.h>
-#include <qthread.h>
#include <qvariant.h>
-#include <qdebug.h>
+
+// qthread(_p).h uses QT_CONFIG(thread) internally and has a dummy
+// interface for the non-thread support case
+#include <qthread.h>
+#include "private/qthread_p.h"
#if QT_CONFIG(thread)
#include <qsemaphore.h>
#endif
-#include "private/qobject_p.h"
-#include "private/qmetaobject_p.h"
-#include "private/qthread_p.h"
-
// for normalizeTypeInternal
#include "private/qmetaobject_moc_p.h"
@@ -97,12 +94,17 @@ using namespace Qt::StringLiterals;
\internal
- \value InvokeSlot
- \value EmitSignal
+ \value InvokeMetaMethod
\value ReadProperty
\value WriteProperty
\value ResetProperty
\value CreateInstance
+ \value IndexOfMethod
+ \value RegisterPropertyMetaType
+ \value RegisterMethodArgumentMetaType
+ \value BindableProperty
+ \value CustomCall
+ \value ConstructInPlace
*/
/*!
@@ -125,7 +127,7 @@ static inline const char *rawStringData(const QMetaObject *mo, int index)
return reinterpret_cast<const char *>(mo->d.stringdata) + offset;
}
-static inline QLatin1StringView stringDataView(const QMetaObject *mo, int index)
+static inline QByteArrayView stringDataView(const QMetaObject *mo, int index)
{
Q_ASSERT(priv(mo->d.data)->revision >= 7);
uint offset = mo->d.stringdata[2*index];
@@ -140,22 +142,12 @@ static inline QByteArray stringData(const QMetaObject *mo, int index)
return QByteArray::fromRawData(view.data(), view.size());
}
-static inline const char *rawTypeNameFromTypeInfo(const QMetaObject *mo, uint typeInfo)
+static inline QByteArrayView typeNameFromTypeInfo(const QMetaObject *mo, uint typeInfo)
{
- if (typeInfo & IsUnresolvedType) {
- return rawStringData(mo, typeInfo & TypeNameIndexMask);
- } else {
- return QMetaType(typeInfo).name();
- }
-}
-
-static inline QByteArray typeNameFromTypeInfo(const QMetaObject *mo, uint typeInfo)
-{
- if (typeInfo & IsUnresolvedType) {
- return stringData(mo, typeInfo & TypeNameIndexMask);
- } else {
- return QMetaType(typeInfo).name();
- }
+ if (typeInfo & IsUnresolvedType)
+ return stringDataView(mo, typeInfo & TypeNameIndexMask);
+ else
+ return QByteArrayView(QMetaType(typeInfo).name());
}
static inline int typeFromTypeInfo(const QMetaObject *mo, uint typeInfo)
@@ -165,6 +157,19 @@ static inline int typeFromTypeInfo(const QMetaObject *mo, uint typeInfo)
return QMetaType::fromName(rawStringData(mo, typeInfo & TypeNameIndexMask)).id();
}
+static auto parse_scope(QByteArrayView qualifiedKey) noexcept
+{
+ struct R {
+ std::optional<QByteArrayView> scope;
+ QByteArrayView key;
+ };
+ const auto scopePos = qualifiedKey.lastIndexOf("::"_L1);
+ if (scopePos < 0)
+ return R{std::nullopt, qualifiedKey};
+ else
+ return R{qualifiedKey.first(scopePos), qualifiedKey.sliced(scopePos + 2)};
+}
+
namespace {
class QMetaMethodPrivate : public QMetaMethodInvoker
{
@@ -199,6 +204,7 @@ private:
enum { MaximumParamCount = 11 }; // up to 10 arguments + 1 return value
+#if QT_VERSION < QT_VERSION_CHECK(7, 0, 0)
/*!
\since 4.5
\obsolete [6.5] Please use the variadic overload of this function
@@ -244,6 +250,7 @@ QObject *QMetaObject::newInstance(QGenericArgument val0,
return newInstanceImpl(this, paramCount, parameters, typeNames, nullptr);
}
+#endif
/*!
\fn template <typename... Args> QObject *QMetaObject::newInstance(Args &&... arguments) const
@@ -327,9 +334,9 @@ int QMetaObject::metacall(QObject *object, Call cl, int idx, void **argv)
return object->qt_metacall(cl, idx, argv);
}
-static inline const char *objectClassName(const QMetaObject *m)
+static inline QByteArrayView objectClassName(const QMetaObject *m)
{
- return rawStringData(m, priv(m->d.data)->className);
+ return stringDataView(m, priv(m->d.data)->className);
}
/*!
@@ -339,7 +346,7 @@ static inline const char *objectClassName(const QMetaObject *m)
*/
const char *QMetaObject::className() const
{
- return objectClassName(this);
+ return objectClassName(this).constData();
}
/*!
@@ -394,7 +401,7 @@ const QObject *QMetaObject::cast(const QObject *obj) const
*/
QString QMetaObject::tr(const char *s, const char *c, int n) const
{
- return QCoreApplication::translate(objectClassName(this), s, c, n);
+ return QCoreApplication::translate(className(), s, c, n);
}
#endif // QT_NO_TRANSLATION
@@ -412,10 +419,30 @@ QMetaType QMetaObject::metaType() const
return QMetaType::fromName(className());
} else {
/* in the metatype array, we store
- idx: 0 propertyCount - 1 propertyCount
- data:QMetaType(prop0), ..., QMetaType(propPropCount-1), QMetaType(class),...
- */
- auto iface = this->d.metaTypes[d->propertyCount];
+
+ | index | data |
+ |----------------------------------------------------------------------|
+ | 0 | QMetaType(property0) |
+ | ... | ... |
+ | propertyCount - 1 | QMetaType(propertyCount - 1) |
+ | propertyCount | QMetaType(enumerator0) |
+ | ... | ... |
+ | propertyCount + enumeratorCount - 1 | QMetaType(enumeratorCount - 1) |
+ | propertyCount + enumeratorCount | QMetaType(class) |
+
+ */
+#if QT_VERSION < QT_VERSION_CHECK(7, 0, 0)
+ // Before revision 12 we only stored metatypes for enums if they showed
+ // up as types of properties or method arguments or return values.
+ // From revision 12 on, we always store them in a predictable place.
+ const qsizetype offset = d->revision < 12
+ ? d->propertyCount
+ : d->propertyCount + d->enumeratorCount;
+#else
+ const qsizetype offset = d->propertyCount + d->enumeratorCount;
+#endif
+
+ auto iface = this->d.metaTypes[offset];
if (iface && QtMetaTypePrivate::isInterfaceFor<void>(iface))
return QMetaType(); // return invalid meta-type for namespaces
if (iface)
@@ -806,7 +833,7 @@ int QMetaObjectPrivate::indexOfSignalRelative(const QMetaObject **baseObject,
QMetaMethod conflictMethod = m->d.superdata->method(conflict);
qWarning("QMetaObject::indexOfSignal: signal %s from %s redefined in %s",
conflictMethod.methodSignature().constData(),
- objectClassName(m->d.superdata), objectClassName(m));
+ m->d.superdata->className(), m->className());
}
}
#endif
@@ -991,8 +1018,8 @@ bool QMetaObjectPrivate::checkConnectArgs(const QMetaMethodPrivate *signal,
uint targetTypeInfo = method->parameterTypeInfo(i);
if ((sourceTypeInfo & IsUnresolvedType)
|| (targetTypeInfo & IsUnresolvedType)) {
- QByteArray sourceName = typeNameFromTypeInfo(smeta, sourceTypeInfo);
- QByteArray targetName = typeNameFromTypeInfo(rmeta, targetTypeInfo);
+ QByteArrayView sourceName = typeNameFromTypeInfo(smeta, sourceTypeInfo);
+ QByteArrayView targetName = typeNameFromTypeInfo(rmeta, targetTypeInfo);
if (sourceName != targetName)
return false;
} else {
@@ -1005,10 +1032,10 @@ bool QMetaObjectPrivate::checkConnectArgs(const QMetaMethodPrivate *signal,
return true;
}
-static const QMetaObject *QMetaObject_findMetaObject(const QMetaObject *self, const char *name)
+static const QMetaObject *QMetaObject_findMetaObject(const QMetaObject *self, QByteArrayView name)
{
while (self) {
- if (strcmp(objectClassName(self), name) == 0)
+ if (objectClassName(self) == name)
return self;
if (self->d.relatedMetaObjects) {
Q_ASSERT(priv(self->d.data)->revision >= 2);
@@ -1034,27 +1061,28 @@ static const QMetaObject *QMetaObject_findMetaObject(const QMetaObject *self, co
*/
int QMetaObject::indexOfEnumerator(const char *name) const
{
- const QMetaObject *m = this;
- while (m) {
- const QMetaObjectPrivate *d = priv(m->d.data);
- for (int i = 0; i < d->enumeratorCount; ++i) {
- const QMetaEnum e(m, i);
- const char *prop = rawStringData(m, e.data.name());
- if (name[0] == prop[0] && strcmp(name + 1, prop + 1) == 0) {
- i += m->enumeratorOffset();
- return i;
- }
- }
- m = m->d.superdata;
+ return QMetaObjectPrivate::indexOfEnumerator(this, name);
+}
+
+int QMetaObjectPrivate::indexOfEnumerator(const QMetaObject *m, QByteArrayView name)
+{
+ using W = QMetaObjectPrivate::Which;
+ for (auto which : { W::Name, W::Alias }) {
+ if (int index = indexOfEnumerator(m, name, which); index != -1)
+ return index;
}
- // Check alias names:
- m = this;
+ return -1;
+}
+
+int QMetaObjectPrivate::indexOfEnumerator(const QMetaObject *m, QByteArrayView name, Which which)
+{
while (m) {
const QMetaObjectPrivate *d = priv(m->d.data);
for (int i = 0; i < d->enumeratorCount; ++i) {
const QMetaEnum e(m, i);
- const char *prop = rawStringData(m, e.data.alias());
- if (name[0] == prop[0] && strcmp(name + 1, prop + 1) == 0) {
+ const quint32 id = which == Which::Name ? e.data.name() : e.data.alias();
+ QByteArrayView prop = stringDataView(m, id);
+ if (name == prop) {
i += m->enumeratorOffset();
return i;
}
@@ -1078,7 +1106,7 @@ int QMetaObject::indexOfProperty(const char *name) const
for (int i = 0; i < d->propertyCount; ++i) {
const QMetaProperty::Data data = QMetaProperty::getMetaPropertyData(m, i);
const char *prop = rawStringData(m, data.name());
- if (name[0] == prop[0] && strcmp(name + 1, prop + 1) == 0) {
+ if (strcmp(name, prop) == 0) {
i += m->propertyOffset();
return i;
}
@@ -1364,7 +1392,7 @@ QByteArray QMetaObject::normalizedSignature(const char *method)
}
Q_DECL_COLD_FUNCTION static inline bool
-printMethodNotFoundWarning(const QMetaObject *meta, QLatin1StringView name, qsizetype paramCount,
+printMethodNotFoundWarning(const QMetaObject *meta, QByteArrayView name, qsizetype paramCount,
const char *const *names,
const QtPrivate::QMetaTypeInterface * const *metaTypes)
{
@@ -1372,7 +1400,7 @@ printMethodNotFoundWarning(const QMetaObject *meta, QLatin1StringView name, qsiz
QByteArray candidateMessage;
for (int i = 0; i < meta->methodCount(); ++i) {
const QMetaMethod method = meta->method(i);
- if (method.name() == QByteArrayView(name))
+ if (method.name() == name)
candidateMessage += " " + method.methodSignature() + '\n';
}
if (!candidateMessage.isEmpty()) {
@@ -1399,8 +1427,8 @@ printMethodNotFoundWarning(const QMetaObject *meta, QLatin1StringView name, qsiz
}
/*!
- \fn template <typename... Args> bool QMetaObject::invokeMethod(QObject *obj, const char *member, Qt::ConnectionType type, QMetaMethodReturnArgument r, Args &&... args)
- \fn template <typename... Args> bool QMetaObject::invokeMethod(QObject *obj, const char *member, QMetaMethodReturnArgument r, Args &&... args)
+ \fn template <typename ReturnArg, typename... Args> bool QMetaObject::invokeMethod(QObject *obj, const char *member, Qt::ConnectionType type, QTemplatedMetaMethodReturnArgument<ReturnArg> ret, Args &&... args)
+ \fn template <typename ReturnArg, typename... Args> bool QMetaObject::invokeMethod(QObject *obj, const char *member, QTemplatedMetaMethodReturnArgument<ReturnArg> ret, Args &&... args)
\fn template <typename... Args> bool QMetaObject::invokeMethod(QObject *obj, const char *member, Qt::ConnectionType type, Args &&... args)
\fn template <typename... Args> bool QMetaObject::invokeMethod(QObject *obj, const char *member, Args &&... args)
\since 6.5
@@ -1410,11 +1438,12 @@ printMethodNotFoundWarning(const QMetaObject *meta, QLatin1StringView name, qsiz
obj. Returns \c true if the member could be invoked. Returns \c false
if there is no such member or the parameters did not match.
- For the overloads with a QMetaMethodReturnArgument parameter, the return
- value of the \a member function call is placed in \a ret. For the overloads
- without such a member, the return value of the called function (if any)
- will be discarded. QMetaMethodReturnArgument is an internal type you should
- not use directly. Instead, use the qReturnArg() function.
+ For the overloads with a QTemplatedMetaMethodReturnArgument parameter, the
+ return value of the \a member function call is placed in \a ret. For the
+ overloads without such a member, the return value of the called function
+ (if any) will be discarded. QTemplatedMetaMethodReturnArgument is an
+ internal type you should not use directly. Instead, use the qReturnArg()
+ function.
The overloads with a Qt::ConnectionType \a type parameter allow explicitly
selecting whether the invocation will be synchronous or not:
@@ -1557,7 +1586,7 @@ bool QMetaObject::invokeMethodImpl(QObject *obj, const char *member, Qt::Connect
Q_ASSERT(typeNames);
// find the method
- QLatin1StringView name(member);
+ QByteArrayView name(member);
if (name.isEmpty())
return false;
@@ -1584,15 +1613,16 @@ bool QMetaObject::invokeMethodImpl(QObject *obj, const char *member, Qt::Connect
return printMethodNotFoundWarning(obj->metaObject(), name, paramCount, typeNames, metaTypes);
}
-bool QMetaObject::invokeMethodImpl(QObject *object, QtPrivate::QSlotObjectBase *slot, Qt::ConnectionType type, void *ret)
+bool QMetaObject::invokeMethodImpl(QObject *object, QtPrivate::QSlotObjectBase *slotObj, Qt::ConnectionType type,
+ qsizetype parameterCount, const void *const *params, const char *const *names,
+ const QtPrivate::QMetaTypeInterface * const *metaTypes)
{
- struct Holder {
- QtPrivate::QSlotObjectBase *obj;
- ~Holder() { obj->destroyIfLastRef(); }
- } holder = { slot };
- Q_UNUSED(holder);
+ // We don't need this now but maybe we want it later, or we may be able to
+ // share more code between the two invokeMethodImpl() overloads:
+ Q_UNUSED(names);
+ auto slot = QtPrivate::SlotObjUniquePtr(slotObj);
- if (! object)
+ if (! object) // ### only if the slot requires the object + not queued?
return false;
Qt::HANDLE currentThreadId = QThread::currentThreadId();
@@ -1604,8 +1634,7 @@ bool QMetaObject::invokeMethodImpl(QObject *object, QtPrivate::QSlotObjectBase *
if (type == Qt::AutoConnection)
type = receiverInSameThread ? Qt::DirectConnection : Qt::QueuedConnection;
- void *argv[] = { ret };
-
+ void **argv = const_cast<void **>(params);
if (type == Qt::DirectConnection) {
slot->call(object, argv);
} else if (type == Qt::QueuedConnection) {
@@ -1614,15 +1643,23 @@ bool QMetaObject::invokeMethodImpl(QObject *object, QtPrivate::QSlotObjectBase *
"queued connections");
return false;
}
+ auto event = std::make_unique<QMetaCallEvent>(std::move(slot), nullptr, -1, parameterCount);
+ void **args = event->args();
+ QMetaType *types = event->types();
- QCoreApplication::postEvent(object, new QMetaCallEvent(slot, nullptr, -1, 1));
+ for (int i = 1; i < parameterCount; ++i) {
+ types[i] = QMetaType(metaTypes[i]);
+ args[i] = types[i].create(argv[i]);
+ }
+
+ QCoreApplication::postEvent(object, event.release());
} else if (type == Qt::BlockingQueuedConnection) {
#if QT_CONFIG(thread)
if (receiverInSameThread)
qWarning("QMetaObject::invokeMethod: Dead lock detected");
QSemaphore semaphore;
- QCoreApplication::postEvent(object, new QMetaCallEvent(slot, nullptr, -1, argv, &semaphore));
+ QCoreApplication::postEvent(object, new QMetaCallEvent(std::move(slot), nullptr, -1, argv, &semaphore));
semaphore.acquire();
#endif // QT_CONFIG(thread)
} else {
@@ -1693,31 +1730,44 @@ bool QMetaObject::invokeMethodImpl(QObject *object, QtPrivate::QSlotObjectBase *
*/
/*!
- \fn template<typename Functor, typename FunctorReturnType> bool QMetaObject::invokeMethod(QObject *context, Functor function, Qt::ConnectionType type, FunctorReturnType *ret)
+ \fn template<typename Functor, typename FunctorReturnType> bool QMetaObject::invokeMethod(QObject *context, Functor &&function, Qt::ConnectionType type, FunctorReturnType *ret)
+ \fn template<typename Functor, typename FunctorReturnType> bool QMetaObject::invokeMethod(QObject *context, Functor &&function, FunctorReturnType *ret)
\since 5.10
-
\threadsafe
- \overload
Invokes the \a function in the event loop of \a context. \a function can be a functor
or a pointer to a member function. Returns \c true if the function could be invoked.
Returns \c false if there is no such function or the parameters did not match.
The return value of the function call is placed in \a ret.
+
+ If \a type is set, then the function is invoked using that connection type. Otherwise,
+ Qt::AutoConnection will be used.
*/
/*!
- \fn template<typename Functor, typename FunctorReturnType> bool QMetaObject::invokeMethod(QObject *context, Functor function, FunctorReturnType *ret)
-
- \since 5.10
+ \fn template<typename Functor, typename FunctorReturnType, typename... Args> bool QMetaObject::invokeMethod(QObject *context, Functor &&function, Qt::ConnectionType type, QTemplatedMetaMethodReturnArgument<FunctorReturnType> ret, Args &&...arguments)
+ \fn template<typename Functor, typename FunctorReturnType, typename... Args> bool QMetaObject::invokeMethod(QObject *context, Functor &&function, QTemplatedMetaMethodReturnArgument<FunctorReturnType> ret, Args &&...arguments)
+ \fn template<typename Functor, typename... Args> bool QMetaObject::invokeMethod(QObject *context, Functor &&function, Qt::ConnectionType type, Args &&...arguments)
+ \fn template<typename Functor, typename... Args> bool QMetaObject::invokeMethod(QObject *context, Functor &&function, Args &&...arguments)
+ \since 6.7
\threadsafe
- \overload
- Invokes the \a function in the event loop of \a context using the connection type Qt::AutoConnection.
- \a function can be a functor or a pointer to a member function. Returns \c true if the function could
- be invoked. Returns \c false if there is no such member or the parameters did not match.
- The return value of the function call is placed in \a ret.
+ Invokes the \a function with \a arguments in the event loop of \a context.
+ \a function can be a functor or a pointer to a member function. Returns
+ \c true if the function could be invoked. The return value of the
+ function call is placed in \a ret. The object used for the \a ret argument
+ should be obtained by passing your object to qReturnArg(). For example:
+
+ \badcode
+ MyClass *obj = ...;
+ int result = 0;
+ QMetaObject::invokeMethod(obj, &MyClass::myMethod, qReturnArg(result), parameter);
+ \endcode
+
+ If \a type is set, then the function is invoked using that connection type.
+ Otherwise, Qt::AutoConnection will be used.
*/
/*!
@@ -1748,6 +1798,7 @@ bool QMetaObject::invokeMethodImpl(QObject *object, QtPrivate::QSlotObjectBase *
function.
\ingroup objectmodel
+ \compares equality
A QMetaMethod has a methodType(), a methodSignature(), a list of
parameterTypes() and parameterNames(), a return typeName(), a
@@ -1775,19 +1826,19 @@ bool QMetaObject::invokeMethodImpl(QObject *object, QtPrivate::QSlotObjectBase *
invoked), otherwise returns \c false.
*/
-/*! \fn bool QMetaMethod::operator==(const QMetaMethod &m1, const QMetaMethod &m2)
+/*! \fn bool QMetaMethod::operator==(const QMetaMethod &lhs, const QMetaMethod &rhs)
\since 5.0
\overload
- Returns \c true if method \a m1 is equal to method \a m2,
+ Returns \c true if method \a lhs is equal to method \a rhs,
otherwise returns \c false.
*/
-/*! \fn bool QMetaMethod::operator!=(const QMetaMethod &m1, const QMetaMethod &m2)
+/*! \fn bool QMetaMethod::operator!=(const QMetaMethod &lhs, const QMetaMethod &rhs)
\since 5.0
\overload
- Returns \c true if method \a m1 is not equal to method \a m2,
+ Returns \c true if method \a lhs is not equal to method \a rhs,
otherwise returns \c false.
*/
@@ -1973,7 +2024,7 @@ void QMetaMethodPrivate::getParameterTypes(int *types) const
QByteArray QMetaMethodPrivate::parameterTypeName(int index) const
{
int paramsIndex = parametersDataIndex();
- return typeNameFromTypeInfo(mobj, mobj->d.data[paramsIndex + index]);
+ return typeNameFromTypeInfo(mobj, mobj->d.data[paramsIndex + index]).toByteArray();
}
QList<QByteArray> QMetaMethodPrivate::parameterTypes() const
@@ -1983,8 +2034,10 @@ QList<QByteArray> QMetaMethodPrivate::parameterTypes() const
QList<QByteArray> list;
list.reserve(argc);
int paramsIndex = parametersDataIndex();
- for (int i = 0; i < argc; ++i)
- list += typeNameFromTypeInfo(mobj, mobj->d.data[paramsIndex + i]);
+ for (int i = 0; i < argc; ++i) {
+ QByteArrayView name = typeNameFromTypeInfo(mobj, mobj->d.data[paramsIndex + i]);
+ list.emplace_back(name.toByteArray());
+ }
return list;
}
@@ -2222,10 +2275,9 @@ const char *QMetaMethod::typeName() const
differently, and treat them according to the specific needs of your
application.
- \note Since Qt 5.0, \c moc expands preprocessor macros, so it is necessary
+ \note \c moc expands preprocessor macros, so it is necessary
to surround the definition with \c #ifndef \c Q_MOC_RUN, as shown in the
- example above. This was not required in Qt 4. The code as shown above works
- with Qt 4 too.
+ example above.
*/
const char *QMetaMethod::tag() const
{
@@ -2339,7 +2391,7 @@ QMetaMethod::MethodType QMetaMethod::methodType() const
\since 5.0
Returns the meta-method that corresponds to the given \a signal, or an
- invalid QMetaMethod if \a signal is not a signal of the class.
+ invalid QMetaMethod if \a signal is \c{nullptr} or not a signal of the class.
Example:
@@ -2367,20 +2419,21 @@ QMetaMethod QMetaMethod::fromSignalImpl(const QMetaObject *metaObject, void **si
}
/*!
- \fn template <typename... Args> bool QMetaMethod::invoke(QObject *obj, Qt::ConnectionType type, QMetaMethodReturnArgument ret, Args &&... arguments) const
+ \fn template <typename ReturnArg, typename... Args> bool QMetaMethod::invoke(QObject *obj, Qt::ConnectionType type, QTemplatedMetaMethodReturnArgument<ReturnArg> ret, Args &&... arguments) const
\fn template <typename... Args> bool QMetaMethod::invoke(QObject *obj, Qt::ConnectionType type, Args &&... arguments) const
- \fn template <typename... Args> bool QMetaMethod::invoke(QObject *obj, QMetaMethodReturnArgument ret, Args &&... arguments) const
+ \fn template <typename ReturnArg, typename... Args> bool QMetaMethod::invoke(QObject *obj, QTemplatedMetaMethodReturnArgument<ReturnArg> ret, Args &&... arguments) const
\fn template <typename... Args> bool QMetaMethod::invoke(QObject *obj, Args &&... arguments) const
\since 6.5
Invokes this method on the object \a object. Returns \c true if the member could be invoked.
Returns \c false if there is no such member or the parameters did not match.
- For the overloads with a QMetaMethodReturnArgument parameter, the return
- value of the \a member function call is placed in \a ret. For the overloads
- without such a member, the return value of the called function (if any)
- will be discarded. QMetaMethodReturnArgument is an internal type you should
- not use directly. Instead, use the qReturnArg() function.
+ For the overloads with a QTemplatedMetaMethodReturnArgument parameter, the
+ return value of the \a member function call is placed in \a ret. For the
+ overloads without such a member, the return value of the called function
+ (if any) will be discarded. QTemplatedMetaMethodReturnArgument is an
+ internal type you should not use directly. Instead, use the qReturnArg()
+ function.
The overloads with a Qt::ConnectionType \a type parameter allow explicitly
selecting whether the invocation will be synchronous or not:
@@ -2583,7 +2636,7 @@ auto QMetaMethodInvoker::invokeImpl(QMetaMethod self, void *target,
// 0 is the return type, 1 is the first formal parameter
auto checkTypesAreCompatible = [=](int idx) {
uint typeInfo = priv->parameterTypeInfo(idx - 1);
- QLatin1StringView userTypeName(typeNames[idx] ? typeNames[idx] : metaTypes[idx]->name);
+ QByteArrayView userTypeName(typeNames[idx] ? typeNames[idx] : metaTypes[idx]->name);
if ((typeInfo & IsUnresolvedType) == 0) {
// this is a built-in type
@@ -2592,7 +2645,7 @@ auto QMetaMethodInvoker::invokeImpl(QMetaMethod self, void *target,
return int(typeInfo) == metaTypes[idx]->typeId;
}
- QLatin1StringView methodTypeName = stringDataView(priv->mobj, typeInfo & TypeNameIndexMask);
+ QByteArrayView methodTypeName = stringDataView(priv->mobj, typeInfo & TypeNameIndexMask);
if ((MetaTypesAreOptional && !metaTypes) || !metaTypes[idx]) {
// compatibility call, compare strings
if (methodTypeName == userTypeName)
@@ -2600,7 +2653,7 @@ auto QMetaMethodInvoker::invokeImpl(QMetaMethod self, void *target,
// maybe the user type needs normalization
QByteArray normalized = normalizeTypeInternal(userTypeName.begin(), userTypeName.end());
- return methodTypeName == QLatin1StringView(normalized);
+ return methodTypeName == normalized;
}
QMetaType userType(metaTypes[idx]);
@@ -2807,7 +2860,7 @@ auto QMetaMethodInvoker::invokeImpl(QMetaMethod self, void *target,
*/
/*!
- \fn template <typename... Args> bool QMetaMethod::invokeOnGadget(void *gadget, QMetaMethodReturnArgument ret, Args &&... arguments) const
+ \fn template <typename ReturnArg, typename... Args> bool QMetaMethod::invokeOnGadget(void *gadget, QTemplatedMetaMethodReturnArgument<ReturnArg> ret, Args &&... arguments) const
\fn template <typename... Args> bool QMetaMethod::invokeOnGadget(void *gadget, Args &&... arguments) const
\since 6.5
@@ -2818,11 +2871,11 @@ auto QMetaMethodInvoker::invokeImpl(QMetaMethod self, void *target,
The invocation is always synchronous.
- For the overload with a QMetaMethodReturnArgument parameter, the return
- value of the \a member function call is placed in \a ret. For the overload
- without it, the return value of the called function (if any) will be
- discarded. QMetaMethodReturnArgument is an internal type you should not use
- directly. Instead, use the qReturnArg() function.
+ For the overload with a QTemplatedMetaMethodReturnArgument parameter, the
+ return value of the \a member function call is placed in \a ret. For the
+ overload without it, the return value of the called function (if any) will
+ be discarded. QTemplatedMetaMethodReturnArgument is an internal type you
+ should not use directly. Instead, use the qReturnArg() function.
\warning this method will not test the validity of the arguments: \a gadget
must be an instance of the class of the QMetaObject of which this QMetaMethod
@@ -3014,7 +3067,7 @@ const char *QMetaEnum::name() const
Returns the enum name of the flag (without the scope).
For example, the Qt::AlignmentFlag flag has \c
- AlignmentFlag as the enum name, but \c Alignment as as the type name.
+ AlignmentFlag as the enum name, but \c Alignment as the type name.
Non flag enums has the same type and enum names.
Enum names have the same scope as the type name.
@@ -3030,6 +3083,32 @@ const char *QMetaEnum::enumName() const
}
/*!
+ Returns the meta type of the enum.
+
+ If the QMetaObject that this enum is part of was generated with Qt 6.5 or
+ earlier, this will be an invalid meta type.
+
+ \note This is the meta type of the enum itself, not of its underlying
+ integral type. You can retrieve the meta type of the underlying type of the
+ enum using \l{QMetaType::underlyingType()}.
+
+ \since 6.6
+*/
+QMetaType QMetaEnum::metaType() const
+{
+ if (!mobj)
+ return {};
+
+ const QMetaObjectPrivate *p = priv(mobj->d.data);
+#if QT_VERSION < QT_VERSION_CHECK(7, 0, 0)
+ if (p->revision < 12)
+ QMetaType();
+#endif
+
+ return QMetaType(mobj->d.metaTypes[data.index(mobj) + p->propertyCount]);
+}
+
+/*!
Returns the number of keys.
\sa key()
@@ -3109,7 +3188,34 @@ bool QMetaEnum::isScoped() const
*/
const char *QMetaEnum::scope() const
{
- return mobj ? objectClassName(mobj) : nullptr;
+ return mobj ? mobj->className() : nullptr;
+}
+
+static bool isScopeMatch(QByteArrayView scope, const QMetaEnum *e)
+{
+ const QByteArrayView className = e->enclosingMetaObject()->className();
+
+ // Typical use-cases:
+ // a) Unscoped: namespace N { class C { enum E { F }; }; }; key == "N::C::F"
+ // b) Scoped: namespace N { class C { enum class E { F }; }; }; key == "N::C::E::F"
+ if (scope == className)
+ return true;
+
+ // Not using name() because if isFlag() is true, we want the actual name
+ // of the enum, e.g. "MyFlag", not "MyFlags", e.g.
+ // enum MyFlag { F1, F2 }; Q_DECLARE_FLAGS(MyFlags, MyFlag);
+ QByteArrayView name = e->enumName();
+
+ // Match fully qualified enumerator in unscoped enums, key == "N::C::E::F"
+ // equivalent to use-case "a" above
+ const auto sz = className.size();
+ if (scope.size() == sz + qsizetype(qstrlen("::")) + name.size()
+ && scope.startsWith(className)
+ && scope.sliced(sz, 2) == "::"
+ && scope.sliced(sz + 2) == name)
+ return true;
+
+ return false;
}
/*!
@@ -3129,19 +3235,11 @@ int QMetaEnum::keyToValue(const char *key, bool *ok) const
*ok = false;
if (!mobj || !key)
return -1;
- uint scope = 0;
- const char *qualified_key = key;
- const char *s = key + qstrlen(key);
- while (s > key && *s != ':')
- --s;
- if (s > key && *(s - 1) == ':') {
- scope = s - key - 1;
- key += scope + 2;
- }
+
+ const auto [scope, enumKey] = parse_scope(QLatin1StringView(key));
for (int i = 0; i < int(data.keyCount()); ++i) {
- const QByteArray className = stringData(mobj, priv(mobj->d.data)->className);
- if ((!scope || (className.size() == int(scope) && strncmp(qualified_key, className.constData(), scope) == 0))
- && strcmp(key, rawStringData(mobj, mobj->d.data[data.data() + 2*i])) == 0) {
+ if ((!scope || isScopeMatch(*scope, this))
+ && enumKey == stringDataView(mobj, mobj->d.data[data.data() + 2 * i])) {
if (ok != nullptr)
*ok = true;
return mobj->d.data[data.data() + 2 * i + 1];
@@ -3168,17 +3266,49 @@ const char *QMetaEnum::valueToKey(int value) const
return nullptr;
}
-static auto parse_scope(QLatin1StringView qualifiedKey) noexcept
+static bool parseEnumFlags(QByteArrayView v, QVarLengthArray<QByteArrayView, 10> &list)
{
- struct R {
- std::optional<QLatin1StringView> scope;
- QLatin1StringView key;
- };
- const auto scopePos = qualifiedKey.lastIndexOf("::"_L1);
- if (scopePos < 0)
- return R{std::nullopt, qualifiedKey};
- else
- return R{qualifiedKey.first(scopePos), qualifiedKey.sliced(scopePos + 2)};
+ v = v.trimmed();
+ if (v.empty()) {
+ qWarning("QMetaEnum::keysToValue: empty keys string.");
+ return false;
+ }
+
+ qsizetype sep = v.indexOf('|', 0);
+ if (sep == 0) {
+ qWarning("QMetaEnum::keysToValue: malformed keys string, starts with '|', \"%s\"",
+ v.constData());
+ return false;
+ }
+
+ if (sep == -1) { // One flag
+ list.push_back(v);
+ return true;
+ }
+
+ if (v.endsWith('|')) {
+ qWarning("QMetaEnum::keysToValue: malformed keys string, ends with '|', \"%s\"",
+ v.constData());
+ return false;
+ }
+
+ const auto begin = v.begin();
+ const auto end = v.end();
+ auto b = begin;
+ for (; b != end && sep != -1; sep = v.indexOf('|', sep)) {
+ list.push_back({b, begin + sep});
+ ++sep; // Skip over '|'
+ b = begin + sep;
+ if (*b == '|') {
+ qWarning("QMetaEnum::keysToValue: malformed keys string, has two consecutive '|': "
+ "\"%s\"", v.constData());
+ return false;
+ }
+ }
+
+ // The rest of the string
+ list.push_back({b, end});
+ return true;
}
/*!
@@ -3198,19 +3328,22 @@ int QMetaEnum::keysToValue(const char *keys, bool *ok) const
if (!mobj || !keys)
return -1;
- auto lookup = [&] (QLatin1StringView key) -> std::optional<int> {
+ auto lookup = [&] (QByteArrayView key) -> std::optional<int> {
for (int i = data.keyCount() - 1; i >= 0; --i) {
if (key == stringDataView(mobj, mobj->d.data[data.data() + 2*i]))
return mobj->d.data[data.data() + 2*i + 1];
}
return std::nullopt;
};
- auto className = [&] { return stringDataView(mobj, priv(mobj->d.data)->className); };
int value = 0;
- for (const QLatin1StringView &untrimmed : qTokenize(QLatin1StringView{keys}, u'|')) {
+ QVarLengthArray<QByteArrayView, 10> list;
+ const bool r = parseEnumFlags(QByteArrayView{keys}, list);
+ if (!r)
+ return -1;
+ for (const auto &untrimmed : list) {
const auto parsed = parse_scope(untrimmed.trimmed());
- if (parsed.scope && *parsed.scope != className())
+ if (parsed.scope && !isScopeMatch(*parsed.scope, this))
return -1; // wrong type name in qualified name
if (auto thisValue = lookup(parsed.key))
value |= *thisValue;
@@ -3255,7 +3388,7 @@ QByteArray QMetaEnum::valueToKeys(int value) const
QByteArray keys;
if (!mobj)
return keys;
- QVarLengthArray<QLatin1StringView, sizeof(int) * CHAR_BIT> parts;
+ QVarLengthArray<QByteArrayView, sizeof(int) * CHAR_BIT> parts;
int v = value;
// reverse iterate to ensure values like Qt::Dialog=0x2|Qt::Window are processed first.
for (int i = data.keyCount() - 1; i >= 0; --i) {
@@ -3278,8 +3411,13 @@ QMetaEnum::QMetaEnum(const QMetaObject *mobj, int index)
Q_ASSERT(index >= 0 && index < priv(mobj->d.data)->enumeratorCount);
}
+int QMetaEnum::Data::index(const QMetaObject *mobj) const
+{
+ return (d - mobj->d.data - priv(mobj->d.data)->enumeratorData) / Size;
+}
+
/*!
- \fn QMetaEnum QMetaEnum::fromType()
+ \fn template<typename T> QMetaEnum QMetaEnum::fromType()
\since 5.5
Returns the QMetaEnum corresponding to the type in the template parameter.
@@ -3365,7 +3503,7 @@ const char *QMetaProperty::typeName() const
// TODO: can the metatype be invalid for dynamic metaobjects?
if (const auto mt = metaType(); mt.isValid())
return mt.name();
- return rawTypeNameFromTypeInfo(mobj, data.type());
+ return typeNameFromTypeInfo(mobj, data.type()).constData();
}
/*! \fn QVariant::Type QMetaProperty::type() const
@@ -3518,38 +3656,31 @@ QMetaProperty::QMetaProperty(const QMetaObject *mobj, int index)
data(getMetaPropertyData(mobj, index))
{
Q_ASSERT(index >= 0 && index < priv(mobj->d.data)->propertyCount);
-
- if (!(data.flags() & EnumOrFlag))
+ // The code below here just resolves menum if the property is an enum type:
+ if (!(data.flags() & EnumOrFlag) || !metaType().flags().testFlag(QMetaType::IsEnumeration))
return;
- const char *type = rawTypeNameFromTypeInfo(mobj, data.type());
- menum = mobj->enumerator(mobj->indexOfEnumerator(type));
+ QByteArrayView enum_name = typeNameFromTypeInfo(mobj, data.type());
+ menum = mobj->enumerator(QMetaObjectPrivate::indexOfEnumerator(mobj, enum_name));
if (menum.isValid())
return;
- const char *enum_name = type;
- const char *scope_name = objectClassName(mobj);
- char *scope_buffer = nullptr;
-
- const char *colon = strrchr(enum_name, ':');
- // ':' will always appear in pairs
- Q_ASSERT(colon <= enum_name || *(colon - 1) == ':');
- if (colon > enum_name) {
- int len = colon - enum_name - 1;
- scope_buffer = (char *)malloc(len + 1);
- memcpy(scope_buffer, enum_name, len);
- scope_buffer[len] = '\0';
- scope_name = scope_buffer;
- enum_name = colon + 1;
+
+ QByteArrayView scope_name;
+ const auto parsed = parse_scope(enum_name);
+ if (parsed.scope) {
+ scope_name = *parsed.scope;
+ enum_name = parsed.key;
+ } else {
+ scope_name = objectClassName(mobj);
}
const QMetaObject *scope = nullptr;
- if (qstrcmp(scope_name, "Qt") == 0)
+ if (scope_name == "Qt")
scope = &Qt::staticMetaObject;
else
- scope = QMetaObject_findMetaObject(mobj, scope_name);
+ scope = QMetaObject_findMetaObject(mobj, QByteArrayView(scope_name));
+
if (scope)
- menum = scope->enumerator(scope->indexOfEnumerator(enum_name));
- if (scope_buffer)
- free(scope_buffer);
+ menum = scope->enumerator(QMetaObjectPrivate::indexOfEnumerator(scope, enum_name));
}
/*!
@@ -3617,31 +3748,43 @@ QVariant QMetaProperty::read(const QObject *object) const
Writes \a value as the property's value to the given \a object. Returns
true if the write succeeded; otherwise returns \c false.
- If \a value is not of the same type type as the property, a conversion
+ If \a value is not of the same type as the property, a conversion
is attempted. An empty QVariant() is equivalent to a call to reset()
if this property is resettable, or setting a default-constructed object
otherwise.
+ \note This function internally makes a copy of \a value. Prefer to use the
+ rvalue overload when possible.
+
\sa read(), reset(), isWritable()
*/
bool QMetaProperty::write(QObject *object, const QVariant &value) const
{
if (!object || !isWritable())
return false;
+ return write(object, QVariant(value));
+}
- QVariant v = value;
+/*!
+ \overload
+ \since 6.6
+*/
+bool QMetaProperty::write(QObject *object, QVariant &&v) const
+{
+ if (!object || !isWritable())
+ return false;
QMetaType t(mobj->d.metaTypes[data.index(mobj)]);
if (t != QMetaType::fromType<QVariant>() && t != v.metaType()) {
if (isEnumType() && !t.metaObject() && v.metaType().id() == QMetaType::QString) {
// Assigning a string to a property of type Q_ENUMS (instead of Q_ENUM)
bool ok;
if (isFlagType())
- v = QVariant(menum.keysToValue(value.toByteArray(), &ok));
+ v = QVariant(menum.keysToValue(v.toByteArray(), &ok));
else
- v = QVariant(menum.keyToValue(value.toByteArray(), &ok));
+ v = QVariant(menum.keyToValue(v.toByteArray(), &ok));
if (!ok)
return false;
- } else if (!value.isValid()) {
+ } else if (!v.isValid()) {
if (isResettable())
return reset(object);
v = QVariant(t, nullptr);
@@ -3736,6 +3879,16 @@ bool QMetaProperty::writeOnGadget(void *gadget, const QVariant &value) const
}
/*!
+ \overload
+ \since 6.6
+*/
+bool QMetaProperty::writeOnGadget(void *gadget, QVariant &&value) const
+{
+ Q_ASSERT(priv(mobj->d.data)->flags & PropertyAccessInStaticMetaCall && mobj->d.static_metacall);
+ return write(reinterpret_cast<QObject*>(gadget), std::move(value));
+}
+
+/*!
\since 5.5
Resets the property for the given \a gadget with a reset method.
@@ -3819,20 +3972,23 @@ int QMetaProperty::notifySignalIndex() const
if (!mobj || data.notifyIndex() == std::numeric_limits<uint>::max())
return -1;
uint methodIndex = data.notifyIndex();
- if (methodIndex & IsUnresolvedSignal) {
- methodIndex &= ~IsUnresolvedSignal;
- const QByteArray signalName = stringData(mobj, methodIndex);
- const QMetaObject *m = mobj;
- const int idx = QMetaObjectPrivate::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();
+ if (!(methodIndex & IsUnresolvedSignal))
+ return methodIndex + mobj->methodOffset();
+ methodIndex &= ~IsUnresolvedSignal;
+ const QByteArray signalName = stringData(mobj, methodIndex);
+ const QMetaObject *m = mobj;
+ // try 0-arg signal
+ int idx = QMetaObjectPrivate::indexOfMethodRelative<MethodSignal>(&m, signalName, 0, nullptr);
+ if (idx >= 0)
+ return idx + m->methodOffset();
+ // try 1-arg signal
+ QArgumentType argType(typeId());
+ idx = QMetaObjectPrivate::indexOfMethodRelative<MethodSignal>(&m, signalName, 1, &argType);
+ if (idx >= 0)
+ return idx + m->methodOffset();
+ qWarning("QMetaProperty::notifySignal: cannot find the NOTIFY signal %s in class %s for property '%s'",
+ signalName.constData(), mobj->className(), name());
+ return -1;
}
// This method has been around for a while, but the documentation was marked \internal until 5.1
@@ -3993,8 +4149,11 @@ bool QMetaProperty::isBindable() const
\snippet code/src_corelib_kernel_qmetaobject.cpp 5
- This mechanism is free for you to use in your Qt applications. Qt
- doesn't use it for any of its classes.
+ This mechanism is free for you to use in your Qt applications.
+
+ \note It's also used by the \l[ActiveQt]{Active Qt},
+ \l[QtDBus]{Qt D-Bus}, \l[QtQml]{Qt Qml}, and \l{Qt Remote Objects}
+ modules. Some keys might be set when using these modules.
\sa QMetaObject
*/
@@ -4048,23 +4207,24 @@ const char *QMetaClassInfo::value() const
*/
/*!
- \macro QGenericArgument Q_ARG(Type, const Type &value)
+ \macro QMetaMethodArgument Q_ARG(Type, const Type &value)
\relates QMetaObject
This macro takes a \a Type and a \a value of that type and
- returns a \l QGenericArgument object that can be passed to
- QMetaObject::invokeMethod().
+ returns a QMetaMethodArgument, which can be passed to the template
+ QMetaObject::invokeMethod() with the \c {Args &&...} arguments.
\sa Q_RETURN_ARG()
*/
/*!
- \macro QGenericReturnArgument Q_RETURN_ARG(Type, Type &value)
+ \macro QMetaMethodReturnArgument Q_RETURN_ARG(Type, Type &value)
\relates QMetaObject
This macro takes a \a Type and a non-const reference to a \a
- value of that type and returns a QGenericReturnArgument object
- that can be passed to QMetaObject::invokeMethod().
+ value of that type and returns a QMetaMethodReturnArgument, which can be
+ passed to the template QMetaObject::invokeMethod() with the \c {Args &&...}
+ arguments.
\sa Q_ARG()
*/
diff --git a/src/corelib/kernel/qmetaobject.h b/src/corelib/kernel/qmetaobject.h
index b92c808c28..91f287a8d3 100644
--- a/src/corelib/kernel/qmetaobject.h
+++ b/src/corelib/kernel/qmetaobject.h
@@ -6,6 +6,7 @@
#define QMETAOBJECT_H
#include <QtCore/qobjectdefs.h>
+#include <QtCore/qcompare.h>
#include <QtCore/qvariant.h>
QT_BEGIN_NAMESPACE
@@ -135,13 +136,13 @@ public:
}
#endif
- template <typename... Args>
+ template <typename ReturnArg, typename... Args>
#ifdef Q_QDOC
bool
#else
QtPrivate::Invoke::IfNotOldStyleArgs<bool, Args...>
#endif
- invoke(QObject *obj, Qt::ConnectionType c, QMetaMethodReturnArgument r,
+ invoke(QObject *obj, Qt::ConnectionType c, QTemplatedMetaMethodReturnArgument<ReturnArg> r,
Args &&... arguments) const
{
auto h = QtPrivate::invokeMethodHelper(r, std::forward<Args>(arguments)...);
@@ -157,16 +158,16 @@ public:
#endif
invoke(QObject *obj, Qt::ConnectionType c, Args &&... arguments) const
{
- return invoke(obj, c, QMetaMethodReturnArgument{}, std::forward<Args>(arguments)...);
+ return invoke(obj, c, QTemplatedMetaMethodReturnArgument<void>{}, std::forward<Args>(arguments)...);
}
- template <typename... Args>
+ template <typename ReturnArg, typename... Args>
#ifdef Q_QDOC
bool
#else
QtPrivate::Invoke::IfNotOldStyleArgs<bool, Args...>
#endif
- invoke(QObject *obj, QMetaMethodReturnArgument r, Args &&... arguments) const
+ invoke(QObject *obj, QTemplatedMetaMethodReturnArgument<ReturnArg> r, Args &&... arguments) const
{
return invoke(obj, Qt::AutoConnection, r, std::forward<Args>(arguments)...);
}
@@ -182,13 +183,13 @@ public:
return invoke(obj, Qt::AutoConnection, std::forward<Args>(arguments)...);
}
- template <typename... Args>
+ template <typename ReturnArg, typename... Args>
#ifdef Q_QDOC
bool
#else
QtPrivate::Invoke::IfNotOldStyleArgs<bool, Args...>
#endif
- invokeOnGadget(void *gadget, QMetaMethodReturnArgument r, Args &&... arguments) const
+ invokeOnGadget(void *gadget, QTemplatedMetaMethodReturnArgument<ReturnArg> r, Args &&... arguments) const
{
auto h = QtPrivate::invokeMethodHelper(r, std::forward<Args>(arguments)...);
return invokeImpl(*this, gadget, Qt::ConnectionType(-1), h.parameterCount(),
@@ -203,7 +204,7 @@ public:
#endif
invokeOnGadget(void *gadget, Args &&... arguments) const
{
- return invokeOnGadget(gadget, QMetaMethodReturnArgument{}, std::forward<Args>(arguments)...);
+ return invokeOnGadget(gadget, QTemplatedMetaMethodReturnArgument<void>{}, std::forward<Args>(arguments)...);
}
inline bool isValid() const { return mobj != nullptr; }
@@ -240,19 +241,22 @@ protected:
const uint *d;
};
+private:
constexpr QMetaMethod(const QMetaObject *metaObject, const Data &data_)
: mobj(metaObject), data(data_)
{}
+protected:
const QMetaObject *mobj;
Data data;
friend struct QMetaObject;
friend struct QMetaObjectPrivate;
friend class QObject;
- friend bool operator==(const QMetaMethod &m1, const QMetaMethod &m2) noexcept
- { return m1.data == m2.data; }
- friend bool operator!=(const QMetaMethod &m1, const QMetaMethod &m2) noexcept
- { return !(m1 == m2); }
+
+private:
+ friend bool comparesEqual(const QMetaMethod &lhs, const QMetaMethod &rhs) noexcept
+ { return lhs.data == rhs.data; }
+ Q_DECLARE_EQUALITY_COMPARABLE(QMetaMethod)
};
Q_DECLARE_TYPEINFO(QMetaMethod, Q_RELOCATABLE_TYPE);
@@ -263,6 +267,8 @@ public:
const char *name() const;
const char *enumName() const;
+ QMetaType metaType() const;
+
bool isFlag() const;
bool isScoped() const;
@@ -300,6 +306,7 @@ private:
quint32 flags() const { return d[2]; }
qint32 keyCount() const { return static_cast<qint32>(d[3]); }
quint32 data() const { return d[4]; }
+ int index(const QMetaObject *mobj) const;
const uint *d;
};
@@ -358,12 +365,14 @@ public:
QVariant read(const QObject *obj) const;
bool write(QObject *obj, const QVariant &value) const;
+ bool write(QObject *obj, QVariant &&value) const;
bool reset(QObject *obj) const;
QUntypedBindable bindable(QObject *object) const;
QVariant readOnGadget(const void *gadget) const;
bool writeOnGadget(void *gadget, const QVariant &value) const;
+ bool writeOnGadget(void *gadget, QVariant &&value) const;
bool resetOnGadget(void *gadget) const;
bool hasStdCppSet() const;
diff --git a/src/corelib/kernel/qmetaobject_p.h b/src/corelib/kernel/qmetaobject_p.h
index c93a82196e..d5dc9a356a 100644
--- a/src/corelib/kernel/qmetaobject_p.h
+++ b/src/corelib/kernel/qmetaobject_p.h
@@ -23,11 +23,14 @@
#ifndef QT_NO_QOBJECT
#include <private/qobject_p.h> // For QObjectPrivate::Connection
#endif
+#include <private/qtools_p.h>
#include <QtCore/qvarlengtharray.h>
QT_BEGIN_NAMESPACE
// ### TODO - QTBUG-87869: wrap in a proper Q_NAMESPACE and use scoped enums, to avoid name clashes
+using namespace QtMiscUtils;
+
enum PropertyFlags {
Invalid = 0x00000000,
Readable = 0x00000001,
@@ -108,22 +111,18 @@ public:
const_cast<QArgumentType *>(this)->_name = QMetaType(_type).name();
return _name;
}
- bool operator==(const QArgumentType &other) const
- {
- if (_type && other._type)
- return _type == other._type;
- else
- return name() == other.name();
- }
- bool operator!=(const QArgumentType &other) const
+
+private:
+ friend bool comparesEqual(const QArgumentType &lhs,
+ const QArgumentType &rhs) noexcept
{
- if (_type && other._type)
- return _type != other._type;
+ if (lhs._type && rhs._type)
+ return lhs._type == rhs._type;
else
- return name() != other.name();
+ return lhs.name() == rhs.name();
}
+ Q_DECLARE_EQUALITY_COMPARABLE(QArgumentType)
-private:
int _type;
QByteArray _name;
};
@@ -172,7 +171,8 @@ struct QMetaObjectPrivate
// revision 10 is Qt 6.2: The metatype of the metaobject is stored in the metatypes array
// and metamethods store a flag stating whether they are const
// revision 11 is Qt 6.5: The metatype for void is stored in the metatypes array
- enum { OutputRevision = 11 }; // Used by moc, qmetaobjectbuilder and qdbus
+ // revision 12 is Qt 6.6: It adds the metatype for enums
+ enum { OutputRevision = 12 }; // Used by moc, qmetaobjectbuilder and qdbus
enum { IntsPerMethod = QMetaMethod::Data::Size };
enum { IntsPerEnum = QMetaEnum::Data::Size };
enum { IntsPerProperty = QMetaProperty::Data::Size };
@@ -208,6 +208,11 @@ struct QMetaObjectPrivate
int argc, const QArgumentType *types);
static int indexOfConstructor(const QMetaObject *m, const QByteArray &name,
int argc, const QArgumentType *types);
+
+ enum class Which { Name, Alias };
+ static int indexOfEnumerator(const QMetaObject *m, QByteArrayView name, Which which);
+ static int indexOfEnumerator(const QMetaObject *m, QByteArrayView name);
+
Q_CORE_EXPORT static QMetaMethod signal(const QMetaObject *m, int signal_index);
static inline int signalOffset(const QMetaObject *m)
{
@@ -265,11 +270,7 @@ enum { MetaObjectPrivateFieldCount = sizeof(QMetaObjectPrivate) / sizeof(int) };
// mirrored in moc's utils.h
static inline bool is_ident_char(char s)
{
- return ((s >= 'a' && s <= 'z')
- || (s >= 'A' && s <= 'Z')
- || (s >= '0' && s <= '9')
- || s == '_'
- );
+ return isAsciiLetterOrNumber(s) || s == '_';
}
static inline bool is_space(char s)
diff --git a/src/corelib/kernel/qmetaobjectbuilder.cpp b/src/corelib/kernel/qmetaobjectbuilder.cpp
index b742320f33..c2b44a4f00 100644
--- a/src/corelib/kernel/qmetaobjectbuilder.cpp
+++ b/src/corelib/kernel/qmetaobjectbuilder.cpp
@@ -53,7 +53,7 @@ Q_CORE_EXPORT bool isBuiltinType(const QByteArray &type)
} // namespace QtPrivate
// copied from qmetaobject.cpp
-[[maybe_unused]] static inline const QMetaObjectPrivate *priv(const uint* data)
+[[maybe_unused]] static inline const QMetaObjectPrivate *qmobPriv(const uint* data)
{ return reinterpret_cast<const QMetaObjectPrivate*>(data); }
class QMetaMethodBuilderPrivate
@@ -159,6 +159,7 @@ public:
QByteArray name;
QByteArray enumName;
+ QMetaType metaType;
bool isFlag;
bool isScoped;
QList<QByteArray> keys;
@@ -597,6 +598,7 @@ QMetaEnumBuilder QMetaObjectBuilder::addEnumerator(const QMetaEnum &prototype)
{
QMetaEnumBuilder en = addEnumerator(prototype.name());
en.setEnumName(prototype.enumName());
+ en.setMetaType(prototype.metaType());
en.setIsFlag(prototype.isFlag());
en.setIsScoped(prototype.isScoped());
int count = prototype.keyCount();
@@ -704,7 +706,7 @@ void QMetaObjectBuilder::addMetaObject(const QMetaObject *prototype,
}
if ((members & RelatedMetaObjects) != 0) {
- Q_ASSERT(priv(prototype->d.data)->revision >= 2);
+ Q_ASSERT(qmobPriv(prototype->d.data)->revision >= 2);
const auto *objects = prototype->d.relatedMetaObjects;
if (objects) {
while (*objects != nullptr) {
@@ -715,7 +717,7 @@ void QMetaObjectBuilder::addMetaObject(const QMetaObject *prototype,
}
if ((members & StaticMetacall) != 0) {
- Q_ASSERT(priv(prototype->d.data)->revision >= 6);
+ Q_ASSERT(qmobPriv(prototype->d.data)->revision >= 6);
if (prototype->d.static_metacall)
setStaticMetacallFunction(prototype->d.static_metacall);
}
@@ -1026,6 +1028,9 @@ int QMetaObjectBuilder::indexOfClassInfo(const QByteArray &name)
}
// Align on a specific type boundary.
+#ifdef ALIGN
+# undef ALIGN
+#endif
#define ALIGN(size,type) \
(size) = ((size) + sizeof(type) - 1) & ~(sizeof(type) - 1)
@@ -1157,8 +1162,8 @@ static int buildMetaObject(QMetaObjectBuilderPrivate *d, char *buf,
}
// Populate the QMetaObjectPrivate structure.
- QMetaObjectPrivate *pmeta
- = reinterpret_cast<QMetaObjectPrivate *>(buf + size);
+ QMetaObjectPrivate *pmeta = buf ? reinterpret_cast<QMetaObjectPrivate *>(buf + size)
+ : nullptr;
//int pmetaSize = size;
dataIndex = MetaObjectPrivateFieldCount;
int methodParametersDataSize =
@@ -1167,7 +1172,7 @@ static int buildMetaObject(QMetaObjectBuilderPrivate *d, char *buf,
- int(d->methods.size()) // return "parameters" don't have names
- int(d->constructors.size()); // "this" parameters don't have names
if constexpr (mode == Construct) {
- static_assert(QMetaObjectPrivate::OutputRevision == 11, "QMetaObjectBuilder should generate the same version as moc");
+ static_assert(QMetaObjectPrivate::OutputRevision == 12, "QMetaObjectBuilder should generate the same version as moc");
pmeta->revision = QMetaObjectPrivate::OutputRevision;
pmeta->flags = d->flags.toInt();
pmeta->className = 0; // Class name is always the first string.
@@ -2303,6 +2308,31 @@ void QMetaEnumBuilder::setEnumName(const QByteArray &alias)
}
/*!
+ Returns the meta type of the enumerator.
+
+ \since 6.6
+*/
+QMetaType QMetaEnumBuilder::metaType() const
+{
+ if (QMetaEnumBuilderPrivate *d = d_func())
+ return d->metaType;
+ return QMetaType();
+}
+
+/*!
+ Sets this enumerator to have the given \c metaType.
+
+ \since 6.6
+ \sa metaType()
+*/
+void QMetaEnumBuilder::setMetaType(QMetaType metaType)
+{
+ QMetaEnumBuilderPrivate *d = d_func();
+ if (d)
+ d->metaType = metaType;
+}
+
+/*!
Returns \c true if this enumerator is used as a flag; otherwise returns
false.
diff --git a/src/corelib/kernel/qmetaobjectbuilder_p.h b/src/corelib/kernel/qmetaobjectbuilder_p.h
index 99eaca2ac9..b52487986c 100644
--- a/src/corelib/kernel/qmetaobjectbuilder_p.h
+++ b/src/corelib/kernel/qmetaobjectbuilder_p.h
@@ -258,6 +258,9 @@ public:
QByteArray enumName() const;
void setEnumName(const QByteArray &alias);
+ QMetaType metaType() const;
+ void setMetaType(QMetaType metaType);
+
bool isFlag() const;
void setIsFlag(bool value);
diff --git a/src/corelib/kernel/qmetatype.cpp b/src/corelib/kernel/qmetatype.cpp
index 81647eb5dc..1c2665e53c 100644
--- a/src/corelib/kernel/qmetatype.cpp
+++ b/src/corelib/kernel/qmetatype.cpp
@@ -5,7 +5,6 @@
#include "qmetatype.h"
#include "qmetatype_p.h"
-#include "qobject.h"
#include "qobjectdefs.h"
#include "qdatetime.h"
#include "qbytearray.h"
@@ -21,14 +20,14 @@
#include "qeasingcurve.h"
#endif
#include "quuid.h"
-#include "qvariant.h"
-#include "qdatastream.h"
#if QT_CONFIG(regularexpression)
# include "qregularexpression.h"
#endif
#ifndef QT_BOOTSTRAPPED
+# include "qdatastream.h"
+
# include "qbitarray.h"
# include "qurl.h"
# include "qvariant.h"
@@ -43,6 +42,7 @@
# include "qmetaobject.h"
# include "qsequentialiterable.h"
# include "qassociativeiterable.h"
+# include "qobject.h"
#endif
#if QT_CONFIG(itemmodel)
@@ -56,7 +56,6 @@
# include "qline.h"
#endif
-#include <bitset>
#include <new>
#include <cstring>
@@ -84,6 +83,20 @@ struct QMetaTypeDeleter
struct QMetaTypeCustomRegistry
{
+
+#if QT_VERSION < QT_VERSION_CHECK(7, 0, 0) && !defined(QT_BOOTSTRAPPED)
+ QMetaTypeCustomRegistry()
+ {
+ /* qfloat16 was neither a builtin, nor unconditionally registered
+ in QtCore in Qt <= 6.2.
+ Inserting it as an alias ensures that a QMetaType::id call
+ will get the correct built-in type-id (the interface pointers
+ might still not match, but we already deal with that case.
+ */
+ aliases.insert("qfloat16", QtPrivate::qMetaTypeInterfaceForType<qfloat16>());
+ }
+#endif
+
QReadWriteLock lock;
QList<const QtPrivate::QMetaTypeInterface *> registry;
QHash<QByteArray, const QtPrivate::QMetaTypeInterface *> aliases;
@@ -137,13 +150,7 @@ struct QMetaTypeCustomRegistry
auto &ti = registry[idx];
// We must unregister all names.
- auto it = aliases.begin();
- while (it != aliases.end()) {
- if (it.value() == ti)
- it = aliases.erase(it);
- else
- ++it;
- }
+ aliases.removeIf([ti] (const auto &kv) { return kv.value() == ti; });
ti = nullptr;
@@ -255,7 +262,7 @@ const char *QtMetaTypePrivate::typedefNameForType(const QtPrivate::QMetaTypeInte
\li Pointers to classes derived from QObject
\li QList<T>, QQueue<T>, QStack<T> or QSet<T>
where T is a registered meta type
- \li QHash<T1, T2>, QMap<T1, T2> or QPair<T1, T2> where T1 and T2 are
+ \li QHash<T1, T2>, QMap<T1, T2> or std::pair<T1, T2> where T1 and T2 are
registered meta types
\li QPointer<T>, QSharedPointer<T>, QWeakPointer<T>, where T is a class that derives from QObject
\li Enumerations registered with Q_ENUM or Q_FLAG
@@ -433,23 +440,23 @@ const char *QtMetaTypePrivate::typedefNameForType(const QtPrivate::QMetaTypeInte
The enum describes attributes of a type supported by QMetaType.
- \value NeedsConstruction This type has a non-trivial default constructor. If the flag is not set, instances can be safely initialized with memset to 0.
- \value NeedsCopyConstruction (since 6.5) This type has a non-trivial copy construtcor. If the flag is not set, instances can be copied with memcpy.
+ \value NeedsConstruction This type has a default constructor. If the flag is not set, instances can be safely initialized with memset to 0.
+ \value NeedsCopyConstruction (since 6.5) This type has a non-trivial copy constructor. If the flag is not set, instances can be copied with memcpy.
\value NeedsMoveConstruction (since 6.5) This type has a non-trivial move constructor. If the flag is not set, instances can be moved with memcpy.
- \value NeedsDestruction This type has a non-trivial destructor. If the flag is not set calls to the destructor are not necessary before discarding objects.
+ \value NeedsDestruction This type has a non-trivial destructor. If the flag is not set, calls to the destructor are not necessary before discarding objects.
\value RelocatableType An instance of a type having this attribute can be safely moved to a different memory location using memcpy.
\omitvalue MovableType
\omitvalue SharedPointerToQObject
\value IsEnumeration This type is an enumeration.
\value IsUnsignedEnumeration If the type is an Enumeration, its underlying type is unsigned.
- \value PointerToQObject This type is a pointer to a derived of QObject.
+ \value PointerToQObject This type is a pointer to a class derived from QObject.
\value IsPointer This type is a pointer to another type.
\omitvalue WeakPointerToQObject
\omitvalue TrackingPointerToQObject
- \omitvalue IsGadget \omit This type is a Q_GADGET and it's corresponding QMetaObject can be accessed with QMetaType::metaObject Since 5.5. \endomit
+ \omitvalue IsGadget \omit (since Qt 5.5) This type is a Q_GADGET and its corresponding QMetaObject can be accessed with QMetaType::metaObject. \endomit
\omitvalue PointerToGadget
\omitvalue IsQmlList
- \value IsConst Indicates that values of this types are immutable; for instance because they are pointers to const objects.
+ \value IsConst Indicates that values of this type are immutable; for instance, because they are pointers to const objects.
\note Before Qt 6.5, both the NeedsConstruction and NeedsDestruction flags
were incorrectly set if the either copy construtor or destructor were
@@ -467,6 +474,7 @@ const char *QtMetaTypePrivate::typedefNameForType(const QtPrivate::QMetaTypeInte
\ingroup objectmodel
\threadsafe
+ \compares equality
The class is used as a helper to marshall types in QVariant and
in queued signals and slots connections. It associates a type
@@ -893,13 +901,22 @@ bool QMetaType::isOrdered() const
*/
void QMetaType::unregisterMetaType(QMetaType type)
{
- if (type.d_ptr && type.d_ptr->typeId.loadRelaxed() >= QMetaType::User) {
- // this is a custom meta type (not read-only)
- auto d = const_cast<QtPrivate::QMetaTypeInterface *>(type.d_ptr);
- if (auto reg = customTypeRegistry())
- reg->unregisterDynamicType(d->typeId.loadRelaxed());
- d->typeId.storeRelease(0);
+ const QtPrivate::QMetaTypeInterface *d_ptr = type.d_ptr;
+ if (!d_ptr)
+ return;
+
+ const int typeId = d_ptr->typeId.loadRelaxed();
+ if (typeId < QMetaType::User)
+ return;
+
+ // this is a custom meta type (not read-only)
+
+ if (auto reg = customTypeRegistry()) {
+ Q_ASSERT(reg->getCustomType(typeId) == d_ptr);
+ reg->unregisterDynamicType(typeId);
}
+
+ const_cast<QtPrivate::QMetaTypeInterface *>(d_ptr)->typeId.storeRelease(0);
}
/*!
@@ -909,22 +926,28 @@ void QMetaType::unregisterMetaType(QMetaType type)
Returns the QMetaType corresponding to the type in the template parameter.
*/
-/*! \fn bool QMetaType::operator==(QMetaType a, QMetaType b)
+/*! \fn bool QMetaType::operator==(const QMetaType &lhs, const QMetaType &rhs)
\since 5.15
\overload
- Returns \c true if the QMetaType \a a represents the same type
- as the QMetaType \a b, otherwise returns \c false.
+ Returns \c true if the QMetaType \a lhs represents the same type
+ as the QMetaType \a rhs, otherwise returns \c false.
*/
-/*! \fn bool QMetaType::operator!=(QMetaType a, QMetaType b)
+/*! \fn bool QMetaType::operator!=(const QMetaType &lhs, const QMetaType &rhs)
\since 5.15
\overload
- Returns \c true if the QMetaType \a a represents a different type
- than the QMetaType \a b, otherwise returns \c false.
+ Returns \c true if the QMetaType \a lhs represents a different type
+ than the QMetaType \a rhs, otherwise returns \c false.
*/
+/*! \internal */
+bool QMetaTypeModuleHelper::convert(const void *, int, void *, int) const
+{
+ return false;
+}
+
#define QT_ADD_STATIC_METATYPE(MetaTypeName, MetaTypeId, RealName) \
{ #RealName, sizeof(#RealName) - 1, MetaTypeId },
@@ -940,7 +963,8 @@ static const struct { const char * typeName; int typeNameLength; int type; } typ
{nullptr, 0, QMetaType::UnknownType}
};
-static const struct : QMetaTypeModuleHelper
+// NOLINTNEXTLINE(cppcoreguidelines-virtual-class-destructor): this is not a base class
+static constexpr struct : QMetaTypeModuleHelper
{
template<typename T, typename LiteralWrapper =
std::conditional_t<std::is_same_v<T, QString>, QLatin1StringView, const char *>>
@@ -987,6 +1011,8 @@ static const struct : QMetaTypeModuleHelper
using Double = double;
using Bool = bool;
using Nullptr = std::nullptr_t;
+ using Char16 = char16_t;
+ using Char32 = char32_t;
#define QMETATYPE_CONVERTER_ASSIGN_DOUBLE(To, From) \
QMETATYPE_CONVERTER(To, From, result = double(source); return true;)
@@ -1141,6 +1167,9 @@ static const struct : QMetaTypeModuleHelper
QMETATYPE_CONVERTER_ASSIGN_QCHAR(ULong);
QMETATYPE_CONVERTER_ASSIGN_QCHAR(UInt);
QMETATYPE_CONVERTER_ASSIGN_QCHAR(ULongLong);
+ QMETATYPE_CONVERTER_ASSIGN_QCHAR(Char16);
+
+ QMETATYPE_CONVERTER(Char16, QChar, result = source.unicode(); return true;)
// conversions to QString
QMETATYPE_CONVERTER_ASSIGN(QString, QChar);
@@ -1178,6 +1207,14 @@ static const struct : QMetaTypeModuleHelper
result = QString::fromLatin1(&s, 1);
return true;
);
+ QMETATYPE_CONVERTER(QString, Char16,
+ result = QChar(source);
+ return true;
+ );
+ QMETATYPE_CONVERTER(QString, Char32,
+ result = QChar::fromUcs4(source).operator QStringView().toString();
+ return true;
+ );
#if QT_CONFIG(datestring)
QMETATYPE_CONVERTER(QString, QDate, result = source.toString(Qt::ISODate); return true;);
QMETATYPE_CONVERTER(QString, QTime, result = source.toString(Qt::ISODateWithMs); return true;);
@@ -1240,34 +1277,36 @@ static const struct : QMetaTypeModuleHelper
QMETATYPE_CONVERTER_ASSIGN(QRectF, QRect);
QMETATYPE_CONVERTER(QPoint, QPointF, result = source.toPoint(); return true;);
QMETATYPE_CONVERTER_ASSIGN(QPointF, QPoint);
- #endif
+#endif
+
+ QMETATYPE_CONVERTER(QStringList, QString, result = QStringList() << source; return true;);
+#ifndef QT_NO_VARIANT
QMETATYPE_CONVERTER(QByteArrayList, QVariantList,
result.reserve(source.size());
- for (auto v: source)
+ for (const auto &v: source)
result.append(v.toByteArray());
return true;
);
QMETATYPE_CONVERTER(QVariantList, QByteArrayList,
result.reserve(source.size());
- for (auto v: source)
+ for (const auto &v: source)
result.append(QVariant(v));
return true;
);
QMETATYPE_CONVERTER(QStringList, QVariantList,
result.reserve(source.size());
- for (auto v: source)
+ for (const auto &v: source)
result.append(v.toString());
return true;
);
QMETATYPE_CONVERTER(QVariantList, QStringList,
result.reserve(source.size());
- for (auto v: source)
+ for (const auto &v: source)
result.append(QVariant(v));
return true;
);
- QMETATYPE_CONVERTER(QStringList, QString, result = QStringList() << source; return true;);
QMETATYPE_CONVERTER(QVariantHash, QVariantMap,
for (auto it = source.begin(); it != source.end(); ++it)
@@ -1279,7 +1318,7 @@ static const struct : QMetaTypeModuleHelper
result.insert(it.key(), it.value());
return true;
);
-
+#endif // !QT_NO_VARIANT
#ifndef QT_BOOTSTRAPPED
QMETATYPE_CONVERTER_ASSIGN(QCborValue, QString);
QMETATYPE_CONVERTER(QString, QCborValue,
@@ -1679,17 +1718,17 @@ private:
QHash<Key, T> map;
};
-typedef QMetaTypeFunctionRegistry<QMetaType::ConverterFunction,QPair<int,int> >
-QMetaTypeConverterRegistry;
+using QMetaTypeConverterRegistry
+ = QMetaTypeFunctionRegistry<QMetaType::ConverterFunction, std::pair<int,int>>;
Q_GLOBAL_STATIC(QMetaTypeConverterRegistry, customTypesConversionRegistry)
using QMetaTypeMutableViewRegistry
- = QMetaTypeFunctionRegistry<QMetaType::MutableViewFunction, QPair<int,int>>;
+ = QMetaTypeFunctionRegistry<QMetaType::MutableViewFunction, std::pair<int,int>>;
Q_GLOBAL_STATIC(QMetaTypeMutableViewRegistry, customTypesMutableViewRegistry)
/*!
- \fn bool QMetaType::registerConverter()
+ \fn template<typename From, typename To> bool QMetaType::registerConverter()
\since 5.2
Registers the possibility of an implicit conversion from type From to type To in the meta
type system. Returns \c true if the registration succeeded, otherwise false.
@@ -1741,7 +1780,7 @@ Q_GLOBAL_STATIC(QMetaTypeMutableViewRegistry, customTypesMutableViewRegistry)
*/
bool QMetaType::registerConverterFunction(const ConverterFunction &f, QMetaType from, QMetaType to)
{
- if (!customTypesConversionRegistry()->insertIfNotContains(qMakePair(from.id(), to.id()), f)) {
+ if (!customTypesConversionRegistry()->insertIfNotContains({from.id(), to.id()}, f)) {
qWarning("Type conversion already registered from type %s to type %s",
from.name(), to.name());
return false;
@@ -1774,7 +1813,7 @@ bool QMetaType::registerConverterFunction(const ConverterFunction &f, QMetaType
*/
bool QMetaType::registerMutableViewFunction(const MutableViewFunction &f, QMetaType from, QMetaType to)
{
- if (!customTypesMutableViewRegistry()->insertIfNotContains(qMakePair(from.id(), to.id()), f)) {
+ if (!customTypesMutableViewRegistry()->insertIfNotContains({from.id(), to.id()}, f)) {
qWarning("Mutable view on type already registered from type %s to type %s",
from.name(), to.name());
return false;
@@ -1842,7 +1881,7 @@ bool QMetaType::debugStream(QDebug& dbg, const void *rhs)
*/
/*!
- \fn bool QMetaType::hasRegisteredDebugStreamOperator()
+ \fn template<typename T> bool QMetaType::hasRegisteredDebugStreamOperator()
\deprecated
\since 5.2
@@ -1947,18 +1986,17 @@ static bool convertFromEnum(QMetaType fromType, const void *from, QMetaType toTy
if (toType.id() != QMetaType::QString && toType.id() != QMetaType::QByteArray)
return QMetaType::convert(QMetaType::fromType<qlonglong>(), &ll, toType, to);
}
- Q_ASSERT(toType.id() == QMetaType::QString || toType.id() == QMetaType::QByteArray);
#ifndef QT_NO_QOBJECT
QMetaEnum en = metaEnumFromType(fromType);
if (en.isValid()) {
if (en.isFlag()) {
- const QByteArray keys = en.valueToKeys(ll);
+ const QByteArray keys = en.valueToKeys(static_cast<int>(ll));
if (toType.id() == QMetaType::QString)
*static_cast<QString *>(to) = QString::fromUtf8(keys);
else
*static_cast<QByteArray *>(to) = keys;
} else {
- const char *key = en.valueToKey(ll);
+ const char *key = en.valueToKey(static_cast<int>(ll));
if (toType.id() == QMetaType::QString)
*static_cast<QString *>(to) = QString::fromUtf8(key);
else
@@ -1967,6 +2005,8 @@ static bool convertFromEnum(QMetaType fromType, const void *from, QMetaType toTy
return true;
}
#endif
+ if (toType.id() == QMetaType::QString || toType.id() == QMetaType::QByteArray)
+ return QMetaType::convert(QMetaType::fromType<qlonglong>(), &ll, toType, to);
return false;
}
@@ -1978,12 +2018,12 @@ static bool convertToEnum(QMetaType fromType, const void *from, QMetaType toType
#ifndef QT_NO_QOBJECT
if (fromTypeId == QMetaType::QString || fromTypeId == QMetaType::QByteArray) {
QMetaEnum en = metaEnumFromType(toType);
- if (!en.isValid())
- return false;
- QByteArray keys = (fromTypeId == QMetaType::QString)
- ? static_cast<const QString *>(from)->toUtf8()
- : *static_cast<const QByteArray *>(from);
- value = en.keysToValue(keys.constData(), &ok);
+ if (en.isValid()) {
+ QByteArray keys = (fromTypeId == QMetaType::QString)
+ ? static_cast<const QString *>(from)->toUtf8()
+ : *static_cast<const QByteArray *>(from);
+ value = en.keysToValue(keys.constData(), &ok);
+ }
}
#endif
if (!ok) {
@@ -2060,13 +2100,12 @@ static bool convertIterableToVariantHash(QMetaType fromType, const void *from, v
h.insert(it.key().toString(), it.value());
return true;
}
-#endif
static bool convertIterableToVariantPair(QMetaType fromType, const void *from, void *to)
{
- const QMetaType::ConverterFunction * const f =
- customTypesConversionRegistry()->function(qMakePair(fromType.id(),
- qMetaTypeId<QtMetaTypePrivate::QPairVariantInterfaceImpl>()));
+ const int targetId = qMetaTypeId<QtMetaTypePrivate::QPairVariantInterfaceImpl>();
+ const auto f = customTypesConversionRegistry()->function({fromType.id(), targetId});
+
if (!f)
return false;
@@ -2092,7 +2131,6 @@ static bool convertIterableToVariantPair(QMetaType fromType, const void *from, v
return true;
}
-#ifndef QT_BOOTSTRAPPED
static bool convertToSequentialIterable(QMetaType fromType, const void *from, void *to)
{
using namespace QtMetaTypePrivate;
@@ -2218,6 +2256,9 @@ static bool convertToAssociativeIterable(QMetaType fromType, const void *from, v
static bool canConvertMetaObject(QMetaType fromType, QMetaType toType)
{
+ if ((fromType.flags() & QMetaType::IsPointer) != (toType.flags() & QMetaType::IsPointer))
+ return false; // Can not convert between pointer and value
+
const QMetaObject *f = fromType.metaObject();
const QMetaObject *t = toType.metaObject();
if (f && t) {
@@ -2288,7 +2329,8 @@ static bool convertMetaObject(QMetaType fromType, const void *from, QMetaType to
*static_cast<void **>(to) = nullptr;
return fromType.metaObject()->inherits(toType.metaObject());
}
- } else {
+ } else if ((fromType.flags() & QMetaType::IsPointer) == (toType.flags() & QMetaType::IsPointer)) {
+ // fromType and toType are of same 'pointedness'
const QMetaObject *f = fromType.metaObject();
const QMetaObject *t = toType.metaObject();
if (f && t && f->inherits(t)) {
@@ -2299,7 +2341,7 @@ static bool convertMetaObject(QMetaType fromType, const void *from, QMetaType to
}
return false;
}
-#endif
+#endif // !QT_BOOTSTRAPPED
/*!
\fn bool QMetaType::convert(const void *from, int fromTypeId, void *to, int toTypeId)
@@ -2340,8 +2382,7 @@ bool QMetaType::convert(QMetaType fromType, const void *from, QMetaType toType,
if (moduleHelper->convert(from, fromTypeId, to, toTypeId))
return true;
}
- const QMetaType::ConverterFunction * const f =
- customTypesConversionRegistry()->function(qMakePair(fromTypeId, toTypeId));
+ const auto f = customTypesConversionRegistry()->function({fromTypeId, toTypeId});
if (f)
return (*f)(from, to);
@@ -2357,10 +2398,11 @@ bool QMetaType::convert(QMetaType fromType, const void *from, QMetaType toType,
}
}
+#ifndef QT_BOOTSTRAPPED
+# ifndef QT_NO_VARIANT
if (toTypeId == QVariantPair && convertIterableToVariantPair(fromType, from, to))
return true;
-#ifndef QT_BOOTSTRAPPED
// handle iterables
if (toTypeId == QVariantList && convertIterableToVariantList(fromType, from, to))
return true;
@@ -2370,6 +2412,7 @@ bool QMetaType::convert(QMetaType fromType, const void *from, QMetaType toType,
if (toTypeId == QVariantHash && convertIterableToVariantHash(fromType, from, to))
return true;
+# endif
if (toTypeId == qMetaTypeId<QSequentialIterable>())
return convertToSequentialIterable(fromType, from, to);
@@ -2396,8 +2439,7 @@ bool QMetaType::view(QMetaType fromType, void *from, QMetaType toType, void *to)
int fromTypeId = fromType.id();
int toTypeId = toType.id();
- const QMetaType::MutableViewFunction * const f =
- customTypesMutableViewRegistry()->function(qMakePair(fromTypeId, toTypeId));
+ const auto f = customTypesMutableViewRegistry()->function({fromTypeId, toTypeId});
if (f)
return (*f)(from, to);
@@ -2439,8 +2481,7 @@ bool QMetaType::canView(QMetaType fromType, QMetaType toType)
if (fromTypeId == UnknownType || toTypeId == UnknownType)
return false;
- const MutableViewFunction * const f =
- customTypesMutableViewRegistry()->function(qMakePair(fromTypeId, toTypeId));
+ const auto f = customTypesMutableViewRegistry()->function({fromTypeId, toTypeId});
if (f)
return true;
@@ -2555,7 +2596,8 @@ bool QMetaType::canConvert(QMetaType fromType, QMetaType toType)
if (toTypeId == qMetaTypeId<QAssociativeIterable>())
return canConvertToAssociativeIterable(fromType);
-
+#endif
+#ifndef QT_NO_VARIANT
if (toTypeId == QVariantList
&& canConvert(fromType, QMetaType::fromType<QSequentialIterable>())) {
return true;
@@ -2565,11 +2607,11 @@ bool QMetaType::canConvert(QMetaType fromType, QMetaType toType)
&& canConvert(fromType, QMetaType::fromType<QAssociativeIterable>())) {
return true;
}
-#endif
if (toTypeId == QVariantPair && hasRegisteredConverterFunction(
fromType, QMetaType::fromType<QtMetaTypePrivate::QPairVariantInterfaceImpl>()))
return true;
+#endif
if (fromType.flags() & IsEnumeration) {
if (toTypeId == QString || toTypeId == QByteArray)
@@ -2601,7 +2643,7 @@ bool QMetaType::canConvert(QMetaType fromType, QMetaType toType)
*/
/*!
- \fn bool QMetaType::hasRegisteredConverterFunction()
+ \fn template<typename From, typename To> bool QMetaType::hasRegisteredConverterFunction()
Returns \c true, if the meta type system has a registered conversion from type From to type To.
\since 5.2
\overload
@@ -2614,11 +2656,41 @@ bool QMetaType::canConvert(QMetaType fromType, QMetaType toType)
*/
bool QMetaType::hasRegisteredConverterFunction(QMetaType fromType, QMetaType toType)
{
- return customTypesConversionRegistry()->contains(qMakePair(fromType.id(), toType.id()));
+ return customTypesConversionRegistry()->contains({fromType.id(), toType.id()});
+}
+
+/*!
+ \internal
+ Non-template helper ("SCARY") for IsMetaTypePair::registerConverter().
+*/
+bool QtPrivate::hasRegisteredConverterFunctionToPairVariantInterface(QMetaType m)
+{
+ const QMetaType to = QMetaType::fromType<QtMetaTypePrivate::QPairVariantInterfaceImpl>();
+ return QMetaType::hasRegisteredConverterFunction(m, to);
+}
+
+/*!
+ \internal
+ Non-template helper ("SCARY") for SequentialValueTypeIsMetaType::registerConverter().
+*/
+bool QtPrivate::hasRegisteredConverterFunctionToIterableMetaSequence(QMetaType m)
+{
+ const QMetaType to = QMetaType::fromType<QIterable<QMetaSequence>>();
+ return QMetaType::hasRegisteredConverterFunction(m, to);
+}
+
+/*!
+ \internal
+ Non-template helper ("SCARY") for AssociativeKeyTypeIsMetaType::registerConverter().
+*/
+bool QtPrivate::hasRegisteredConverterFunctionToIterableMetaAssociation(QMetaType m)
+{
+ const QMetaType to = QMetaType::fromType<QIterable<QMetaAssociation>>();
+ return QMetaType::hasRegisteredConverterFunction(m, to);
}
/*!
- \fn bool QMetaType::hasRegisteredMutableViewFunction()
+ \fn template<typename From, typename To> bool QMetaType::hasRegisteredMutableViewFunction()
Returns \c true, if the meta type system has a registered mutable view on type From of type To.
\since 6.0
\overload
@@ -2631,7 +2703,27 @@ bool QMetaType::hasRegisteredConverterFunction(QMetaType fromType, QMetaType toT
*/
bool QMetaType::hasRegisteredMutableViewFunction(QMetaType fromType, QMetaType toType)
{
- return customTypesMutableViewRegistry()->contains(qMakePair(fromType.id(), toType.id()));
+ return customTypesMutableViewRegistry()->contains({fromType.id(), toType.id()});
+}
+
+/*!
+ \internal
+ Non-template helper ("SCARY") for SequentialValueTypeIsMetaType::registerMutableView().
+*/
+bool QtPrivate::hasRegisteredMutableViewFunctionToIterableMetaSequence(QMetaType m)
+{
+ const QMetaType to = QMetaType::fromType<QIterable<QMetaSequence>>();
+ return QMetaType::hasRegisteredMutableViewFunction(m, to);
+}
+
+/*!
+ \internal
+ Non-template helper ("SCARY") for AssociativeKeyTypeIsMetaType::registerMutableView().
+*/
+bool QtPrivate::hasRegisteredMutableViewFunctionToIterableMetaAssociation(QMetaType m)
+{
+ const QMetaType to = QMetaType::fromType<QIterable<QMetaAssociation>>();
+ return QMetaType::hasRegisteredMutableViewFunction(m, to);
}
/*!
@@ -2882,6 +2974,59 @@ bool QMetaType::hasRegisteredDataStreamOperators() const
}
/*!
+ \since 6.6
+
+ If this metatype represents an enumeration, this method returns a
+ metatype of a numeric class of the same signedness and size as the
+ enums underlying type.
+ If it represents a QFlags type, it returns QMetaType::Int.
+ In all other cases an invalid QMetaType is returned.
+ */
+QMetaType QMetaType::underlyingType() const
+{
+ if (!d_ptr || !(flags() & IsEnumeration))
+ return {};
+ /* QFlags has enumeration set so that's handled here (qint32
+ case), as QFlags uses int as the underlying type
+ Note that we do some approximation here, as we cannot
+ differentiate between different underlying types of the
+ same size and signedness (consider char <-> (un)signed char,
+ int <-> long <-> long long).
+
+ ### TODO PENDING: QTBUG-111926 - QFlags supporting >32 bit int
+ */
+ if (flags() & IsUnsignedEnumeration) {
+ switch (sizeOf()) {
+ case 1:
+ return QMetaType::fromType<quint8>();
+ case 2:
+ return QMetaType::fromType<quint16>();
+ case 4:
+ return QMetaType::fromType<quint32>();
+ case 8:
+ return QMetaType::fromType<quint64>();
+ default:
+ break;
+ }
+ } else {
+ switch (sizeOf()) {
+ case 1:
+ return QMetaType::fromType<qint8>();
+ case 2:
+ return QMetaType::fromType<qint16>();
+ case 4:
+ return QMetaType::fromType<qint32>();
+ case 8:
+ return QMetaType::fromType<qint64>();
+ default:
+ break;
+ }
+ }
+ // int128 can be handled above once we have qint128
+ return QMetaType();
+}
+
+/*!
\fn bool QMetaType::load(QDataStream &stream, int type, void *data)
\overload
\deprecated
@@ -2993,7 +3138,7 @@ QMetaType QMetaType::fromName(QByteArrayView typeName)
*/
/*!
- \fn int qRegisterMetaType(const char *typeName)
+ \fn template <typename T> int qRegisterMetaType(const char *typeName)
\relates QMetaType
\obsolete
\threadsafe
@@ -3027,7 +3172,7 @@ QMetaType QMetaType::fromName(QByteArrayView typeName)
*/
/*!
- \fn int qRegisterMetaType()
+ \fn template <typename T> int qRegisterMetaType()
\relates QMetaType
\threadsafe
\since 4.2
@@ -3083,7 +3228,7 @@ QMetaType QMetaType::fromName(QByteArrayView typeName)
*/
/*!
- \fn int qMetaTypeId()
+ \fn template <typename T> int qMetaTypeId()
\relates QMetaType
\threadsafe
\since 4.1
@@ -3148,6 +3293,7 @@ QT_FOR_EACH_STATIC_PRIMITIVE_POINTER(QT_METATYPE_DECLARE_TEMPLATE_ITER)
QT_FOR_EACH_STATIC_CORE_CLASS(QT_METATYPE_DECLARE_TEMPLATE_ITER)
QT_FOR_EACH_STATIC_CORE_POINTER(QT_METATYPE_DECLARE_TEMPLATE_ITER)
QT_FOR_EACH_STATIC_CORE_TEMPLATE(QT_METATYPE_DECLARE_TEMPLATE_ITER)
+
#undef QT_METATYPE_DECLARE_TEMPLATE_ITER
#endif
}
diff --git a/src/corelib/kernel/qmetatype.h b/src/corelib/kernel/qmetatype.h
index 4c92a52f9e..1e944660c6 100644
--- a/src/corelib/kernel/qmetatype.h
+++ b/src/corelib/kernel/qmetatype.h
@@ -18,6 +18,7 @@
#include <QtCore/qobjectdefs.h>
#endif
#include <QtCore/qscopeguard.h>
+#include <QtCore/qttypetraits.h>
#include <array>
#include <new>
@@ -26,7 +27,7 @@
#include <map>
#include <functional>
#include <optional>
-#include <type_traits>
+#include <QtCore/q20type_traits.h>
#ifdef Bool
#error qmetatype.h must be included before any header file that defines Bool
@@ -92,6 +93,12 @@ inline constexpr int qMetaTypeId();
#else
# define QT_FOR_EACH_STATIC_REGULAR_EXPRESSION(F)
#endif
+#ifndef QT_NO_VARIANT
+# define QT_FOR_EACH_STATIC_QVARIANT(F) \
+ F(QVariant, 41, QVariant)
+#else
+# define QT_FOR_EACH_STATIC_QVARIANT(F)
+#endif
#define QT_FOR_EACH_STATIC_CORE_CLASS(F)\
F(QChar, 7, QChar) \
@@ -113,7 +120,7 @@ inline constexpr int qMetaTypeId();
F(QPointF, 26, QPointF) \
QT_FOR_EACH_STATIC_EASINGCURVE(F) \
F(QUuid, 30, QUuid) \
- F(QVariant, 41, QVariant) \
+ QT_FOR_EACH_STATIC_QVARIANT(F) \
QT_FOR_EACH_STATIC_REGULAR_EXPRESSION(F) \
F(QJsonValue, 45, QJsonValue) \
F(QJsonObject, 46, QJsonObject) \
@@ -128,13 +135,20 @@ inline constexpr int qMetaTypeId();
#define QT_FOR_EACH_STATIC_CORE_POINTER(F)\
F(QObjectStar, 39, QObject*)
-#define QT_FOR_EACH_STATIC_CORE_TEMPLATE(F)\
+#ifndef QT_NO_VARIANT
+# define QT_FOR_EACH_STATIC_CORE_TEMPLATE(F)\
F(QVariantMap, 8, QVariantMap) \
F(QVariantList, 9, QVariantList) \
F(QVariantHash, 28, QVariantHash) \
F(QVariantPair, 58, QVariantPair) \
F(QByteArrayList, 49, QByteArrayList) \
F(QStringList, 11, QStringList) \
+ /**/
+#else
+# define QT_FOR_EACH_STATIC_CORE_TEMPLATE(F)\
+ F(QByteArrayList, 49, QByteArrayList) \
+ F(QStringList, 11, QStringList)
+#endif
#if QT_CONFIG(shortcut)
#define QT_FOR_EACH_STATIC_KEYSEQUENCE_CLASS(F)\
@@ -188,12 +202,20 @@ inline constexpr int qMetaTypeId();
F(UInt, -1, uint, "quint32") \
F(LongLong, -1, qlonglong, "qint64") \
F(ULongLong, -1, qulonglong, "quint64") \
+ F(QByteArrayList, -1, QByteArrayList, "QList<QByteArray>") \
+ F(QStringList, -1, QStringList, "QList<QString>") \
+ QT_FOR_EACH_STATIC_VARIANT_ALIAS_TYPE(F)
+
+#ifndef QT_NO_VARIANT
+#define QT_FOR_EACH_STATIC_VARIANT_ALIAS_TYPE(F) \
F(QVariantList, -1, QVariantList, "QList<QVariant>") \
F(QVariantMap, -1, QVariantMap, "QMap<QString,QVariant>") \
F(QVariantHash, -1, QVariantHash, "QHash<QString,QVariant>") \
F(QVariantPair, -1, QVariantPair, "QPair<QVariant,QVariant>") \
- F(QByteArrayList, -1, QByteArrayList, "QList<QByteArray>") \
- F(QStringList, -1, QStringList, "QList<QString>") \
+ /**/
+#else
+#define QT_FOR_EACH_STATIC_VARIANT_ALIAS_TYPE(F)
+#endif
#define QT_FOR_EACH_STATIC_TYPE(F)\
QT_FOR_EACH_STATIC_PRIMITIVE_TYPE(F)\
@@ -246,7 +268,14 @@ using NonConstMetaTypeInterface = const QMetaTypeInterface;
class QMetaTypeInterface
{
public:
- ushort revision; // 0 in Qt 6.0. Can increase if new field are added
+
+ /* Revision: Can increase if new field are added, or if semantics changes
+ 0: Initial Revision
+ 1: the meaning of the NeedsDestruction flag changed
+ */
+ static inline constexpr ushort CurrentRevision = 1;
+
+ ushort revision;
ushort alignment;
uint size;
uint flags;
@@ -485,23 +514,25 @@ public:
#endif
#endif
+ QMetaType underlyingType() const;
+
template<typename T>
constexpr static QMetaType fromType();
static QMetaType fromName(QByteArrayView name);
-
- friend bool operator==(QMetaType a, QMetaType b)
+private:
+ friend bool comparesEqual(const QMetaType &lhs,
+ const QMetaType &rhs) noexcept
{
- if (a.d_ptr == b.d_ptr)
+ if (lhs.d_ptr == rhs.d_ptr)
return true;
- if (!a.d_ptr || !b.d_ptr)
+ if (!lhs.d_ptr || !rhs.d_ptr)
return false; // one type is undefined, the other is defined
// avoid id call if we already have the id
- const int aId = a.id();
- const int bId = b.id();
+ const int aId = lhs.id();
+ const int bId = rhs.id();
return aId == bId;
}
- friend bool operator!=(QMetaType a, QMetaType b) { return !(a == b); }
-
+ Q_DECLARE_EQUALITY_COMPARABLE(QMetaType)
#ifndef QT_NO_DEBUG_STREAM
private:
friend Q_CORE_EXPORT QDebug operator<<(QDebug d, QMetaType m);
@@ -606,8 +637,7 @@ public:
const From *f = static_cast<const From *>(from);
To *t = static_cast<To *>(to);
auto &&r = function(*f);
- if constexpr (std::is_same_v<std::remove_cv_t<std::remove_reference_t<decltype(r)>>,
- std::optional<To>>) {
+ if constexpr (std::is_same_v<q20::remove_cvref_t<decltype(r)>, std::optional<To>>) {
if (!r)
return false;
*t = *std::forward<decltype(r)>(r);
@@ -1251,7 +1281,7 @@ namespace QtPrivate {
struct QMetaTypeTypeFlags
{
enum { Flags = (QTypeInfo<T>::isRelocatable ? QMetaType::RelocatableType : 0)
- | (!std::is_trivially_default_constructible_v<T> ? QMetaType::NeedsConstruction : 0)
+ | ((!std::is_default_constructible_v<T> || !QTypeInfo<T>::isValueInitializationBitwiseZero) ? QMetaType::NeedsConstruction : 0)
| (!std::is_trivially_destructible_v<T> ? QMetaType::NeedsDestruction : 0)
| (!std::is_trivially_copy_constructible_v<T> ? QMetaType::NeedsCopyConstruction : 0)
| (!std::is_trivially_move_constructible_v<T> ? QMetaType::NeedsMoveConstruction : 0)
@@ -1262,7 +1292,7 @@ namespace QtPrivate {
| (IsEnumOrFlags<T>::value ? QMetaType::IsEnumeration : 0)
| (IsGadgetHelper<T>::IsGadgetOrDerivedFrom ? QMetaType::IsGadget : 0)
| (IsPointerToGadgetHelper<T>::IsGadgetOrDerivedFrom ? QMetaType::PointerToGadget : 0)
- | (QTypeInfo<T>::isPointer ? QMetaType::IsPointer : 0)
+ | (std::is_pointer_v<T> ? QMetaType::IsPointer : 0)
| (IsUnsignedEnum<T> ? QMetaType::IsUnsignedEnumeration : 0)
| (IsQmlListType<T> ? QMetaType::IsQmlList : 0)
| (std::is_const_v<std::remove_pointer_t<T>> ? QMetaType::IsConst : 0)
@@ -1721,11 +1751,19 @@ QT_FOR_EACH_STATIC_TYPE(Q_DECLARE_BUILTIN_METATYPE)
QT_BEGIN_NAMESPACE
+namespace QtPrivate {
+// out-of-line helpers to reduce template code bloat ("SCARY") and improve compile times:
+Q_CORE_EXPORT bool hasRegisteredConverterFunctionToPairVariantInterface(QMetaType m);
+Q_CORE_EXPORT bool hasRegisteredConverterFunctionToIterableMetaSequence(QMetaType m);
+Q_CORE_EXPORT bool hasRegisteredMutableViewFunctionToIterableMetaSequence(QMetaType m);
+Q_CORE_EXPORT bool hasRegisteredConverterFunctionToIterableMetaAssociation(QMetaType m);
+Q_CORE_EXPORT bool hasRegisteredMutableViewFunctionToIterableMetaAssociation(QMetaType m);
+}
+
template <typename T>
inline bool QtPrivate::IsMetaTypePair<T, true>::registerConverter()
{
- const QMetaType to = QMetaType::fromType<QtMetaTypePrivate::QPairVariantInterfaceImpl>();
- if (!QMetaType::hasRegisteredConverterFunction(QMetaType::fromType<T>(), to)) {
+ if (!QtPrivate::hasRegisteredConverterFunctionToPairVariantInterface(QMetaType::fromType<T>())) {
QtMetaTypePrivate::QPairVariantInterfaceConvertFunctor<T> o;
return QMetaType::registerConverter<T, QtMetaTypePrivate::QPairVariantInterfaceImpl>(o);
}
@@ -1757,8 +1795,7 @@ struct SequentialValueTypeIsMetaType<T, true>
{
static bool registerConverter()
{
- const QMetaType to = QMetaType::fromType<QIterable<QMetaSequence>>();
- if (!QMetaType::hasRegisteredConverterFunction(QMetaType::fromType<T>(), to)) {
+ if (!QtPrivate::hasRegisteredConverterFunctionToIterableMetaSequence(QMetaType::fromType<T>())) {
QSequentialIterableConvertFunctor<T> o;
return QMetaType::registerConverter<T, QIterable<QMetaSequence>>(o);
}
@@ -1767,8 +1804,7 @@ struct SequentialValueTypeIsMetaType<T, true>
static bool registerMutableView()
{
- const QMetaType to = QMetaType::fromType<QIterable<QMetaSequence>>();
- if (!QMetaType::hasRegisteredMutableViewFunction(QMetaType::fromType<T>(), to)) {
+ if (!QtPrivate::hasRegisteredMutableViewFunctionToIterableMetaSequence(QMetaType::fromType<T>())) {
QSequentialIterableMutableViewFunctor<T> o;
return QMetaType::registerMutableView<T, QIterable<QMetaSequence>>(o);
}
@@ -1801,8 +1837,7 @@ struct AssociativeKeyTypeIsMetaType<T, true> : AssociativeMappedTypeIsMetaType<T
{
static bool registerConverter()
{
- const QMetaType to = QMetaType::fromType<QIterable<QMetaAssociation>>();
- if (!QMetaType::hasRegisteredConverterFunction(QMetaType::fromType<T>(), to)) {
+ if (!QtPrivate::hasRegisteredConverterFunctionToIterableMetaAssociation(QMetaType::fromType<T>())) {
QAssociativeIterableConvertFunctor<T> o;
return QMetaType::registerConverter<T, QIterable<QMetaAssociation>>(o);
}
@@ -1811,8 +1846,7 @@ struct AssociativeKeyTypeIsMetaType<T, true> : AssociativeMappedTypeIsMetaType<T
static bool registerMutableView()
{
- const QMetaType to = QMetaType::fromType<QIterable<QMetaAssociation>>();
- if (!QMetaType::hasRegisteredMutableViewFunction(QMetaType::fromType<T>(), to)) {
+ if (!QtPrivate::hasRegisteredMutableViewFunctionToIterableMetaAssociation(QMetaType::fromType<T>())) {
QAssociativeIterableMutableViewFunctor<T> o;
return QMetaType::registerMutableView<T, QIterable<QMetaAssociation>>(o);
}
@@ -2227,6 +2261,7 @@ struct is_std_pair<std::pair<T1_, T2_>> : std::true_type {
using T2 = T2_;
};
+namespace TypeNameHelper {
template<typename T>
constexpr auto typenameHelper()
{
@@ -2268,15 +2303,15 @@ constexpr auto typenameHelper()
QT_STRINGIFY(QT_NAMESPACE) "::"
#endif
#if defined(Q_CC_MSVC) && defined(Q_CC_CLANG)
- "auto __cdecl QtPrivate::typenameHelper(void) [T = "
+ "auto __cdecl QtPrivate::TypeNameHelper::typenameHelper(void) [T = "
#elif defined(Q_CC_MSVC)
- "auto __cdecl QtPrivate::typenameHelper<"
+ "auto __cdecl QtPrivate::TypeNameHelper::typenameHelper<"
#elif defined(Q_CC_CLANG)
- "auto QtPrivate::typenameHelper() [T = "
+ "auto QtPrivate::TypeNameHelper::typenameHelper() [T = "
#elif defined(Q_CC_GHS)
- "auto QtPrivate::typenameHelper<T>()[with T="
+ "auto QtPrivate::TypeNameHelper::typenameHelper<T>()[with T="
#else
- "constexpr auto QtPrivate::typenameHelper() [with T = "
+ "constexpr auto QtPrivate::TypeNameHelper::typenameHelper() [with T = "
#endif
) - 1;
#if defined(Q_CC_MSVC) && !defined(Q_CC_CLANG)
@@ -2302,6 +2337,8 @@ constexpr auto typenameHelper()
return result;
}
}
+} // namespace TypeNameHelper
+using TypeNameHelper::typenameHelper;
template<typename T, typename = void>
struct BuiltinMetaType : std::integral_constant<int, 0>
@@ -2358,18 +2395,20 @@ struct QDebugStreamOperatorForType <T, false>
template<typename T, bool = QTypeTraits::has_stream_operator_v<QDataStream, T>>
struct QDataStreamOperatorForType
{
- static void dataStreamOut(const QMetaTypeInterface *, QDataStream &ds, const void *a)
- { ds << *reinterpret_cast<const T *>(a); }
- static void dataStreamIn(const QMetaTypeInterface *, QDataStream &ds, void *a)
- { ds >> *reinterpret_cast<T *>(a); }
+ static constexpr QMetaTypeInterface::DataStreamOutFn dataStreamOut = nullptr;
+ static constexpr QMetaTypeInterface::DataStreamInFn dataStreamIn = nullptr;
};
+#ifndef QT_NO_DATASTREAM
template<typename T>
-struct QDataStreamOperatorForType <T, false>
+struct QDataStreamOperatorForType <T, true>
{
- static constexpr QMetaTypeInterface::DataStreamOutFn dataStreamOut = nullptr;
- static constexpr QMetaTypeInterface::DataStreamInFn dataStreamIn = nullptr;
+ static void dataStreamOut(const QMetaTypeInterface *, QDataStream &ds, const void *a)
+ { ds << *reinterpret_cast<const T *>(a); }
+ static void dataStreamIn(const QMetaTypeInterface *, QDataStream &ds, void *a)
+ { ds >> *reinterpret_cast<T *>(a); }
};
+#endif
// Performance optimization:
//
@@ -2394,7 +2433,7 @@ public:
static constexpr QMetaTypeInterface::DefaultCtrFn getDefaultCtr()
{
- if constexpr (std::is_default_constructible_v<S> && !std::is_trivially_default_constructible_v<S>) {
+ if constexpr (std::is_default_constructible_v<S> && !QTypeInfo<S>::isValueInitializationBitwiseZero) {
return [](const QMetaTypeInterface *, void *addr) { new (addr) S(); };
} else {
return nullptr;
@@ -2462,7 +2501,7 @@ struct QMetaTypeInterfaceWrapper
using InterfaceType = std::conditional_t<IsConstMetaTypeInterface, const QMetaTypeInterface, NonConstMetaTypeInterface>;
static inline InterfaceType metaType = {
- /*.revision=*/ 0,
+ /*.revision=*/ QMetaTypeInterface::CurrentRevision,
/*.alignment=*/ alignof(T),
/*.size=*/ sizeof(T),
/*.flags=*/ QMetaTypeForType<T>::Flags,
diff --git a/src/corelib/kernel/qmetatype_p.h b/src/corelib/kernel/qmetatype_p.h
index 3defbc70ef..7e0457771f 100644
--- a/src/corelib/kernel/qmetatype_p.h
+++ b/src/corelib/kernel/qmetatype_p.h
@@ -38,20 +38,21 @@ QT_BEGIN_NAMESPACE
assign_and_return \
}
-class QMetaTypeModuleHelper
+class Q_CORE_EXPORT QMetaTypeModuleHelper
{
Q_DISABLE_COPY_MOVE(QMetaTypeModuleHelper)
protected:
QMetaTypeModuleHelper() = default;
~QMetaTypeModuleHelper() = default;
public:
+ Q_WEAK_OVERLOAD // prevent it from entering the ABI and rendering constexpr useless
static constexpr auto makePair(int from, int to) -> quint64
{
return (quint64(from) << 32) + quint64(to);
}
virtual const QtPrivate::QMetaTypeInterface *interfaceForType(int) const = 0;
- virtual bool convert(const void *, int, void *, int) const { return false; }
+ virtual bool convert(const void *, int, void *, int) const;
};
extern Q_CORE_EXPORT const QMetaTypeModuleHelper *qMetaTypeGuiHelper;
@@ -152,7 +153,12 @@ inline bool isMoveConstructible(const QtPrivate::QMetaTypeInterface *iface) noex
inline bool isDestructible(const QtPrivate::QMetaTypeInterface *iface) noexcept
{
- return checkMetaTypeFlagOrPointer(iface, iface->dtor, QMetaType::NeedsDestruction);
+ /* For metatypes of revision 1, the NeedsDestruction was set even for trivially
+ destructible types, but their dtor pointer would be null.
+ For that reason, we need the additional check here.
+ */
+ return iface->revision < 1 ||
+ checkMetaTypeFlagOrPointer(iface, iface->dtor, QMetaType::NeedsDestruction);
}
inline void defaultConstruct(const QtPrivate::QMetaTypeInterface *iface, void *where)
@@ -173,6 +179,15 @@ inline void copyConstruct(const QtPrivate::QMetaTypeInterface *iface, void *wher
memcpy(where, copy, iface->size);
}
+inline void moveConstruct(const QtPrivate::QMetaTypeInterface *iface, void *where, void *copy)
+{
+ Q_ASSERT(isMoveConstructible(iface));
+ if (iface->moveCtr)
+ iface->moveCtr(iface, where, copy);
+ else
+ memcpy(where, copy, iface->size);
+}
+
inline void construct(const QtPrivate::QMetaTypeInterface *iface, void *where, const void *copy)
{
if (copy)
diff --git a/src/corelib/kernel/qmimedata.cpp b/src/corelib/kernel/qmimedata.cpp
index 6650d2f626..2c0a89dbd7 100644
--- a/src/corelib/kernel/qmimedata.cpp
+++ b/src/corelib/kernel/qmimedata.cpp
@@ -12,12 +12,12 @@ QT_BEGIN_NAMESPACE
using namespace Qt::StringLiterals;
-static inline QString textUriListLiteral() { return QStringLiteral("text/uri-list"); }
-static inline QString textHtmlLiteral() { return QStringLiteral("text/html"); }
-static inline QString textPlainLiteral() { return QStringLiteral("text/plain"); }
-static inline QString textPlainUtf8Literal() { return QStringLiteral("text/plain;charset=utf-8"); }
-static inline QString applicationXColorLiteral() { return QStringLiteral("application/x-color"); }
-static inline QString applicationXQtImageLiteral() { return QStringLiteral("application/x-qt-image"); }
+static inline QString textUriListLiteral() { return u"text/uri-list"_s; }
+static inline QString textHtmlLiteral() { return u"text/html"_s; }
+static inline QString textPlainLiteral() { return u"text/plain"_s; }
+static inline QString textPlainUtf8Literal() { return u"text/plain;charset=utf-8"_s; }
+static inline QString applicationXColorLiteral() { return u"application/x-color"_s; }
+static inline QString applicationXQtImageLiteral() { return u"application/x-qt-image"_s; }
struct QMimeDataStruct
{
@@ -76,6 +76,28 @@ QVariant QMimeDataPrivate::getData(const QString &format) const
return it->data;
}
+static QList<QVariant> dataToUrls(QByteArrayView text)
+{
+ QList<QVariant> list;
+ qsizetype newLineIndex = -1;
+ qsizetype from = 0;
+ const char *begin = text.data();
+ while ((newLineIndex = text.indexOf('\n', from)) != -1) {
+ const auto bav = QByteArrayView(begin + from, begin + newLineIndex).trimmed();
+ if (!bav.isEmpty())
+ list.push_back(QUrl::fromEncoded(bav));
+ from = newLineIndex + 1;
+ if (from >= text.size())
+ break;
+ }
+ if (from != text.size()) {
+ const auto bav = QByteArrayView(begin + from, text.end()).trimmed();
+ if (!bav.isEmpty())
+ list.push_back(QUrl::fromEncoded(bav));
+ }
+ return list;
+}
+
QVariant QMimeDataPrivate::retrieveTypedData(const QString &format, QMetaType type) const
{
Q_Q(const QMimeData);
@@ -92,9 +114,10 @@ QVariant QMimeDataPrivate::retrieveTypedData(const QString &format, QMetaType ty
QString text;
int numUrls = 0;
const QList<QVariant> list = data.toList();
- for (int i = 0; i < list.size(); ++i) {
- if (list.at(i).metaType().id() == QMetaType::QUrl) {
- text += list.at(i).toUrl().toDisplayString() + u'\n';
+ for (const auto &element : list) {
+ if (element.metaType().id() == QMetaType::QUrl) {
+ text += element.toUrl().toDisplayString();
+ text += u'\n';
++numUrls;
}
}
@@ -146,21 +169,13 @@ QVariant QMimeDataPrivate::retrieveTypedData(const QString &format, QMetaType ty
Q_FALLTHROUGH();
}
case QMetaType::QUrl: {
- QByteArray ba = data.toByteArray();
+ auto bav = data.view<QByteArrayView>();
// Qt 3.x will send text/uri-list with a trailing
// null-terminator (that is *not* sent for any other
// text/* mime-type), so chop it off
- if (ba.endsWith('\0'))
- ba.chop(1);
-
- QList<QByteArray> urls = ba.split('\n');
- QList<QVariant> list;
- for (int i = 0; i < urls.size(); ++i) {
- QByteArray ba = urls.at(i).trimmed();
- if (!ba.isEmpty())
- list.append(QUrl::fromEncoded(ba));
- }
- return list;
+ if (bav.endsWith('\0'))
+ bav.chop(1);
+ return dataToUrls(bav);
}
default:
break;
@@ -180,10 +195,10 @@ QVariant QMimeDataPrivate::retrieveTypedData(const QString &format, QMetaType ty
case QMetaType::QVariantList: {
// has to be list of URLs
QByteArray result;
- QList<QVariant> list = data.toList();
- for (int i = 0; i < list.size(); ++i) {
- if (list.at(i).metaType().id() == QMetaType::QUrl) {
- result += list.at(i).toUrl().toEncoded();
+ const QList<QVariant> list = data.toList();
+ for (const auto &element : list) {
+ if (element.metaType().id() == QMetaType::QUrl) {
+ result += element.toUrl().toEncoded();
result += "\r\n";
}
}
@@ -318,10 +333,10 @@ QList<QUrl> QMimeData::urls() const
if (data.metaType().id() == QMetaType::QUrl)
urls.append(data.toUrl());
else if (data.metaType().id() == QMetaType::QVariantList) {
- QList<QVariant> list = data.toList();
- for (int i = 0; i < list.size(); ++i) {
- if (list.at(i).metaType().id() == QMetaType::QUrl)
- urls.append(list.at(i).toUrl());
+ const QList<QVariant> list = data.toList();
+ for (const auto &element : list) {
+ if (element.metaType().id() == QMetaType::QUrl)
+ urls.append(element.toUrl());
}
}
return urls;
@@ -341,13 +356,7 @@ QList<QUrl> QMimeData::urls() const
void QMimeData::setUrls(const QList<QUrl> &urls)
{
Q_D(QMimeData);
- QList<QVariant> list;
- const int numUrls = urls.size();
- list.reserve(numUrls);
- for (int i = 0; i < numUrls; ++i)
- list.append(urls.at(i));
-
- d->setData(textUriListLiteral(), list);
+ d->setData(textUriListLiteral(), QList<QVariant>(urls.cbegin(), urls.cend()));
}
/*!
@@ -560,17 +569,10 @@ void QMimeData::setData(const QString &mimeType, const QByteArray &data)
Q_D(QMimeData);
if (mimeType == "text/uri-list"_L1) {
- QByteArray ba = data;
+ auto ba = QByteArrayView(data);
if (ba.endsWith('\0'))
ba.chop(1);
- QList<QByteArray> urls = ba.split('\n');
- QList<QVariant> list;
- for (int i = 0; i < urls.size(); ++i) {
- QByteArray ba = urls.at(i).trimmed();
- if (!ba.isEmpty())
- list.append(QUrl::fromEncoded(ba));
- }
- d->setData(mimeType, list);
+ d->setData(mimeType, dataToUrls(ba));
} else {
d->setData(mimeType, QVariant(data));
}
diff --git a/src/corelib/kernel/qobject.cpp b/src/corelib/kernel/qobject.cpp
index 81bcf19114..31b779dc9b 100644
--- a/src/corelib/kernel/qobject.cpp
+++ b/src/corelib/kernel/qobject.cpp
@@ -12,6 +12,7 @@
#include "qabstracteventdispatcher_p.h"
#include "qcoreapplication.h"
#include "qcoreapplication_p.h"
+#include "qcoreevent_p.h"
#include "qloggingcategory.h"
#include "qvariant.h"
#include "qmetaobject.h"
@@ -21,14 +22,12 @@
#include <qthread.h>
#include <private/qthread_p.h>
#include <qdebug.h>
-#include <qpair.h>
#include <qvarlengtharray.h>
#include <qscopeguard.h>
#include <qset.h>
#if QT_CONFIG(thread)
#include <qsemaphore.h>
#endif
-#include <qsharedpointer.h>
#include <private/qorderedmutexlocker_p.h>
#include <private/qhooks_p.h>
@@ -43,6 +42,17 @@
QT_BEGIN_NAMESPACE
+Q_TRACE_POINT(qtcore, QObject_ctor, QObject *object);
+Q_TRACE_POINT(qtcore, QObject_dtor, QObject *object);
+Q_TRACE_POINT(qtcore, QMetaObject_activate_entry, QObject *sender, int signalIndex);
+Q_TRACE_POINT(qtcore, QMetaObject_activate_exit);
+Q_TRACE_POINT(qtcore, QMetaObject_activate_slot_entry, QObject *receiver, int slotIndex);
+Q_TRACE_POINT(qtcore, QMetaObject_activate_slot_exit);
+Q_TRACE_POINT(qtcore, QMetaObject_activate_slot_functor_entry, void *slotObject);
+Q_TRACE_POINT(qtcore, QMetaObject_activate_slot_functor_exit);
+Q_TRACE_POINT(qtcore, QMetaObject_activate_declarative_signal_entry, QObject *sender, int signalIndex);
+Q_TRACE_POINT(qtcore, QMetaObject_activate_declarative_signal_exit);
+
static int DIRECT_CONNECTION_ONLY = 0;
Q_LOGGING_CATEGORY(lcConnectSlotsByName, "qt.core.qmetaobject.connectslotsbyname")
@@ -74,6 +84,8 @@ static int *queuedConnectionTypes(const QMetaMethod &method)
typeIds[i] = QMetaType::VoidStar;
else
typeIds[i] = metaType.id();
+ if (!typeIds[i] && method.parameterTypeName(i).endsWith('*'))
+ typeIds[i] = QMetaType::VoidStar;
if (!typeIds[i]) {
const QByteArray typeName = method.parameterTypeName(i);
qCWarning(lcConnect,
@@ -166,6 +178,7 @@ QObjectPrivate::QObjectPrivate(int version)
isQuickItem = false;
willBeWidget = false;
wasWidget = false;
+ receiveParentEvents = false; // If object wants ParentAboutToChange and ParentChange
}
QObjectPrivate::~QObjectPrivate()
@@ -178,8 +191,8 @@ QObjectPrivate::~QObjectPrivate()
thisThreadData->eventDispatcher.loadRelaxed()->unregisterTimers(q_ptr);
// release the timer ids back to the pool
- for (int i = 0; i < extraData->runningTimers.size(); ++i)
- QAbstractEventDispatcherPrivate::releaseTimerId(extraData->runningTimers.at(i));
+ for (auto id : std::as_const(extraData->runningTimers))
+ QAbstractEventDispatcherPrivate::releaseTimerId(id);
} else {
qWarning("QObject::~QObject: Timers cannot be stopped from another thread");
}
@@ -214,32 +227,11 @@ static void computeOffsets(const QMetaObject *metaobject, int *signalOffset, int
}
// Used by QAccessibleWidget
-bool QObjectPrivate::isSender(const QObject *receiver, const char *signal) const
-{
- Q_Q(const QObject);
- int signal_index = signalIndex(signal);
- ConnectionData *cd = connections.loadRelaxed();
- if (signal_index < 0 || !cd)
- return false;
- QBasicMutexLocker locker(signalSlotLock(q));
- if (signal_index < cd->signalVectorCount()) {
- const QObjectPrivate::Connection *c = cd->signalVector.loadRelaxed()->at(signal_index).first.loadRelaxed();
-
- while (c) {
- if (c->receiver.loadRelaxed() == receiver)
- return true;
- c = c->nextConnectionList.loadRelaxed();
- }
- }
- return false;
-}
-
-// Used by QAccessibleWidget
QObjectList QObjectPrivate::receiverList(const char *signal) const
{
QObjectList returnValue;
int signal_index = signalIndex(signal);
- ConnectionData *cd = connections.loadRelaxed();
+ ConnectionData *cd = connections.loadAcquire();
if (signal_index < 0 || !cd)
return returnValue;
if (signal_index < cd->signalVectorCount()) {
@@ -255,26 +247,17 @@ QObjectList QObjectPrivate::receiverList(const char *signal) const
return returnValue;
}
-// Used by QAccessibleWidget
-QObjectList QObjectPrivate::senderList() const
-{
- QObjectList returnValue;
- ConnectionData *cd = connections.loadRelaxed();
- if (cd) {
- QBasicMutexLocker locker(signalSlotLock(q_func()));
- for (Connection *c = cd->senders; c; c = c->next)
- returnValue << c->sender;
- }
- return returnValue;
-}
-
+/*!
+ \internal
+ The signalSlotLock() of the sender must be locked while calling this function
+*/
inline void QObjectPrivate::ensureConnectionData()
{
if (connections.loadRelaxed())
return;
ConnectionData *cd = new ConnectionData;
cd->ref.ref();
- connections.storeRelaxed(cd);
+ connections.storeRelease(cd);
}
/*!
@@ -357,16 +340,16 @@ void QObjectPrivate::ConnectionData::removeConnection(QObjectPrivate::Connection
c->prevConnectionList->nextConnectionList.storeRelaxed(n);
c->prevConnectionList = nullptr;
- Q_ASSERT(c != orphaned.loadRelaxed());
+ Q_ASSERT(c != static_cast<Connection *>(orphaned.load(std::memory_order_relaxed)));
// add c to orphanedConnections
- Connection *o = nullptr;
+ TaggedSignalVector o = nullptr;
/* No ABA issue here: When adding a node, we only care about the list head, it doesn't
* matter if the tail changes.
*/
+ o = orphaned.load(std::memory_order_acquire);
do {
- o = orphaned.loadRelaxed();
c->nextInOrphanList = o;
- } while (!orphaned.testAndSetRelease(o, c));
+ } while (!orphaned.compare_exchange_strong(o, TaggedSignalVector(c), std::memory_order_release));
#ifndef QT_NO_DEBUG
found = false;
@@ -384,7 +367,7 @@ void QObjectPrivate::ConnectionData::removeConnection(QObjectPrivate::Connection
void QObjectPrivate::ConnectionData::cleanOrphanedConnectionsImpl(QObject *sender, LockPolicy lockPolicy)
{
QBasicMutex *senderMutex = signalSlotLock(sender);
- ConnectionOrSignalVector *c = nullptr;
+ TaggedSignalVector c = nullptr;
{
std::unique_lock<QBasicMutex> lock(*senderMutex, std::defer_lock_t{});
if (lockPolicy == NeedToLock)
@@ -395,7 +378,7 @@ void QObjectPrivate::ConnectionData::cleanOrphanedConnectionsImpl(QObject *sende
// Since ref == 1, no activate() is in process since we locked the mutex. That implies,
// that nothing can reference the orphaned connection objects anymore and they can
// be safely deleted
- c = orphaned.fetchAndStoreRelaxed(nullptr);
+ c = orphaned.exchange(nullptr, std::memory_order_relaxed);
}
if (c) {
// Deleting c might run arbitrary user code, so we must not hold the lock
@@ -409,11 +392,11 @@ void QObjectPrivate::ConnectionData::cleanOrphanedConnectionsImpl(QObject *sende
}
}
-inline void QObjectPrivate::ConnectionData::deleteOrphaned(QObjectPrivate::ConnectionOrSignalVector *o)
+inline void QObjectPrivate::ConnectionData::deleteOrphaned(TaggedSignalVector o)
{
while (o) {
- QObjectPrivate::ConnectionOrSignalVector *next = nullptr;
- if (SignalVector *v = ConnectionOrSignalVector::asSignalVector(o)) {
+ TaggedSignalVector next = nullptr;
+ if (SignalVector *v = static_cast<SignalVector *>(o)) {
next = v->nextInOrphanList;
free(v);
} else {
@@ -439,7 +422,7 @@ bool QObjectPrivate::isSignalConnected(uint signalIndex, bool checkDeclarative)
if (checkDeclarative && isDeclarativeSignalConnected(signalIndex))
return true;
- ConnectionData *cd = connections.loadRelaxed();
+ ConnectionData *cd = connections.loadAcquire();
if (!cd)
return false;
SignalVector *signalVector = cd->signalVector.loadRelaxed();
@@ -462,7 +445,7 @@ bool QObjectPrivate::isSignalConnected(uint signalIndex, bool checkDeclarative)
bool QObjectPrivate::maybeSignalConnected(uint signalIndex) const
{
- ConnectionData *cd = connections.loadRelaxed();
+ ConnectionData *cd = connections.loadAcquire();
if (!cd)
return false;
SignalVector *signalVector = cd->signalVector.loadRelaxed();
@@ -539,7 +522,7 @@ QMetaCallEvent::QMetaCallEvent(QtPrivate::QSlotObjectBase *slotO,
const QObject *sender, int signalId,
void **args, QSemaphore *semaphore)
: QAbstractMetaCallEvent(sender, signalId, semaphore),
- d({slotO, args, nullptr, 0, 0, ushort(-1)}),
+ d({QtPrivate::SlotObjUniquePtr{slotO}, args, nullptr, 0, 0, ushort(-1)}),
prealloc_()
{
if (d.slotObj_)
@@ -549,6 +532,21 @@ QMetaCallEvent::QMetaCallEvent(QtPrivate::QSlotObjectBase *slotO,
/*!
\internal
+ Used for blocking queued connections, just passes \a args through without
+ allocating any memory.
+ */
+QMetaCallEvent::QMetaCallEvent(QtPrivate::SlotObjUniquePtr slotO,
+ const QObject *sender, int signalId,
+ void **args, QSemaphore *semaphore)
+ : QAbstractMetaCallEvent(sender, signalId, semaphore),
+ d{std::move(slotO), args, nullptr, 0, 0, ushort(-1)},
+ prealloc_()
+{
+}
+
+/*!
+ \internal
+
Allocates memory for \a nargs; code creating an event needs to initialize
the void* and int arrays by accessing \a args() and \a types(), respectively.
*/
@@ -573,7 +571,7 @@ QMetaCallEvent::QMetaCallEvent(QtPrivate::QSlotObjectBase *slotO,
const QObject *sender, int signalId,
int nargs)
: QAbstractMetaCallEvent(sender, signalId),
- d({slotO, nullptr, nullptr, nargs, 0, ushort(-1)}),
+ d({QtPrivate::SlotObjUniquePtr(slotO), nullptr, nullptr, nargs, 0, ushort(-1)}),
prealloc_()
{
if (d.slotObj_)
@@ -583,6 +581,22 @@ QMetaCallEvent::QMetaCallEvent(QtPrivate::QSlotObjectBase *slotO,
/*!
\internal
+
+ Allocates memory for \a nargs; code creating an event needs to initialize
+ the void* and int arrays by accessing \a args() and \a types(), respectively.
+ */
+QMetaCallEvent::QMetaCallEvent(QtPrivate::SlotObjUniquePtr slotO,
+ const QObject *sender, int signalId,
+ int nargs)
+ : QAbstractMetaCallEvent(sender, signalId),
+ d{std::move(slotO), nullptr, nullptr, nargs, 0, ushort(-1)},
+ prealloc_()
+{
+ allocArgs();
+}
+
+/*!
+ \internal
*/
QMetaCallEvent::~QMetaCallEvent()
{
@@ -595,8 +609,6 @@ QMetaCallEvent::~QMetaCallEvent()
if (reinterpret_cast<void *>(d.args_) != reinterpret_cast<void *>(prealloc_))
free(d.args_);
}
- if (d.slotObj_)
- d.slotObj_->destroyIfLastRef();
}
/*!
@@ -614,6 +626,25 @@ void QMetaCallEvent::placeMetaCall(QObject *object)
}
}
+QMetaCallEvent* QMetaCallEvent::create_impl(QtPrivate::SlotObjUniquePtr slotObj,
+ const QObject *sender, int signal_index,
+ size_t argc, const void* const argp[],
+ const QMetaType metaTypes[])
+{
+ auto metaCallEvent = std::make_unique<QMetaCallEvent>(std::move(slotObj), sender,
+ signal_index, int(argc));
+
+ void **args = metaCallEvent->args();
+ QMetaType *types = metaCallEvent->types();
+ for (size_t i = 0; i < argc; ++i) {
+ types[i] = metaTypes[i];
+ args[i] = types[i].create(argp[i]);
+ Q_CHECK_PTR(!i || args[i]);
+ }
+
+ return metaCallEvent.release();
+}
+
/*!
\class QSignalBlocker
\brief Exception-safe wrapper around QObject::blockSignals().
@@ -701,6 +732,14 @@ void QMetaCallEvent::placeMetaCall(QObject *object)
*/
/*!
+ \fn void QSignalBlocker::dismiss()
+ \since 6.7
+ Dismisses the QSignalBlocker. It will no longer access the QObject
+ passed to its constructor. unblock(), reblock(), as well as
+ ~QSignalBlocker() will have no effect.
+*/
+
+/*!
\class QObject
\inmodule QtCore
\brief The QObject class is the base class of all Qt objects.
@@ -741,7 +780,7 @@ void QMetaCallEvent::placeMetaCall(QObject *object)
to catch child events.
Last but not least, QObject provides the basic timer support in
- Qt; see QTimer for high-level support for timers.
+ Qt; see QChronoTimer for high-level support for timers.
Notice that the Q_OBJECT macro is mandatory for any object that
implements signals, slots or properties. You also need to run the
@@ -824,20 +863,20 @@ void QMetaCallEvent::placeMetaCall(QObject *object)
\l uic generates code that invokes this function to enable
auto-connection to be performed between widgets on forms created
- with \e{Qt Designer}. More information about using auto-connection with \e{Qt Designer} is
+ with \e{\QD}. More information about using auto-connection with \e{\QD} is
given in the \l{Using a Designer UI File in Your Application} section of
- the \e{Qt Designer} manual.
+ the \l{Qt Widgets Designer Manual}{\QD} manual.
\section1 Dynamic Properties
- From Qt 4.2, dynamic properties can be added to and removed from QObject
+ Dynamic properties can be added to and removed from QObject
instances at run-time. Dynamic properties do not need to be declared at
compile-time, yet they provide the same advantages as static properties
and are manipulated using the same API - using property() to read them
and setProperty() to write them.
- From Qt 4.3, dynamic properties are supported by
- \l{Qt Designer's Widget Editing Mode#The Property Editor}{Qt Designer},
+ Dynamic properties are supported by
+ \l{Qt Widgets Designer's Widget Editing Mode#The Property Editor}{\QD},
and both standard Qt widgets and user-created forms can be given dynamic
properties.
@@ -954,8 +993,8 @@ void QObjectPrivate::clearBindingStorage()
outside the parent. If you still do, the destroyed() signal gives
you an opportunity to detect when an object is destroyed.
- \warning Deleting a QObject while pending events are waiting to
- be delivered can cause a crash. You must not delete the QObject
+ \warning Deleting a QObject while it is handling an event
+ delivered to it can cause a crash. You must not delete the QObject
directly if it exists in a different thread than the one currently
executing. Use deleteLater() instead, which will cause the event
loop to delete the object after all pending events have been
@@ -1004,7 +1043,7 @@ QObject::~QObject()
if (!d->isDeletingChildren && d->declarativeData && QAbstractDeclarativeData::destroyed)
QAbstractDeclarativeData::destroyed(d->declarativeData, this);
- QObjectPrivate::ConnectionData *cd = d->connections.loadRelaxed();
+ QObjectPrivate::ConnectionData *cd = d->connections.loadAcquire();
if (cd) {
if (cd->currentSender) {
cd->currentSender->receiverDeleted();
@@ -1012,7 +1051,7 @@ QObject::~QObject()
}
QBasicMutex *signalSlotMutex = signalSlotLock(this);
- QBasicMutexLocker locker(signalSlotMutex);
+ QMutexLocker locker(signalSlotMutex);
// disconnect all receivers
int receiverCount = cd->signalVectorCount();
@@ -1190,8 +1229,7 @@ inline QObjectPrivate::Connection::~Connection()
\c dynamic_cast(), with the advantages that it doesn't require
RTTI support and it works across dynamic library boundaries.
- qobject_cast() can also be used in conjunction with interfaces;
- see the \l{tools/plugandpaint/app}{Plug & Paint} example for details.
+ qobject_cast() can also be used in conjunction with interfaces.
\warning If T isn't declared with the Q_OBJECT macro, this
function's return value is undefined.
@@ -1261,7 +1299,7 @@ void QObject::doSetObjectName(const QString &name)
d->extraData->objectName.removeBindingUnlessInWrapper();
- if (d->extraData->objectName != name) {
+ if (d->extraData->objectName.valueBypassingBindings() != name) {
d->extraData->objectName.setValueBypassingBindings(name);
d->extraData->objectName.notify(); // also emits a signal
}
@@ -1279,7 +1317,7 @@ void QObject::setObjectName(QAnyStringView name)
d->extraData->objectName.removeBindingUnlessInWrapper();
- if (d->extraData->objectName != name) {
+ if (d->extraData->objectName.valueBypassingBindings() != name) {
d->extraData->objectName.setValueBypassingBindings(name.toString());
d->extraData->objectName.notify(); // also emits a signal
}
@@ -1362,18 +1400,21 @@ bool QObject::event(QEvent *e)
break;
case QEvent::DeferredDelete:
- qDeleteInEventHandler(this);
+ qCDebug(lcDeleteLater) << "Deferred deleting" << this;
+ delete this;
break;
case QEvent::MetaCall:
{
QAbstractMetaCallEvent *mce = static_cast<QAbstractMetaCallEvent*>(e);
- if (!d_func()->connections.loadRelaxed()) {
- QBasicMutexLocker locker(signalSlotLock(this));
+ QObjectPrivate::ConnectionData *connections = d_func()->connections.loadAcquire();
+ if (!connections) {
+ QMutexLocker locker(signalSlotLock(this));
d_func()->ensureConnectionData();
+ connections = d_func()->connections.loadRelaxed();
}
- QObjectPrivate::Sender sender(this, const_cast<QObject*>(mce->sender()), mce->signalId());
+ QObjectPrivate::Sender sender(this, const_cast<QObject*>(mce->sender()), mce->signalId(), connections);
mce->placeMetaCall(this);
break;
@@ -1384,12 +1425,20 @@ bool QObject::event(QEvent *e)
QThreadData *threadData = d->threadData.loadRelaxed();
QAbstractEventDispatcher *eventDispatcher = threadData->eventDispatcher.loadRelaxed();
if (eventDispatcher) {
- QList<QAbstractEventDispatcher::TimerInfo> timers = eventDispatcher->registeredTimers(this);
+ QList<QAbstractEventDispatcher::TimerInfoV2> timers = eventDispatcher->timersForObject(this);
if (!timers.isEmpty()) {
+ const bool res = eventDispatcher->unregisterTimers(this);
// do not to release our timer ids back to the pool (since the timer ids are moving to a new thread).
- eventDispatcher->unregisterTimers(this);
- QMetaObject::invokeMethod(this, "_q_reregisterTimers", Qt::QueuedConnection,
- Q_ARG(void*, (new QList<QAbstractEventDispatcher::TimerInfo>(timers))));
+ Q_ASSERT_X(res, Q_FUNC_INFO,
+ "QAbstractEventDispatcher::unregisterTimers() returned false,"
+ " but there are timers associated with this object.");
+ auto reRegisterTimers = [this, timers = std::move(timers)]() {
+ QAbstractEventDispatcher *eventDispatcher =
+ d_func()->threadData.loadRelaxed()->eventDispatcher.loadRelaxed();
+ for (const auto &ti : timers)
+ eventDispatcher->registerTimer(ti.timerId, ti.interval, ti.timerType, this);
+ };
+ QMetaObject::invokeMethod(this, std::move(reRegisterTimers), Qt::QueuedConnection);
}
}
break;
@@ -1411,9 +1460,9 @@ bool QObject::event(QEvent *e)
This event handler can be reimplemented in a subclass to receive
timer events for the object.
- QTimer provides a higher-level interface to the timer
- functionality, and also more general information about timers. The
- timer event is passed in the \a event parameter.
+ QChronoTimer provides higher-level interfaces to the timer functionality,
+ and also more general information about timers. The timer event is passed
+ in the \a event parameter.
\sa startTimer(), killTimer(), event()
*/
@@ -1552,9 +1601,9 @@ QThread *QObject::thread() const
}
/*!
- Changes the thread affinity for this object and its children. The
- object cannot be moved if it has a parent. Event processing will
- continue in the \a targetThread.
+ Changes the thread affinity for this object and its children and
+ returns \c true on success. The object cannot be moved if it has a
+ parent. Event processing will continue in the \a targetThread.
To move an object to the main thread, use QApplication::instance()
to retrieve a pointer to the current application, and then use
@@ -1591,26 +1640,26 @@ QThread *QObject::thread() const
\sa thread()
*/
-void QObject::moveToThread(QThread *targetThread)
+bool QObject::moveToThread(QThread *targetThread QT6_IMPL_NEW_OVERLOAD_TAIL)
{
Q_D(QObject);
if (d->threadData.loadRelaxed()->thread.loadAcquire() == targetThread) {
// object is already in this thread
- return;
+ return true;
}
if (d->parent != nullptr) {
qWarning("QObject::moveToThread: Cannot move objects with a parent");
- return;
+ return false;
}
if (d->isWidget) {
qWarning("QObject::moveToThread: Widgets cannot be moved to a new thread");
- return;
+ return false;
}
if (!d->bindingStorage.isEmpty()) {
qWarning("QObject::moveToThread: Can not move objects that contain bindings or are used in bindings to a new thread.");
- return;
+ return false;
}
QThreadData *currentData = QThreadData::current();
@@ -1624,13 +1673,13 @@ void QObject::moveToThread(QThread *targetThread)
"Cannot move to target thread (%p)\n",
currentData->thread.loadRelaxed(), thisThreadData->thread.loadRelaxed(), targetData ? targetData->thread.loadRelaxed() : nullptr);
-#ifdef Q_OS_MAC
+#ifdef Q_OS_DARWIN
qWarning("You might be loading two sets of Qt binaries into the same process. "
"Check that all plugins are compiled against the right Qt binaries. Export "
"DYLD_PRINT_LIBRARIES=1 and check that only one set of binaries are being loaded.");
#endif
- return;
+ return false;
}
// prepare to move
@@ -1664,6 +1713,7 @@ void QObject::moveToThread(QThread *targetThread)
// now currentData can commit suicide if it wants to
currentData->deref();
+ return true;
}
void QObjectPrivate::moveToThread_helper()
@@ -1706,7 +1756,7 @@ void QObjectPrivate::setThreadData_helper(QThreadData *currentData, QThreadData
}
// the current emitting thread shouldn't restore currentSender after calling moveToThread()
- ConnectionData *cd = connections.loadRelaxed();
+ ConnectionData *cd = connections.loadAcquire();
if (cd) {
if (cd->currentSender) {
cd->currentSender->receiverDeleted();
@@ -1745,19 +1795,6 @@ void QObjectPrivate::setThreadData_helper(QThreadData *currentData, QThreadData
}
}
-void QObjectPrivate::_q_reregisterTimers(void *pointer)
-{
- Q_Q(QObject);
- QList<QAbstractEventDispatcher::TimerInfo> *timerList = reinterpret_cast<QList<QAbstractEventDispatcher::TimerInfo> *>(pointer);
- QAbstractEventDispatcher *eventDispatcher = threadData.loadRelaxed()->eventDispatcher.loadRelaxed();
- for (int i = 0; i < timerList->size(); ++i) {
- const QAbstractEventDispatcher::TimerInfo &ti = timerList->at(i);
- eventDispatcher->registerTimer(ti.timerId, ti.interval, ti.timerType, q);
- }
- delete timerList;
-}
-
-
//
// The timer flag hasTimer is set when startTimer is called.
// It is not reset when killing the timer because more than
@@ -1765,13 +1802,36 @@ void QObjectPrivate::_q_reregisterTimers(void *pointer)
//
/*!
+ \fn int QObject::startTimer(int interval, Qt::TimerType timerType)
+
+ This is an overloaded function that will start a timer of type
+ \a timerType and a timeout of \a interval milliseconds. This is
+ equivalent to calling:
+ \code
+ startTimer(std::chrono::milliseconds{interval}, timerType);
+ \endcode
+
+ \sa timerEvent(), killTimer(), QChronoTimer::singleShot()
+*/
+
+int QObject::startTimer(int interval, Qt::TimerType timerType)
+{
+ // no overflow can happen here:
+ // 2^31 ms * 1,000,000 always fits a 64-bit signed integer type
+ return startTimer(std::chrono::milliseconds{interval}, timerType);
+}
+
+/*!
+ \since 5.9
+ \overload
+
Starts a timer and returns a timer identifier, or returns zero if
it could not start a timer.
- A timer event will occur every \a interval milliseconds until
- killTimer() is called. If \a interval is 0, then the timer event
- occurs once every time there are no more window system events to
- process.
+ A timer event will occur every \a interval until killTimer()
+ is called. If \a interval is equal to \c{std::chrono::duration::zero()},
+ then the timer event occurs once every time there are no more window
+ system events to process.
The virtual timerEvent() function is called with the QTimerEvent
event parameter class when a timer event occurs. Reimplement this
@@ -1784,26 +1844,41 @@ void QObjectPrivate::_q_reregisterTimers(void *pointer)
\snippet code/src_corelib_kernel_qobject.cpp 8
- Note that QTimer's accuracy depends on the underlying operating system and
- hardware. The \a timerType argument allows you to customize the accuracy of
+ Note that the accuracy of QChronoTimer depends on the underlying operating
+ system and hardware.
+
+ The \a timerType argument allows you to customize the accuracy of
the timer. See Qt::TimerType for information on the different timer types.
Most platforms support an accuracy of 20 milliseconds; some provide more.
If Qt is unable to deliver the requested number of timer events, it will
silently discard some.
- The QTimer class provides a high-level programming interface with
- single-shot timers and timer signals instead of events. There is
- also a QBasicTimer class that is more lightweight than QTimer and
- less clumsy than using timer IDs directly.
+ The QTimer and QChronoTimer classes provide a high-level programming
+ interface with single-shot timers and timer signals instead of
+ events. There is also a QBasicTimer class that is more lightweight than
+ QChronoTimer but less clumsy than using timer IDs directly.
- \sa timerEvent(), killTimer(), QTimer::singleShot()
-*/
+ \sa timerEvent(), killTimer(), QChronoTimer::singleShot()
-int QObject::startTimer(int interval, Qt::TimerType timerType)
+ \note Starting from Qt 6.8 the type of \a interval
+ is \c std::chrono::nanoseconds, prior to that it was \c
+ std::chrono::milliseconds. This change is backwards compatible with
+ older releases of Qt.
+
+ \note In Qt 6.8, QObject was changed to use Qt::TimerId to represent timer
+ IDs. This method converts the TimerId to int for backwards compatibility
+ reasons, however you can use Qt::TimerId to check the value returned by
+ this method, for example:
+ \snippet code/src_corelib_kernel_qobject.cpp invalid-timer-id
+
+*/
+int QObject::startTimer(std::chrono::nanoseconds interval, Qt::TimerType timerType)
{
Q_D(QObject);
- if (Q_UNLIKELY(interval < 0)) {
+ using namespace std::chrono_literals;
+
+ if (Q_UNLIKELY(interval < 0ns)) {
qWarning("QObject::startTimer: Timers cannot have negative intervals");
return 0;
}
@@ -1817,52 +1892,15 @@ int QObject::startTimer(int interval, Qt::TimerType timerType)
qWarning("QObject::startTimer: Timers cannot be started from another thread");
return 0;
}
- int timerId = thisThreadData->eventDispatcher.loadRelaxed()->registerTimer(interval, timerType, this);
+
+ auto dispatcher = thisThreadData->eventDispatcher.loadRelaxed();
+ Qt::TimerId timerId = dispatcher->registerTimer(interval, timerType, this);
d->ensureExtraData();
d->extraData->runningTimers.append(timerId);
- return timerId;
+ return int(timerId);
}
/*!
- \since 5.9
- \overload
- \fn int QObject::startTimer(std::chrono::milliseconds time, Qt::TimerType timerType)
-
- Starts a timer and returns a timer identifier, or returns zero if
- it could not start a timer.
-
- A timer event will occur every \a time interval until killTimer()
- is called. If \a time is equal to \c{std::chrono::duration::zero()},
- then the timer event occurs once every time there are no more window
- system events to process.
-
- The virtual timerEvent() function is called with the QTimerEvent
- event parameter class when a timer event occurs. Reimplement this
- function to get timer events.
-
- If multiple timers are running, the QTimerEvent::timerId() can be
- used to find out which timer was activated.
-
- Example:
-
- \snippet code/src_corelib_kernel_qobject.cpp 8
-
- Note that QTimer's accuracy depends on the underlying operating system and
- hardware. The \a timerType argument allows you to customize the accuracy of
- the timer. See Qt::TimerType for information on the different timer types.
- Most platforms support an accuracy of 20 milliseconds; some provide more.
- If Qt is unable to deliver the requested number of timer events, it will
- silently discard some.
-
- The QTimer class provides a high-level programming interface with
- single-shot timers and timer signals instead of events. There is
- also a QBasicTimer class that is more lightweight than QTimer and
- less clumsy than using timer IDs directly.
-
- \sa timerEvent(), killTimer(), QTimer::singleShot()
-*/
-
-/*!
Kills the timer with timer identifier, \a id.
The timer identifier is returned by startTimer() when a timer
@@ -1873,17 +1911,26 @@ int QObject::startTimer(int interval, Qt::TimerType timerType)
void QObject::killTimer(int id)
{
+ killTimer(Qt::TimerId{id});
+}
+
+/*!
+ \since 6.8
+ \overload
+*/
+void QObject::killTimer(Qt::TimerId id)
+{
Q_D(QObject);
if (Q_UNLIKELY(thread() != QThread::currentThread())) {
qWarning("QObject::killTimer: Timers cannot be stopped from another thread");
return;
}
- if (id) {
+ if (id > Qt::TimerId::Invalid) {
int at = d->extraData ? d->extraData->runningTimers.indexOf(id) : -1;
if (at == -1) {
// timer isn't owned by this object
qWarning("QObject::killTimer(): Error: timer id %d is not valid for object %p (%s, %ls), timer has not been killed",
- id,
+ qToUnderlying(id),
this,
metaObject()->className(),
qUtf16Printable(objectName()));
@@ -1899,7 +1946,6 @@ void QObject::killTimer(int id)
}
}
-
/*!
\fn QObject *QObject::parent() const
@@ -1933,18 +1979,19 @@ void QObject::killTimer(int id)
/*!
- \fn template<typename T> T *QObject::findChild(const QString &name, Qt::FindChildOptions options) const
+ \fn template<typename T> T *QObject::findChild(QAnyStringView name, Qt::FindChildOptions options) const
Returns the child of this object that can be cast into type T and
that is called \a name, or \nullptr if there is no such object.
- Omitting the \a name argument causes all object names to be matched.
+ A null \a name argument causes all objects to be matched. An empty,
+ non-null \a name matches only objects whose \l objectName is empty.
The search is performed recursively, unless \a options specifies the
option FindDirectChildrenOnly.
- If there is more than one child matching the search, the most
- direct ancestor is returned. If there are several direct
- ancestors, it is undefined which one will be returned. In that
- case, findChildren() should be used.
+ If there is more than one child matching the search, the most-direct
+ ancestor is returned. If there are several most-direct ancestors, the
+ first child in children() will be returned. In that case, it's better
+ to use findChildren() to get the complete list of all children.
This example returns a child \c{QPushButton} of \c{parentWidget}
named \c{"button1"}, even if the button isn't a direct child of
@@ -1966,11 +2013,32 @@ void QObject::killTimer(int id)
\snippet code/src_corelib_kernel_qobject.cpp 42
+ \note In Qt versions prior to 6.7, this function took \a name as
+ \c{QString}, not \c{QAnyStringView}.
+
\sa findChildren()
*/
/*!
- \fn template<typename T> QList<T> QObject::findChildren(const QString &name, Qt::FindChildOptions options) const
+ \fn template<typename T> T *QObject::findChild(Qt::FindChildOptions options) const
+ \overload
+ \since 6.7
+
+ Returns the child of this object that can be cast into type T, or
+ \nullptr if there is no such object.
+ The search is performed recursively, unless \a options specifies the
+ option FindDirectChildrenOnly.
+
+ If there is more than one child matching the search, the most-direct ancestor
+ is returned. If there are several most-direct ancestors, the first child in
+ children() will be returned. In that case, it's better to use findChildren()
+ to get the complete list of all children.
+
+ \sa findChildren()
+*/
+
+/*!
+ \fn template<typename T> QList<T> QObject::findChildren(QAnyStringView name, Qt::FindChildOptions options) const
Returns all children of this object with the given \a name that can be
cast to type T, or an empty list if there are no such objects.
@@ -1992,6 +2060,9 @@ void QObject::killTimer(int id)
\snippet code/src_corelib_kernel_qobject.cpp 43
+ \note In Qt versions prior to 6.7, this function took \a name as
+ \c{QString}, not \c{QAnyStringView}.
+
\sa findChild()
*/
@@ -2009,7 +2080,7 @@ void QObject::killTimer(int id)
*/
/*!
- \fn QList<T> QObject::findChildren(const QRegularExpression &re, Qt::FindChildOptions options) const
+ \fn template<typename T> QList<T> QObject::findChildren(const QRegularExpression &re, Qt::FindChildOptions options) const
\overload findChildren()
\since 5.0
@@ -2053,46 +2124,26 @@ void QObject::killTimer(int id)
\sa QObject::findChildren()
*/
-static void qt_qFindChildren_with_name(const QObject *parent, const QString &name,
- const QMetaObject &mo, QList<void *> *list,
- Qt::FindChildOptions options)
+static bool matches_objectName_non_null(QObject *obj, QAnyStringView name)
{
- Q_ASSERT(parent);
- Q_ASSERT(list);
- Q_ASSERT(!name.isNull());
- for (QObject *obj : parent->children()) {
- if (mo.cast(obj) && obj->objectName() == name)
- list->append(obj);
- if (options & Qt::FindChildrenRecursively)
- qt_qFindChildren_with_name(obj, name, mo, list, options);
- }
+ if (auto ext = QObjectPrivate::get(obj)->extraData)
+ return ext ->objectName.valueBypassingBindings() == name;
+ return name.isEmpty();
}
/*!
\internal
*/
-void qt_qFindChildren_helper(const QObject *parent, const QString &name,
+void qt_qFindChildren_helper(const QObject *parent, QAnyStringView name,
const QMetaObject &mo, QList<void*> *list, Qt::FindChildOptions options)
{
- if (name.isNull())
- return qt_qFindChildren_helper(parent, mo, list, options);
- else
- return qt_qFindChildren_with_name(parent, name, mo, list, options);
-}
-
-/*!
- \internal
-*/
-void qt_qFindChildren_helper(const QObject *parent, const QMetaObject &mo,
- QList<void*> *list, Qt::FindChildOptions options)
-{
Q_ASSERT(parent);
Q_ASSERT(list);
for (QObject *obj : parent->children()) {
- if (mo.cast(obj))
+ if (mo.cast(obj) && (name.isNull() || matches_objectName_non_null(obj, name)))
list->append(obj);
if (options & Qt::FindChildrenRecursively)
- qt_qFindChildren_helper(obj, mo, list, options);
+ qt_qFindChildren_helper(obj, name, mo, list, options);
}
}
@@ -2119,13 +2170,13 @@ void qt_qFindChildren_helper(const QObject *parent, const QRegularExpression &re
/*!
\internal
- */
-QObject *qt_qFindChild_helper(const QObject *parent, const QString &name, const QMetaObject &mo, Qt::FindChildOptions options)
+*/
+QObject *qt_qFindChild_helper(const QObject *parent, QAnyStringView name, const QMetaObject &mo, Qt::FindChildOptions options)
{
Q_ASSERT(parent);
for (QObject *obj : parent->children()) {
- if (mo.cast(obj) && (name.isNull() || obj->objectName() == name))
- return obj;
+ if (mo.cast(obj) && (name.isNull() || matches_objectName_non_null(obj, name)))
+ return obj;
}
if (options & Qt::FindChildrenRecursively) {
for (QObject *child : parent->children()) {
@@ -2208,7 +2259,15 @@ void QObjectPrivate::setParent_helper(QObject *o)
}
}
}
+
+ if (receiveParentEvents) {
+ Q_ASSERT(!isWidget); // Handled in QWidget
+ QEvent e(QEvent::ParentAboutToChange);
+ QCoreApplication::sendEvent(q, &e);
+ }
+
parent = o;
+
if (parent) {
// object hierarchies are constrained to a single thread
if (threadData.loadRelaxed() != parent->d_func()->threadData.loadRelaxed()) {
@@ -2224,6 +2283,12 @@ void QObjectPrivate::setParent_helper(QObject *o)
}
}
}
+
+ if (receiveParentEvents) {
+ Q_ASSERT(!isWidget); // Handled in QWidget
+ QEvent e(QEvent::ParentChange);
+ QCoreApplication::sendEvent(q, &e);
+ }
}
/*!
@@ -2242,6 +2307,9 @@ void QObjectPrivate::setParent_helper(QObject *o)
If multiple event filters are installed on a single object, the
filter that was installed last is activated first.
+ If \a filterObj has already been installed for this object,
+ this function moves it so it acts as if it was installed last.
+
Here's a \c KeyPressEater class that eats the key presses of its
monitored objects:
@@ -2280,9 +2348,9 @@ void QObject::installEventFilter(QObject *obj)
d->ensureExtraData();
- // clean up unused items in the list
- d->extraData->eventFilters.removeAll((QObject *)nullptr);
- d->extraData->eventFilters.removeAll(obj);
+ // clean up unused items in the list along the way:
+ auto isNullOrEquals = [](auto obj) { return [obj](const auto &p) { return !p || p == obj; }; };
+ d->extraData->eventFilters.removeIf(isNullOrEquals(obj));
d->extraData->eventFilters.prepend(obj);
}
@@ -2303,9 +2371,11 @@ void QObject::removeEventFilter(QObject *obj)
{
Q_D(QObject);
if (d->extraData) {
- for (int i = 0; i < d->extraData->eventFilters.size(); ++i) {
- if (d->extraData->eventFilters.at(i) == obj)
- d->extraData->eventFilters[i] = nullptr;
+ for (auto &filter : d->extraData->eventFilters) {
+ if (filter == obj) {
+ filter = nullptr;
+ break;
+ }
}
}
}
@@ -2334,7 +2404,7 @@ void QObject::removeEventFilter(QObject *obj)
QCoreApplication::exec()), the object will be deleted once the
event loop is started. If deleteLater() is called after the main event loop
has stopped, the object will not be deleted.
- Since Qt 4.8, if deleteLater() is called on an object that lives in a
+ If deleteLater() is called on an object that lives in a
thread with no running event loop, the object will be destroyed when the
thread finishes.
@@ -2345,9 +2415,20 @@ void QObject::removeEventFilter(QObject *obj)
event loop was still running: the Qt event loop will delete those objects
as soon as the new nested event loop starts.
- \note It is safe to call this function more than once; when the
- first deferred deletion event is delivered, any pending events for the
- object are removed from the event queue.
+ In situations where Qt is not driving the event dispatcher via e.g.
+ QCoreApplication::exec() or QEventLoop::exec(), deferred deletes
+ will not be processed automatically. To ensure deferred deletion in
+ this scenario, the following workaround can be used:
+
+ \code
+ const auto *eventDispatcher = QThread::currentThread()->eventDispatcher();
+ QObject::connect(eventDispatcher, &QAbstractEventDispatcher::aboutToBlock,
+ QThread::currentThread(), []{
+ if (QThread::currentThread()->loopLevel() == 0)
+ QCoreApplication::sendPostedEvents(nullptr, QEvent::DeferredDelete);
+ }
+ );
+ \endcode
\sa destroyed(), QPointer
*/
@@ -2357,7 +2438,62 @@ void QObject::deleteLater()
if (qApp == this)
qWarning("You are deferring the delete of QCoreApplication, this may not work as expected.");
#endif
- QCoreApplication::postEvent(this, new QDeferredDeleteEvent());
+
+
+ // De-bounce QDeferredDeleteEvents. Use the post event list mutex
+ // to guard access to deleteLaterCalled, so we don't need a separate
+ // mutex in QObjectData.
+ auto eventListLocker = QCoreApplicationPrivate::lockThreadPostEventList(this);
+ if (!eventListLocker.threadData)
+ return;
+
+ // FIXME: The deleteLaterCalled flag is part of a bit field,
+ // so we likely have data races here, even with the mutex above,
+ // as long as we're not guarding every access to the bit field.
+
+ Q_D(QObject);
+ if (d->deleteLaterCalled) {
+ qCDebug(lcDeleteLater) << "Skipping deleteLater for already deferred object" << this;
+ return;
+ }
+
+ d->deleteLaterCalled = true;
+
+ int loopLevel = 0;
+ int scopeLevel = 0;
+
+ auto *objectThreadData = eventListLocker.threadData;
+ if (objectThreadData == QThreadData::current()) {
+ // Remember the current running eventloop for deleteLater
+ // calls in the object's own thread.
+
+ // Events sent by non-Qt event handlers (such as glib) may not
+ // have the scopeLevel set correctly. The scope level makes sure that
+ // code like this:
+ // foo->deleteLater();
+ // qApp->processEvents(); // without passing QEvent::DeferredDelete
+ // will not cause "foo" to be deleted before returning to the event loop.
+
+ loopLevel = objectThreadData->loopLevel;
+ scopeLevel = objectThreadData->scopeLevel;
+
+ // If the scope level is 0 while loopLevel != 0, we are called from a
+ // non-conformant code path, and our best guess is that the scope level
+ // should be 1. (Loop level 0 is special: it means that no event loops
+ // are running.)
+ if (scopeLevel == 0 && loopLevel != 0) {
+ qCDebug(lcDeleteLater) << "Delete later called with scope level 0"
+ << "but loop level is > 0. Assuming scope is 1";
+ scopeLevel = 1;
+ }
+ }
+
+ qCDebug(lcDeleteLater) << "Posting deferred delete for" << this
+ << "with loop level" << loopLevel << "and scope level" << scopeLevel;
+
+ eventListLocker.unlock();
+ QCoreApplication::postEvent(this,
+ new QDeferredDeleteEvent(loopLevel, scopeLevel));
}
/*!
@@ -2370,13 +2506,12 @@ void QObject::deleteLater()
translated string is available.
Example:
- \snippet ../widgets/mainwindows/sdi/mainwindow.cpp implicit tr context
+ \snippet ../widgets/itemviews/spreadsheet/spreadsheet.cpp implicit tr context
\dots
If the same \a sourceText is used in different roles within the
same context, an additional identifying string may be passed in
- \a disambiguation (\nullptr by default). In Qt 4.4 and earlier, this was
- the preferred way to pass comments to translators.
+ \a disambiguation (\nullptr by default).
Example:
@@ -2385,8 +2520,8 @@ void QObject::deleteLater()
See \l{Writing Source Code for Translation} for a detailed description of
Qt's translation mechanisms in general, and the
- \l{Writing Source Code for Translation#Disambiguation}{Disambiguation}
- section for information on disambiguation.
+ \l{Writing Source Code for Translation#Disambiguate Identical Text}
+ {Disambiguate Identical Text} section for information on disambiguation.
\warning This method is reentrant only if all translators are
installed \e before calling this method. Installing or removing
@@ -2524,7 +2659,7 @@ QObject *QObject::sender() const
{
Q_D(const QObject);
- QBasicMutexLocker locker(signalSlotLock(this));
+ QMutexLocker locker(signalSlotLock(this));
QObjectPrivate::ConnectionData *cd = d->connections.loadRelaxed();
if (!cd || !cd->currentSender)
return nullptr;
@@ -2566,7 +2701,7 @@ int QObject::senderSignalIndex() const
{
Q_D(const QObject);
- QBasicMutexLocker locker(signalSlotLock(this));
+ QMutexLocker locker(signalSlotLock(this));
QObjectPrivate::ConnectionData *cd = d->connections.loadRelaxed();
if (!cd || !cd->currentSender)
return -1;
@@ -2630,8 +2765,8 @@ int QObject::receivers(const char *signal) const
signal_index);
}
+ QMutexLocker locker(signalSlotLock(this));
QObjectPrivate::ConnectionData *cd = d->connections.loadRelaxed();
- QBasicMutexLocker locker(signalSlotLock(this));
if (cd && signal_index < cd->signalVectorCount()) {
const QObjectPrivate::Connection *c = cd->signalVector.loadRelaxed()->at(signal_index).first.loadRelaxed();
while (c) {
@@ -2678,7 +2813,7 @@ bool QObject::isSignalConnected(const QMetaMethod &signal) const
signalIndex += QMetaObjectPrivate::signalOffset(signal.mobj);
- QBasicMutexLocker locker(signalSlotLock(this));
+ QMutexLocker locker(signalSlotLock(this));
return d->isSignalConnected(signalIndex, true);
}
@@ -3584,7 +3719,7 @@ bool QMetaObjectPrivate::disconnect(const QObject *sender,
QObject *s = const_cast<QObject *>(sender);
QBasicMutex *senderMutex = signalSlotLock(sender);
- QBasicMutexLocker locker(senderMutex);
+ QMutexLocker locker(senderMutex);
QObjectPrivate::ConnectionData *scd = QObjectPrivate::get(s)->connections.loadRelaxed();
if (!scd)
@@ -3765,7 +3900,7 @@ struct SlotObjectGuard {
SlotObjectGuard() = default;
// move would be fine, but we do not need it currently
Q_DISABLE_COPY_MOVE(SlotObjectGuard)
- explicit SlotObjectGuard(QtPrivate::QSlotObjectBase *slotObject)
+ Q_NODISCARD_CTOR explicit SlotObjectGuard(QtPrivate::QSlotObjectBase *slotObject)
: m_slotObject(slotObject)
{
if (m_slotObject)
@@ -3773,17 +3908,14 @@ struct SlotObjectGuard {
}
QtPrivate::QSlotObjectBase const *operator->() const
- { return m_slotObject; }
+ { return m_slotObject.get(); }
QtPrivate::QSlotObjectBase *operator->()
- { return m_slotObject; }
+ { return m_slotObject.get(); }
- ~SlotObjectGuard() {
- if (m_slotObject)
- m_slotObject->destroyIfLastRef();
- }
+ ~SlotObjectGuard() = default;
private:
- QtPrivate::QSlotObjectBase *m_slotObject = nullptr;
+ QtPrivate::SlotObjUniquePtr m_slotObject;
};
/*!
@@ -3811,7 +3943,7 @@ static void queued_activate(QObject *sender, int signal, QObjectPrivate::Connect
while (argumentTypes[nargs - 1])
++nargs;
- QBasicMutexLocker locker(signalSlotLock(c->receiver.loadRelaxed()));
+ QMutexLocker locker(signalSlotLock(c->receiver.loadRelaxed()));
QObject *receiver = c->receiver.loadRelaxed();
if (!receiver) {
// the connection has been disconnected before we got the lock
@@ -3892,8 +4024,8 @@ void doActivate(QObject *sender, int signal_index, void **argv)
bool senderDeleted = false;
{
- Q_ASSERT(sp->connections.loadAcquire());
- QObjectPrivate::ConnectionDataPointer connections(sp->connections.loadRelaxed());
+ Q_ASSERT(sp->connections.loadRelaxed());
+ QObjectPrivate::ConnectionDataPointer connections(sp->connections.loadAcquire());
QObjectPrivate::SignalVector *signalVector = connections->signalVector.loadRelaxed();
const QObjectPrivate::ConnectionList *list;
@@ -3952,7 +4084,7 @@ void doActivate(QObject *sender, int signal_index, void **argv)
QSemaphore semaphore;
{
- QBasicMutexLocker locker(signalSlotLock(receiver));
+ QMutexLocker locker(signalSlotLock(receiver));
if (!c->isSingleShot && !c->receiver.loadAcquire())
continue;
QMetaCallEvent *ev = c->isSlotObject ?
@@ -3969,7 +4101,9 @@ void doActivate(QObject *sender, int signal_index, void **argv)
if (c->isSingleShot && !QObjectPrivate::removeConnection(c))
continue;
- QObjectPrivate::Sender senderData(receiverInSameThread ? receiver : nullptr, sender, signal_index);
+ QObjectPrivate::Sender senderData(
+ receiverInSameThread ? receiver : nullptr, sender, signal_index,
+ receiverInSameThread ? QObjectPrivate::get(receiver)->connections.loadAcquire() : nullptr);
if (c->isSlotObject) {
SlotObjectGuard obj{c->slotObj};
@@ -4018,7 +4152,7 @@ void doActivate(QObject *sender, int signal_index, void **argv)
senderDeleted = true;
}
if (!senderDeleted) {
- sp->connections.loadRelaxed()->cleanOrphanedConnections(sender);
+ sp->connections.loadAcquire()->cleanOrphanedConnections(sender);
if (callbacks_enabled && signal_spy_set->signal_end_callback != nullptr)
signal_spy_set->signal_end_callback(sender, signal_index);
@@ -4096,6 +4230,8 @@ int QObjectPrivate::signalIndex(const char *signalName,
*****************************************************************************/
/*!
+ \fn bool QObject::setProperty(const char *name, const QVariant &value)
+
Sets the value of the object's \a name property to \a value.
If the property is defined in the class using Q_PROPERTY then
@@ -4116,9 +4252,17 @@ int QObjectPrivate::signalIndex(const char *signalName,
\sa property(), metaObject(), dynamicPropertyNames(), QMetaProperty::write()
*/
-bool QObject::setProperty(const char *name, const QVariant &value)
+
+/*!
+ \fn bool QObject::setProperty(const char *name, QVariant &&value)
+ \since 6.6
+ \overload setProperty
+*/
+
+bool QObject::doSetProperty(const char *name, const QVariant *lvalue, QVariant *rvalue)
{
Q_D(QObject);
+ const auto &value =*lvalue;
const QMetaObject *meta = metaObject();
if (!name || !meta)
return false;
@@ -4137,12 +4281,18 @@ bool QObject::setProperty(const char *name, const QVariant &value)
} else {
if (idx == -1) {
d->extraData->propertyNames.append(name);
- d->extraData->propertyValues.append(value);
+ if (rvalue)
+ d->extraData->propertyValues.append(std::move(*rvalue));
+ else
+ d->extraData->propertyValues.append(*lvalue);
} else {
if (value.userType() == d->extraData->propertyValues.at(idx).userType()
&& value == d->extraData->propertyValues.at(idx))
return false;
- d->extraData->propertyValues[idx] = value;
+ if (rvalue)
+ d->extraData->propertyValues[idx] = std::move(*rvalue);
+ else
+ d->extraData->propertyValues[idx] = *lvalue;
}
}
@@ -4157,7 +4307,7 @@ bool QObject::setProperty(const char *name, const QVariant &value)
qWarning("%s::setProperty: Property \"%s\" invalid,"
" read-only or does not exist", metaObject()->className(), name);
#endif
- return p.write(this, value);
+ return rvalue ? p.write(this, std::move(*rvalue)) : p.write(this, *lvalue);
}
/*!
@@ -4257,7 +4407,7 @@ void QObject::dumpObjectInfo() const
objectName().isEmpty() ? "unnamed" : objectName().toLocal8Bit().data());
Q_D(const QObject);
- QBasicMutexLocker locker(signalSlotLock(this));
+ QMutexLocker locker(signalSlotLock(this));
// first, look for connections where this object is the sender
qDebug(" SIGNALS OUT");
@@ -4319,15 +4469,23 @@ void QObject::dumpObjectInfo() const
#ifndef QT_NO_DEBUG_STREAM
+void QObjectPrivate::writeToDebugStream(QDebug &dbg) const
+{
+ Q_Q(const QObject);
+ dbg.nospace() << q->metaObject()->className() << '(' << (const void *)q;
+ if (!q->objectName().isEmpty())
+ dbg << ", name = " << q->objectName();
+ dbg << ')';
+}
+
QDebug operator<<(QDebug dbg, const QObject *o)
{
QDebugStateSaver saver(dbg);
if (!o)
return dbg << "QObject(0x0)";
- dbg.nospace() << o->metaObject()->className() << '(' << (const void *)o;
- if (!o->objectName().isEmpty())
- dbg << ", name = " << o->objectName();
- dbg << ')';
+
+ const QObjectPrivate *d = QObjectPrivate::get(o);
+ d->writeToDebugStream(dbg);
return dbg;
}
#endif
@@ -4337,19 +4495,22 @@ QDebug operator<<(QDebug dbg, const QObject *o)
\relates QObject
This macro associates extra information to the class, which is available
- using QObject::metaObject(). Qt makes only limited use of this feature in
- \l{Qt D-Bus} and \l{Qt QML} modules.
-
- The extra information takes the form of a \a Name string and a \a Value
- literal string.
+ using QObject::metaObject(). The extra information takes the form of a
+ \a Name string and a \a Value literal string.
Example:
\snippet code/src_corelib_kernel_qobject.cpp 35
+ Qt makes use of the macro in \l{Qt D-Bus} and \l{Qt Qml} modules.
+ For instance, when defining \l{QML Object Types} in C++, you can
+ designate a property as the \e default one:
+
+ \snippet code/doc_src_properties.cpp 7
+
\sa QMetaObject::classInfo()
\sa {Using Qt D-Bus Adaptors}
- \sa {Extending QML}
+ \sa {Defining QML Types from C++}
*/
/*!
@@ -4359,15 +4520,6 @@ QDebug operator<<(QDebug dbg, const QObject *o)
This macro tells Qt which interfaces the class implements. This
is used when implementing plugins.
- Example:
-
- \snippet ../widgets/tools/plugandpaint/plugins/basictools/basictoolsplugin.h 1
- \dots
- \snippet ../widgets/tools/plugandpaint/plugins/basictools/basictoolsplugin.h 3
-
- See the \l{tools/plugandpaint/plugins/basictools}{Plug & Paint
- Basic Tools} example for details.
-
\sa Q_DECLARE_INTERFACE(), Q_PLUGIN_METADATA(), {How to Create Qt Plugins}
*/
@@ -4546,16 +4698,25 @@ QDebug operator<<(QDebug dbg, const QObject *o)
\sa {Qt's Property System}
*/
-
/*!
\macro Q_OBJECT
\relates QObject
- The Q_OBJECT macro must appear in the private section of a class
- definition that declares its own signals and slots or that uses
- other services provided by Qt's meta-object system.
+ The Q_OBJECT macro is used to enable meta-object features, such as dynamic
+ properties, signals, and slots.
- For example:
+ You can add the Q_OBJECT macro to any section of a class definition that
+ declares its own signals and slots or that uses other services provided by
+ Qt's meta-object system.
+
+//! [qobject-macros-private-access-specifier]
+ \note This macro expansion ends with a \c private: access specifier. If you
+ declare members immediately after this macro, those members will also be
+ private. To add public (or protected) members right after the macro, use a
+ \c {public:} (or \c {protected:}) access specifier.
+//! [qobject-macros-private-access-specifier]
+
+ Example:
\snippet signalsandslots/signalsandslots.h 1
\codeline
@@ -4563,8 +4724,8 @@ QDebug operator<<(QDebug dbg, const QObject *o)
\snippet signalsandslots/signalsandslots.h 3
\note This macro requires the class to be a subclass of QObject. Use
- Q_GADGET or Q_GADGET_EXPORT instead of Q_OBJECT to enable the meta object system's support
- for enums in a class that is not a QObject subclass.
+ Q_GADGET or Q_GADGET_EXPORT instead of Q_OBJECT to enable the meta object
+ system's support for enums in a class that is not a QObject subclass.
\sa {Meta-Object System}, {Signals and Slots}, {Qt's Property System}
*/
@@ -4575,15 +4736,16 @@ QDebug operator<<(QDebug dbg, const QObject *o)
The Q_GADGET macro is a lighter version of the Q_OBJECT macro for classes
that do not inherit from QObject but still want to use some of the
- reflection capabilities offered by QMetaObject. Just like the Q_OBJECT
- macro, it must appear in the private section of a class definition.
+ reflection capabilities offered by QMetaObject.
+
+ \include qobject.cpp qobject-macros-private-access-specifier
Q_GADGETs can have Q_ENUM, Q_PROPERTY and Q_INVOKABLE, but they cannot have
signals or slots.
Q_GADGET makes a class member, \c{staticMetaObject}, available.
\c{staticMetaObject} is of type QMetaObject and provides access to the
- enums declared with Q_ENUMS.
+ enums declared with Q_ENUM.
\sa Q_GADGET_EXPORT
*/
@@ -4600,6 +4762,8 @@ QDebug operator<<(QDebug dbg, const QObject *o)
enclosing class as a whole should not be (e.g. because it consists of mostly
inline functions).
+ \include qobject.cpp qobject-macros-private-access-specifier
+
For example:
\code
@@ -4840,17 +5004,34 @@ QDebug operator<<(QDebug dbg, const QObject *o)
*/
/*!
+ \macro QT_NO_CONTEXTLESS_CONNECT
+ \relates QObject
+ \since 6.7
+
+ Defining this macro will disable the overload of QObject::connect() that
+ connects a signal to a functor, without also specifying a QObject
+ as a receiver/context object (that is, the 3-arguments overload
+ of QObject::connect()).
+
+ Using the context-less overload is error prone, because it is easy
+ to connect to functors that depend on some local state of the
+ receiving end. If such local state gets destroyed, the connection
+ does not get automatically disconnected.
+
+ Moreover, such connections are always direct connections, which may
+ cause issues in multithreaded scenarios (for instance, if the
+ signal is emitted from another thread).
+
+ \sa QObject::connect, Qt::ConnectionType
+*/
+
+/*!
\typedef QObjectList
\relates QObject
Synonym for QList<QObject *>.
*/
-void qDeleteInEventHandler(QObject *o)
-{
- delete o;
-}
-
/*!
\fn template<typename PointerToMemberFunction> QMetaObject::Connection QObject::connect(const QObject *sender, PointerToMemberFunction signal, const QObject *receiver, PointerToMemberFunction method, Qt::ConnectionType type)
\overload connect()
@@ -4944,6 +5125,11 @@ void qDeleteInEventHandler(QObject *o)
However, you should take care that any objects used within the functor
are still alive when the signal is emitted.
+ For this reason, it is recommended to use the overload of connect()
+ that also takes a QObject as a receiver/context. It is possible
+ to disable the usage of the context-less overload by defining the
+ \c{QT_NO_CONTEXTLESS_CONNECT} macro.
+
Overloaded functions can be resolved with help of \l qOverload.
*/
@@ -5009,13 +5195,12 @@ void qDeleteInEventHandler(QObject *o)
*/
QMetaObject::Connection QObject::connectImpl(const QObject *sender, void **signal,
const QObject *receiver, void **slot,
- QtPrivate::QSlotObjectBase *slotObj, Qt::ConnectionType type,
+ QtPrivate::QSlotObjectBase *slotObjRaw, Qt::ConnectionType type,
const int *types, const QMetaObject *senderMetaObject)
{
+ QtPrivate::SlotObjUniquePtr slotObj(slotObjRaw);
if (!signal) {
qCWarning(lcConnect, "QObject::connect: invalid nullptr parameter");
- if (slotObj)
- slotObj->destroyIfLastRef();
return QMetaObject::Connection();
}
@@ -5028,11 +5213,10 @@ QMetaObject::Connection QObject::connectImpl(const QObject *sender, void **signa
}
if (!senderMetaObject) {
qCWarning(lcConnect, "QObject::connect: signal not found in %s", sender->metaObject()->className());
- slotObj->destroyIfLastRef();
return QMetaObject::Connection(nullptr);
}
signal_index += QMetaObjectPrivate::signalOffset(senderMetaObject);
- return QObjectPrivate::connectImpl(sender, signal_index, receiver, slot, slotObj, type, types, senderMetaObject);
+ return QObjectPrivate::connectImpl(sender, signal_index, receiver, slot, slotObj.release(), type, types, senderMetaObject);
}
static void connectWarning(const QObject *sender,
@@ -5057,14 +5241,10 @@ static void connectWarning(const QObject *sender,
*/
QMetaObject::Connection QObjectPrivate::connectImpl(const QObject *sender, int signal_index,
const QObject *receiver, void **slot,
- QtPrivate::QSlotObjectBase *slotObj, int type,
+ QtPrivate::QSlotObjectBase *slotObjRaw, int type,
const int *types, const QMetaObject *senderMetaObject)
{
- auto connectFailureGuard = qScopeGuard([&]()
- {
- if (slotObj)
- slotObj->destroyIfLastRef();
- });
+ QtPrivate::SlotObjUniquePtr slotObj(slotObjRaw);
if (!sender || !receiver || !slotObj || !senderMetaObject) {
connectWarning(sender, senderMetaObject, receiver, "invalid nullptr parameter");
@@ -5076,24 +5256,20 @@ QMetaObject::Connection QObjectPrivate::connectImpl(const QObject *sender, int s
return QMetaObject::Connection();
}
- connectFailureGuard.dismiss();
-
QObject *s = const_cast<QObject *>(sender);
QObject *r = const_cast<QObject *>(receiver);
QOrderedMutexLocker locker(signalSlotLock(sender),
signalSlotLock(receiver));
- if (type & Qt::UniqueConnection && slot && QObjectPrivate::get(s)->connections.loadRelaxed()) {
+ if (type & Qt::UniqueConnection && slot) {
QObjectPrivate::ConnectionData *connections = QObjectPrivate::get(s)->connections.loadRelaxed();
- if (connections->signalVectorCount() > signal_index) {
+ if (connections && connections->signalVectorCount() > signal_index) {
const QObjectPrivate::Connection *c2 = connections->signalVector.loadRelaxed()->at(signal_index).first.loadRelaxed();
while (c2) {
- if (c2->receiver.loadRelaxed() == receiver && c2->isSlotObject && c2->slotObj->compare(slot)) {
- slotObj->destroyIfLastRef();
+ if (c2->receiver.loadRelaxed() == receiver && c2->isSlotObject && c2->slotObj->compare(slot))
return QMetaObject::Connection();
- }
c2 = c2->nextConnectionList.loadRelaxed();
}
}
@@ -5113,9 +5289,9 @@ QMetaObject::Connection QObjectPrivate::connectImpl(const QObject *sender, int s
td->ref();
c->receiverThreadData.storeRelaxed(td);
c->receiver.storeRelaxed(r);
- c->slotObj = slotObj;
c->connectionType = type;
c->isSlotObject = true;
+ c->slotObj = slotObj.release();
if (types) {
c->argumentTypes.storeRelaxed(types);
c->ownArgumentTypes = false;
@@ -5264,20 +5440,19 @@ QMetaObject::Connection QObjectPrivate::connect(const QObject *sender, int signa
*/
QMetaObject::Connection QObjectPrivate::connect(const QObject *sender, int signal_index,
const QObject *receiver,
- QtPrivate::QSlotObjectBase *slotObj,
+ QtPrivate::QSlotObjectBase *slotObjRaw,
Qt::ConnectionType type)
{
+ QtPrivate::SlotObjUniquePtr slotObj(slotObjRaw);
if (!sender) {
qCWarning(lcConnect, "QObject::connect: invalid nullptr parameter");
- if (slotObj)
- slotObj->destroyIfLastRef();
return QMetaObject::Connection();
}
const QMetaObject *senderMetaObject = sender->metaObject();
signal_index = methodIndexToSignalIndex(&senderMetaObject, signal_index);
- return QObjectPrivate::connectImpl(sender, signal_index, receiver, /*slot*/ nullptr, slotObj,
- type, /*types*/ nullptr, senderMetaObject);
+ return connectImpl(sender, signal_index, receiver, /*slot*/ nullptr, slotObj.release(),
+ type, /*types*/ nullptr, senderMetaObject);
}
/*!
@@ -5366,11 +5541,13 @@ inline bool QObjectPrivate::removeConnection(QObjectPrivate::Connection *c)
QtPrivate::QPropertyAdaptorSlotObject *
QObjectPrivate::getPropertyAdaptorSlotObject(const QMetaProperty &property)
{
- if (auto conns = connections.loadRelaxed()) {
+ if (auto conns = connections.loadAcquire()) {
Q_Q(QObject);
const QMetaObject *metaObject = q->metaObject();
int signal_index = methodIndexToSignalIndex(&metaObject, property.notifySignalIndex());
- auto connectionList = conns->connectionsForSignal(signal_index);
+ if (signal_index >= conns->signalVectorCount())
+ return nullptr;
+ const auto &connectionList = conns->connectionsForSignal(signal_index);
for (auto c = connectionList.first.loadRelaxed(); c;
c = c->nextConnectionList.loadRelaxed()) {
if (c->isSlotObject) {
diff --git a/src/corelib/kernel/qobject.h b/src/corelib/kernel/qobject.h
index 075529571f..06cfefd61b 100644
--- a/src/corelib/kernel/qobject.h
+++ b/src/corelib/kernel/qobject.h
@@ -19,6 +19,7 @@
#include <QtCore/qobject_impl.h>
#include <QtCore/qbindingstorage.h>
+#include <QtCore/qtcoreexports.h>
#include <chrono>
@@ -43,13 +44,24 @@ struct QDynamicMetaObjectData;
typedef QList<QObject*> QObjectList;
+#if QT_CORE_REMOVED_SINCE(6, 7)
Q_CORE_EXPORT void qt_qFindChildren_helper(const QObject *parent, const QString &name,
const QMetaObject &mo, QList<void *> *list, Qt::FindChildOptions options);
+#endif
+Q_CORE_EXPORT void qt_qFindChildren_helper(const QObject *parent, QAnyStringView name,
+ const QMetaObject &mo, QList<void *> *list,
+ Qt::FindChildOptions options);
+#if QT_CORE_REMOVED_SINCE(6, 7)
Q_CORE_EXPORT void qt_qFindChildren_helper(const QObject *parent, const QMetaObject &mo,
QList<void *> *list, Qt::FindChildOptions options);
+#endif
Q_CORE_EXPORT void qt_qFindChildren_helper(const QObject *parent, const QRegularExpression &re,
const QMetaObject &mo, QList<void *> *list, Qt::FindChildOptions options);
+#if QT_CORE_REMOVED_SINCE(6, 7)
Q_CORE_EXPORT QObject *qt_qFindChild_helper(const QObject *parent, const QString &name, const QMetaObject &mo, Qt::FindChildOptions options);
+#endif
+Q_CORE_EXPORT QObject *qt_qFindChild_helper(const QObject *parent, QAnyStringView name,
+ const QMetaObject &mo, Qt::FindChildOptions options);
class Q_CORE_EXPORT QObjectData
{
@@ -72,7 +84,8 @@ public:
uint isQuickItem : 1;
uint willBeWidget : 1; // for handling widget-specific bits in QObject's ctor
uint wasWidget : 1; // for properly cleaning up in QObject's dtor
- uint unused : 21;
+ uint receiveParentEvents: 1;
+ uint unused : 20;
QAtomicInt postedEvents;
QDynamicMetaObjectData *metaObject;
QBindingStorage bindingStorage;
@@ -123,27 +136,36 @@ public:
bool blockSignals(bool b) noexcept;
QThread *thread() const;
+#if QT_CORE_REMOVED_SINCE(6, 7)
void moveToThread(QThread *thread);
+#endif
+ bool moveToThread(QThread *thread QT6_DECL_NEW_OVERLOAD_TAIL);
int startTimer(int interval, Qt::TimerType timerType = Qt::CoarseTimer);
- Q_ALWAYS_INLINE
- int startTimer(std::chrono::milliseconds time, Qt::TimerType timerType = Qt::CoarseTimer)
- {
- return startTimer(int(time.count()), timerType);
- }
+
+#if QT_CORE_REMOVED_SINCE(6, 8)
+ int startTimer(std::chrono::milliseconds time, Qt::TimerType timerType = Qt::CoarseTimer);
+#endif
+ int startTimer(std::chrono::nanoseconds time, Qt::TimerType timerType = Qt::CoarseTimer);
+
void killTimer(int id);
+ void killTimer(Qt::TimerId id);
template<typename T>
- inline T findChild(const QString &aName = QString(), Qt::FindChildOptions options = Qt::FindChildrenRecursively) const
+ T findChild(QAnyStringView aName, Qt::FindChildOptions options = Qt::FindChildrenRecursively) const
{
typedef typename std::remove_cv<typename std::remove_pointer<T>::type>::type ObjType;
+ static_assert(QtPrivate::HasQ_OBJECT_Macro<ObjType>::Value,
+ "No Q_OBJECT in the class passed to QObject::findChild");
return static_cast<T>(qt_qFindChild_helper(this, aName, ObjType::staticMetaObject, options));
}
template<typename T>
- inline QList<T> findChildren(const QString &aName, Qt::FindChildOptions options = Qt::FindChildrenRecursively) const
+ QList<T> findChildren(QAnyStringView aName, Qt::FindChildOptions options = Qt::FindChildrenRecursively) const
{
typedef typename std::remove_cv<typename std::remove_pointer<T>::type>::type ObjType;
+ static_assert(QtPrivate::HasQ_OBJECT_Macro<ObjType>::Value,
+ "No Q_OBJECT in the class passed to QObject::findChildren");
QList<T> list;
qt_qFindChildren_helper(this, aName, ObjType::staticMetaObject,
reinterpret_cast<QList<void *> *>(&list), options);
@@ -151,13 +173,15 @@ public:
}
template<typename T>
+ T findChild(Qt::FindChildOptions options = Qt::FindChildrenRecursively) const
+ {
+ return findChild<T>({}, options);
+ }
+
+ template<typename T>
QList<T> findChildren(Qt::FindChildOptions options = Qt::FindChildrenRecursively) const
{
- typedef typename std::remove_cv<typename std::remove_pointer<T>::type>::type ObjType;
- QList<T> list;
- qt_qFindChildren_helper(this, ObjType::staticMetaObject,
- reinterpret_cast<QList<void *> *>(&list), options);
- return list;
+ return findChildren<T>(QAnyStringView{}, options);
}
#if QT_CONFIG(regularexpression)
@@ -165,6 +189,8 @@ public:
inline QList<T> findChildren(const QRegularExpression &re, Qt::FindChildOptions options = Qt::FindChildrenRecursively) const
{
typedef typename std::remove_cv<typename std::remove_pointer<T>::type>::type ObjType;
+ static_assert(QtPrivate::HasQ_OBJECT_Macro<ObjType>::Value,
+ "No Q_OBJECT in the class passed to QObject::findChildren");
QList<T> list;
qt_qFindChildren_helper(this, re, ObjType::staticMetaObject,
reinterpret_cast<QList<void *> *>(&list), options);
@@ -196,54 +222,28 @@ public:
template<typename PointerToMemberFunction, typename Functor>
static QMetaObject::Connection connect(const QObject *sender, PointerToMemberFunction signal, const QObject *context, Functor functor, Qt::ConnectionType type = Qt::AutoConnection);
#else
- //Connect a signal to a pointer to qobject member function
+ //connect with context
template <typename Func1, typename Func2>
- static inline QMetaObject::Connection connect(const typename QtPrivate::FunctionPointer<Func1>::Object *sender, Func1 signal,
- const typename QtPrivate::FunctionPointer<Func2>::Object *receiver, Func2 slot,
- Qt::ConnectionType type = Qt::AutoConnection)
+ static inline QMetaObject::Connection
+ connect(const typename QtPrivate::FunctionPointer<Func1>::Object *sender, Func1 signal,
+ const typename QtPrivate::ContextTypeForFunctor<Func2>::ContextType *context, Func2 &&slot,
+ Qt::ConnectionType type = Qt::AutoConnection)
{
typedef QtPrivate::FunctionPointer<Func1> SignalType;
- typedef QtPrivate::FunctionPointer<Func2> SlotType;
+ typedef QtPrivate::FunctionPointer<std::decay_t<Func2>> SlotType;
- static_assert(QtPrivate::HasQ_OBJECT_Macro<typename SignalType::Object>::Value,
- "No Q_OBJECT in the class with the signal");
-
- //compilation error if the arguments does not match.
- static_assert(int(SignalType::ArgumentCount) >= int(SlotType::ArgumentCount),
- "The slot requires more arguments than the signal provides.");
- static_assert((QtPrivate::CheckCompatibleArguments<typename SignalType::Arguments, typename SlotType::Arguments>::value),
- "Signal and slot arguments are not compatible.");
- static_assert((QtPrivate::AreArgumentsCompatible<typename SlotType::ReturnType, typename SignalType::ReturnType>::value),
- "Return type of the slot is not compatible with the return type of the signal.");
-
- const int *types = nullptr;
- if (type == Qt::QueuedConnection || type == Qt::BlockingQueuedConnection)
- types = QtPrivate::ConnectionTypes<typename SignalType::Arguments>::types();
-
- return connectImpl(sender, reinterpret_cast<void **>(&signal),
- receiver, reinterpret_cast<void **>(&slot),
- new QtPrivate::QSlotObject<Func2, typename QtPrivate::List_Left<typename SignalType::Arguments, SlotType::ArgumentCount>::Value,
- typename SignalType::ReturnType>(slot),
- type, types, &SignalType::Object::staticMetaObject);
- }
-
- //connect to a function pointer (not a member)
- template <typename Func1, typename Func2>
- static inline typename std::enable_if<int(QtPrivate::FunctionPointer<Func2>::ArgumentCount) >= 0, QMetaObject::Connection>::type
- connect(const typename QtPrivate::FunctionPointer<Func1>::Object *sender, Func1 signal, Func2 slot)
- {
- return connect(sender, signal, sender, slot, Qt::DirectConnection);
- }
+ if constexpr (SlotType::ArgumentCount != -1) {
+ static_assert((QtPrivate::AreArgumentsCompatible<typename SlotType::ReturnType, typename SignalType::ReturnType>::value),
+ "Return type of the slot is not compatible with the return type of the signal.");
+ } else {
+ constexpr int FunctorArgumentCount = QtPrivate::ComputeFunctorArgumentCount<std::decay_t<Func2>, typename SignalType::Arguments>::Value;
+ [[maybe_unused]]
+ constexpr int SlotArgumentCount = (FunctorArgumentCount >= 0) ? FunctorArgumentCount : 0;
+ typedef typename QtPrivate::FunctorReturnType<std::decay_t<Func2>, typename QtPrivate::List_Left<typename SignalType::Arguments, SlotArgumentCount>::Value>::Value SlotReturnType;
- //connect to a function pointer (not a member)
- template <typename Func1, typename Func2>
- static inline typename std::enable_if<int(QtPrivate::FunctionPointer<Func2>::ArgumentCount) >= 0 &&
- !QtPrivate::FunctionPointer<Func2>::IsPointerToMemberFunction, QMetaObject::Connection>::type
- connect(const typename QtPrivate::FunctionPointer<Func1>::Object *sender, Func1 signal, const QObject *context, Func2 slot,
- Qt::ConnectionType type = Qt::AutoConnection)
- {
- typedef QtPrivate::FunctionPointer<Func1> SignalType;
- typedef QtPrivate::FunctionPointer<Func2> SlotType;
+ static_assert((QtPrivate::AreArgumentsCompatible<SlotReturnType, typename SignalType::ReturnType>::value),
+ "Return type of the slot is not compatible with the return type of the signal.");
+ }
static_assert(QtPrivate::HasQ_OBJECT_Macro<typename SignalType::Object>::Value,
"No Q_OBJECT in the class with the signal");
@@ -251,66 +251,34 @@ public:
//compilation error if the arguments does not match.
static_assert(int(SignalType::ArgumentCount) >= int(SlotType::ArgumentCount),
"The slot requires more arguments than the signal provides.");
- static_assert((QtPrivate::CheckCompatibleArguments<typename SignalType::Arguments, typename SlotType::Arguments>::value),
- "Signal and slot arguments are not compatible.");
- static_assert((QtPrivate::AreArgumentsCompatible<typename SlotType::ReturnType, typename SignalType::ReturnType>::value),
- "Return type of the slot is not compatible with the return type of the signal.");
const int *types = nullptr;
if (type == Qt::QueuedConnection || type == Qt::BlockingQueuedConnection)
types = QtPrivate::ConnectionTypes<typename SignalType::Arguments>::types();
- return connectImpl(sender, reinterpret_cast<void **>(&signal), context, nullptr,
- new QtPrivate::QStaticSlotObject<Func2,
- typename QtPrivate::List_Left<typename SignalType::Arguments, SlotType::ArgumentCount>::Value,
- typename SignalType::ReturnType>(slot),
+ void **pSlot = nullptr;
+ if constexpr (std::is_member_function_pointer_v<std::decay_t<Func2>>) {
+ pSlot = const_cast<void **>(reinterpret_cast<void *const *>(&slot));
+ } else {
+ Q_ASSERT_X((type & Qt::UniqueConnection) == 0, "",
+ "QObject::connect: Unique connection requires the slot to be a pointer to "
+ "a member function of a QObject subclass.");
+ }
+
+ return connectImpl(sender, reinterpret_cast<void **>(&signal), context, pSlot,
+ QtPrivate::makeCallableObject<Func1>(std::forward<Func2>(slot)),
type, types, &SignalType::Object::staticMetaObject);
}
- //connect to a functor
+#ifndef QT_NO_CONTEXTLESS_CONNECT
+ //connect without context
template <typename Func1, typename Func2>
- static inline typename std::enable_if<
- QtPrivate::FunctionPointer<Func2>::ArgumentCount == -1 &&
- !std::is_convertible_v<Func2, const char*>, // don't match old-style connect
- QMetaObject::Connection>::type
- connect(const typename QtPrivate::FunctionPointer<Func1>::Object *sender, Func1 signal, Func2 slot)
+ static inline QMetaObject::Connection
+ connect(const typename QtPrivate::FunctionPointer<Func1>::Object *sender, Func1 signal, Func2 &&slot)
{
- 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
- template <typename Func1, typename Func2>
- static inline typename std::enable_if<
- QtPrivate::FunctionPointer<Func2>::ArgumentCount == -1 &&
- !std::is_convertible_v<Func2, const char*>, // don't match old-style connect
- QMetaObject::Connection>::type
- connect(const typename QtPrivate::FunctionPointer<Func1>::Object *sender, Func1 signal, const QObject *context, Func2 slot,
- Qt::ConnectionType type = Qt::AutoConnection)
- {
- typedef QtPrivate::FunctionPointer<Func1> SignalType;
- const int FunctorArgumentCount = QtPrivate::ComputeFunctorArgumentCount<Func2 , typename SignalType::Arguments>::Value;
-
- static_assert((FunctorArgumentCount >= 0),
- "Signal and slot arguments are not compatible.");
- const int SlotArgumentCount = (FunctorArgumentCount >= 0) ? FunctorArgumentCount : 0;
- typedef typename QtPrivate::FunctorReturnType<Func2, typename QtPrivate::List_Left<typename SignalType::Arguments, SlotArgumentCount>::Value>::Value SlotReturnType;
-
- static_assert((QtPrivate::AreArgumentsCompatible<SlotReturnType, typename SignalType::ReturnType>::value),
- "Return type of the slot is not compatible with the return type of the signal.");
-
- static_assert(QtPrivate::HasQ_OBJECT_Macro<typename SignalType::Object>::Value,
- "No Q_OBJECT in the class with the signal");
-
- const int *types = nullptr;
- if (type == Qt::QueuedConnection || type == Qt::BlockingQueuedConnection)
- types = QtPrivate::ConnectionTypes<typename SignalType::Arguments>::types();
-
- return connectImpl(sender, reinterpret_cast<void **>(&signal), context, nullptr,
- new QtPrivate::QFunctorSlotObject<Func2, SlotArgumentCount,
- typename QtPrivate::List_Left<typename SignalType::Arguments, SlotArgumentCount>::Value,
- typename SignalType::ReturnType>(std::move(slot)),
- type, types, &SignalType::Object::staticMetaObject);
+ return connect(sender, signal, sender, std::forward<Func2>(slot), Qt::DirectConnection);
}
+#endif // QT_NO_CONTEXTLESS_CONNECT
#endif //Q_QDOC
static bool disconnect(const QObject *sender, const char *signal,
@@ -362,7 +330,9 @@ public:
void dumpObjectTree() const;
void dumpObjectInfo() const;
+ QT_CORE_INLINE_SINCE(6, 6)
bool setProperty(const char *name, const QVariant &value);
+ inline bool setProperty(const char *name, QVariant &&value);
QVariant property(const char *name) const;
QList<QByteArray> dynamicPropertyNames() const;
QBindingStorage *bindingStorage() { return &d_ptr->bindingStorage; }
@@ -415,8 +385,9 @@ protected:
private:
void doSetObjectName(const QString &name);
+ bool doSetProperty(const char *name, const QVariant *lvalue, QVariant *rvalue);
+
Q_DISABLE_COPY(QObject)
- Q_PRIVATE_SLOT(d_func(), void _q_reregisterTimers(void *))
private:
static QMetaObject::Connection connectImpl(const QObject *sender, void **signal,
@@ -433,9 +404,22 @@ inline QMetaObject::Connection QObject::connect(const QObject *asender, const ch
const char *amember, Qt::ConnectionType atype) const
{ return connect(asender, asignal, this, amember, atype); }
+#if QT_CORE_INLINE_IMPL_SINCE(6, 6)
+bool QObject::setProperty(const char *name, const QVariant &value)
+{
+ return doSetProperty(name, &value, nullptr);
+}
+#endif // inline since 6.6
+bool QObject::setProperty(const char *name, QVariant &&value)
+{
+ return doSetProperty(name, &value, &value);
+}
+
template <class T>
inline T qobject_cast(QObject *object)
{
+ static_assert(std::is_pointer_v<T>,
+ "qobject_cast requires to cast towards a pointer type");
typedef typename std::remove_cv<typename std::remove_pointer<T>::type>::type ObjType;
static_assert(QtPrivate::HasQ_OBJECT_Macro<ObjType>::Value,
"qobject_cast requires the type to have a Q_OBJECT macro");
@@ -445,6 +429,10 @@ inline T qobject_cast(QObject *object)
template <class T>
inline T qobject_cast(const QObject *object)
{
+ static_assert(std::is_pointer_v<T>,
+ "qobject_cast requires to cast towards a pointer type");
+ static_assert(std::is_const_v<std::remove_pointer_t<T>>,
+ "qobject_cast cannot cast away constness (use const_cast)");
typedef typename std::remove_cv<typename std::remove_pointer<T>::type>::type ObjType;
static_assert(QtPrivate::HasQ_OBJECT_Macro<ObjType>::Value,
"qobject_cast requires the type to have a Q_OBJECT macro");
@@ -494,15 +482,19 @@ Q_CORE_EXPORT QDebug operator<<(QDebug, const QObject *);
class QSignalBlocker
{
public:
+ Q_NODISCARD_CTOR
inline explicit QSignalBlocker(QObject *o) noexcept;
+ Q_NODISCARD_CTOR
inline explicit QSignalBlocker(QObject &o) noexcept;
inline ~QSignalBlocker();
+ Q_NODISCARD_CTOR
inline QSignalBlocker(QSignalBlocker &&other) noexcept;
inline QSignalBlocker &operator=(QSignalBlocker &&other) noexcept;
inline void reblock() noexcept;
inline void unblock() noexcept;
+ inline void dismiss() noexcept;
private:
Q_DISABLE_COPY(QSignalBlocker)
@@ -567,6 +559,11 @@ void QSignalBlocker::unblock() noexcept
m_inhibited = true;
}
+void QSignalBlocker::dismiss() noexcept
+{
+ m_o = nullptr;
+}
+
namespace QtPrivate {
inline QObject & deref_for_methodcall(QObject &o) { return o; }
inline QObject & deref_for_methodcall(QObject *o) { return *o; }
diff --git a/src/corelib/kernel/qobject_impl.h b/src/corelib/kernel/qobject_impl.h
index 4d6830a3cc..b57d7e50cc 100644
--- a/src/corelib/kernel/qobject_impl.h
+++ b/src/corelib/kernel/qobject_impl.h
@@ -37,30 +37,6 @@ namespace QtPrivate {
{ static const int *types() { return nullptr; } };
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; } };
-
- // implementation of QSlotObjectBase for which the slot is a static function
- // Args and R are the List of arguments and the return type of the signal to which the slot is connected.
- template<typename Func, typename Args, typename R> class QStaticSlotObject : 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<QStaticSlotObject*>(this_);
- break;
- case Call:
- FuncType::template call<Args, R>(static_cast<QStaticSlotObject*>(this_)->function, r, a);
- break;
- case Compare: // not implemented
- case NumOperations:
- Q_UNUSED(ret);
- }
- }
- public:
- explicit QStaticSlotObject(Func f) : QSlotObjectBase(&impl), function(f) {}
- };
}
diff --git a/src/corelib/kernel/qobject_p.h b/src/corelib/kernel/qobject_p.h
index dbb4eb2326..0ab9bf02ed 100644
--- a/src/corelib/kernel/qobject_p.h
+++ b/src/corelib/kernel/qobject_p.h
@@ -18,18 +18,27 @@
#include <QtCore/private/qglobal_p.h>
#include "QtCore/qcoreevent.h"
+#include <QtCore/qfunctionaltools_impl.h>
#include "QtCore/qlist.h"
#include "QtCore/qobject.h"
#include "QtCore/qpointer.h"
-#include "QtCore/qsharedpointer.h"
#include "QtCore/qvariant.h"
#include "QtCore/qproperty.h"
+#include <QtCore/qshareddata.h>
#include "QtCore/private/qproperty_p.h"
#include <string>
QT_BEGIN_NAMESPACE
+#ifdef Q_MOC_RUN
+#define QT_ANONYMOUS_PROPERTY(text) QT_ANONYMOUS_PROPERTY(text)
+#define QT_ANONYMOUS_PRIVATE_PROPERTY(d, text) QT_ANONYMOUS_PRIVATE_PROPERTY(d, text)
+#elif !defined QT_NO_META_MACROS
+#define QT_ANONYMOUS_PROPERTY(...) QT_ANNOTATE_CLASS(qt_anonymous_property, __VA_ARGS__)
+#define QT_ANONYMOUS_PRIVATE_PROPERTY(d, text) QT_ANNOTATE_CLASS2(qt_anonymous_private_property, d, text)
+#endif
+
class QVariant;
class QThreadData;
class QObjectConnectionListVector;
@@ -82,7 +91,7 @@ public:
QList<QByteArray> propertyNames;
QList<QVariant> propertyValues;
- QList<int> runningTimers;
+ QList<Qt::TimerId> runningTimers;
QList<QPointer<QObject>> eventFilters;
Q_OBJECT_COMPAT_PROPERTY(QObjectPrivate::ExtraData, QString, objectName,
&QObjectPrivate::ExtraData::setObjectNameForwarder,
@@ -103,6 +112,7 @@ public:
struct ConnectionOrSignalVector;
struct SignalVector;
struct Sender;
+ struct TaggedSignalVector;
/*
This contains the all connections from and to an object.
@@ -130,11 +140,8 @@ public:
void setParent_helper(QObject *);
void moveToThread_helper();
void setThreadData_helper(QThreadData *currentData, QThreadData *targetData, QBindingStatus *status);
- void _q_reregisterTimers(void *pointer);
- bool isSender(const QObject *receiver, const char *signal) const;
QObjectList receiverList(const char *signal) const;
- QObjectList senderList() const;
inline void ensureConnectionData();
inline void addConnection(int signal, Connection *c);
@@ -179,6 +186,10 @@ public:
virtual std::string flagsForDumping() const;
+#ifndef QT_NO_DEBUG_STREAM
+ virtual void writeToDebugStream(QDebug &) const;
+#endif
+
QtPrivate::QPropertyAdaptorSlotObject *
getPropertyAdaptorSlotObject(const QMetaProperty &property);
@@ -243,30 +254,10 @@ inline void QObjectPrivate::disconnectNotify(const QMetaMethod &signal)
}
namespace QtPrivate {
+inline const QObject *getQObject(const QObjectPrivate *d) { return d->q_func(); }
template <typename Func>
-struct FunctionStorageByValue
-{
- Func f;
- Func &func() noexcept { return f; }
-};
-
-template <typename Func>
-struct FunctionStorageEmptyBaseClassOptimization : Func
-{
- Func &func() noexcept { return *this; }
- using Func::Func;
-};
-
-template <typename Func>
-using FunctionStorage = typename std::conditional_t<
- std::conjunction_v<
- std::is_empty<Func>,
- std::negation<std::is_final<Func>>
- >,
- FunctionStorageEmptyBaseClassOptimization<Func>,
- FunctionStorageByValue<Func>
- >;
+using FunctionStorage = QtPrivate::CompactStorage<Func>;
template <typename ObjPrivate> inline void assertObjectType(QObjectPrivate *d)
{
@@ -278,18 +269,23 @@ template<typename Func, typename Args, typename R>
class QPrivateSlotObject : public QSlotObjectBase, private FunctionStorage<Func>
{
typedef QtPrivate::FunctionPointer<Func> FuncType;
+#if QT_VERSION < QT_VERSION_CHECK(7, 0, 0)
static void impl(int which, QSlotObjectBase *this_, QObject *r, void **a, bool *ret)
+#else
+ static void impl(QSlotObjectBase *this_, QObject *r, void **a, int which, bool *ret)
+#endif
{
+ const auto that = static_cast<QPrivateSlotObject*>(this_);
switch (which) {
case Destroy:
- delete static_cast<QPrivateSlotObject*>(this_);
+ delete that;
break;
case Call:
- FuncType::template call<Args, R>(static_cast<QPrivateSlotObject*>(this_)->func(),
+ FuncType::template call<Args, R>(that->object(),
static_cast<typename FuncType::Object *>(QObjectPrivate::get(r)), a);
break;
case Compare:
- *ret = *reinterpret_cast<Func *>(a) == static_cast<QPrivateSlotObject*>(this_)->func();
+ *ret = *reinterpret_cast<Func *>(a) == that->object();
break;
case NumOperations: ;
}
@@ -322,7 +318,7 @@ inline QMetaObject::Connection QObjectPrivate::connect(const typename QtPrivate:
types = QtPrivate::ConnectionTypes<typename SignalType::Arguments>::types();
return QObject::connectImpl(sender, reinterpret_cast<void **>(&signal),
- receiverPrivate->q_ptr, reinterpret_cast<void **>(&slot),
+ QtPrivate::getQObject(receiverPrivate), reinterpret_cast<void **>(&slot),
new QtPrivate::QPrivateSlotObject<Func2, typename QtPrivate::List_Left<typename SignalType::Arguments, SlotType::ArgumentCount>::Value,
typename SignalType::ReturnType>(slot),
type, types, &SignalType::Object::staticMetaObject);
@@ -380,6 +376,9 @@ public:
QMetaCallEvent(QtPrivate::QSlotObjectBase *slotObj,
const QObject *sender, int signalId,
void **args, QSemaphore *semaphore);
+ QMetaCallEvent(QtPrivate::SlotObjUniquePtr slotObj,
+ const QObject *sender, int signalId,
+ void **args, QSemaphore *semaphore);
// queued - args allocated by event, copied by caller
QMetaCallEvent(ushort method_offset, ushort method_relative,
@@ -389,9 +388,31 @@ public:
QMetaCallEvent(QtPrivate::QSlotObjectBase *slotObj,
const QObject *sender, int signalId,
int nargs);
+ QMetaCallEvent(QtPrivate::SlotObjUniquePtr slotObj,
+ const QObject *sender, int signalId,
+ int nargs);
~QMetaCallEvent() override;
+ template<typename ...Args>
+ static QMetaCallEvent *create(QtPrivate::QSlotObjectBase *slotObj, const QObject *sender,
+ int signal_index, const Args &...argv)
+ {
+ const void* const argp[] = { nullptr, std::addressof(argv)... };
+ const QMetaType metaTypes[] = { QMetaType::fromType<void>(), QMetaType::fromType<Args>()... };
+ constexpr auto argc = sizeof...(Args) + 1;
+ return create_impl(slotObj, sender, signal_index, argc, argp, metaTypes);
+ }
+ template<typename ...Args>
+ static QMetaCallEvent *create(QtPrivate::SlotObjUniquePtr slotObj, const QObject *sender,
+ int signal_index, const Args &...argv)
+ {
+ const void* const argp[] = { nullptr, std::addressof(argv)... };
+ const QMetaType metaTypes[] = { QMetaType::fromType<void>(), QMetaType::fromType<Args>()... };
+ constexpr auto argc = sizeof...(Args) + 1;
+ return create_impl(std::move(slotObj), sender, signal_index, argc, argp, metaTypes);
+ }
+
inline int id() const { return d.method_offset_ + d.method_relative_; }
inline const void * const* args() const { return d.args_; }
inline void ** args() { return d.args_; }
@@ -401,10 +422,22 @@ public:
virtual void placeMetaCall(QObject *object) override;
private:
+ static QMetaCallEvent *create_impl(QtPrivate::QSlotObjectBase *slotObj, const QObject *sender,
+ int signal_index, size_t argc, const void * const argp[],
+ const QMetaType metaTypes[])
+ {
+ if (slotObj)
+ slotObj->ref();
+ return create_impl(QtPrivate::SlotObjUniquePtr{slotObj}, sender,
+ signal_index, argc, argp, metaTypes);
+ }
+ static QMetaCallEvent *create_impl(QtPrivate::SlotObjUniquePtr slotObj, const QObject *sender,
+ int signal_index, size_t argc, const void * const argp[],
+ const QMetaType metaTypes[]);
inline void allocArgs();
struct Data {
- QtPrivate::QSlotObjectBase *slotObj_;
+ QtPrivate::SlotObjUniquePtr slotObj_;
void **args_;
QObjectPrivate::StaticMetaCallFunction callFunction_;
int nargs_;
@@ -419,7 +452,9 @@ class QBoolBlocker
{
Q_DISABLE_COPY_MOVE(QBoolBlocker)
public:
- explicit inline QBoolBlocker(bool &b, bool value = true) : block(b), reset(b) { block = value; }
+ Q_NODISCARD_CTOR explicit QBoolBlocker(bool &b, bool value = true)
+ : block(b), reset(b)
+ { block = value; }
inline ~QBoolBlocker() { block = reset; }
private:
@@ -427,8 +462,6 @@ private:
bool reset;
};
-void Q_CORE_EXPORT qDeleteInEventHandler(QObject *o);
-
struct QAbstractDynamicMetaObject;
struct Q_CORE_EXPORT QDynamicMetaObjectData
{
diff --git a/src/corelib/kernel/qobject_p_p.h b/src/corelib/kernel/qobject_p_p.h
index d79ce7e2b9..2277af0497 100644
--- a/src/corelib/kernel/qobject_p_p.h
+++ b/src/corelib/kernel/qobject_p_p.h
@@ -34,25 +34,35 @@ struct QObjectPrivate::ConnectionList
static_assert(std::is_trivially_destructible_v<QObjectPrivate::ConnectionList>);
Q_DECLARE_TYPEINFO(QObjectPrivate::ConnectionList, Q_RELOCATABLE_TYPE);
-struct QObjectPrivate::ConnectionOrSignalVector
+struct QObjectPrivate::TaggedSignalVector
{
- union {
- // linked list of orphaned connections that need cleaning up
- ConnectionOrSignalVector *nextInOrphanList;
- // linked list of connections connected to slots in this object
- Connection *next;
- };
+ quintptr c;
- static SignalVector *asSignalVector(ConnectionOrSignalVector *c)
+ TaggedSignalVector() = default;
+ TaggedSignalVector(std::nullptr_t) noexcept : c(0) {}
+ TaggedSignalVector(Connection *v) noexcept : c(reinterpret_cast<quintptr>(v)) { Q_ASSERT(v && (reinterpret_cast<quintptr>(v) & 0x1) == 0); }
+ TaggedSignalVector(SignalVector *v) noexcept : c(reinterpret_cast<quintptr>(v) | quintptr(1u)) { Q_ASSERT(v); }
+ explicit operator SignalVector *() const noexcept
{
- if (reinterpret_cast<quintptr>(c) & 1)
- return reinterpret_cast<SignalVector *>(reinterpret_cast<quintptr>(c) & ~quintptr(1u));
+ if (c & 0x1)
+ return reinterpret_cast<SignalVector *>(c & ~quintptr(1u));
return nullptr;
}
- static Connection *fromSignalVector(SignalVector *v)
+ explicit operator Connection *() const noexcept
{
- return reinterpret_cast<Connection *>(reinterpret_cast<quintptr>(v) | quintptr(1u));
+ return reinterpret_cast<Connection *>(c);
}
+ operator uintptr_t() const noexcept { return c; }
+};
+
+struct QObjectPrivate::ConnectionOrSignalVector
+{
+ union {
+ // linked list of orphaned connections that need cleaning up
+ TaggedSignalVector nextInOrphanList;
+ // linked list of connections connected to slots in this object
+ Connection *next;
+ };
};
static_assert(std::is_trivial_v<QObjectPrivate::ConnectionOrSignalVector>);
@@ -132,12 +142,12 @@ struct QObjectPrivate::ConnectionData
QAtomicPointer<SignalVector> signalVector;
Connection *senders = nullptr;
Sender *currentSender = nullptr; // object currently activating the object
- QAtomicPointer<Connection> orphaned;
+ std::atomic<TaggedSignalVector> orphaned = {};
~ConnectionData()
{
Q_ASSERT(ref.loadRelaxed() == 0);
- auto *c = orphaned.fetchAndStoreRelaxed(nullptr);
+ TaggedSignalVector c = orphaned.exchange(nullptr, std::memory_order_relaxed);
if (c)
deleteOrphaned(c);
SignalVector *v = signalVector.loadRelaxed();
@@ -159,7 +169,7 @@ struct QObjectPrivate::ConnectionData
};
void cleanOrphanedConnections(QObject *sender, LockPolicy lockPolicy = NeedToLock)
{
- if (orphaned.loadRelaxed() && ref.loadAcquire() == 1)
+ if (orphaned.load(std::memory_order_relaxed) && ref.loadAcquire() == 1)
cleanOrphanedConnectionsImpl(sender, lockPolicy);
}
void cleanOrphanedConnectionsImpl(QObject *sender, LockPolicy lockPolicy);
@@ -194,15 +204,14 @@ struct QObjectPrivate::ConnectionData
signalVector.storeRelaxed(newVector);
if (vector) {
- Connection *o = nullptr;
+ TaggedSignalVector o = nullptr;
/* No ABA issue here: When adding a node, we only care about the list head, it doesn't
* matter if the tail changes.
*/
+ o = orphaned.load(std::memory_order_acquire);
do {
- o = orphaned.loadRelaxed();
vector->nextInOrphanList = o;
- } while (!orphaned.testAndSetRelease(
- o, ConnectionOrSignalVector::fromSignalVector(vector)));
+ } while (!orphaned.compare_exchange_strong(o, TaggedSignalVector(vector), std::memory_order_release));
}
}
int signalVectorCount() const
@@ -210,24 +219,23 @@ struct QObjectPrivate::ConnectionData
return signalVector.loadAcquire() ? signalVector.loadRelaxed()->count() : -1;
}
- static void deleteOrphaned(ConnectionOrSignalVector *c);
+ static void deleteOrphaned(TaggedSignalVector o);
};
struct QObjectPrivate::Sender
{
- Sender(QObject *receiver, QObject *sender, int signal)
+ Sender(QObject *receiver, QObject *sender, int signal, ConnectionData *receiverConnections)
: receiver(receiver), sender(sender), signal(signal)
{
- if (receiver) {
- ConnectionData *cd = receiver->d_func()->connections.loadRelaxed();
- previous = cd->currentSender;
- cd->currentSender = this;
+ if (receiverConnections) {
+ previous = receiverConnections->currentSender;
+ receiverConnections->currentSender = this;
}
}
~Sender()
{
if (receiver)
- receiver->d_func()->connections.loadRelaxed()->currentSender = previous;
+ receiver->d_func()->connections.loadAcquire()->currentSender = previous;
}
void receiverDeleted()
{
@@ -237,7 +245,7 @@ struct QObjectPrivate::Sender
s = s->previous;
}
}
- Sender *previous;
+ Sender *previous = nullptr;
QObject *receiver;
QObject *sender;
int signal;
diff --git a/src/corelib/kernel/qobjectcleanuphandler.cpp b/src/corelib/kernel/qobjectcleanuphandler.cpp
index aae93ecc00..f46afc2f07 100644
--- a/src/corelib/kernel/qobjectcleanuphandler.cpp
+++ b/src/corelib/kernel/qobjectcleanuphandler.cpp
@@ -60,7 +60,7 @@ QObject *QObjectCleanupHandler::add(QObject *object)
if (!object)
return nullptr;
- connect(object, SIGNAL(destroyed(QObject *)), this, SLOT(objectDestroyed(QObject *)));
+ connect(object, SIGNAL(destroyed(QObject*)), this, SLOT(objectDestroyed(QObject*)));
cleanupObjects.insert(0, object);
return object;
}
@@ -76,7 +76,7 @@ void QObjectCleanupHandler::remove(QObject *object)
int index;
if ((index = cleanupObjects.indexOf(object)) != -1) {
cleanupObjects.removeAt(index);
- disconnect(object, SIGNAL(destroyed(QObject *)), this, SLOT(objectDestroyed(QObject *)));
+ disconnect(object, SIGNAL(destroyed(QObject*)), this, SLOT(objectDestroyed(QObject*)));
}
}
diff --git a/src/corelib/kernel/qobjectdefs.h b/src/corelib/kernel/qobjectdefs.h
index 673d588e32..190901d5d1 100644
--- a/src/corelib/kernel/qobjectdefs.h
+++ b/src/corelib/kernel/qobjectdefs.h
@@ -11,6 +11,7 @@
#include <QtCore/qnamespace.h>
#include <QtCore/qobjectdefs_impl.h>
+#include <QtCore/qtcoreexports.h>
#include <QtCore/qtmetamacros.h>
QT_BEGIN_NAMESPACE
@@ -78,7 +79,7 @@ struct QMethodRawArguments
void **arguments;
};
-#if QT_VERSION <= QT_VERSION_CHECK(7, 0, 0)
+#if QT_VERSION < QT_VERSION_CHECK(7, 0, 0)
class Q_CORE_EXPORT QGenericArgument
{
public:
@@ -142,12 +143,18 @@ struct QMetaMethodReturnArgument
void *data;
};
+template <typename T>
+struct QTemplatedMetaMethodReturnArgument : QMetaMethodReturnArgument
+{
+ using Type = T;
+};
+
namespace QtPrivate {
namespace Invoke {
#if QT_VERSION <= QT_VERSION_CHECK(7, 0, 0)
-template <typename... Args> struct AreOldStyleArgs :
- std::disjunction<std::is_base_of<QGenericArgument, Args>...>
-{};
+template <typename... Args>
+using AreOldStyleArgs = std::disjunction<std::is_base_of<QGenericArgument, Args>...>;
+
template <typename T, typename... Args> using IfNotOldStyleArgs =
std::enable_if_t<!AreOldStyleArgs<Args...>::value, T>;
#else
@@ -164,7 +171,8 @@ template <typename T> inline QMetaMethodArgument argument(const char *name, cons
}
}
-template <typename T> inline QMetaMethodReturnArgument returnArgument(const char *name, T &t)
+template <typename T>
+inline QTemplatedMetaMethodReturnArgument<T> returnArgument(const char *name, T &t)
{
return { qMetaTypeInterfaceForType<T>(), name, std::addressof(t) };
}
@@ -216,8 +224,8 @@ template <typename... Args> inline auto invokeMethodHelper(QMetaMethodReturnArgu
}
} // namespace QtPrivate
-template <typename T> inline QMetaMethodReturnArgument qReturnArg(T &&) = delete;
-template <typename T> inline QMetaMethodReturnArgument qReturnArg(T &data)
+template <typename T> void qReturnArg(const T &&) = delete;
+template <typename T> inline QTemplatedMetaMethodReturnArgument<T> qReturnArg(T &data)
{
return QtPrivate::Invoke::returnArgument(nullptr, data);
}
@@ -255,6 +263,7 @@ struct Q_CORE_EXPORT QMetaObject
int indexOfSignal(const char *signal) const;
int indexOfSlot(const char *slot) const;
int indexOfEnumerator(const char *name) const;
+
int indexOfProperty(const char *name) const;
int indexOfClassInfo(const char *name) const;
@@ -354,14 +363,14 @@ struct Q_CORE_EXPORT QMetaObject
}
#endif // Qt < 7.0
- template <typename... Args> static
+ template <typename ReturnArg, typename... Args> static
#ifdef Q_QDOC
bool
#else
QtPrivate::Invoke::IfNotOldStyleArgs<bool, Args...>
#endif
invokeMethod(QObject *obj, const char *member, Qt::ConnectionType c,
- QMetaMethodReturnArgument r, Args &&... arguments)
+ QTemplatedMetaMethodReturnArgument<ReturnArg> r, Args &&... arguments)
{
auto h = QtPrivate::invokeMethodHelper(r, std::forward<Args>(arguments)...);
return invokeMethodImpl(obj, member, c, h.parameterCount(), h.parameters.data(),
@@ -376,17 +385,17 @@ struct Q_CORE_EXPORT QMetaObject
#endif
invokeMethod(QObject *obj, const char *member, Qt::ConnectionType c, Args &&... arguments)
{
- QMetaMethodReturnArgument r = {};
+ QTemplatedMetaMethodReturnArgument<void> r = {};
return invokeMethod(obj, member, c, r, std::forward<Args>(arguments)...);
}
- template <typename... Args> static
+ template <typename ReturnArg, typename... Args> static
#ifdef Q_QDOC
bool
#else
QtPrivate::Invoke::IfNotOldStyleArgs<bool, Args...>
#endif
- invokeMethod(QObject *obj, const char *member, QMetaMethodReturnArgument r,
+ invokeMethod(QObject *obj, const char *member, QTemplatedMetaMethodReturnArgument<ReturnArg> r,
Args &&... arguments)
{
return invokeMethod(obj, member, Qt::AutoConnection, r, std::forward<Args>(arguments)...);
@@ -400,92 +409,113 @@ struct Q_CORE_EXPORT QMetaObject
#endif
invokeMethod(QObject *obj, const char *member, Args &&... arguments)
{
- QMetaMethodReturnArgument r = {};
+ QTemplatedMetaMethodReturnArgument<void> r = {};
return invokeMethod(obj, member, Qt::AutoConnection, r, std::forward<Args>(arguments)...);
}
#ifdef Q_QDOC
template<typename Functor, typename FunctorReturnType>
- static bool invokeMethod(QObject *context, Functor function, Qt::ConnectionType type = Qt::AutoConnection, FunctorReturnType *ret = nullptr);
+ 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);
+ static bool invokeMethod(QObject *context, Functor &&function, FunctorReturnType *ret);
+
+ template<typename Functor, typename FunctorReturnType, typename... Args>
+ static bool invokeMethod(QObject *context, Functor &&function, Qt::ConnectionType type, QTemplatedMetaMethodReturnArgument<FunctorReturnType> ret, Args &&...arguments);
+ template<typename Functor, typename FunctorReturnType, typename... Args>
+ static bool invokeMethod(QObject *context, Functor &&function, QTemplatedMetaMethodReturnArgument<FunctorReturnType> ret, Args &&...arguments);
+ template<typename Functor, typename... Args>
+ static bool invokeMethod(QObject *context, Functor &&function, Qt::ConnectionType type, Args &&...arguments);
+ template<typename Functor, typename... Args>
+ static bool invokeMethod(QObject *context, Functor &&function, Args &&...arguments);
#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)
+ static std::enable_if_t<!std::disjunction_v<std::is_convertible<Func, const char *>,
+ QtPrivate::Invoke::AreOldStyleArgs<Func>>,
+ bool>
+ invokeMethod(typename QtPrivate::ContextTypeForFunctor<Func>::ContextType *object,
+ Func &&function, Qt::ConnectionType type,
+ typename QtPrivate::Callable<Func>::ReturnType *ret)
{
- return invokeMethodImpl(object, new QtPrivate::QSlotObjectWithNoArgs<Func>(function), type, ret);
+ using R = typename QtPrivate::Callable<Func>::ReturnType;
+ const auto getReturnArg = [ret]() -> QTemplatedMetaMethodReturnArgument<R> {
+ if constexpr (std::is_void_v<R>)
+ return {};
+ else
+ return ret ? qReturnArg(*ret) : QTemplatedMetaMethodReturnArgument<R>{};
+ };
+ return invokeMethod(object, std::forward<Func>(function), type, getReturnArg());
}
-
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)
+ static std::enable_if_t<!std::disjunction_v<std::is_convertible<Func, const char *>,
+ QtPrivate::Invoke::AreOldStyleArgs<Func>>,
+ bool>
+ invokeMethod(typename QtPrivate::ContextTypeForFunctor<Func>::ContextType *object,
+ Func &&function, typename QtPrivate::Callable<Func>::ReturnType *ret)
{
- return invokeMethodImpl(object, new QtPrivate::QSlotObjectWithNoArgs<Func>(function), Qt::AutoConnection, ret);
+ return invokeMethod(object, std::forward<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)
+ template <typename Func, typename... Args>
+ static std::enable_if_t<!std::disjunction_v<std::is_convertible<Func, const char *>,
+ QtPrivate::Invoke::AreOldStyleArgs<Args...>>,
+ bool>
+ invokeMethod(typename QtPrivate::ContextTypeForFunctor<Func>::ContextType *object,
+ Func &&function, Qt::ConnectionType type,
+ QTemplatedMetaMethodReturnArgument<
+ typename QtPrivate::Callable<Func, Args...>::ReturnType>
+ ret,
+ Args &&...args)
{
- return invokeMethodImpl(context, new QtPrivate::QFunctorSlotObjectWithNoArgsImplicitReturn<Func>(function), type, ret);
+ return invokeMethodCallableHelper(object, std::forward<Func>(function), type, ret,
+ std::forward<Args>(args)...);
}
- 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)
+ template <typename Func, typename... Args>
+ static std::enable_if_t<!std::disjunction_v<std::is_convertible<Func, const char *>,
+ QtPrivate::Invoke::AreOldStyleArgs<Args...>>,
+ bool>
+ invokeMethod(typename QtPrivate::ContextTypeForFunctor<Func>::ContextType *object,
+ Func &&function, Qt::ConnectionType type, Args &&...args)
{
- return invokeMethodImpl(context, new QtPrivate::QFunctorSlotObjectWithNoArgsImplicitReturn<Func>(function), Qt::AutoConnection, ret);
+ using R = typename QtPrivate::Callable<Func, Args...>::ReturnType;
+ QTemplatedMetaMethodReturnArgument<R> r{ QtPrivate::qMetaTypeInterfaceForType<R>(), nullptr,
+ nullptr };
+ return invokeMethod(object, std::forward<Func>(function), type, r,
+ std::forward<Args>(args)...);
}
- // 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)
+ template <typename Func, typename... Args>
+ static std::enable_if_t<!std::disjunction_v<std::is_convertible<Func, const char *>,
+ QtPrivate::Invoke::AreOldStyleArgs<Args...>>,
+ bool>
+ invokeMethod(typename QtPrivate::ContextTypeForFunctor<Func>::ContextType *object,
+ Func &&function,
+ QTemplatedMetaMethodReturnArgument<
+ typename QtPrivate::Callable<Func, Args...>::ReturnType>
+ ret,
+ Args &&...args)
{
- return invokeMethodImpl(context,
- new QtPrivate::QFunctorSlotObjectWithNoArgs<Func, decltype(function())>(std::move(function)),
- type,
- ret);
+ return invokeMethod(object, std::forward<Func>(function), Qt::AutoConnection, ret,
+ std::forward<Args>(args)...);
}
- 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, decltype(function()) *ret)
+ template <typename Func, typename... Args>
+ static std::enable_if_t<!std::disjunction_v<std::is_convertible<Func, const char *>,
+ QtPrivate::Invoke::AreOldStyleArgs<Args...>>,
+ bool>
+ invokeMethod(typename QtPrivate::ContextTypeForFunctor<Func>::ContextType *object,
+ Func &&function, Args &&...args)
{
- return invokeMethodImpl(context,
- new QtPrivate::QFunctorSlotObjectWithNoArgs<Func, decltype(function())>(std::move(function)),
- Qt::AutoConnection,
- ret);
+ using R = typename QtPrivate::Callable<Func, Args...>::ReturnType;
+ QTemplatedMetaMethodReturnArgument<R> r{ QtPrivate::qMetaTypeInterfaceForType<R>(), nullptr,
+ nullptr };
+ return invokeMethod(object, std::forward<Func>(function), Qt::AutoConnection, r,
+ std::forward<Args>(args)...);
}
#endif
-#if QT_VERSION <= QT_VERSION_CHECK(7, 0, 0)
+#if QT_VERSION < QT_VERSION_CHECK(7, 0, 0)
QObject *newInstance(QGenericArgument val0,
QGenericArgument val1 = QGenericArgument(),
QGenericArgument val2 = QGenericArgument(),
@@ -521,7 +551,8 @@ struct Q_CORE_EXPORT QMetaObject
RegisterPropertyMetaType,
RegisterMethodArgumentMetaType,
BindableProperty,
- CustomCall
+ CustomCall,
+ ConstructInPlace,
};
int static_metacall(Call, int, void **) const;
@@ -569,14 +600,46 @@ struct Q_CORE_EXPORT QMetaObject
} d;
private:
+ // Just need to have this here with a separate name so the other inline
+ // functions can call this without any ambiguity
+ template <typename Func, typename... Args>
+ static bool
+ invokeMethodCallableHelper(typename QtPrivate::ContextTypeForFunctor<Func>::ContextType *object,
+ Func &&function, Qt::ConnectionType type, const QMetaMethodReturnArgument &ret,
+ Args &&...args)
+ {
+ using Callable = QtPrivate::Callable<Func, Args...>;
+ using ExpectedArguments = typename Callable::Arguments;
+ static_assert(sizeof...(Args) <= ExpectedArguments::size, "Too many arguments");
+ using ActualArguments = QtPrivate::List<Args...>;
+ static_assert(QtPrivate::CheckCompatibleArguments<ActualArguments,
+ ExpectedArguments>::value,
+ "Incompatible arguments");
+
+ auto h = QtPrivate::invokeMethodHelper(ret, std::forward<Args>(args)...);
+
+ // NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks)
+ auto callable = new QtPrivate::QCallableObject<std::decay_t<Func>, ActualArguments,
+ typename Callable::ReturnType>(std::forward<Func>(function));
+ return invokeMethodImpl(object, callable, type, h.parameterCount(), h.parameters.data(),
+ h.typeNames.data(), h.metaTypes.data());
+ }
+
static bool invokeMethodImpl(QObject *object, const char *member, Qt::ConnectionType type,
qsizetype parameterCount, const void *const *parameters, const char *const *names,
const QtPrivate::QMetaTypeInterface * const *metaTypes);
+ static bool invokeMethodImpl(QObject *object, QtPrivate::QSlotObjectBase *slotObj,
+ Qt::ConnectionType type, qsizetype parameterCount,
+ const void *const *params, const char *const *names,
+ const QtPrivate::QMetaTypeInterface *const *metaTypes);
+#if QT_CORE_REMOVED_SINCE(6, 7)
static bool invokeMethodImpl(QObject *object, QtPrivate::QSlotObjectBase *slot, Qt::ConnectionType type, void *ret);
+#endif
static QObject *newInstanceImpl(const QMetaObject *mobj, qsizetype parameterCount,
const void **parameters, const char **typeNames,
const QtPrivate::QMetaTypeInterface **metaTypes);
friend class QTimer;
+ friend class QChronoTimer;
};
class Q_CORE_EXPORT QMetaObject::Connection {
@@ -614,7 +677,7 @@ inline const QMetaObject *QMetaObject::superClass() const
{ return d.superdata; }
namespace QtPrivate {
- /* Trait that tells is a the Object has a Q_OBJECT macro */
+ // Trait that tells if a QObject has a Q_OBJECT macro
template <typename Object> struct HasQ_OBJECT_Macro {
template <typename T>
static char test(int (T::*)(QMetaObject::Call, int, void **));
diff --git a/src/corelib/kernel/qobjectdefs_impl.h b/src/corelib/kernel/qobjectdefs_impl.h
index 10f06609ec..1e953f29b6 100644
--- a/src/corelib/kernel/qobjectdefs_impl.h
+++ b/src/corelib/kernel/qobjectdefs_impl.h
@@ -12,9 +12,15 @@
#pragma qt_sync_stop_processing
#endif
+#include <QtCore/qfunctionaltools_impl.h>
+
+#include <memory>
+
QT_BEGIN_NAMESPACE
class QObject;
class QObjectPrivate;
+class QMetaMethod;
+class QByteArray;
namespace QtPrivate {
template <typename T> struct RemoveRef { typedef T Type; };
@@ -29,37 +35,43 @@ namespace QtPrivate {
the list composed of the first N element of the list
*/
// With variadic template, lists are represented using a variadic template argument instead of the lisp way
- template <typename...> struct List {};
- template <typename Head, typename... Tail> struct List<Head, Tail...> { typedef Head Car; typedef List<Tail...> Cdr; };
+ template <typename... Ts> struct List { static constexpr size_t size = sizeof...(Ts); };
+ template<typename T> struct SizeOfList { static constexpr size_t value = 1; };
+ template<> struct SizeOfList<List<>> { static constexpr size_t value = 0; };
+ template<typename ...Ts> struct SizeOfList<List<Ts...>> { static constexpr size_t value = List<Ts...>::size; };
+ template <typename Head, typename... Tail> struct List<Head, Tail...> {
+ static constexpr size_t size = 1 + sizeof...(Tail);
+ typedef Head Car; typedef List<Tail...> Cdr;
+ };
template <typename, typename> struct List_Append;
template <typename... L1, typename...L2> struct List_Append<List<L1...>, List<L2...>> { typedef List<L1..., L2...> Value; };
template <typename L, int N> struct List_Left {
typedef typename List_Append<List<typename L::Car>,typename List_Left<typename L::Cdr, N - 1>::Value>::Value Value;
};
template <typename L> struct List_Left<L, 0> { typedef List<> Value; };
- // List_Select<L,N> returns (via typedef Value) the Nth element of the list L
- template <typename L, int N> struct List_Select { typedef typename List_Select<typename L::Cdr, N - 1>::Value Value; };
- template <typename L> struct List_Select<L,0> { typedef typename L::Car Value; };
/*
- trick to set the return value of a slot that works even if the signal or the slot returns void
- to be used like function(), ApplyReturnValue<ReturnType>(&return_value)
- if function() returns a value, the operator,(T, ApplyReturnValue<ReturnType>) is called, but if it
- returns void, the builtin one is used without an error.
- */
- template <typename T>
- struct ApplyReturnValue {
- void *data;
- explicit ApplyReturnValue(void *data_) : data(data_) {}
+ This is used to store the return value from a slot, whether the caller
+ wants to store this value (QMetaObject::invokeMethod() with
+ qReturnArg() or non-void signal ) or not.
+ */
+ struct FunctorCallBase
+ {
+ template <typename R, typename Lambda>
+ static void call_internal(void **args, Lambda &&fn) noexcept(noexcept(fn()))
+ {
+ using SlotRet = decltype(fn());
+ if constexpr (std::is_void_v<R> || std::is_void_v<SlotRet>) {
+ Q_UNUSED(args);
+ } else {
+ if (args[0]) {
+ *reinterpret_cast<R *>(args[0]) = fn();
+ return;
+ }
+ }
+ fn();
+ }
};
- template<typename T, typename U>
- void operator,(T &&value, const ApplyReturnValue<U> &container) {
- if (container.data)
- *reinterpret_cast<U *>(container.data) = std::forward<T>(value);
- }
- template<typename T>
- void operator,(T, const ApplyReturnValue<void> &) {}
-
/*
The FunctionPointer<Func> struct is a type trait for function pointer.
@@ -75,7 +87,7 @@ namespace QtPrivate {
and args is the array of pointer to arguments, as used in qt_metacall
The Functor<Func,N> struct is the helper to call a functor of N argument.
- its call function is the same as the FunctionPointer::call function.
+ Its call function is the same as the FunctionPointer::call function.
*/
template<class T> using InvokeGenSeq = typename T::Type;
@@ -122,41 +134,57 @@ namespace QtPrivate {
template <typename, typename, typename, typename> struct FunctorCall;
template <int... II, typename... SignalArgs, typename R, typename Function>
- struct FunctorCall<IndexesList<II...>, List<SignalArgs...>, R, Function> {
- static void call(Function &f, void **arg) {
- f((*reinterpret_cast<typename RemoveRef<SignalArgs>::Type *>(arg[II+1]))...), ApplyReturnValue<R>(arg[0]);
+ struct FunctorCall<IndexesList<II...>, List<SignalArgs...>, R, Function> : FunctorCallBase
+ {
+ static void call(Function &f, void **arg)
+ {
+ call_internal<R>(arg, [&] {
+ return f((*reinterpret_cast<typename RemoveRef<SignalArgs>::Type *>(arg[II+1]))...);
+ });
}
};
template <int... II, typename... SignalArgs, typename R, typename... SlotArgs, typename SlotRet, class Obj>
- struct FunctorCall<IndexesList<II...>, List<SignalArgs...>, R, SlotRet (Obj::*)(SlotArgs...)> {
+ struct FunctorCall<IndexesList<II...>, List<SignalArgs...>, R, SlotRet (Obj::*)(SlotArgs...)> : FunctorCallBase
+ {
static void call(SlotRet (Obj::*f)(SlotArgs...), Obj *o, void **arg)
{
assertObjectType<Obj>(o);
- (o->*f)((*reinterpret_cast<typename RemoveRef<SignalArgs>::Type *>(arg[II+1]))...), ApplyReturnValue<R>(arg[0]);
+ call_internal<R>(arg, [&] {
+ return (o->*f)((*reinterpret_cast<typename RemoveRef<SignalArgs>::Type *>(arg[II+1]))...);
+ });
}
};
template <int... II, typename... SignalArgs, typename R, typename... SlotArgs, typename SlotRet, class Obj>
- struct FunctorCall<IndexesList<II...>, List<SignalArgs...>, R, SlotRet (Obj::*)(SlotArgs...) const> {
+ struct FunctorCall<IndexesList<II...>, List<SignalArgs...>, R, SlotRet (Obj::*)(SlotArgs...) const> : FunctorCallBase
+ {
static void call(SlotRet (Obj::*f)(SlotArgs...) const, Obj *o, void **arg)
{
assertObjectType<Obj>(o);
- (o->*f)((*reinterpret_cast<typename RemoveRef<SignalArgs>::Type *>(arg[II+1]))...), ApplyReturnValue<R>(arg[0]);
+ call_internal<R>(arg, [&] {
+ return (o->*f)((*reinterpret_cast<typename RemoveRef<SignalArgs>::Type *>(arg[II+1]))...);
+ });
}
};
template <int... II, typename... SignalArgs, typename R, typename... SlotArgs, typename SlotRet, class Obj>
- struct FunctorCall<IndexesList<II...>, List<SignalArgs...>, R, SlotRet (Obj::*)(SlotArgs...) noexcept> {
+ struct FunctorCall<IndexesList<II...>, List<SignalArgs...>, R, SlotRet (Obj::*)(SlotArgs...) noexcept> : FunctorCallBase
+ {
static void call(SlotRet (Obj::*f)(SlotArgs...) noexcept, Obj *o, void **arg)
{
assertObjectType<Obj>(o);
- (o->*f)((*reinterpret_cast<typename RemoveRef<SignalArgs>::Type *>(arg[II+1]))...), ApplyReturnValue<R>(arg[0]);
+ call_internal<R>(arg, [&]() noexcept {
+ return (o->*f)((*reinterpret_cast<typename RemoveRef<SignalArgs>::Type *>(arg[II+1]))...);
+ });
}
};
template <int... II, typename... SignalArgs, typename R, typename... SlotArgs, typename SlotRet, class Obj>
- struct FunctorCall<IndexesList<II...>, List<SignalArgs...>, R, SlotRet (Obj::*)(SlotArgs...) const noexcept> {
+ struct FunctorCall<IndexesList<II...>, List<SignalArgs...>, R, SlotRet (Obj::*)(SlotArgs...) const noexcept> : FunctorCallBase
+ {
static void call(SlotRet (Obj::*f)(SlotArgs...) const noexcept, Obj *o, void **arg)
{
assertObjectType<Obj>(o);
- (o->*f)((*reinterpret_cast<typename RemoveRef<SignalArgs>::Type *>(arg[II+1]))...), ApplyReturnValue<R>(arg[0]);
+ call_internal<R>(arg, [&]() noexcept {
+ return (o->*f)((*reinterpret_cast<typename RemoveRef<SignalArgs>::Type *>(arg[II+1]))...);
+ });
}
};
@@ -234,14 +262,6 @@ namespace QtPrivate {
}
};
- template<typename Function, int N> struct Functor
- {
- template <typename SignalArgs, typename R>
- static void call(Function &f, void *, void **arg) {
- FunctorCall<typename Indexes<N>::Value, SignalArgs, R, Function>::call(f, arg);
- }
- };
-
// Traits to detect if there is a conversion between two types,
// and that conversion does not include a narrowing conversion.
template <typename T>
@@ -275,10 +295,9 @@ namespace QtPrivate {
static_assert(CheckCompatibleArguments<FunctionPointer<Signal>::Arguments, FunctionPointer<Slot>::Arguments>::value)
*/
template<typename A1, typename A2> struct AreArgumentsCompatible {
- static int test(const typename RemoveRef<A2>::Type&);
+ static int test(const std::remove_reference_t<A2>&);
static char test(...);
- static const typename RemoveRef<A1>::Type &dummy();
- enum { value = sizeof(test(dummy())) == sizeof(int) };
+ enum { value = sizeof(test(std::declval<std::remove_reference_t<A1>>())) == sizeof(int) };
#ifdef QT_NO_NARROWING_CONVERSIONS_IN_CONNECT
using AreArgumentsConvertibleWithoutNarrowing = AreArgumentsConvertibleWithoutNarrowingBase<std::decay_t<A1>, std::decay_t<A2>>;
static_assert(AreArgumentsConvertibleWithoutNarrowing::value, "Signal and slot arguments are not compatible (narrowing)");
@@ -317,11 +336,10 @@ namespace QtPrivate {
template <typename Functor, typename... ArgList> struct ComputeFunctorArgumentCount<Functor, List<ArgList...>>
{
- template <typename D> static D dummy();
- template <typename F> static auto test(F f) -> decltype(((f.operator()((dummy<ArgList>())...)), int()));
+ template <typename F> static auto test(F f) -> decltype(((f.operator()((std::declval<ArgList>())...)), int()));
static char test(...);
enum {
- Ok = sizeof(test(dummy<Functor>())) == sizeof(int),
+ Ok = sizeof(test(std::declval<Functor>())) == sizeof(int),
Value = Ok ? int(sizeof...(ArgList)) : int(ComputeFunctorArgumentCountHelper<Functor, List<ArgList...>, Ok>::Value)
};
};
@@ -329,19 +347,115 @@ namespace QtPrivate {
/* get the return type of a functor, given the signal argument list */
template <typename Functor, typename ArgList> struct FunctorReturnType;
template <typename Functor, typename ... ArgList> struct FunctorReturnType<Functor, List<ArgList...>> {
- template <typename D> static D dummy();
- typedef decltype(dummy<Functor>().operator()((dummy<ArgList>())...)) Value;
+ typedef decltype(std::declval<Functor>().operator()((std::declval<ArgList>())...)) Value;
};
+ template<typename Func, typename... Args>
+ struct FunctorCallable
+ {
+ using ReturnType = decltype(std::declval<Func>()(std::declval<Args>()...));
+ using Function = ReturnType(*)(Args...);
+ enum {ArgumentCount = sizeof...(Args)};
+ using Arguments = QtPrivate::List<Args...>;
+
+ template <typename SignalArgs, typename R>
+ static void call(Func &f, void *, void **arg) {
+ FunctorCall<typename Indexes<ArgumentCount>::Value, SignalArgs, R, Func>::call(f, arg);
+ }
+ };
+
+ template <typename Functor, typename... Args>
+ struct HasCallOperatorAcceptingArgs
+ {
+ private:
+ template <typename F, typename = void>
+ struct Test : std::false_type
+ {
+ };
+ // We explicitly use .operator() to not return true for pointers to free/static function
+ template <typename F>
+ struct Test<F, std::void_t<decltype(std::declval<F>().operator()(std::declval<Args>()...))>>
+ : std::true_type
+ {
+ };
+
+ public:
+ using Type = Test<Functor>;
+ static constexpr bool value = Type::value;
+ };
+
+ template <typename Functor, typename... Args>
+ constexpr bool
+ HasCallOperatorAcceptingArgs_v = HasCallOperatorAcceptingArgs<Functor, Args...>::value;
+
+ template <typename Func, typename... Args>
+ struct CallableHelper
+ {
+ private:
+ // Could've been std::conditional_t, but that requires all branches to
+ // be valid
+ static auto Resolve(std::true_type CallOperator) -> FunctorCallable<Func, Args...>;
+ static auto Resolve(std::false_type CallOperator) -> FunctionPointer<std::decay_t<Func>>;
+
+ public:
+ using Type = decltype(Resolve(typename HasCallOperatorAcceptingArgs<std::decay_t<Func>,
+ Args...>::Type{}));
+ };
+
+ template<typename Func, typename... Args>
+ struct Callable : CallableHelper<Func, Args...>::Type
+ {};
+ template<typename Func, typename... Args>
+ struct Callable<Func, List<Args...>> : CallableHelper<Func, Args...>::Type
+ {};
+
+ /*
+ Wrapper around ComputeFunctorArgumentCount and CheckCompatibleArgument,
+ depending on whether \a Functor is a PMF or not. Returns -1 if \a Func is
+ not compatible with the \a ExpectedArguments, otherwise returns >= 0.
+ */
+ template<typename Prototype, typename Functor>
+ inline constexpr std::enable_if_t<!std::disjunction_v<std::is_convertible<Prototype, const char *>,
+ std::is_same<std::decay_t<Prototype>, QMetaMethod>,
+ std::is_convertible<Functor, const char *>,
+ std::is_same<std::decay_t<Functor>, QMetaMethod>
+ >,
+ int>
+ countMatchingArguments()
+ {
+ using ExpectedArguments = typename QtPrivate::FunctionPointer<Prototype>::Arguments;
+ using Actual = std::decay_t<Functor>;
+
+ if constexpr (QtPrivate::FunctionPointer<Actual>::IsPointerToMemberFunction
+ || QtPrivate::FunctionPointer<Actual>::ArgumentCount >= 0) {
+ // PMF or free function
+ using ActualArguments = typename QtPrivate::FunctionPointer<Actual>::Arguments;
+ if constexpr (QtPrivate::CheckCompatibleArguments<ExpectedArguments, ActualArguments>::value)
+ return QtPrivate::FunctionPointer<Actual>::ArgumentCount;
+ else
+ return -1;
+ } else {
+ // lambda or functor
+ return QtPrivate::ComputeFunctorArgumentCount<Actual, ExpectedArguments>::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
+ class QSlotObjectBase
+ {
+ // 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, and the
// Operations enum below to distinguish requests
+#if QT_VERSION < QT_VERSION_CHECK(7, 0, 0)
+ QAtomicInt m_ref = 1;
typedef void (*ImplFn)(int which, QSlotObjectBase* this_, QObject *receiver, void **args, bool *ret);
const ImplFn m_impl;
+#else
+ using ImplFn = void (*)(QSlotObjectBase* this_, QObject *receiver, void **args, int which, bool *ret);
+ const ImplFn m_impl;
+ QAtomicInt m_ref = 1;
+#endif
protected:
// The operations that can be requested by calls to m_impl,
// see the member functions that call m_impl below for details
@@ -353,14 +467,36 @@ namespace QtPrivate {
NumOperations
};
public:
- explicit QSlotObjectBase(ImplFn fn) : m_ref(1), m_impl(fn) {}
+ explicit QSlotObjectBase(ImplFn fn) : m_impl(fn) {}
+
+ // A custom deleter compatible with std protocols (op()()) we well as
+ // the legacy QScopedPointer protocol (cleanup()).
+ struct Deleter {
+ void operator()(QSlotObjectBase *p) const noexcept
+ { if (p) p->destroyIfLastRef(); }
+ // for the non-standard QScopedPointer protocol:
+ static void cleanup(QSlotObjectBase *p) noexcept { Deleter{}(p); }
+ };
- inline int ref() noexcept { return m_ref.ref(); }
+ bool ref() noexcept { return m_ref.ref(); }
+#if QT_VERSION < QT_VERSION_CHECK(7, 0, 0)
inline void destroyIfLastRef() noexcept
{ if (!m_ref.deref()) m_impl(Destroy, this, nullptr, nullptr, nullptr); }
inline bool compare(void **a) { bool ret = false; m_impl(Compare, this, nullptr, a, &ret); return ret; }
- inline void call(QObject *r, void **a) { m_impl(Call, this, r, a, nullptr); }
+ inline void call(QObject *r, void **a) { m_impl(Call, this, r, a, nullptr); }
+#else
+ inline void destroyIfLastRef() noexcept
+ { if (!m_ref.deref()) m_impl(this, nullptr, nullptr, Destroy, nullptr); }
+
+ inline bool compare(void **a)
+ {
+ bool ret = false;
+ m_impl(this, nullptr, a, Compare, &ret);
+ return ret;
+ }
+ inline void call(QObject *r, void **a) { m_impl(this, r, a, Call, nullptr); }
+#endif
bool isImpl(ImplFn f) const { return m_impl == f; }
protected:
~QSlotObjectBase() {}
@@ -368,66 +504,160 @@ namespace QtPrivate {
Q_DISABLE_COPY_MOVE(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 return type of the signal to which the slot is connected.
- template<typename Func, typename Args, typename R> class QSlotObject : public QSlotObjectBase
+ using SlotObjUniquePtr = std::unique_ptr<QSlotObjectBase,
+ QSlotObjectBase::Deleter>;
+ inline SlotObjUniquePtr copy(const SlotObjUniquePtr &other) noexcept
{
- typedef QtPrivate::FunctionPointer<Func> FuncType;
- Func function;
- static void impl(int which, QSlotObjectBase *this_, QObject *r, void **a, bool *ret)
+ if (other)
+ other->ref();
+ return SlotObjUniquePtr{other.get()};
+ }
+
+ class SlotObjSharedPtr {
+ SlotObjUniquePtr obj;
+ public:
+ Q_NODISCARD_CTOR Q_IMPLICIT SlotObjSharedPtr() noexcept = default;
+ Q_NODISCARD_CTOR Q_IMPLICIT SlotObjSharedPtr(std::nullptr_t) noexcept : SlotObjSharedPtr() {}
+ Q_NODISCARD_CTOR explicit SlotObjSharedPtr(SlotObjUniquePtr o)
+ : obj(std::move(o))
{
- 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: ;
- }
+ // does NOT ref() (takes unique_ptr by value)
+ // (that's why (QSlotObjectBase*) ctor doesn't exisit: don't know whether that one _should_)
}
- public:
- explicit QSlotObject(Func f) : QSlotObjectBase(&impl), function(f) {}
+ Q_NODISCARD_CTOR SlotObjSharedPtr(const SlotObjSharedPtr &other) noexcept
+ : obj{copy(other.obj)} {}
+ SlotObjSharedPtr &operator=(const SlotObjSharedPtr &other) noexcept
+ { auto copy = other; swap(copy); return *this; }
+
+ Q_NODISCARD_CTOR SlotObjSharedPtr(SlotObjSharedPtr &&other) noexcept = default;
+ SlotObjSharedPtr &operator=(SlotObjSharedPtr &&other) noexcept = default;
+ ~SlotObjSharedPtr() = default;
+
+ void swap(SlotObjSharedPtr &other) noexcept { obj.swap(other.obj); }
+
+ auto get() const noexcept { return obj.get(); }
+ auto operator->() const noexcept { return get(); }
+
+ explicit operator bool() const noexcept { return bool(obj); }
};
- // implementation of QSlotObjectBase for which the slot is a functor (or lambda)
- // N is the number of arguments
+
+
+ // Implementation of QSlotObjectBase for which the slot is a callable (function, PMF, functor, or lambda).
// Args and R are the List of arguments and the return type of the signal to which the slot is connected.
- template<typename Func, int N, typename Args, typename R> class QFunctorSlotObject : public QSlotObjectBase
+ template <typename Func, typename Args, typename R>
+ class QCallableObject : public QSlotObjectBase,
+ private QtPrivate::CompactStorage<std::decay_t<Func>>
{
- typedef QtPrivate::Functor<Func, N> FuncType;
- Func function;
- static void impl(int which, QSlotObjectBase *this_, QObject *r, void **a, bool *ret)
+ using FunctorValue = std::decay_t<Func>;
+ using Storage = QtPrivate::CompactStorage<FunctorValue>;
+ using FuncType = Callable<Func, Args>;
+
+#if QT_VERSION < QT_VERSION_CHECK(7, 0, 0)
+ Q_DECL_HIDDEN static void impl(int which, QSlotObjectBase *this_, QObject *r, void **a, bool *ret)
+#else
+ // Design note: the first three arguments match those for typical Call
+ // and Destroy uses. We return void to enable tail call optimization
+ // for those too.
+ Q_DECL_HIDDEN static void impl(QSlotObjectBase *this_, QObject *r, void **a, int which, bool *ret)
+#endif
{
+ const auto that = static_cast<QCallableObject*>(this_);
switch (which) {
case Destroy:
- delete static_cast<QFunctorSlotObject*>(this_);
+ delete that;
break;
case Call:
- FuncType::template call<Args, R>(static_cast<QFunctorSlotObject*>(this_)->function, r, a);
+ if constexpr (std::is_member_function_pointer_v<FunctorValue>)
+ FuncType::template call<Args, R>(that->object(), static_cast<typename FuncType::Object *>(r), a);
+ else
+ FuncType::template call<Args, R>(that->object(), r, a);
break;
- case Compare: // not implemented
+ case Compare:
+ if constexpr (std::is_member_function_pointer_v<FunctorValue>) {
+ *ret = *reinterpret_cast<FunctorValue *>(a) == that->object();
+ break;
+ }
+ // not implemented otherwise
+ Q_FALLTHROUGH();
case NumOperations:
Q_UNUSED(ret);
}
}
public:
- explicit QFunctorSlotObject(Func f) : QSlotObjectBase(&impl), function(std::move(f)) {}
+ explicit QCallableObject(Func &&f) : QSlotObjectBase(&impl), Storage{std::move(f)} {}
+ explicit QCallableObject(const Func &f) : QSlotObjectBase(&impl), Storage{f} {}
};
- // typedefs for readability for when there are no parameters
+ // Helper to detect the context object type based on the functor type:
+ // QObject for free functions and lambdas; the callee for member function
+ // pointers. The default declaration doesn't have the ContextType typedef,
+ // and so non-functor APIs (like old-style string-based slots) are removed
+ // from the overload set.
+ template <typename Func, typename = void>
+ struct ContextTypeForFunctor {};
+
+ template <typename Func>
+ struct ContextTypeForFunctor<Func,
+ std::enable_if_t<!std::disjunction_v<std::is_convertible<Func, const char *>,
+ std::is_member_function_pointer<Func>
+ >
+ >
+ >
+ {
+ using ContextType = QObject;
+ };
template <typename Func>
- using QSlotObjectWithNoArgs = QSlotObject<Func,
- QtPrivate::List<>,
- typename QtPrivate::FunctionPointer<Func>::ReturnType>;
+ struct ContextTypeForFunctor<Func,
+ std::enable_if_t<std::conjunction_v<std::negation<std::is_convertible<Func, const char *>>,
+ std::is_member_function_pointer<Func>,
+ std::is_convertible<typename QtPrivate::FunctionPointer<Func>::Object *, QObject *>
+ >
+ >
+ >
+ {
+ using ContextType = typename QtPrivate::FunctionPointer<Func>::Object;
+ };
- template <typename Func, typename R>
- using QFunctorSlotObjectWithNoArgs = QFunctorSlotObject<Func, 0, QtPrivate::List<>, R>;
+ /*
+ Returns a suitable QSlotObjectBase object that holds \a func, if possible.
- template <typename Func>
- using QFunctorSlotObjectWithNoArgsImplicitReturn = QFunctorSlotObjectWithNoArgs<Func, typename QtPrivate::FunctionPointer<Func>::ReturnType>;
+ Not available (and thus produces compile-time errors) if the Functor provided is
+ not compatible with the expected Prototype.
+ */
+ template <typename Prototype, typename Functor>
+ static constexpr std::enable_if_t<QtPrivate::countMatchingArguments<Prototype, Functor>() >= 0,
+ QtPrivate::QSlotObjectBase *>
+ makeCallableObject(Functor &&func)
+ {
+ using ExpectedSignature = QtPrivate::FunctionPointer<Prototype>;
+ using ExpectedReturnType = typename ExpectedSignature::ReturnType;
+ using ExpectedArguments = typename ExpectedSignature::Arguments;
+
+ using ActualSignature = QtPrivate::FunctionPointer<Functor>;
+ constexpr int MatchingArgumentCount = QtPrivate::countMatchingArguments<Prototype, Functor>();
+ using ActualArguments = typename QtPrivate::List_Left<ExpectedArguments, MatchingArgumentCount>::Value;
+
+ static_assert(int(ActualSignature::ArgumentCount) <= int(ExpectedSignature::ArgumentCount),
+ "Functor requires more arguments than what can be provided.");
+
+ // NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks)
+ return new QtPrivate::QCallableObject<std::decay_t<Functor>, ActualArguments, ExpectedReturnType>(std::forward<Functor>(func));
+ }
+
+ template<typename Prototype, typename Functor, typename = void>
+ struct AreFunctionsCompatible : std::false_type {};
+ template<typename Prototype, typename Functor>
+ struct AreFunctionsCompatible<Prototype, Functor, std::enable_if_t<
+ std::is_same_v<decltype(QtPrivate::makeCallableObject<Prototype>(std::forward<Functor>(std::declval<Functor>()))),
+ QtPrivate::QSlotObjectBase *>>
+ > : std::true_type {};
+
+ template<typename Prototype, typename Functor>
+ inline constexpr bool AssertCompatibleFunctions() {
+ static_assert(AreFunctionsCompatible<Prototype, Functor>::value,
+ "Functor is not compatible with expected prototype!");
+ return true;
+ }
}
QT_END_NAMESPACE
diff --git a/src/corelib/kernel/qpermissions.cpp b/src/corelib/kernel/qpermissions.cpp
index 4fded99915..d0d2d9eb10 100644
--- a/src/corelib/kernel/qpermissions.cpp
+++ b/src/corelib/kernel/qpermissions.cpp
@@ -46,21 +46,18 @@ Q_LOGGING_CATEGORY(lcPermissions, "qt.permissions", QtWarningMsg);
\code
void VoiceMemoWidget::onRecordingInitiated()
{
- #if QT_CONFIG(permissions)
QMicrophonePermission microphonePermission;
switch (qApp->checkPermission(microphonePermission)) {
case Qt::PermissionStatus::Undetermined:
- qApp->requestPermission(microphonePermission, this
+ qApp->requestPermission(microphonePermission, this,
&VoiceMemoWidget::onRecordingInitiated);
return;
case Qt::PermissionStatus::Denied:
m_permissionInstructionsDialog->show();
return;
case Qt::PermissionStatus::Granted:
- break; // Proceed
+ m_microphone->startRecording();
}
- #endif
- m_microphone->startRecording();
}
\endcode
@@ -76,8 +73,8 @@ Q_LOGGING_CATEGORY(lcPermissions, "qt.permissions", QtWarningMsg);
why we can not record voice memos at this time (if the permission was denied),
or proceed to using the microphone (if permission was granted).
- The use of the \c{QT_CONFIG(permissions)} macro ensures that the code
- will work as before on platforms where permissions are not available.
+ \note On \macOS and iOS permissions can currently only be requested for
+ GUI applications.
\section2 Declaring Permissions
@@ -174,6 +171,7 @@ Q_LOGGING_CATEGORY(lcPermissions, "qt.permissions", QtWarningMsg);
\class QPermission
\inmodule QtCore
\inheaderfile QPermissions
+ \since 6.5
\brief An opaque wrapper of a typed permission.
The QPermission class is an opaque wrapper of a \l{typed permission},
@@ -186,7 +184,7 @@ Q_LOGGING_CATEGORY(lcPermissions, "qt.permissions", QtWarningMsg);
\endcode
When requesting permissions, the given functor will
- be passed an instance of a QPermissions, which can be used
+ be passed an instance of a QPermission, which can be used
to check the result of the request:
\code
@@ -196,8 +194,8 @@ Q_LOGGING_CATEGORY(lcPermissions, "qt.permissions", QtWarningMsg);
});
\endcode
- To inspect the properties of the original typed permission,
- use the data() function:
+ To inspect the properties of the original, typed permission,
+ use the \l {QPermission::}{value()} function:
\code
QLocationPermission locationPermission;
@@ -210,8 +208,8 @@ Q_LOGGING_CATEGORY(lcPermissions, "qt.permissions", QtWarningMsg);
{
if (permission.status() != Qt::PermissionStatus:Granted)
return;
- auto locationPermission = permission.data<QLocationPermission>();
- if (locationPermission.accuracy() != QLocationPerission::Precise)
+ auto locationPermission = permission.value<QLocationPermission>();
+ if (!locationPermission || locationPermission->accuracy() != QLocationPermission::Precise)
return;
updatePreciseLocation();
}
@@ -228,46 +226,72 @@ Q_LOGGING_CATEGORY(lcPermissions, "qt.permissions", QtWarningMsg);
*/
/*!
- \fn template <typename Type> QPermission::QPermission(const Type &type)
+ \fn template <typename T, QPermission::if_permission<T>> QPermission::QPermission(const T &type)
Constructs a permission from the given \l{typed permission} \a type.
You do not need to construct this type explicitly, as the type is automatically
used when checking or requesting permissions.
+
+ This constructor participates in overload resolution only if \c T is one of
+ the \l{typed permission} classes:
+
+ \annotatedlist permissions
*/
/*!
- \fn template <typename Type> Type QPermission::data() const
+ \fn template <typename T, QPermission::if_permission<T>> std::optional<T> QPermission::value() const
+
+ Returns the \l{typed permission} of type \c T, or \c{std::nullopt} if this
+ QPermission object doesn't contain one.
- Returns the \l{typed permission} of type \c Type.
+ Use type() for dynamically choosing which typed permission to request.
- The type must match the type that was originally used to request
- the permission. Use type() for dynamically choosing which typed
- permission to request.
+ This function participates in overload resolution only if \c T is one of
+ the \l{typed permission} classes:
+
+ \annotatedlist permissions
*/
/*!
+ \fn Qt::PermissionStatus QPermission::status() const
Returns the status of the permission.
*/
-Qt::PermissionStatus QPermission::status() const
-{
- return m_status;
-}
/*!
+ \fn QMetaType QPermission::type() const
Returns the type of the permission.
*/
-QMetaType QPermission::type() const
+
+/*
+ \internal
+*/
+const void *QPermission::data(QMetaType requestedType) const
{
- return m_data.metaType();
+ const auto actualType = type();
+ if (requestedType != actualType)
+ return nullptr;
+ return m_data.data();
}
-#define QT_DEFINE_PERMISSION_SPECIAL_FUNCTIONS(ClassName) \
- ClassName::ClassName() : d(new ClassName##Private) {} \
+// check alignof(AlignmentCheck) instead of alignof(void*), in case
+// pointers have different alignment inside structs:
+struct AlignmentCheck { void *p; };
+
+#define QT_PERMISSION_IMPL_COMMON(ClassName) \
+ /* Class##Private is unused until we need it: */ \
+ static_assert(sizeof(ClassName) == sizeof(void*), \
+ "You have added too many members to " #ClassName "::ShortData. " \
+ "Decrease their size or switch to using a d-pointer."); \
+ static_assert(alignof(ClassName) == alignof(AlignmentCheck), \
+ "You have added members to " #ClassName "::ShortData that are overaligned. " \
+ "Decrease their alignment or switch to using a d-pointer."); \
ClassName::ClassName(const ClassName &other) noexcept = default; \
- ClassName::ClassName(ClassName &&other) noexcept = default; \
- ClassName::~ClassName() noexcept = default; \
- ClassName &ClassName::operator=(const ClassName &other) noexcept = default;
+ ClassName::~ClassName() = default; \
+ ClassName &ClassName::operator=(const ClassName &other) noexcept = default; \
+ ClassName::ClassName() \
+ /* impl supplied by caller */
+
/*!
\class QCameraPermission
@@ -288,8 +312,10 @@ QMetaType QPermission::type() const
\include permissions.qdocinc permission-metadata
*/
-class QCameraPermissionPrivate : public QSharedData {};
-QT_DEFINE_PERMISSION_SPECIAL_FUNCTIONS(QCameraPermission)
+
+QT_PERMISSION_IMPL_COMMON(QCameraPermission)
+ : u{} // stateless, atm
+{}
/*!
\class QMicrophonePermission
@@ -310,8 +336,10 @@ QT_DEFINE_PERMISSION_SPECIAL_FUNCTIONS(QCameraPermission)
\include permissions.qdocinc permission-metadata
*/
-class QMicrophonePermissionPrivate : public QSharedData {};
-QT_DEFINE_PERMISSION_SPECIAL_FUNCTIONS(QMicrophonePermission)
+
+QT_PERMISSION_IMPL_COMMON(QMicrophonePermission)
+ : u{} // stateless, atm
+{}
/*!
\class QBluetoothPermission
@@ -327,13 +355,85 @@ QT_DEFINE_PERMISSION_SPECIAL_FUNCTIONS(QMicrophonePermission)
\row
\li Android
\li \l{android-uses-permission}{\c{uses-permission}}
- \li \c android.permission.BLUETOOTH
+ \li Up to Android 11 (API Level < 31):
+ \list
+ \li \c android.permission.BLUETOOTH
+ \li \c android.permission.ACCESS_FINE_LOCATION
+ \endlist
+
+ Starting from Android 12 (API Level >= 31):
+ \list
+ \li \c android.permission.BLUETOOTH_ADVERTISE
+ \li \c android.permission.BLUETOOTH_CONNECT
+ \li \c android.permission.BLUETOOTH_SCAN
+ \li \c android.permission.ACCESS_FINE_LOCATION
+ \endlist
\include permissions.qdocinc end-usage-declarations
+ \note Currently on Android the \c android.permission.ACCESS_FINE_LOCATION
+ permission is requested together with Bluetooth permissions. This is
+ required for Bluetooth to work properly, unless the application provides a
+ strong assertion in the application manifest that it does not use Bluetooth
+ to derive a physical location. This permission coupling may change in
+ future.
+
\include permissions.qdocinc permission-metadata
*/
-class QBluetoothPermissionPrivate : public QSharedData {};
-QT_DEFINE_PERMISSION_SPECIAL_FUNCTIONS(QBluetoothPermission)
+
+QT_PERMISSION_IMPL_COMMON(QBluetoothPermission)
+ : u{ShortData{CommunicationMode::Default, {}}}
+{}
+
+/*!
+ \enum QBluetoothPermission::CommunicationMode
+ \since 6.6
+
+ This enum is used to control the allowed Bluetooth communication modes.
+
+ \value Access Allow this device to access other Bluetooth devices. This
+ includes scanning for nearby devices and connecting to them.
+ \value Advertise Allow other Bluetooth devices to discover this device.
+ \value Default This configuration is used by default.
+
+ \note The fine-grained permissions are currently supported only on
+ Android 12 and newer. On older Android versions, as well as on Apple
+ operating systems, any mode results in full Bluetooth access.
+
+ \note For now the \c Access mode on Android also requests the
+ \l {QLocationPermission::Precise}{precise location} permission.
+ This permission coupling may change in the future.
+*/
+
+/*!
+ \since 6.6
+
+ Sets the allowed Bluetooth communication modes to \a modes.
+
+ \note A default-constructed instance of \l {QBluetoothPermission::}
+ {CommunicationModes} has no sense, so an attempt to set such a mode will
+ raise a \c {qWarning()} and fall back to using the
+ \l {QBluetoothPermission::}{Default} mode.
+*/
+void QBluetoothPermission::setCommunicationModes(CommunicationModes modes)
+{
+ if (modes == CommunicationModes{}) {
+ qCWarning(lcPermissions, "QBluetoothPermission: trying to set an invalid empty mode. "
+ "Falling back to CommunicationMode::Default.");
+ u.data.mode = Default;
+ } else {
+ u.data.mode = static_cast<CommunicationMode>(modes.toInt());
+ }
+}
+
+/*!
+ \since 6.6
+
+ Returns the allowed Bluetooth communication modes.
+*/
+QBluetoothPermission::CommunicationModes QBluetoothPermission::communicationModes() const
+{
+ return u.data.mode;
+}
/*!
\class QLocationPermission
@@ -348,10 +448,14 @@ QT_DEFINE_PERMISSION_SPECIAL_FUNCTIONS(QBluetoothPermission)
\include permissions.qdocinc begin-usage-declarations
\row
- \li Apple
+ \li \macos
+ \li \l{apple-usage-description}{Usage description}
+ \li \c NSLocationUsageDescription
+ \row
+ \li iOS
\li \l{apple-usage-description}{Usage description}
\li \c NSLocationWhenInUseUsageDescription, and
- \c NSLocationAlwaysUsageDescription if requesting
+ \c NSLocationAlwaysAndWhenInUseUsageDescription if requesting
QLocationPermission::Always
\row
\li Android
@@ -368,17 +472,10 @@ QT_DEFINE_PERMISSION_SPECIAL_FUNCTIONS(QBluetoothPermission)
\include permissions.qdocinc permission-metadata
*/
-class QLocationPermissionPrivate : public QSharedData
-{
-public:
- using Accuracy = QLocationPermission::Accuracy;
- Accuracy accuracy = Accuracy::Approximate;
- using Availability = QLocationPermission::Availability;
- Availability availability = Availability::WhenInUse;
-};
-
-QT_DEFINE_PERMISSION_SPECIAL_FUNCTIONS(QLocationPermission)
+QT_PERMISSION_IMPL_COMMON(QLocationPermission)
+ : u{ShortData{Accuracy::Approximate, Availability::WhenInUse, {}}}
+{}
/*!
\enum QLocationPermission::Accuracy
@@ -405,8 +502,7 @@ QT_DEFINE_PERMISSION_SPECIAL_FUNCTIONS(QLocationPermission)
*/
void QLocationPermission::setAccuracy(Accuracy accuracy)
{
- d.detach();
- d->accuracy = accuracy;
+ u.data.accuracy = accuracy;
}
/*!
@@ -414,7 +510,7 @@ void QLocationPermission::setAccuracy(Accuracy accuracy)
*/
QLocationPermission::Accuracy QLocationPermission::accuracy() const
{
- return d->accuracy;
+ return u.data.accuracy;
}
/*!
@@ -422,8 +518,7 @@ QLocationPermission::Accuracy QLocationPermission::accuracy() const
*/
void QLocationPermission::setAvailability(Availability availability)
{
- d.detach();
- d->availability = availability;
+ u.data.availability = availability;
}
/*!
@@ -431,15 +526,15 @@ void QLocationPermission::setAvailability(Availability availability)
*/
QLocationPermission::Availability QLocationPermission::availability() const
{
- return d->availability;
+ return u.data.availability;
}
/*!
\class QContactsPermission
\brief Access the user's contacts.
- By default the request is for both read and write access.
- Use setReadOnly() to override the default.
+ By default the request is for read-only access.
+ Use setAccessMode() to override the default.
\section1 Requirements
@@ -452,42 +547,51 @@ QLocationPermission::Availability QLocationPermission::availability() const
\li Android
\li \l{android-uses-permission}{\c{uses-permission}}
\li \c android.permission.READ_CONTACTS. \c android.permission.WRITE_CONTACTS if
- QContactsPermission::isReadOnly() is set to \c false.
+ QContactsPermission::accessMode() is set to AccessMode::ReadWrite.
\include permissions.qdocinc end-usage-declarations
\include permissions.qdocinc permission-metadata
*/
-class QContactsPermissionPrivate : public QSharedData
-{
-public:
- bool isReadOnly = false;
-};
-QT_DEFINE_PERMISSION_SPECIAL_FUNCTIONS(QContactsPermission)
+/*!
+ \enum QContactsPermission::AccessMode
+
+ This enum is used to control access to the contacts data.
+
+ \value ReadOnly Read-only access to the contacts data (the default).
+ \value ReadWrite Read and write access to the contacts data.
+
+ \sa setAccessMode, accessMode
+*/
+
+QT_PERMISSION_IMPL_COMMON(QContactsPermission)
+ : u{ShortData{AccessMode::ReadOnly, {}}}
+{}
/*!
- Sets whether to \a enable read-only access to the contacts.
+ Sets whether the request is for read-write (\a mode == AccessMode::ReadOnly) or
+ read-only (\a mode == AccessMode::ReadOnly) access to the contacts.
*/
-void QContactsPermission::setReadOnly(bool enable)
+void QContactsPermission::setAccessMode(AccessMode mode)
{
- d.detach();
- d->isReadOnly = enable;
+ u.data.mode = mode;
}
/*!
- Returns whether the request is for read-only access to the contacts.
+ Returns AccessMode::ReadWrite when the request is for read-write and
+ AccessMode::ReadOnly when it is for read-only access to the contacts.
*/
-bool QContactsPermission::isReadOnly() const
+QContactsPermission::AccessMode QContactsPermission::accessMode() const
{
- return d->isReadOnly;
+ return u.data.mode;
}
/*!
\class QCalendarPermission
\brief Access the user's calendar.
- By default the request is for both read and write access.
- Use setReadOnly() to override the default.
+ By default the request is for read-only access.
+ Use setAccessMode() to override the default.
\section1 Requirements
@@ -500,34 +604,43 @@ bool QContactsPermission::isReadOnly() const
\li Android
\li \l{android-uses-permission}{\c{uses-permission}}
\li \c android.permission.READ_CALENDAR. \c android.permission.WRITE_CALENDAR if
- QContactsPermission::isReadOnly() is set to \c false.
+ QCalendarPermission::accessMode() is set to AccessMode::ReadWrite.
\include permissions.qdocinc end-usage-declarations
\include permissions.qdocinc permission-metadata
*/
-class QCalendarPermissionPrivate : public QSharedData
-{
-public:
- bool isReadOnly = false;
-};
-QT_DEFINE_PERMISSION_SPECIAL_FUNCTIONS(QCalendarPermission)
+/*!
+ \enum QCalendarPermission::AccessMode
+
+ This enum is used to control access to the calendar data.
+
+ \value ReadOnly Read-only access to the calendar data (the default).
+ \value ReadWrite Read and write access to the calendar data.
+
+ \sa setAccessMode, accessMode
+*/
+
+QT_PERMISSION_IMPL_COMMON(QCalendarPermission)
+ : u{ShortData{AccessMode::ReadOnly, {}}}
+{}
/*!
- Sets whether to \a enable read-only access to the calendar.
+ Sets whether the request is for read-write (\a mode == AccessMode::ReadOnly) or
+ read-only (\a mode == AccessMode::ReadOnly) access to the calendar.
*/
-void QCalendarPermission::setReadOnly(bool enable)
+void QCalendarPermission::setAccessMode(AccessMode mode)
{
- d.detach();
- d->isReadOnly = enable;
+ u.data.mode = mode;
}
/*!
- Returns whether the request is for read-only access to the calendar.
+ Returns AccessMode::ReadWrite when the request is for read-write and
+ AccessMode::ReadOnly when it is for read-only access to the calendar.
*/
-bool QCalendarPermission::isReadOnly() const
+QCalendarPermission::AccessMode QCalendarPermission::accessMode() const
{
- return d->isReadOnly;
+ return u.data.mode;
}
/*!
@@ -551,6 +664,30 @@ QDebug operator<<(QDebug debug, const QPermission &permission)
}
#endif
+#undef QT_PERMISSION_IMPL_COMMON
+
+#if !defined(Q_OS_DARWIN) && !defined(Q_OS_ANDROID) && !defined(Q_OS_WASM)
+// Default backend for platforms without a permission implementation.
+// Always returns Granted, to match behavior when not using permission APIs
+// https://bugreports.qt.io/browse/QTBUG-90498?focusedCommentId=725085#comment-725085
+namespace QPermissions::Private
+{
+ Qt::PermissionStatus checkPermission(const QPermission &permission)
+ {
+ qCDebug(lcPermissions) << "No permission backend on this platform."
+ << "Optimistically returning Granted for" << permission;
+ return Qt::PermissionStatus::Granted;
+ }
+
+ void requestPermission(const QPermission &permission, const PermissionCallback &callback)
+ {
+ qCDebug(lcPermissions) << "No permission backend on this platform."
+ << "Optimistically returning Granted for" << permission;
+ callback(Qt::PermissionStatus::Granted);
+ }
+}
+#endif
+
QT_END_NAMESPACE
#include "moc_qpermissions.cpp"
diff --git a/src/corelib/kernel/qpermissions.h b/src/corelib/kernel/qpermissions.h
index 94ca5fff5b..9573e377e5 100644
--- a/src/corelib/kernel/qpermissions.h
+++ b/src/corelib/kernel/qpermissions.h
@@ -16,6 +16,8 @@
#include <QtCore/qtypeinfo.h>
#include <QtCore/qmetatype.h>
+#include <optional>
+
#if !defined(Q_QDOC)
QT_REQUIRE_CONFIG(permissions);
#endif
@@ -29,124 +31,185 @@ class QDebug;
struct QMetaObject;
class QCoreApplication;
-class Q_CORE_EXPORT QPermission
+class QPermission
{
- Q_GADGET
-
template <typename T, typename Enable = void>
- struct is_permission : public std::false_type {};
+ static constexpr inline bool is_permission_v = false;
template <typename T>
- struct is_permission<T, typename T::QtPermissionHelper> : public std::true_type {};
-
+ using if_permission = std::enable_if_t<is_permission_v<T>, bool>;
public:
explicit QPermission() = default;
-#ifdef Q_QDOC
- template <typename Type>
- QPermission(const Type &type);
-#else
- template <typename T, std::enable_if_t<is_permission<T>::value, bool> = true>
+ template <typename T, if_permission<T> = true>
QPermission(const T &t) : m_data(QVariant::fromValue(t)) {}
-#endif
- Qt::PermissionStatus status() const;
+ Qt::PermissionStatus status() const { return m_status; }
- QMetaType type() const;
+ QMetaType type() const { return m_data.metaType(); }
-#ifdef Q_QDOC
- template <typename Type>
- Type data() const;
-#else
- template <typename T, std::enable_if_t<is_permission<T>::value, bool> = true>
- T data() const
+ template <typename T, if_permission<T> = true>
+ std::optional<T> value() const
{
- auto requestedType = QMetaType::fromType<T>();
- if (type() != requestedType) {
- qWarning() << "Can not convert from" << type().name()
- << "to" << requestedType.name();
- return T{};
- }
- return m_data.value<T>();
+ if (auto p = data(QMetaType::fromType<T>()))
+ return *static_cast<const T *>(p);
+ return std::nullopt;
}
-#endif
#ifndef QT_NO_DEBUG_STREAM
friend Q_CORE_EXPORT QDebug operator<<(QDebug debug, const QPermission &);
#endif
private:
+ Q_CORE_EXPORT const void *data(QMetaType id) const;
+
Qt::PermissionStatus m_status = Qt::PermissionStatus::Undetermined;
QVariant m_data;
friend class QCoreApplication;
};
+template <typename T>
+constexpr bool QPermission::is_permission_v<T, typename T::QtPermissionHelper> = true;
+
#define QT_PERMISSION(ClassName) \
- Q_GADGET \
using QtPermissionHelper = void; \
friend class QPermission; \
+ union U { \
+ U() : d(nullptr) {} \
+ U(ShortData _data) : data(_data) {} \
+ U(ClassName##Private *_d) : d(_d) {} \
+ ShortData data; \
+ ClassName##Private *d; \
+ } u; \
public: \
- ClassName(); \
- ClassName(const ClassName &other) noexcept; \
- ClassName(ClassName &&other) noexcept; \
- ~ClassName() noexcept; \
- ClassName &operator=(const ClassName &other) noexcept; \
- QT_MOVE_ASSIGNMENT_OPERATOR_IMPL_VIA_PURE_SWAP(ClassName) \
- void swap(ClassName &other) noexcept { d.swap(other.d); } \
+ Q_CORE_EXPORT ClassName(); \
+ Q_CORE_EXPORT ClassName(const ClassName &other) noexcept; \
+ ClassName(ClassName &&other) noexcept \
+ : u{other.u} { other.u.d = nullptr; } \
+ Q_CORE_EXPORT ~ClassName(); \
+ Q_CORE_EXPORT ClassName &operator=(const ClassName &other) noexcept; \
+ QT_MOVE_ASSIGNMENT_OPERATOR_IMPL_VIA_MOVE_AND_SWAP(ClassName) \
+ void swap(ClassName &other) noexcept { std::swap(u, other.u); } \
private: \
- QtPrivate::QExplicitlySharedDataPointerV2<ClassName##Private> d;
+ /*end*/
class QLocationPermissionPrivate;
-class Q_CORE_EXPORT QLocationPermission
+class QLocationPermission
{
- QT_PERMISSION(QLocationPermission)
+ Q_GADGET_EXPORT(Q_CORE_EXPORT)
public:
- enum Accuracy { Approximate, Precise };
+ enum Accuracy : quint8 {
+ Approximate,
+ Precise,
+ };
Q_ENUM(Accuracy)
- void setAccuracy(Accuracy accuracy);
- Accuracy accuracy() const;
+ Q_CORE_EXPORT void setAccuracy(Accuracy accuracy);
+ Q_CORE_EXPORT Accuracy accuracy() const;
- enum Availability { WhenInUse, Always };
+ enum Availability : quint8 {
+ WhenInUse,
+ Always,
+ };
Q_ENUM(Availability)
- void setAvailability(Availability availability);
- Availability availability() const;
+ Q_CORE_EXPORT void setAvailability(Availability availability);
+ Q_CORE_EXPORT Availability availability() const;
+
+private:
+ struct ShortData {
+ Accuracy accuracy;
+ Availability availability;
+ char reserved[sizeof(void*) - sizeof(accuracy) - sizeof(availability)];
+ };
+ QT_PERMISSION(QLocationPermission)
};
-Q_DECLARE_SHARED(QLocationPermission);
+Q_DECLARE_SHARED(QLocationPermission)
class QCalendarPermissionPrivate;
-class Q_CORE_EXPORT QCalendarPermission
+class QCalendarPermission
{
- QT_PERMISSION(QCalendarPermission)
+ Q_GADGET_EXPORT(Q_CORE_EXPORT)
public:
- void setReadOnly(bool enable);
- bool isReadOnly() const;
+ enum AccessMode : quint8 {
+ ReadOnly,
+ ReadWrite,
+ };
+ Q_ENUM(AccessMode)
+
+ Q_CORE_EXPORT void setAccessMode(AccessMode mode);
+ Q_CORE_EXPORT AccessMode accessMode() const;
+
+private:
+ struct ShortData {
+ AccessMode mode;
+ char reserved[sizeof(void*) - sizeof(mode)];
+ };
+ QT_PERMISSION(QCalendarPermission)
};
-Q_DECLARE_SHARED(QCalendarPermission);
+Q_DECLARE_SHARED(QCalendarPermission)
class QContactsPermissionPrivate;
-class Q_CORE_EXPORT QContactsPermission
+class QContactsPermission
{
+ Q_GADGET_EXPORT(Q_CORE_EXPORT)
+public:
+ enum AccessMode : quint8 {
+ ReadOnly,
+ ReadWrite,
+ };
+ Q_ENUM(AccessMode)
+
+ Q_CORE_EXPORT void setAccessMode(AccessMode mode);
+ Q_CORE_EXPORT AccessMode accessMode() const;
+
+private:
+ struct ShortData {
+ AccessMode mode;
+ char reserved[sizeof(void*) - sizeof(mode)];
+ };
QT_PERMISSION(QContactsPermission)
+};
+Q_DECLARE_SHARED(QContactsPermission)
+
+class QBluetoothPermissionPrivate;
+class QBluetoothPermission
+{
+ Q_GADGET_EXPORT(Q_CORE_EXPORT)
public:
- void setReadOnly(bool enable);
- bool isReadOnly() const;
+ enum CommunicationMode : quint8 {
+ Access = 0x01,
+ Advertise = 0x02,
+ Default = Access | Advertise,
+ };
+ Q_DECLARE_FLAGS(CommunicationModes, CommunicationMode)
+ Q_FLAG(CommunicationModes)
+
+ Q_CORE_EXPORT void setCommunicationModes(CommunicationModes modes);
+ Q_CORE_EXPORT CommunicationModes communicationModes() const;
+
+private:
+ struct ShortData {
+ CommunicationMode mode;
+ char reserved[sizeof(void*) - sizeof(mode)];
+ };
+ QT_PERMISSION(QBluetoothPermission)
};
-Q_DECLARE_SHARED(QContactsPermission);
+Q_DECLARE_OPERATORS_FOR_FLAGS(QBluetoothPermission::CommunicationModes)
+Q_DECLARE_SHARED(QBluetoothPermission)
#define Q_DECLARE_MINIMAL_PERMISSION(ClassName) \
class ClassName##Private; \
- class Q_CORE_EXPORT ClassName \
+ class ClassName \
{ \
+ struct ShortData { char reserved[sizeof(void*)]; }; \
QT_PERMISSION(ClassName) \
}; \
- Q_DECLARE_SHARED(ClassName);
+ Q_DECLARE_SHARED(ClassName)
-Q_DECLARE_MINIMAL_PERMISSION(QCameraPermission);
-Q_DECLARE_MINIMAL_PERMISSION(QMicrophonePermission);
-Q_DECLARE_MINIMAL_PERMISSION(QBluetoothPermission);
+Q_DECLARE_MINIMAL_PERMISSION(QCameraPermission)
+Q_DECLARE_MINIMAL_PERMISSION(QMicrophonePermission)
#undef QT_PERMISSION
#undef Q_DECLARE_MINIMAL_PERMISSION
diff --git a/src/corelib/kernel/qpermissions_android.cpp b/src/corelib/kernel/qpermissions_android.cpp
index cdafb0144c..6c21ded72c 100644
--- a/src/corelib/kernel/qpermissions_android.cpp
+++ b/src/corelib/kernel/qpermissions_android.cpp
@@ -10,10 +10,10 @@
#include "private/qandroidextras_p.h"
-using namespace Qt::StringLiterals;
-
QT_BEGIN_NAMESPACE
+using namespace Qt::StringLiterals;
+
static QStringList nativeLocationPermission(const QLocationPermission &permission)
{
QStringList nativeLocationPermissionList;
@@ -49,28 +49,63 @@ static QStringList nativeLocationPermission(const QLocationPermission &permissio
return nativeLocationPermissionList;
}
+static QStringList nativeBluetoothPermission(const QBluetoothPermission &permission)
+{
+ // See https://developer.android.com/guide/topics/connectivity/bluetooth/permissions
+ // for the details.
+
+ // API Level < 31
+ static QString bluetoothGeneral = u"android.permission.BLUETOOTH"_s;
+ // API Level >= 31
+ static QString bluetoothScan = u"android.permission.BLUETOOTH_SCAN"_s;
+ static QString bluetoothAdvertise = u"android.permission.BLUETOOTH_ADVERTISE"_s;
+ static QString bluetoothConnect = u"android.permission.BLUETOOTH_CONNECT"_s;
+ // Fine location is currently required for ALL API levels, but that is not
+ // strictly necessary for API Level >= 31. See QTBUG-112164.
+ static QString fineLocation = u"android.permission.ACCESS_FINE_LOCATION"_s;
+
+ if (QtAndroidPrivate::androidSdkVersion() < 31) {
+ return {bluetoothGeneral, fineLocation};
+ } else {
+ const auto modes = permission.communicationModes();
+ QStringList permissionList;
+ if (modes & QBluetoothPermission::Advertise)
+ permissionList << bluetoothAdvertise;
+ if (modes & QBluetoothPermission::Access)
+ permissionList << bluetoothScan << bluetoothConnect << fineLocation;
+ return permissionList;
+ }
+}
+
static QStringList nativeStringsFromPermission(const QPermission &permission)
{
const auto id = permission.type().id();
if (id == qMetaTypeId<QLocationPermission>()) {
- return nativeLocationPermission(permission.data<QLocationPermission>());
+ return nativeLocationPermission(*permission.value<QLocationPermission>());
} else if (id == qMetaTypeId<QCameraPermission>()) {
return { u"android.permission.CAMERA"_s };
} else if (id == qMetaTypeId<QMicrophonePermission>()) {
return { u"android.permission.RECORD_AUDIO"_s };
} else if (id == qMetaTypeId<QBluetoothPermission>()) {
- // TODO: handle Android 12 new bluetooth permissions
- return { u"android.permission.BLUETOOTH"_s };
+ return nativeBluetoothPermission(*permission.value<QBluetoothPermission>());
} else if (id == qMetaTypeId<QContactsPermission>()) {
const auto readContactsString = u"android.permission.READ_CONTACTS"_s;
- if (permission.data<QContactsPermission>().isReadOnly())
+ switch (permission.value<QContactsPermission>()->accessMode()) {
+ case QContactsPermission::AccessMode::ReadOnly:
return { readContactsString };
- return { readContactsString, u"android.permission.WRITE_CONTACTS"_s };
+ case QContactsPermission::AccessMode::ReadWrite:
+ return { readContactsString, u"android.permission.WRITE_CONTACTS"_s };
+ }
+ Q_UNREACHABLE_RETURN({});
} else if (id == qMetaTypeId<QCalendarPermission>()) {
const auto readContactsString = u"android.permission.READ_CALENDAR"_s;
- if (permission.data<QCalendarPermission>().isReadOnly())
+ switch (permission.value<QCalendarPermission>()->accessMode()) {
+ case QCalendarPermission::AccessMode::ReadOnly:
return { readContactsString };
- return { readContactsString, u"android.permission.WRITE_CALENDAR"_s };
+ case QCalendarPermission::AccessMode::ReadWrite:
+ return { readContactsString, u"android.permission.WRITE_CALENDAR"_s };
+ }
+ Q_UNREACHABLE_RETURN({});
}
return {};
@@ -96,6 +131,18 @@ Q_GLOBAL_STATIC_WITH_ARGS(PermissionStatusHash, g_permissionStatusHash, ({
{ qMetaTypeId<QLocationPermission>(), Qt::PermissionStatus::Undetermined }
}));
+static Qt::PermissionStatus
+getCombinedStatus(const QList<QtAndroidPrivate::PermissionResult> &androidResults)
+{
+ // Android returns only Denied or Granted
+ for (const auto &result : androidResults) {
+ const auto status = permissionStatusForAndroidResult(result);
+ if (status == Qt::PermissionStatus::Denied)
+ return status;
+ }
+ return Qt::PermissionStatus::Granted;
+}
+
namespace QPermissions::Private
{
Qt::PermissionStatus checkPermission(const QPermission &permission)
@@ -104,8 +151,12 @@ namespace QPermissions::Private
if (nativePermissionList.isEmpty())
return Qt::PermissionStatus::Granted;
- const auto result = QtAndroidPrivate::checkPermission(nativePermissionList.first()).result();
- const auto status = permissionStatusForAndroidResult(result);
+ QList<QtAndroidPrivate::PermissionResult> androidResults;
+ androidResults.reserve(nativePermissionList.size());
+ for (const auto &nativePermission : nativePermissionList)
+ androidResults.push_back(QtAndroidPrivate::checkPermission(nativePermission).result());
+
+ const auto status = getCombinedStatus(androidResults);
const auto it = g_permissionStatusHash->constFind(permission.type().id());
const bool foundStatus = (it != g_permissionStatusHash->constEnd());
const bool itUndetermined = foundStatus && (*it) == Qt::PermissionStatus::Undetermined;
@@ -125,8 +176,9 @@ namespace QPermissions::Private
QtAndroidPrivate::requestPermissions(nativePermissionList).then(qApp,
[callback, permission](QFuture<QtAndroidPrivate::PermissionResult> future) {
- const auto result = future.isValid() ? future.result() : QtAndroidPrivate::Denied;
- const auto status = permissionStatusForAndroidResult(result);
+ const auto androidResults = future.isValid() ? future.results()
+ : QList{QtAndroidPrivate::Denied};
+ const auto status = getCombinedStatus(androidResults);
g_permissionStatusHash->insert(permission.type().id(), status);
callback(status);
}
diff --git a/src/corelib/kernel/qpermissions_wasm.cpp b/src/corelib/kernel/qpermissions_wasm.cpp
index 60e2def853..11bd4e864f 100644
--- a/src/corelib/kernel/qpermissions_wasm.cpp
+++ b/src/corelib/kernel/qpermissions_wasm.cpp
@@ -51,9 +51,13 @@ namespace
{
updatePermission(permissionName, permissionState["state"].as<std::string>(), {});
};
- callbacks.catchFunc = [permissionName](val)
+ callbacks.catchFunc = [permissionName](val err)
{
- updatePermission(permissionName, wapiDenied, {});
+ if (err["name"].as<std::string>() == "NotAllowedError")
+ return updatePermission(permissionName, wapiDenied, {});
+
+ qCInfo(lcPermissions, "'%s' '%s'", err["name"].as<std::string>().c_str(),
+ err["message"].as<std::string>().c_str());
};
val query = val::object();
@@ -131,11 +135,8 @@ namespace
return cb(Qt::PermissionStatus::Denied);
qstdweb::PromiseCallbacks queryCallbacks;
- queryCallbacks.thenFunc = [device, cb](val mediaStream)
+ queryCallbacks.thenFunc = [device, cb](val)
{
- val tracks = mediaStream.call<val>("getTracks");
- if (!tracks.isUndefined() && !tracks.isNull())
- tracks[0].call<void>("stop");
updatePermission(device, wapiGranted, cb);
};
queryCallbacks.catchFunc = [device, cb](val error)
@@ -216,7 +217,7 @@ namespace
Q_ASSERT(!geolocation.isNull());
const auto &permission = geolocationRequestQueue->front().first;
- const auto &locationPermission = permission.data<QLocationPermission>();
+ const auto locationPermission = *permission.value<QLocationPermission>();
const bool highAccuracy = locationPermission.accuracy() == QLocationPermission::Precise;
val options = val::object();
diff --git a/src/corelib/kernel/qpointer.h b/src/corelib/kernel/qpointer.h
index 2c82441164..8875eef5c3 100644
--- a/src/corelib/kernel/qpointer.h
+++ b/src/corelib/kernel/qpointer.h
@@ -4,6 +4,7 @@
#ifndef QPOINTER_H
#define QPOINTER_H
+#include <QtCore/qcompare.h>
#include <QtCore/qsharedpointer.h>
#include <QtCore/qtypeinfo.h>
@@ -18,15 +19,48 @@ class QPointer
{
static_assert(!std::is_pointer<T>::value, "QPointer's template type must not be a pointer type");
+ template <typename X>
+ using if_convertible = std::enable_if_t<std::is_convertible_v<X*, T*>, bool>;
+ template <typename X>
+ friend class QPointer;
+
using QObjectType =
typename std::conditional<std::is_const<T>::value, const QObject, QObject>::type;
QWeakPointer<QObjectType> wp;
public:
- QPointer() = default;
+ Q_NODISCARD_CTOR
+ QPointer() noexcept = default;
+ Q_NODISCARD_CTOR
+ constexpr QPointer(std::nullptr_t) noexcept : QPointer{} {}
+ Q_WEAK_OVERLOAD
+ Q_NODISCARD_CTOR
inline QPointer(T *p) : wp(p, true) { }
// compiler-generated copy/move ctor/assignment operators are fine!
// compiler-generated dtor is fine!
+ template <typename X, if_convertible<X> = true>
+ Q_NODISCARD_CTOR
+ QPointer(QPointer<X> &&other) noexcept
+ : wp(std::exchange(other.wp, nullptr).internalData(), true) {}
+ template <typename X, if_convertible<X> = true>
+ Q_NODISCARD_CTOR
+ QPointer(const QPointer<X> &other) noexcept
+ : wp(other.wp.internalData(), true) {}
+
+ template <typename X, if_convertible<X> = true>
+ QPointer &operator=(const QPointer<X> &other) noexcept
+ {
+ QPointer(other).swap(*this);
+ return *this;
+ }
+
+ template <typename X, if_convertible<X> = true>
+ QPointer &operator=(QPointer<X> &&other) noexcept
+ {
+ QPointer(std::move(other)).swap(*this);
+ return *this;
+ }
+
#ifdef Q_QDOC
// Stop qdoc from complaining about missing function
~QPointer();
@@ -37,44 +71,41 @@ public:
inline QPointer<T> &operator=(T* p)
{ wp.assign(static_cast<QObjectType*>(p)); return *this; }
- inline T* data() const
+ T* data() const noexcept
{ return static_cast<T*>(wp.internalData()); }
- inline T* get() const
+ T* get() const noexcept
{ return data(); }
- inline T* operator->() const
+ T* operator->() const noexcept
{ return data(); }
- inline T& operator*() const
+ T& operator*() const noexcept
{ return *data(); }
- inline operator T*() const
+ operator T*() const noexcept
{ return data(); }
- inline bool isNull() const
+ bool isNull() const noexcept
{ return wp.isNull(); }
- inline void clear()
+ void clear() noexcept
{ wp.clear(); }
-#define DECLARE_COMPARE_SET(T1, A1, T2, A2) \
- friend bool operator==(T1, T2) \
- { return A1 == A2; } \
- friend bool operator!=(T1, T2) \
- { return A1 != A2; }
-
-#define DECLARE_TEMPLATE_COMPARE_SET(T1, A1, T2, A2) \
- template <typename X> \
- friend bool operator==(T1, T2) noexcept \
- { return A1 == A2; } \
- template <typename X> \
- friend bool operator!=(T1, T2) noexcept \
- { return A1 != A2; }
-
- DECLARE_TEMPLATE_COMPARE_SET(const QPointer &p1, p1.data(), const QPointer<X> &p2, p2.data())
- DECLARE_TEMPLATE_COMPARE_SET(const QPointer &p1, p1.data(), X *ptr, ptr)
- DECLARE_TEMPLATE_COMPARE_SET(X *ptr, ptr, const QPointer &p2, p2.data())
- DECLARE_COMPARE_SET(const QPointer &p1, p1.data(), std::nullptr_t, nullptr)
- DECLARE_COMPARE_SET(std::nullptr_t, nullptr, const QPointer &p2, p2.data())
-#undef DECLARE_COMPARE_SET
-#undef DECLARE_TEMPLATE_COMPARE_SET
+ friend void swap(QPointer &lhs, QPointer &rhs) noexcept
+ { lhs.swap(rhs); }
+
+private:
+ template <typename X>
+ friend bool comparesEqual(const QPointer &lhs, const QPointer<X> &rhs) noexcept
+ { return lhs.data() == rhs.data(); }
+ QT_DECLARE_EQUALITY_OPERATORS_HELPER(QPointer, QPointer<X>, /* non-constexpr */,
+ template <typename X>)
+
+ template <typename X>
+ friend bool comparesEqual(const QPointer &lhs, X *rhs) noexcept
+ { return lhs.data() == rhs; }
+ Q_DECLARE_EQUALITY_COMPARABLE(QPointer, X*, template <typename X>)
+
+ friend bool comparesEqual(const QPointer &lhs, std::nullptr_t) noexcept
+ { return lhs.data() == nullptr; }
+ Q_DECLARE_EQUALITY_COMPARABLE(QPointer, std::nullptr_t)
};
template <class T> Q_DECLARE_TYPEINFO_BODY(QPointer<T>, Q_RELOCATABLE_TYPE);
@@ -86,10 +117,6 @@ qPointerFromVariant(const QVariant &variant)
return QPointer<T>{qobject_cast<T*>(QtPrivate::EnableInternalData::internalData(wp))};
}
-template <class T>
-inline void swap(QPointer<T> &p1, QPointer<T> &p2) noexcept
-{ p1.swap(p2); }
-
QT_END_NAMESPACE
#endif // QT_NO_QOBJECT
diff --git a/src/corelib/kernel/qpointer.cpp b/src/corelib/kernel/qpointer.qdoc
index aebdd68428..63a2a72e6f 100644
--- a/src/corelib/kernel/qpointer.cpp
+++ b/src/corelib/kernel/qpointer.qdoc
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
/*!
\class QPointer
@@ -8,6 +8,12 @@
\ingroup objectmodel
+ \compares equality
+ \compareswith equality QPointer<X> X* std::nullptr_t
+ Where X and T are compatible types, which means that they are either the same (except
+ for their cv-qualifiers), or one is a base type of the other.
+ \endcompareswith
+
A guarded pointer, QPointer<T>, behaves like a normal C++
pointer \c{T *}, except that it is automatically cleared when the
referenced object is destroyed (unlike normal C++ pointers, which
@@ -76,6 +82,7 @@
/*!
\fn template <class T> QPointer<T>::QPointer()
+ \fn template <class T> QPointer<T>::QPointer(std::nullptr_t)
Constructs a guarded pointer with value \nullptr.
@@ -98,6 +105,42 @@
*/
/*!
+ \fn template <class T> template <typename X, QPointer<T>::if_convertible<X> = true> QPointer<T>::QPointer(QPointer<X> &&other)
+ \fn template <class T> template <typename X, QPointer<T>::if_convertible<X> = true> QPointer<T>::QPointer(const QPointer<X> &other)
+ \since 6.6
+
+ Conversion constructor. Constructs a new QPointer by moving or copying from
+ \a other.
+
+ The moved-from QPointer is reset to nullptr.
+
+ \note These constructors participate in overload resolution only if \c{X*}
+ is convertible to \c{T*}.
+*/
+
+/*!
+ \fn template <class T> template <typename X, QPointer<T>::if_convertible<X> = true> QPointer<T> &QPointer<T>::operator=(const QPointer<X> &other)
+ \since 6.6
+
+ Conversion assignment operator. Makes this guarded pointer guard the
+ same object guarded by \a other.
+
+ \note This operator participates in overload resolution only if \c{X*}
+ is convertible to \c{T*}.
+*/
+
+/*!
+ \fn template <class T> template <typename X, QPointer<T>::if_convertible<X> = true> &QPointer<T>::operator=(QPointer<X> &&other)
+ \since 6.6.1
+
+ Conversion move-assignment operator. Makes this guarded pointer guard the
+ same object guarded by \a other and resets \a other to nullptr.
+
+ \note This operator participates in overload resolution only if \c{X*}
+ is convertible to \c{T*}.
+*/
+
+/*!
\fn template <class T> void QPointer<T>::swap(QPointer &other)
\since 5.6
@@ -165,38 +208,38 @@
*/
/*!
- \fn template <typename T, typename X> bool QPointer<T>::operator==(X *o, const QPointer<T> &p)
+ \fn template <typename T> template<typename X> bool QPointer<T>::operator==(X* const &lhs, const QPointer<T> &rhs)
- Equality operator. Returns \c true if \a o and the guarded
- pointer \a p are pointing to the same object, otherwise
+ Equality operator. Returns \c true if \a lhs and the guarded
+ pointer \a rhs are pointing to the same object, otherwise
returns \c false.
*/
/*!
- \fn template <typename T, typename X> bool QPointer<T>::operator==(const QPointer<T> &p, X *o)
+ \fn template <typename T> template<typename X> bool QPointer<T>::operator==(const QPointer<T> &lhs, X* const &rhs)
- Equality operator. Returns \c true if \a o and the guarded
- pointer \a p are pointing to the same object, otherwise
+ Equality operator. Returns \c true if \a rhs and the guarded
+ pointer \a lhs are pointing to the same object, otherwise
returns \c false.
*/
/*!
- \fn template <typename T, typename X> bool QPointer<T>::operator==(const QPointer<T> &p1, const QPointer<X> &p2)
+ \fn template <typename T> template<typename X> bool QPointer<T>::operator==(const QPointer<T> &lhs, const QPointer<X> &rhs)
- Equality operator. Returns \c true if the guarded pointers \a p1 and \a p2
+ Equality operator. Returns \c true if the guarded pointers \a lhs and \a rhs
are pointing to the same object, otherwise
returns \c false.
*/
/*!
- \fn template <typename T> bool QPointer<T>::operator==(std::nullptr_t, const QPointer<T> &rhs)
+ \fn template <typename T> bool QPointer<T>::operator==(std::nullptr_t const &lhs, const QPointer<T> &rhs)
Equality operator. Returns \c true if the pointer guarded by \a rhs
is \nullptr, otherwise
returns \c false.
*/
/*!
- \fn template <typename T> bool QPointer<T>::operator==(const QPointer<T> &lhs, std::nullptr_t)
+ \fn template <typename T> bool QPointer<T>::operator==(const QPointer<T> &lhs, std::nullptr_t const &rhs)
Equality operator. Returns \c true if the pointer guarded by \a lhs
is \nullptr, otherwise
@@ -204,35 +247,35 @@
*/
/*!
- \fn template <typename T, typename X> bool QPointer<T>::operator!=(const QPointer<T> &p, X *o)
+ \fn template <typename T> template<typename X> bool QPointer<T>::operator!=(const QPointer<T> &lhs, X* const &rhs)
- Inequality operator. Returns \c true if \a o and the guarded
- pointer \a p are not pointing to the same object, otherwise
+ Inequality operator. Returns \c true if \a rhs and the guarded
+ pointer \a lhs are not pointing to the same object, otherwise
returns \c false.
*/
/*!
- \fn template <typename T, typename X> bool QPointer<T>::operator!=(X *o, const QPointer<T> &p)
+ \fn template <typename T> template<typename X> bool QPointer<T>::operator!=(X* const &lhs, const QPointer<T> &rhs)
- Inequality operator. Returns \c true if \a o and the guarded
- pointer \a p are not pointing to the same object, otherwise
+ Inequality operator. Returns \c true if \a lhs and the guarded
+ pointer \a rhs are not pointing to the same object, otherwise
returns \c false.
*/
/*!
- \fn template <typename T, typename X> bool QPointer<T>::operator!=(const QPointer<T> &p1, const QPointer<X> &p2)
+ \fn template <typename T> template<typename X> bool QPointer<T>::operator!=(const QPointer<T> &lhs, const QPointer<X> &rhs)
- Inequality operator. Returns \c true if the guarded pointers \a p1 and
- \a p2 are not pointing to the same object, otherwise
+ Inequality operator. Returns \c true if the guarded pointers \a lhs and
+ \a rhs are not pointing to the same object, otherwise
returns \c false.
*/
/*!
- \fn template <typename T> bool QPointer<T>::operator!=(std::nullptr_t, const QPointer<T> &rhs)
+ \fn template <typename T> bool QPointer<T>::operator!=(std::nullptr_t const &lhs, const QPointer<T> &rhs)
Inequality operator. Returns \c true if the pointer guarded by \a rhs is
a valid (ie not \nullptr) pointer, otherwise
returns \c false.
*/
/*!
- \fn template <typename T> bool QPointer<T>::operator!=(const QPointer<T> &lhs, std::nullptr_t)
+ \fn template <typename T> bool QPointer<T>::operator!=(const QPointer<T> &lhs, std::nullptr_t const &rhs)
Inequality operator. Returns \c true if the pointer guarded by \a lhs is
a valid (ie not \nullptr) pointer, otherwise
diff --git a/src/corelib/kernel/qpoll.cpp b/src/corelib/kernel/qpoll.cpp
index bbd197f292..58fb0234b4 100644
--- a/src/corelib/kernel/qpoll.cpp
+++ b/src/corelib/kernel/qpoll.cpp
@@ -54,7 +54,7 @@ static inline void qt_poll_examine_ready_read(struct pollfd &pfd)
int res;
char data;
- EINTR_LOOP(res, ::recv(pfd.fd, &data, sizeof(data), MSG_PEEK));
+ QT_EINTR_LOOP(res, ::recv(pfd.fd, &data, sizeof(data), MSG_PEEK));
const int error = (res < 0) ? errno : 0;
if (res == 0) {
@@ -109,7 +109,7 @@ static inline bool qt_poll_is_bad_fd(int fd)
#endif
int ret;
- EINTR_LOOP(ret, fcntl(fd, F_GETFD));
+ QT_EINTR_LOOP(ret, fcntl(fd, F_GETFD));
return (ret == -1 && errno == EBADF);
}
diff --git a/src/corelib/kernel/qproperty.cpp b/src/corelib/kernel/qproperty.cpp
index 742d2c5f40..caa9fce787 100644
--- a/src/corelib/kernel/qproperty.cpp
+++ b/src/corelib/kernel/qproperty.cpp
@@ -27,9 +27,9 @@ void QPropertyBindingPrivatePtr::reset(QtPrivate::RefCounted *ptr) noexcept
{
if (ptr != d) {
if (ptr)
- ptr->ref++;
+ ptr->addRef();
auto *old = std::exchange(d, ptr);
- if (old && (--old->ref == 0))
+ if (old && !old->deref())
QPropertyBindingPrivate::destroyAndFreeMemory(static_cast<QPropertyBindingPrivate *>(d));
}
}
@@ -121,12 +121,11 @@ struct QPropertyDelayedNotifications
Change notifications are sent later with notify (following the logic of separating
binding updates and notifications used in non-deferred updates).
*/
- [[nodiscard]] PendingBindingObserverList evaluateBindings(int index, QBindingStatus *status) {
- PendingBindingObserverList bindingObservers;
+ void evaluateBindings(PendingBindingObserverList &bindingObservers, qsizetype index, QBindingStatus *status) {
auto *delayed = delayedProperties + index;
auto *bindingData = delayed->originalBindingData;
if (!bindingData)
- return bindingObservers;
+ return;
bindingData->d_ptr = delayed->d_ptr;
Q_ASSERT(!(bindingData->d_ptr & QPropertyBindingData::DelayedNotificationBit));
@@ -139,7 +138,6 @@ struct QPropertyDelayedNotifications
QPropertyObserverPointer observer = bindingDataPointer.firstObserver();
if (observer)
observer.evaluateBindings(bindingObservers, status);
- return bindingObservers;
}
/*!
@@ -151,17 +149,17 @@ struct QPropertyDelayedNotifications
\li sends any pending notifications.
\endlist
*/
- void notify(int index) {
+ void notify(qsizetype index) {
auto *delayed = delayedProperties + index;
- auto *bindingData = delayed->originalBindingData;
- if (!bindingData)
+ if (delayed->d_ptr & QPropertyBindingData::BindingBit)
+ return; // already handled
+ if (!delayed->originalBindingData)
return;
-
delayed->originalBindingData = nullptr;
+
+ QPropertyObserverPointer observer { reinterpret_cast<QPropertyObserver *>(delayed->d_ptr & ~QPropertyBindingData::DelayedNotificationBit) };
delayed->d_ptr = 0;
- QPropertyBindingDataPointer bindingDataPointer{bindingData};
- QPropertyObserverPointer observer = bindingDataPointer.firstObserver();
if (observer)
observer.notify(delayed->propertyData);
}
@@ -187,7 +185,7 @@ Q_CONSTINIT static thread_local QBindingStatus bindingStatus;
properties need to be updated, preventing any external observer from noticing an inconsistent
state.
- \sa Qt::endPropertyUpdateGroup
+ \sa Qt::endPropertyUpdateGroup, QScopedPropertyUpdateGroup
*/
void Qt::beginPropertyUpdateGroup()
{
@@ -207,7 +205,7 @@ void Qt::beginPropertyUpdateGroup()
\warning Calling endPropertyUpdateGroup without a preceding call to beginPropertyUpdateGroup
results in undefined behavior.
- \sa Qt::beginPropertyUpdateGroup
+ \sa Qt::beginPropertyUpdateGroup, QScopedPropertyUpdateGroup
*/
void Qt::endPropertyUpdateGroup()
{
@@ -218,27 +216,64 @@ void Qt::endPropertyUpdateGroup()
if (--data->ref)
return;
groupUpdateData = nullptr;
+ // ensures that bindings are kept alive until endPropertyUpdateGroup concludes
+ PendingBindingObserverList bindingObservers;
// update all delayed properties
auto start = data;
while (data) {
- for (int i = 0; i < data->used; ++i) {
- PendingBindingObserverList bindingObserves = data->evaluateBindings(i, status);
- Q_UNUSED(bindingObserves);
- // ### TODO: Use bindingObservers for notify
- }
+ for (qsizetype i = 0; i < data->used; ++i)
+ data->evaluateBindings(bindingObservers, i, status);
data = data->next;
}
- // notify all delayed properties
+ // notify all delayed notifications from binding evaluation
+ for (const QBindingObserverPtr &observer: bindingObservers) {
+ QPropertyBindingPrivate *binding = observer.binding();
+ binding->notifyNonRecursive();
+ }
+ // do the same for properties which only have observers
data = start;
while (data) {
- for (int i = 0; i < data->used; ++i)
+ for (qsizetype i = 0; i < data->used; ++i)
data->notify(i);
- auto *next = data->next;
- delete data;
- data = next;
+ delete std::exchange(data, data->next);
}
}
+/*!
+ \since 6.6
+ \class QScopedPropertyUpdateGroup
+ \inmodule QtCore
+ \ingroup tools
+ \brief RAII class around Qt::beginPropertyUpdateGroup()/Qt::endPropertyUpdateGroup().
+
+ This class calls Qt::beginPropertyUpdateGroup() in its constructor and
+ Qt::endPropertyUpdateGroup() in its destructor, making sure the latter
+ function is reliably called even in the presence of early returns or thrown
+ exceptions.
+
+ \note Qt::endPropertyUpdateGroup() may re-throw exceptions thrown by
+ binding evaluations. This means your application may crash
+ (\c{std::terminate()} called) if another exception is causing
+ QScopedPropertyUpdateGroup's destructor to be called during stack
+ unwinding. If you expect exceptions from binding evaluations, use manual
+ Qt::endPropertyUpdateGroup() calls and \c{try}/\c{catch} blocks.
+
+ \sa QProperty
+*/
+
+/*!
+ \fn QScopedPropertyUpdateGroup::QScopedPropertyUpdateGroup()
+
+ Calls Qt::beginPropertyUpdateGroup().
+*/
+
+/*!
+ \fn QScopedPropertyUpdateGroup::~QScopedPropertyUpdateGroup()
+
+ Calls Qt::endPropertyUpdateGroup().
+*/
+
+
// check everything stored in QPropertyBindingPrivate's union is trivially destructible
// (though the compiler would also complain if that weren't the case)
static_assert(std::is_trivially_destructible_v<QPropertyBindingSourceLocation>);
@@ -275,7 +310,7 @@ void QPropertyBindingPrivate::unlinkAndDeref()
{
clearDependencyObservers();
propertyDataPtr = nullptr;
- if (--ref == 0)
+ if (!deref())
destroyAndFreeMemory(this);
}
@@ -286,22 +321,6 @@ bool QPropertyBindingPrivate::evaluateRecursive(PendingBindingObserverList &bind
return evaluateRecursive_inline(bindingObservers, status);
}
-void QPropertyBindingPrivate::notifyRecursive()
-{
- if (!pendingNotify)
- return;
- pendingNotify = false;
- Q_ASSERT(!updating);
- updating = true;
- if (firstObserver) {
- firstObserver.noSelfDependencies(this);
- firstObserver.notify(propertyDataPtr);
- }
- if (hasStaticObserver)
- staticObserverCallback(propertyDataPtr);
- updating = false;
-}
-
void QPropertyBindingPrivate::notifyNonRecursive(const PendingBindingObserverList &bindingObservers)
{
notifyNonRecursive();
@@ -319,7 +338,7 @@ QPropertyBindingPrivate::NotificationState QPropertyBindingPrivate::notifyNonRec
updating = true;
if (firstObserver) {
firstObserver.noSelfDependencies(this);
- firstObserver.notifyOnlyChangeHandler(propertyDataPtr);
+ firstObserver.notify(propertyDataPtr);
}
if (hasStaticObserver)
staticObserverCallback(propertyDataPtr);
@@ -436,7 +455,7 @@ QPropertyBindingError QUntypedPropertyBinding::error() const
/*!
Returns the meta-type of the binding.
- If the QUntypedProperyBinding is null, an invalid QMetaType is returned.
+ If the QUntypedPropertyBinding is null, an invalid QMetaType is returned.
*/
QMetaType QUntypedPropertyBinding::valueMetaType() const
{
@@ -448,6 +467,8 @@ QMetaType QUntypedPropertyBinding::valueMetaType() const
QPropertyBindingData::~QPropertyBindingData()
{
QPropertyBindingDataPointer d{this};
+ if (isNotificationDelayed())
+ proxyData()->originalBindingData = nullptr;
for (auto observer = d.firstObserver(); observer;) {
auto next = observer.nextObserver();
observer.unlink();
@@ -582,6 +603,11 @@ void QPropertyBindingData::registerWithCurrentlyEvaluatingBinding_helper(Binding
{
QPropertyBindingDataPointer d{this};
+ if (currentState->alreadyCaptureProperties.contains(this))
+ return;
+ else
+ currentState->alreadyCaptureProperties.push_back(this);
+
QPropertyObserverPointer dependencyObserver = currentState->binding->allocateDependencyObserver();
Q_ASSERT(QPropertyObserver::ObserverNotifiesBinding == 0);
dependencyObserver.setBindingToNotify_unsafe(currentState->binding);
@@ -602,9 +628,17 @@ void QPropertyBindingData::notifyObservers(QUntypedPropertyData *propertyDataPtr
PendingBindingObserverList bindingObservers;
if (QPropertyObserverPointer observer = d.firstObserver()) {
if (notifyObserver_helper(propertyDataPtr, storage, observer, bindingObservers) == Evaluated) {
- // evaluateBindings() can trash the observers. We need to re-fetch here.
+ /* evaluateBindings() can trash the observers. We need to re-fetch here.
+ "this" might also no longer be valid in case we have a QObjectBindableProperty
+ and consequently d isn't either (this happens when binding evaluation has
+ caused the binding storage to resize.
+ If storage is nullptr, then there is no dynamically resizable storage,
+ and we cannot run into the issue.
+ */
+ if (storage)
+ d = QPropertyBindingDataPointer {storage->bindingData(propertyDataPtr)};
if (QPropertyObserverPointer observer = d.firstObserver())
- observer.notifyOnlyChangeHandler(propertyDataPtr);
+ observer.notify(propertyDataPtr);
for (auto &&bindingObserver: bindingObservers)
bindingObserver.binding()->notifyNonRecursive();
}
@@ -641,11 +675,15 @@ QPropertyObserver::QPropertyObserver(ChangeHandler changeHandler)
d.setChangeHandler(changeHandler);
}
+#if QT_DEPRECATED_SINCE(6, 6)
QPropertyObserver::QPropertyObserver(QUntypedPropertyData *data)
{
+ QT_WARNING_PUSH QT_WARNING_DISABLE_DEPRECATED
aliasData = data;
next.setTag(ObserverIsAlias);
+ QT_WARNING_POP
}
+#endif
/*! \internal
*/
@@ -715,16 +753,9 @@ void QPropertyObserverPointer::setChangeHandler(QPropertyObserver::ChangeHandler
ptr->next.setTag(QPropertyObserver::ObserverNotifiesChangeHandler);
}
-void QPropertyObserverPointer::setBindingToNotify(QPropertyBindingPrivate *binding)
-{
- Q_ASSERT(ptr->next.tag() != QPropertyObserver::ObserverIsPlaceholder);
- ptr->binding = binding;
- ptr->next.setTag(QPropertyObserver::ObserverNotifiesBinding);
-}
-
/*!
\internal
- The same as as setBindingToNotify, but assumes that the tag is already correct.
+ The same as setBindingToNotify, but assumes that the tag is already correct.
*/
void QPropertyObserverPointer::setBindingToNotify_unsafe(QPropertyBindingPrivate *binding)
{
@@ -1154,13 +1185,20 @@ QString QPropertyBindingError::description() const
usable directly without reading through a QBindable use \l QProperty or
\l QObjectBindableProperty.
+ \code
+ QProperty<QString> displayText;
+ QDateTimeEdit *dateTimeEdit = findDateTimeEdit();
+ QBindable<QDateTime> dateTimeBindable(dateTimeEdit, "dateTime");
+ displayText.setBinding([dateTimeBindable](){ return dateTimeBindable.value().toString(); });
+ \endcode
+
\sa QProperty, QObjectBindableProperty, {Qt Bindable Properties}
*/
/*!
\fn template<typename T> QBindable<T>::QBindable(QObject *obj, const QMetaProperty &property)
- See \c \l QBindable::QBindable(QObject *obj, const char *property)
+ See \l QBindable::QBindable(QObject *obj, const char *property)
*/
/*!
@@ -1242,15 +1280,13 @@ QString QPropertyBindingError::description() const
can be used to express relationships between different properties in your
application.
- \note In the case of QML it is important that \l QProperty needs to be exposed
- in \l Q_PROPERTY with the BINDABLE keyword. As a result the QML engine, uses it
- as the bindable interface to set up the property binding. In turn, the binding
- can be then interacted with C++ via the normal API like:
-
+ \note For QML, it's important to expose the \l QProperty in \l Q_PROPERTY
+ with the BINDABLE keyword. As a result, the QML engine uses
+ it as the bindable interface to set up the property binding. In turn, the
+ binding can then be interacted with C++ via the normal API:
QProperty<T>::onValueChanged, QProperty::takeBinding and QBindable::hasBinding
-
- If the property is BINDABLE, then the engine will use the change-tracking
- inherent to the C++ property system for getting notified about changes; and
+ If the property is BINDABLE, the engine will use the change-tracking
+ inherent to the C++ property system for getting notified about changes, and it
won't rely on signals being emitted.
*/
@@ -1326,15 +1362,6 @@ QString QPropertyBindingError::description() const
*/
/*!
- \fn template <typename T> QProperty<T> &QProperty<T>::operator=(const QPropertyBinding<T> &newBinding)
-
- Associates the value of this property with the provided \a newBinding
- expression and returns a reference to this property. The property's value is
- set to the result of evaluating the new binding. Whenever a dependency of the
- binding changes, the binding will be re-evaluated.
-*/
-
-/*!
\fn template <typename T> QPropertyBinding<T> QProperty<T>::setBinding(const QPropertyBinding<T> &newBinding)
Associates the value of this property with the provided \a newBinding
@@ -1647,13 +1674,13 @@ QString QPropertyBindingError::description() const
have changed. Whenever a bindable property used in the callback changes,
this happens automatically. If the result of the callback might change
because of a change in a value which is not a bindable property,
- it is the developer's responsibility to call markDirty
+ it is the developer's responsibility to call \c notify
on the QObjectComputedProperty object.
This will inform dependent properties about the potential change.
- Note that calling markDirty might trigger change handlers in dependent
+ Note that calling \c notify might trigger change handlers in dependent
properties, which might in turn use the object the QObjectComputedProperty
- is a member of. So markDirty must not be called when in a transitional
+ is a member of. So \c notify must not be called when in a transitional
or invalid state.
QObjectComputedProperty is not suitable for use with a computation that depends
@@ -1999,26 +2026,6 @@ QString QPropertyBindingError::description() const
*/
/*!
- \fn template <typename T> QPropertyAlias<T> &QPropertyAlias<T>::operator=(T &&newValue)
- \overload
-
- Assigns \a newValue to the aliased property and returns a reference to this
- QPropertyAlias.
-*/
-
-/*!
- \fn template <typename T> QPropertyAlias<T> &QPropertyAlias<T>::operator=(const QPropertyBinding<T> &newBinding)
- \overload
-
- Associates the value of the aliased property with the provided \a newBinding
- expression and returns a reference to this alias. The property's value is set
- to the result of evaluating the new binding. Whenever a dependency of the
- binding changes, the binding will be re-evaluated, and the property's value
- gets updated accordingly.
-
-*/
-
-/*!
\fn template <typename T> QPropertyBinding<T> QPropertyAlias<T>::setBinding(const QPropertyBinding<T> &newBinding)
Associates the value of the aliased property with the provided \a newBinding
@@ -2470,8 +2477,13 @@ QPropertyAdaptorSlotObject::QPropertyAdaptorSlotObject(QObject *o, const QMetaPr
{
}
+#if QT_VERSION < QT_VERSION_CHECK(7, 0, 0)
void QPropertyAdaptorSlotObject::impl(int which, QSlotObjectBase *this_, QObject *r, void **a,
bool *ret)
+#else
+void QPropertyAdaptorSlotObject::impl(QSlotObjectBase *this_, QObject *r, void **a, int which,
+ bool *ret)
+#endif
{
auto self = static_cast<QPropertyAdaptorSlotObject *>(this_);
switch (which) {
diff --git a/src/corelib/kernel/qproperty.h b/src/corelib/kernel/qproperty.h
index cce9e9791f..8c4b13cf5e 100644
--- a/src/corelib/kernel/qproperty.h
+++ b/src/corelib/kernel/qproperty.h
@@ -7,6 +7,7 @@
#include <QtCore/qglobal.h>
#include <QtCore/qshareddata.h>
#include <QtCore/qstring.h>
+#include <QtCore/qttypetraits.h>
#include <QtCore/qbindingstorage.h>
#include <type_traits>
@@ -53,6 +54,17 @@ Q_CORE_EXPORT void beginPropertyUpdateGroup();
Q_CORE_EXPORT void endPropertyUpdateGroup();
}
+class QScopedPropertyUpdateGroup
+{
+ Q_DISABLE_COPY_MOVE(QScopedPropertyUpdateGroup)
+public:
+ Q_NODISCARD_CTOR
+ QScopedPropertyUpdateGroup()
+ { Qt::beginPropertyUpdateGroup(); }
+ ~QScopedPropertyUpdateGroup() noexcept(false)
+ { Qt::endPropertyUpdateGroup(); }
+};
+
template <typename T>
class QPropertyData : public QUntypedPropertyData
{
@@ -217,7 +229,9 @@ public:
ObserverNotifiesBinding, // observer was installed to notify bindings that obsverved property changed
ObserverNotifiesChangeHandler, // observer is a change handler, which runs on every change
ObserverIsPlaceholder, // the observer before this one is currently evaluated in QPropertyObserver::notifyObservers.
- ObserverIsAlias
+#if QT_DEPRECATED_SINCE(6, 6)
+ ObserverIsAlias QT_DEPRECATED_VERSION_X_6_6("Use QProperty and add a binding to the target.")
+#endif
};
protected:
using ChangeHandler = void (*)(QPropertyObserver*, QUntypedPropertyData *);
@@ -250,14 +264,17 @@ public:
QPropertyObserver &operator=(QPropertyObserver &&other) noexcept;
~QPropertyObserver();
- template<typename Property, typename = typename Property::InheritsQUntypedPropertyData>
+ template <typename Property, QtPrivate::IsUntypedPropertyData<Property> = true>
void setSource(const Property &property)
{ setSource(property.bindingData()); }
void setSource(const QtPrivate::QPropertyBindingData &property);
protected:
QPropertyObserver(ChangeHandler changeHandler);
+#if QT_DEPRECATED_SINCE(6, 6)
+ QT_DEPRECATED_VERSION_X_6_6("This constructor was only meant for internal use. Use QProperty and add a binding to the target.")
QPropertyObserver(QUntypedPropertyData *aliasedPropertyPtr);
+#endif
QUntypedPropertyData *aliasedProperty() const
{
@@ -272,10 +289,11 @@ private:
};
template <typename Functor>
-class [[nodiscard]] QPropertyChangeHandler : public QPropertyObserver
+class QPropertyChangeHandler : public QPropertyObserver
{
Functor m_handler;
public:
+ Q_NODISCARD_CTOR
QPropertyChangeHandler(Functor handler)
: QPropertyObserver([](QPropertyObserver *self, QUntypedPropertyData *) {
auto This = static_cast<QPropertyChangeHandler<Functor>*>(self);
@@ -285,7 +303,8 @@ public:
{
}
- template<typename Property, typename = typename Property::InheritsQUntypedPropertyData>
+ template <typename Property, QtPrivate::IsUntypedPropertyData<Property> = true>
+ Q_NODISCARD_CTOR
QPropertyChangeHandler(const Property &property, Functor handler)
: QPropertyObserver([](QPropertyObserver *self, QUntypedPropertyData *) {
auto This = static_cast<QPropertyChangeHandler<Functor>*>(self);
@@ -297,12 +316,14 @@ public:
}
};
-class [[nodiscard]] QPropertyNotifier : public QPropertyObserver
+class QPropertyNotifier : public QPropertyObserver
{
std::function<void()> m_handler;
public:
+ Q_NODISCARD_CTOR
QPropertyNotifier() = default;
template<typename Functor>
+ Q_NODISCARD_CTOR
QPropertyNotifier(Functor handler)
: QPropertyObserver([](QPropertyObserver *self, QUntypedPropertyData *) {
auto This = static_cast<QPropertyNotifier *>(self);
@@ -312,7 +333,9 @@ public:
{
}
- template<typename Functor, typename Property, typename = typename Property::InheritsQUntypedPropertyData>
+ template <typename Functor, typename Property,
+ QtPrivate::IsUntypedPropertyData<Property> = true>
+ Q_NODISCARD_CTOR
QPropertyNotifier(const Property &property, Functor handler)
: QPropertyObserver([](QPropertyObserver *self, QUntypedPropertyData *) {
auto This = static_cast<QPropertyNotifier *>(self);
@@ -663,8 +686,8 @@ protected:
: data(d), iface(i)
{}
- Q_CORE_EXPORT QUntypedBindable(QObject* obj, const QMetaProperty &property, const QtPrivate::QBindableInterface *i);
- Q_CORE_EXPORT QUntypedBindable(QObject* obj, const char* property, const QtPrivate::QBindableInterface *i);
+ Q_CORE_EXPORT explicit QUntypedBindable(QObject* obj, const QMetaProperty &property, const QtPrivate::QBindableInterface *i);
+ Q_CORE_EXPORT explicit QUntypedBindable(QObject* obj, const char* property, const QtPrivate::QBindableInterface *i);
public:
constexpr QUntypedBindable() = default;
@@ -802,10 +825,10 @@ public:
}
}
- QBindable(QObject *obj, const QMetaProperty &property)
+ explicit QBindable(QObject *obj, const QMetaProperty &property)
: QUntypedBindable(obj, property, &QtPrivate::PropertyAdaptorSlotObjectHelpers::iface<T>) {}
- QBindable(QObject *obj, const char *property)
+ explicit QBindable(QObject *obj, const char *property)
: QUntypedBindable(obj, property, &QtPrivate::PropertyAdaptorSlotObjectHelpers::iface<T>) {}
QPropertyBinding<T> makeBinding(const QPropertyBindingSourceLocation &location = QT_PROPERTY_DEFAULT_BINDING_LOCATION) const
@@ -867,13 +890,16 @@ public:
}
};
+#if QT_DEPRECATED_SINCE(6, 6)
template<typename T>
-class QPropertyAlias : public QPropertyObserver
+class QT_DEPRECATED_VERSION_X_6_6("Class was only meant for internal use, use a QProperty and add a binding to the target")
+QPropertyAlias : public QPropertyObserver
{
Q_DISABLE_COPY_MOVE(QPropertyAlias)
const QtPrivate::QBindableInterface *iface = nullptr;
public:
+ QT_WARNING_PUSH QT_WARNING_DISABLE_DEPRECATED
QPropertyAlias(QProperty<T> *property)
: QPropertyObserver(property),
iface(&QtPrivate::QBindableInterfaceForProperty<QProperty<T>>::iface)
@@ -882,7 +908,7 @@ public:
iface->setObserver(aliasedProperty(), this);
}
- template<typename Property, typename = typename Property::InheritsQUntypedPropertyData>
+ template <typename Property, QtPrivate::IsUntypedPropertyData<Property> = true>
QPropertyAlias(Property *property)
: QPropertyObserver(property),
iface(&QtPrivate::QBindableInterfaceForProperty<Property>::iface)
@@ -982,14 +1008,16 @@ public:
template<typename Functor>
QPropertyNotifier addNotifier(Functor f)
{
- return QBindable<T>(aliasedProperty(), iface).notify(f);
+ return QBindable<T>(aliasedProperty(), iface).addNotifier(f);
}
bool isValid() const
{
return aliasedProperty() != nullptr;
}
+ QT_WARNING_POP
};
+#endif // QT_DEPRECATED_SINCE(6, 6)
template<typename Class, typename T, auto Offset, auto Signal = nullptr>
class QObjectBindableProperty : public QPropertyData<T>
diff --git a/src/corelib/kernel/qproperty_p.h b/src/corelib/kernel/qproperty_p.h
index a7fb9e9a1a..376482a6af 100644
--- a/src/corelib/kernel/qproperty_p.h
+++ b/src/corelib/kernel/qproperty_p.h
@@ -19,12 +19,13 @@
#include <qproperty.h>
#include <qmetaobject.h>
-#include <qscopedpointer.h>
#include <qscopedvaluerollback.h>
#include <qvariant.h>
#include <vector>
#include <QtCore/QVarLengthArray>
+#include <memory>
+
QT_BEGIN_NAMESPACE
namespace QtPrivate {
@@ -83,6 +84,7 @@ struct QPropertyBindingDataPointer
void Q_ALWAYS_INLINE addObserver(QPropertyObserver *observer);
inline void setFirstObserver(QPropertyObserver *observer);
inline QPropertyObserverPointer firstObserver() const;
+ static QPropertyProxyBindingData *proxyData(QtPrivate::QPropertyBindingData *ptr);
inline int observerCount() const;
@@ -93,11 +95,12 @@ struct QPropertyBindingDataPointer
}
};
-struct [[nodiscard]] QPropertyObserverNodeProtector
+struct QPropertyObserverNodeProtector
{
Q_DISABLE_COPY_MOVE(QPropertyObserverNodeProtector)
QPropertyObserverBase m_placeHolder;
+ Q_NODISCARD_CTOR
QPropertyObserverNodeProtector(QPropertyObserver *observer)
{
// insert m_placeholder after observer into the linked list
@@ -123,25 +126,37 @@ struct QPropertyObserverPointer
void unlink()
{
unlink_common();
+#if QT_DEPRECATED_SINCE(6, 6)
+ QT_WARNING_PUSH QT_WARNING_DISABLE_DEPRECATED
if (ptr->next.tag() == QPropertyObserver::ObserverIsAlias)
ptr->aliasData = nullptr;
+ QT_WARNING_POP
+#endif
}
void unlink_fast()
{
+#if QT_DEPRECATED_SINCE(6, 6)
+ QT_WARNING_PUSH QT_WARNING_DISABLE_DEPRECATED
Q_ASSERT(ptr->next.tag() != QPropertyObserver::ObserverIsAlias);
+ QT_WARNING_POP
+#endif
unlink_common();
}
- void setBindingToNotify(QPropertyBindingPrivate *binding);
+ void setBindingToNotify(QPropertyBindingPrivate *binding)
+ {
+ Q_ASSERT(ptr->next.tag() != QPropertyObserver::ObserverIsPlaceholder);
+ ptr->binding = binding;
+ ptr->next.setTag(QPropertyObserver::ObserverNotifiesBinding);
+ }
+
void setBindingToNotify_unsafe(QPropertyBindingPrivate *binding);
void setChangeHandler(QPropertyObserver::ChangeHandler changeHandler);
enum class Notify {Everything, OnlyChangeHandlers};
- template<Notify notifyPolicy = Notify::Everything>
void notify(QUntypedPropertyData *propertyDataPtr);
- void notifyOnlyChangeHandler(QUntypedPropertyData *propertyDataPtr);
#ifndef QT_NO_DEBUG
void noSelfDependencies(QPropertyBindingPrivate *binding);
#else
@@ -158,7 +173,7 @@ struct QPropertyObserverPointer
{
Q_ASSERT(ptr->next.tag() == QPropertyObserver::ObserverNotifiesBinding);
return ptr->binding;
- };
+ }
private:
void unlink_common()
@@ -192,6 +207,7 @@ struct BindingEvaluationState
QPropertyBindingPrivate *binding;
BindingEvaluationState *previousState = nullptr;
BindingEvaluationState **currentState = nullptr;
+ QVarLengthArray<const QPropertyBindingData *, 8> alreadyCaptureProperties;
};
/*!
@@ -216,6 +232,33 @@ struct CompatPropertySafePoint
QtPrivate::BindingEvaluationState *bindingState = nullptr;
};
+/*!
+ * \internal
+ * While the regular QProperty notification for a compat property runs we
+ * don't want to have any currentCompatProperty set. This would be a _different_
+ * one than the one we are current evaluating. Therefore it's misleading and
+ * prevents the registering of actual dependencies.
+ */
+struct CurrentCompatPropertyThief
+{
+ Q_DISABLE_COPY_MOVE(CurrentCompatPropertyThief)
+public:
+ CurrentCompatPropertyThief(QBindingStatus *status)
+ : status(&status->currentCompatProperty)
+ , stolen(std::exchange(status->currentCompatProperty, nullptr))
+ {
+ }
+
+ ~CurrentCompatPropertyThief()
+ {
+ *status = stolen;
+ }
+
+private:
+ CompatPropertySafePoint **status = nullptr;
+ CompatPropertySafePoint *stolen = nullptr;
+};
+
}
class Q_CORE_EXPORT QPropertyBindingPrivate : public QtPrivate::RefCounted
@@ -250,7 +293,7 @@ private:
ObserverArray inlineDependencyObservers; // for things we are observing
QPropertyObserverPointer firstObserver; // list of observers observing us
- QScopedPointer<std::vector<QPropertyObserver>> heapObservers; // for things we are observing
+ std::unique_ptr<std::vector<QPropertyObserver>> heapObservers; // for things we are observing
protected:
QUntypedPropertyData *propertyDataPtr = nullptr;
@@ -366,7 +409,6 @@ public:
bool Q_ALWAYS_INLINE evaluateRecursive_inline(PendingBindingObserverList &bindingObservers, QBindingStatus *status);
- void notifyRecursive();
void notifyNonRecursive(const PendingBindingObserverList &bindingObservers);
enum NotificationState : bool { Delayed, Sent };
NotificationState notifyNonRecursive();
@@ -418,9 +460,9 @@ inline void QPropertyBindingDataPointer::fixupAfterMove(QtPrivate::QPropertyBind
{
auto &d = ptr->d_ref();
if (ptr->isNotificationDelayed()) {
- QPropertyProxyBindingData *proxyData
- = reinterpret_cast<QPropertyProxyBindingData*>(d & ~QtPrivate::QPropertyBindingData::BindingBit);
- proxyData->originalBindingData = ptr;
+ QPropertyProxyBindingData *proxy = ptr->proxyData();
+ Q_ASSERT(proxy);
+ proxy->originalBindingData = ptr;
}
// If QPropertyBindingData has been moved, and it has an observer
// we have to adjust the firstObserver's prev pointer to point to
@@ -438,6 +480,17 @@ inline QPropertyObserverPointer QPropertyBindingDataPointer::firstObserver() con
return { reinterpret_cast<QPropertyObserver *>(ptr->d()) };
}
+/*!
+ \internal
+ Returns the proxy data of \a ptr, or \c nullptr if \a ptr has no delayed notification
+ */
+inline QPropertyProxyBindingData *QPropertyBindingDataPointer::proxyData(QtPrivate::QPropertyBindingData *ptr)
+{
+ if (!ptr->isNotificationDelayed())
+ return nullptr;
+ return ptr->proxyData();
+}
+
inline int QPropertyBindingDataPointer::observerCount() const
{
int count = 0;
@@ -474,13 +527,16 @@ class QObjectCompatProperty : public QPropertyData<T>
static bool bindingWrapper(QMetaType type, QUntypedPropertyData *dataPtr, QtPrivate::QPropertyBindingFunction binding)
{
auto *thisData = static_cast<ThisType *>(dataPtr);
+ QBindingStorage *storage = qGetBindingStorage(thisData->owner());
QPropertyData<T> copy;
- binding.vtable->call(type, &copy, binding.functor);
- if constexpr (QTypeTraits::has_operator_equal_v<T>)
- if (copy.valueBypassingBindings() == thisData->valueBypassingBindings())
- return false;
+ {
+ QtPrivate::CurrentCompatPropertyThief thief(storage->bindingStatus);
+ binding.vtable->call(type, &copy, binding.functor);
+ if constexpr (QTypeTraits::has_operator_equal_v<T>)
+ if (copy.valueBypassingBindings() == thisData->valueBypassingBindings())
+ return false;
+ }
// ensure value and setValue know we're currently evaluating our binding
- QBindingStorage *storage = qGetBindingStorage(thisData->owner());
QtPrivate::CompatPropertySafePoint guardThis(storage->bindingStatus, thisData);
(thisData->owner()->*Setter)(copy.valueBypassingBindings());
return true;
@@ -616,7 +672,7 @@ public:
== QtPrivate::QPropertyBindingData::Evaluated) {
// evaluateBindings() can trash the observers. We need to re-fetch here.
if (QPropertyObserverPointer observer = d.firstObserver())
- observer.notifyOnlyChangeHandler(this);
+ observer.notify(this);
for (auto&& bindingObserver: bindingObservers)
bindingObserver.binding()->notifyNonRecursive();
}
@@ -818,7 +874,14 @@ inline bool QPropertyBindingPrivate::evaluateRecursive_inline(PendingBindingObse
return true;
}
-template<QPropertyObserverPointer::Notify notifyPolicy>
+/*!
+ \internal
+
+ Walks through the list of property observers, and calls any ChangeHandler
+ found there.
+ It doesn't do anything with bindings, which are only handled in
+ QPropertyBindingPrivate::evaluateRecursive.
+ */
inline void QPropertyObserverPointer::notify(QUntypedPropertyData *propertyDataPtr)
{
auto observer = const_cast<QPropertyObserver*>(ptr);
@@ -857,31 +920,22 @@ inline void QPropertyObserverPointer::notify(QUntypedPropertyData *propertyDataP
break;
}
case QPropertyObserver::ObserverNotifiesBinding:
- {
- if constexpr (notifyPolicy == Notify::Everything) {
- auto bindingToNotify = observer->binding;
- QPropertyObserverNodeProtector protector(observer);
- bindingToNotify->notifyRecursive();
- next = protector.next();
- }
break;
- }
case QPropertyObserver::ObserverIsPlaceholder:
// recursion is already properly handled somewhere else
break;
+#if QT_DEPRECATED_SINCE(6, 6)
+ QT_WARNING_PUSH QT_WARNING_DISABLE_DEPRECATED
case QPropertyObserver::ObserverIsAlias:
break;
+ QT_WARNING_POP
+#endif
default: Q_UNREACHABLE();
}
observer = next;
}
}
-inline void QPropertyObserverPointer::notifyOnlyChangeHandler(QUntypedPropertyData *propertyDataPtr)
-{
- notify<Notify::OnlyChangeHandlers>(propertyDataPtr);
-}
-
inline QPropertyObserverNodeProtector::~QPropertyObserverNodeProtector()
{
QPropertyObserverPointer d{static_cast<QPropertyObserver *>(&m_placeHolder)};
@@ -894,7 +948,15 @@ QBindingObserverPtr::QBindingObserverPtr(QPropertyObserver *observer) noexcept :
QPropertyObserverPointer{d}.binding()->addRef();
}
-QBindingObserverPtr::~QBindingObserverPtr() { if (d) QPropertyObserverPointer{d}.binding()->deref(); }
+QBindingObserverPtr::~QBindingObserverPtr()
+{
+ if (!d)
+ return;
+
+ QPropertyBindingPrivate *bindingPrivate = binding();
+ if (!bindingPrivate->deref())
+ QPropertyBindingPrivate::destroyAndFreeMemory(bindingPrivate);
+}
QPropertyBindingPrivate *QBindingObserverPtr::binding() const noexcept { return QPropertyObserverPointer{d}.binding(); }
@@ -907,7 +969,11 @@ class QPropertyAdaptorSlotObject : public QUntypedPropertyData, public QSlotObje
QObject *obj;
QMetaProperty metaProperty_;
+#if QT_VERSION < QT_VERSION_CHECK(7, 0, 0)
static void impl(int which, QSlotObjectBase *this_, QObject *r, void **a, bool *ret);
+#else
+ static void impl(QSlotObjectBase *this_, QObject *r, void **a, int which, bool *ret);
+#endif
QPropertyAdaptorSlotObject(QObject *o, const QMetaProperty& p);
diff --git a/src/corelib/kernel/qpropertyprivate.h b/src/corelib/kernel/qpropertyprivate.h
index 4387f0fbeb..c4a73f2c91 100644
--- a/src/corelib/kernel/qpropertyprivate.h
+++ b/src/corelib/kernel/qpropertyprivate.h
@@ -19,6 +19,7 @@
#include <QtCore/qtaggedpointer.h>
#include <QtCore/qmetatype.h>
#include <QtCore/qcontainerfwd.h>
+#include <QtCore/qttypetraits.h>
#include <functional>
@@ -36,9 +37,13 @@ namespace QtPrivate {
// QPropertyBindingPrivatePtr operates on a RefCountingMixin solely so that we can inline
// the constructor and copy constructor
struct RefCounted {
+
+ int refCount() const { return ref; }
+ void addRef() { ++ref; }
+ bool deref() { return --ref != 0; }
+
+private:
int ref = 0;
- void addRef() {++ref;}
- bool deref() {--ref; return ref;}
};
}
@@ -61,7 +66,7 @@ public:
QPropertyBindingPrivatePtr() noexcept : d(nullptr) { }
~QPropertyBindingPrivatePtr()
{
- if (d && (--d->ref == 0))
+ if (d && !d->deref())
destroyAndFreeMemory();
}
Q_CORE_EXPORT void destroyAndFreeMemory();
@@ -91,28 +96,20 @@ public:
void swap(QPropertyBindingPrivatePtr &other) noexcept
{ qt_ptr_swap(d, other.d); }
- friend bool operator==(const QPropertyBindingPrivatePtr &p1, const QPropertyBindingPrivatePtr &p2) noexcept
- { return p1.d == p2.d; }
- friend bool operator!=(const QPropertyBindingPrivatePtr &p1, const QPropertyBindingPrivatePtr &p2) noexcept
- { return p1.d != p2.d; }
- friend bool operator==(const QPropertyBindingPrivatePtr &p1, const T *ptr) noexcept
- { return p1.d == ptr; }
- friend bool operator!=(const QPropertyBindingPrivatePtr &p1, const T *ptr) noexcept
- { return p1.d != ptr; }
- friend bool operator==(const T *ptr, const QPropertyBindingPrivatePtr &p2) noexcept
- { return ptr == p2.d; }
- friend bool operator!=(const T *ptr, const QPropertyBindingPrivatePtr &p2) noexcept
- { return ptr != p2.d; }
- friend bool operator==(const QPropertyBindingPrivatePtr &p1, std::nullptr_t) noexcept
- { return !p1; }
- friend bool operator!=(const QPropertyBindingPrivatePtr &p1, std::nullptr_t) noexcept
- { return p1; }
- friend bool operator==(std::nullptr_t, const QPropertyBindingPrivatePtr &p2) noexcept
- { return !p2; }
- friend bool operator!=(std::nullptr_t, const QPropertyBindingPrivatePtr &p2) noexcept
- { return p2; }
-
private:
+ friend bool comparesEqual(const QPropertyBindingPrivatePtr &lhs,
+ const QPropertyBindingPrivatePtr &rhs) noexcept
+ { return lhs.d == rhs.d; }
+ Q_DECLARE_EQUALITY_COMPARABLE(QPropertyBindingPrivatePtr)
+ friend bool comparesEqual(const QPropertyBindingPrivatePtr &lhs,
+ const T *rhs) noexcept
+ { return lhs.d == rhs; }
+ Q_DECLARE_EQUALITY_COMPARABLE(QPropertyBindingPrivatePtr, T*)
+ friend bool comparesEqual(const QPropertyBindingPrivatePtr &lhs,
+ std::nullptr_t) noexcept
+ { return !lhs; }
+ Q_DECLARE_EQUALITY_COMPARABLE(QPropertyBindingPrivatePtr, std::nullptr_t)
+
QtPrivate::RefCounted *d;
};
@@ -124,11 +121,13 @@ struct QPropertyObserverPointer;
class QUntypedPropertyData
{
-public:
- // sentinel to check whether a class inherits QUntypedPropertyData
- struct InheritsQUntypedPropertyData {};
};
+namespace QtPrivate {
+template <typename T>
+using IsUntypedPropertyData = std::enable_if_t<std::is_base_of_v<QUntypedPropertyData, T>, bool>;
+}
+
template <typename T>
class QPropertyData;
@@ -303,10 +302,15 @@ private:
{
quintptr &d = d_ptr;
if (isNotificationDelayed())
- return reinterpret_cast<QPropertyProxyBindingData *>(d_ptr & ~(BindingBit|DelayedNotificationBit))->d_ptr;
+ return proxyData()->d_ptr;
return d;
}
quintptr d() const { return d_ref(); }
+ QPropertyProxyBindingData *proxyData() const
+ {
+ Q_ASSERT(isNotificationDelayed());
+ return reinterpret_cast<QPropertyProxyBindingData *>(d_ptr & ~(BindingBit|DelayedNotificationBit));
+ }
void registerWithCurrentlyEvaluatingBinding_helper(BindingEvaluationState *currentBinding) const;
void removeBinding_helper();
diff --git a/src/corelib/kernel/qsharedmemory.cpp b/src/corelib/kernel/qsharedmemory.cpp
deleted file mode 100644
index 2449564151..0000000000
--- a/src/corelib/kernel/qsharedmemory.cpp
+++ /dev/null
@@ -1,714 +0,0 @@
-// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
-
-#include "qsharedmemory.h"
-#include "qsharedmemory_p.h"
-#include "qsystemsemaphore.h"
-#include <qdir.h>
-#include <qcryptographichash.h>
-#include <qdebug.h>
-#ifdef Q_OS_WIN
-# include <qt_windows.h>
-#endif
-
-#if defined(Q_OS_DARWIN)
-# include "qcore_mac_p.h"
-# if !defined(SHM_NAME_MAX)
- // Based on PSEMNAMLEN in XNU's posix_sem.c, which would
- // indicate the max length is 31, _excluding_ the zero
- // terminator. But in practice (possibly due to an off-
- // by-one bug in the kernel) the usable bytes are only 30.
-# define SHM_NAME_MAX 30
-# endif
-#endif
-
-QT_BEGIN_NAMESPACE
-
-using namespace Qt::StringLiterals;
-
-#if QT_CONFIG(sharedmemory) || QT_CONFIG(systemsemaphore)
-/*!
- \internal
-
- Generate a string from the key which can be any unicode string into
- the subset that the win/unix kernel allows.
-
- On Unix this will be a file name
- */
-QString
-QSharedMemoryPrivate::makePlatformSafeKey(const QString &key,
- const QString &prefix)
-{
- if (key.isEmpty())
- return QString();
-
- QByteArray hex = QCryptographicHash::hash(key.toUtf8(), QCryptographicHash::Sha1).toHex();
-
-#if defined(Q_OS_DARWIN) && defined(QT_POSIX_IPC)
- if (qt_apple_isSandboxed()) {
- // Sandboxed applications on Apple platforms require the shared memory name
- // to be in the form <application group identifier>/<custom identifier>.
- // Since we don't know which application group identifier the user wants
- // to apply, we instead document that requirement, and use the key directly.
- return key;
- } else {
- // The shared memory name limit on Apple platforms is very low (30 characters),
- // so we can't use the logic below of combining the prefix, key, and a hash,
- // to ensure a unique and valid name. Instead we use the first part of the
- // hash, which should still long enough to avoid collisions in practice.
- return u'/' + hex.left(SHM_NAME_MAX - 1);
- }
-#endif
-
- QString result = prefix;
- for (QChar ch : key) {
- if ((ch >= u'a' && ch <= u'z') ||
- (ch >= u'A' && ch <= u'Z'))
- result += ch;
- }
- result.append(QLatin1StringView(hex));
-
-#ifdef Q_OS_WIN
- return result;
-#elif defined(QT_POSIX_IPC)
- return u'/' + result;
-#else
- return QDir::tempPath() + u'/' + result;
-#endif
-}
-#endif // QT_CONFIG(sharedmemory) || QT_CONFIG(systemsemaphore)
-
-#if QT_CONFIG(sharedmemory)
-
-/*!
- \class QSharedMemory
- \inmodule QtCore
- \since 4.4
-
- \brief The QSharedMemory class provides access to a shared memory segment.
-
- QSharedMemory provides access to a shared memory segment by multiple
- threads and processes. It also provides a way for a single thread or
- process to lock the memory for exclusive access.
-
- When using this class, be aware of the following platform
- differences:
-
- \list
-
- \li Windows: QSharedMemory does not "own" the shared memory segment.
- When all threads or processes that have an instance of QSharedMemory
- attached to a particular shared memory segment have either destroyed
- their instance of QSharedMemory or exited, the Windows kernel
- releases the shared memory segment automatically.
-
- \li Unix: QSharedMemory "owns" the shared memory segment. When the
- last thread or process that has an instance of QSharedMemory
- attached to a particular shared memory segment detaches from the
- segment by destroying its instance of QSharedMemory, the destructor
- releases the shared memory segment. But if that last thread or
- process crashes without running the QSharedMemory destructor, the
- shared memory segment survives the crash.
-
- \li Unix: QSharedMemory can be implemented by one of two different
- backends, selected at Qt build time: System V or POSIX. Qt defaults to
- using the System V API if it is available, and POSIX if not. These two
- backends do not interoperate, so two applications must ensure they use the
- same one, even if the native key (see setNativeKey()) is the same.
-
- The POSIX backend can be explicitly selected using the
- \c{-feature-ipc_posix} option to the Qt configure script. If it is enabled,
- the \c{QT_POSIX_IPC} macro will be defined.
-
- \li Sandboxed applications on Apple platforms (including apps
- shipped through the Apple App Store): This environment requires
- the use of POSIX shared memory (instead of System V shared memory).
-
- Qt for iOS is built with support for POSIX shared memory out of the box.
- However, Qt for \macos builds (including those from the Qt installer) default
- to System V, making them unsuitable for App Store submission if QSharedMemory
- is needed. See above for instructions to explicitly select the POSIX backend
- when building Qt.
-
- In addition, in a sandboxed environment, the following caveats apply:
-
- \list
- \li The key must be in the form \c {<application group identifier>/<custom identifier>},
- as documented \l {https://developer.apple.com/library/archive/documentation/Security/Conceptual/AppSandboxDesignGuide/AppSandboxInDepth/AppSandboxInDepth.html#//apple_ref/doc/uid/TP40011183-CH3-SW24}
- {here} and \l {https://developer.apple.com/documentation/bundleresources/entitlements/com_apple_security_application-groups}
- {here}.
-
- \li The key length is limited to 30 characters.
-
- \li On process exit, the named shared memory entries are not
- cleaned up, so restarting the application and re-creating the
- shared memory under the same name will fail. To work around this,
- fall back to attaching to the existing shared memory entry:
-
- \code
-
- QSharedMemory shm("DEVTEAMID.app-group/shared");
- if (!shm.create(42) && shm.error() == QSharedMemory::AlreadyExists)
- shm.attach();
-
- \endcode
-
- \endlist
-
- \li Android: QSharedMemory is not supported.
-
- \endlist
-
- Remember to lock the shared memory with lock() before reading from
- or writing to the shared memory, and remember to release the lock
- with unlock() after you are done.
-
- QSharedMemory automatically destroys the shared memory segment when
- the last instance of QSharedMemory is detached from the segment, and
- no references to the segment remain.
-
- \warning QSharedMemory changes the key in a Qt-specific way, unless otherwise
- specified. Interoperation with non-Qt applications is achieved by first creating
- a default shared memory with QSharedMemory() and then setting a native key with
- setNativeKey(), after ensuring they use the same low-level API (System V or
- POSIX). When using native keys, shared memory is not protected against multiple
- accesses on it (for example, unable to lock()) and a user-defined mechanism
- should be used to achieve such protection.
-
- \section2 Alternative: Memory-Mapped File
-
- Another way to share memory between processes is by opening the same file
- using \l QFile and mapping it into memory using QFile::map() (without
- specifying the QFileDevice::MapPrivateOption option). Any writes to the
- mapped segment will be observed by all other processes that have mapped the
- same file. This solution has the major advantages of being independent of the
- backend API and of being simpler to interoperate with from non-Qt
- applications. And since \l{QTemporaryFile} is a \l{QFile}, applications can
- use that class to achieve clean-up semantics and to create unique shared
- memory segments too.
-
- To achieve locking of the shared memory segment, applications will need to
- deploy their own mechanisms. This can be achieved by using \l
- QBasicAtomicInteger or \c{std::atomic} in a pre-determined offset in the
- segment itself. Higher-level locking primitives may be available on some
- operating systems; for example, on Linux, \c{pthread_mutex_create()} can be
- passed a flag to indicate that the mutex resides in a shared memory segment.
-
- A major drawback of using file-backed shared memory is that the operating
- system will attempt to write the data to permanent storage, possibly causing
- noticeable performance penalties. To avoid this, applications should locate a
- RAM-backed filesystem, such as \c{tmpfs} on Linux (see
- QStorageInfo::fileSystemType()), or pass a flag to the native file-opening
- function to inform the OS to avoid committing the contents to storage.
-
- File-backed shared memory must be used with care if another process
- participating is untrusted. The files may be truncated/shrunk and cause
- applications accessing memory beyond the file's size to crash.
-
- \section3 Linux hints on memory-mapped files
-
- On modern Linux systems, while the \c{/tmp} directory is often a \c{tmpfs}
- mount point, that is not a requirement. However, the \c{/dev/shm} directory
- is required to be a \c{tmpfs} and exists for this very purpose. Do note that
- it is world-readable and writable (like \c{/tmp} and \c{/var/tmp}), so one
- must be careful of the contents revealed there. Another alternative is to use
- the XDG Runtime Directory (see QStandardPaths::writableLocation() and
- \l{QStandardPaths::RuntimeLocation}), which on Linux systems using systemd is
- a user-specific \c{tmpfs}.
-
- An even more secure solution is to create a "memfd" using \c{memfd_create(2)}
- and use interprocess communication to pass the file descriptor, like
- \l{QDBusUnixFileDescriptor} or by letting the child process of a \l{QProcess}
- inherit it. "memfds" can also be sealed against being shrunk, so they are
- safe to be used when communicating with processes with a different privilege
- level.
-
- \section3 FreeBSD hints on memory-mapped files
-
- FreeBSD also has \c{memfd_create(2)} and can pass file descriptors to other
- processes using the same techniques as Linux. It does not have temporary
- filesystems mounted by default.
-
- \section3 Windows hints on memory-mapped files
-
- On Windows, the application can request the operating system avoid committing
- the file's contents to permanent storage. This request is performed by
- passing the \c{FILE_ATTRIBUTE_TEMPORARY} flag in the \c{dwFlagsAndAttributes}
- \c{CreateFile} Win32 function, the \c{_O_SHORT_LIVED} flag to \c{_open()}
- low-level function, or by including the modifier "T" to the \c{fopen()} C
- runtime function.
-
- There's also a flag to inform the operating system to delete the file when
- the last handle to it is closed (\c{FILE_FLAG_DELETE_ON_CLOSE},
- \c{_O_TEMPORARY}, and the "D" modifier), but do note that all processes
- attempting to open the file must agree on using this flag or not using it. A
- mismatch will likely cause a sharing violation and failure to open the file.
- */
-
-/*!
- \overload QSharedMemory()
-
- Constructs a shared memory object with the given \a parent. The
- shared memory object's key is not set by the constructor, so the
- shared memory object does not have an underlying shared memory
- segment attached. The key must be set with setKey() or setNativeKey()
- before create() or attach() can be used.
-
- \sa setKey()
- */
-
-QSharedMemory::QSharedMemory(QObject *parent)
- : QObject(*new QSharedMemoryPrivate, parent)
-{
-}
-
-/*!
- Constructs a shared memory object with the given \a parent and with
- its key set to \a key. Because its key is set, its create() and
- attach() functions can be called.
-
- \sa setKey(), create(), attach()
- */
-QSharedMemory::QSharedMemory(const QString &key, QObject *parent)
- : QObject(*new QSharedMemoryPrivate, parent)
-{
- setKey(key);
-}
-
-/*!
- The destructor clears the key, which forces the shared memory object
- to \l {detach()} {detach} from its underlying shared memory
- segment. If this shared memory object is the last one connected to
- the shared memory segment, the detach() operation destroys the
- shared memory segment.
-
- \sa detach(), isAttached()
- */
-QSharedMemory::~QSharedMemory()
-{
- setKey(QString());
-}
-
-/*!
- Sets the platform independent \a key for this shared memory object. If \a key
- is the same as the current key, the function returns without doing anything.
-
- You can call key() to retrieve the platform independent key. Internally,
- QSharedMemory converts this key into a platform specific key. If you instead
- call nativeKey(), you will get the platform specific, converted key.
-
- If the shared memory object is attached to an underlying shared memory
- segment, it will \l {detach()} {detach} from it before setting the new key.
- This function does not do an attach().
-
- \sa key(), nativeKey(), isAttached()
-*/
-void QSharedMemory::setKey(const QString &key)
-{
- Q_D(QSharedMemory);
- if (key == d->key && d->makePlatformSafeKey(key) == d->nativeKey)
- return;
-
- if (isAttached())
- detach();
- d->cleanHandle();
- d->key = key;
- d->nativeKey = d->makePlatformSafeKey(key);
-}
-
-/*!
- \since 4.8
-
- Sets the native, platform specific, \a key for this shared memory object. If
- \a key is the same as the current native key, the function returns without
- doing anything. If all you want is to assign a key to a segment, you should
- call setKey() instead.
-
- You can call nativeKey() to retrieve the native key. If a native key has been
- assigned, calling key() will return a null string.
-
- If the shared memory object is attached to an underlying shared memory
- segment, it will \l {detach()} {detach} from it before setting the new key.
- This function does not do an attach().
-
- The application will not be portable if you set a native key.
-
- \sa nativeKey(), key(), isAttached()
-*/
-void QSharedMemory::setNativeKey(const QString &key)
-{
- Q_D(QSharedMemory);
- if (key == d->nativeKey && d->key.isNull())
- return;
-
- if (isAttached())
- detach();
- d->cleanHandle();
- d->key = QString();
- d->nativeKey = key;
-}
-
-bool QSharedMemoryPrivate::initKey()
-{
- if (!cleanHandle())
- return false;
-#if QT_CONFIG(systemsemaphore)
- systemSemaphore.setKey(QString(), 1);
- systemSemaphore.setKey(key, 1);
- if (systemSemaphore.error() != QSystemSemaphore::NoError) {
- QString function = "QSharedMemoryPrivate::initKey"_L1;
- errorString = QSharedMemory::tr("%1: unable to set key on lock").arg(function);
- switch(systemSemaphore.error()) {
- case QSystemSemaphore::PermissionDenied:
- error = QSharedMemory::PermissionDenied;
- break;
- case QSystemSemaphore::KeyError:
- error = QSharedMemory::KeyError;
- break;
- case QSystemSemaphore::AlreadyExists:
- error = QSharedMemory::AlreadyExists;
- break;
- case QSystemSemaphore::NotFound:
- error = QSharedMemory::NotFound;
- break;
- case QSystemSemaphore::OutOfResources:
- error = QSharedMemory::OutOfResources;
- break;
- case QSystemSemaphore::UnknownError:
- default:
- error = QSharedMemory::UnknownError;
- break;
- }
- return false;
- }
-#endif
- errorString = QString();
- error = QSharedMemory::NoError;
- return true;
-}
-
-/*!
- Returns the key assigned with setKey() to this shared memory, or a null key
- if no key has been assigned, or if the segment is using a nativeKey(). The
- key is the identifier used by Qt applications to identify the shared memory
- segment.
-
- You can find the native, platform specific, key used by the operating system
- by calling nativeKey().
-
- \sa setKey(), setNativeKey()
- */
-QString QSharedMemory::key() const
-{
- Q_D(const QSharedMemory);
- return d->key;
-}
-
-/*!
- \since 4.8
-
- Returns the native, platform specific, key for this shared memory object. The
- native key is the identifier used by the operating system to identify the
- shared memory segment.
-
- You can use the native key to access shared memory segments that have not
- been created by Qt, or to grant shared memory access to non-Qt applications.
-
- \sa setKey(), setNativeKey()
-*/
-QString QSharedMemory::nativeKey() const
-{
- Q_D(const QSharedMemory);
- return d->nativeKey;
-}
-
-/*!
- Creates a shared memory segment of \a size bytes with the key passed to the
- constructor, set with setKey() or set with setNativeKey(), then attaches to
- the new shared memory segment with the given access \a mode and returns
- \tt true. If a shared memory segment identified by the key already exists,
- the attach operation is not performed and \tt false is returned. When the
- return value is \tt false, call error() to determine which error occurred.
-
- \sa error()
- */
-bool QSharedMemory::create(qsizetype size, AccessMode mode)
-{
- Q_D(QSharedMemory);
-
- if (!d->initKey())
- return false;
-
-#if QT_CONFIG(systemsemaphore)
-#ifndef Q_OS_WIN
- // Take ownership and force set initialValue because the semaphore
- // might have already existed from a previous crash.
- d->systemSemaphore.setKey(d->key, 1, QSystemSemaphore::Create);
-#endif
-#endif
-
- QString function = "QSharedMemory::create"_L1;
-#if QT_CONFIG(systemsemaphore)
- QSharedMemoryLocker lock(this);
- if (!d->key.isNull() && !d->tryLocker(&lock, function))
- return false;
-#endif
-
- if (size <= 0) {
- d->error = QSharedMemory::InvalidSize;
- d->errorString =
- QSharedMemory::tr("%1: create size is less then 0").arg(function);
- return false;
- }
-
- if (!d->create(size))
- return false;
-
- return d->attach(mode);
-}
-
-/*!
- Returns the size of the attached shared memory segment. If no shared
- memory segment is attached, 0 is returned.
-
- \note The size of the segment may be larger than the requested size that was
- passed to create().
-
- \sa create(), attach()
- */
-qsizetype QSharedMemory::size() const
-{
- Q_D(const QSharedMemory);
- return d->size;
-}
-
-/*!
- \enum QSharedMemory::AccessMode
-
- \value ReadOnly The shared memory segment is read-only. Writing to
- the shared memory segment is not allowed. An attempt to write to a
- shared memory segment created with ReadOnly causes the program to
- abort.
-
- \value ReadWrite Reading and writing the shared memory segment are
- both allowed.
-*/
-
-/*!
- Attempts to attach the process to the shared memory segment
- identified by the key that was passed to the constructor or to a
- call to setKey() or setNativeKey(). The access \a mode is \l {QSharedMemory::}
- {ReadWrite} by default. It can also be \l {QSharedMemory::}
- {ReadOnly}. Returns \c true if the attach operation is successful. If
- false is returned, call error() to determine which error occurred.
- After attaching the shared memory segment, a pointer to the shared
- memory can be obtained by calling data().
-
- \sa isAttached(), detach(), create()
- */
-bool QSharedMemory::attach(AccessMode mode)
-{
- Q_D(QSharedMemory);
-
- if (isAttached() || !d->initKey())
- return false;
-#if QT_CONFIG(systemsemaphore)
- QSharedMemoryLocker lock(this);
- if (!d->key.isNull() && !d->tryLocker(&lock, "QSharedMemory::attach"_L1))
- return false;
-#endif
-
- if (isAttached() || !d->handle())
- return false;
-
- return d->attach(mode);
-}
-
-/*!
- Returns \c true if this process is attached to the shared memory
- segment.
-
- \sa attach(), detach()
- */
-bool QSharedMemory::isAttached() const
-{
- Q_D(const QSharedMemory);
- return (nullptr != d->memory);
-}
-
-/*!
- Detaches the process from the shared memory segment. If this was the
- last process attached to the shared memory segment, then the shared
- memory segment is released by the system, i.e., the contents are
- destroyed. The function returns \c true if it detaches the shared
- memory segment. If it returns \c false, it usually means the segment
- either isn't attached, or it is locked by another process.
-
- \sa attach(), isAttached()
- */
-bool QSharedMemory::detach()
-{
- Q_D(QSharedMemory);
- if (!isAttached())
- return false;
-
-#if QT_CONFIG(systemsemaphore)
- QSharedMemoryLocker lock(this);
- if (!d->key.isNull() && !d->tryLocker(&lock, "QSharedMemory::detach"_L1))
- return false;
-#endif
-
- return d->detach();
-}
-
-/*!
- Returns a pointer to the contents of the shared memory segment, if
- one is attached. Otherwise it returns null. Remember to lock the
- shared memory with lock() before reading from or writing to the
- shared memory, and remember to release the lock with unlock() after
- you are done.
-
- \sa attach()
- */
-void *QSharedMemory::data()
-{
- Q_D(QSharedMemory);
- return d->memory;
-}
-
-/*!
- Returns a const pointer to the contents of the shared memory
- segment, if one is attached. Otherwise it returns null. Remember to
- lock the shared memory with lock() before reading from or writing to
- the shared memory, and remember to release the lock with unlock()
- after you are done.
-
- \sa attach(), create()
- */
-const void *QSharedMemory::constData() const
-{
- Q_D(const QSharedMemory);
- return d->memory;
-}
-
-/*!
- \overload data()
- */
-const void *QSharedMemory::data() const
-{
- Q_D(const QSharedMemory);
- return d->memory;
-}
-
-#if QT_CONFIG(systemsemaphore)
-/*!
- This is a semaphore that locks the shared memory segment for access
- by this process and returns \c true. If another process has locked the
- segment, this function blocks until the lock is released. Then it
- acquires the lock and returns \c true. If this function returns \c false,
- it means that you have ignored a false return from create() or attach(),
- that you have set the key with setNativeKey() or that
- QSystemSemaphore::acquire() failed due to an unknown system error.
-
- \sa unlock(), data(), QSystemSemaphore::acquire()
- */
-bool QSharedMemory::lock()
-{
- Q_D(QSharedMemory);
- if (d->lockedByMe) {
- qWarning("QSharedMemory::lock: already locked");
- return true;
- }
- if (d->systemSemaphore.acquire()) {
- d->lockedByMe = true;
- return true;
- }
- const auto function = "QSharedMemory::lock"_L1;
- d->errorString = QSharedMemory::tr("%1: unable to lock").arg(function);
- d->error = QSharedMemory::LockError;
- return false;
-}
-
-/*!
- Releases the lock on the shared memory segment and returns \c true, if
- the lock is currently held by this process. If the segment is not
- locked, or if the lock is held by another process, nothing happens
- and false is returned.
-
- \sa lock()
- */
-bool QSharedMemory::unlock()
-{
- Q_D(QSharedMemory);
- if (!d->lockedByMe)
- return false;
- d->lockedByMe = false;
- if (d->systemSemaphore.release())
- return true;
- const auto function = "QSharedMemory::unlock"_L1;
- d->errorString = QSharedMemory::tr("%1: unable to unlock").arg(function);
- d->error = QSharedMemory::LockError;
- return false;
-}
-#endif // QT_CONFIG(systemsemaphore)
-
-/*!
- \enum QSharedMemory::SharedMemoryError
-
- \value NoError No error occurred.
-
- \value PermissionDenied The operation failed because the caller
- didn't have the required permissions.
-
- \value InvalidSize A create operation failed because the requested
- size was invalid.
-
- \value KeyError The operation failed because of an invalid key.
-
- \value AlreadyExists A create() operation failed because a shared
- memory segment with the specified key already existed.
-
- \value NotFound An attach() failed because a shared memory segment
- with the specified key could not be found.
-
- \value LockError The attempt to lock() the shared memory segment
- failed because create() or attach() failed and returned false, or
- because a system error occurred in QSystemSemaphore::acquire().
-
- \value OutOfResources A create() operation failed because there was
- not enough memory available to fill the request.
-
- \value UnknownError Something else happened and it was bad.
-*/
-
-/*!
- Returns a value indicating whether an error occurred, and, if so,
- which error it was.
-
- \sa errorString()
- */
-QSharedMemory::SharedMemoryError QSharedMemory::error() const
-{
- Q_D(const QSharedMemory);
- return d->error;
-}
-
-/*!
- Returns a text description of the last error that occurred. If
- error() returns an \l {QSharedMemory::SharedMemoryError} {error
- value}, call this function to get a text string that describes the
- error.
-
- \sa error()
- */
-QString QSharedMemory::errorString() const
-{
- Q_D(const QSharedMemory);
- return d->errorString;
-}
-
-#endif // QT_CONFIG(sharedmemory)
-
-QT_END_NAMESPACE
-
-#include "moc_qsharedmemory.cpp"
diff --git a/src/corelib/kernel/qsharedmemory.h b/src/corelib/kernel/qsharedmemory.h
deleted file mode 100644
index 653fb1fb64..0000000000
--- a/src/corelib/kernel/qsharedmemory.h
+++ /dev/null
@@ -1,84 +0,0 @@
-// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
-
-#ifndef QSHAREDMEMORY_H
-#define QSHAREDMEMORY_H
-
-#include <QtCore/qglobal.h>
-#ifndef QT_NO_QOBJECT
-# include <QtCore/qobject.h>
-#else
-# include <QtCore/qobjectdefs.h>
-# include <QtCore/qscopedpointer.h>
-# include <QtCore/qstring.h>
-#endif
-QT_BEGIN_NAMESPACE
-
-
-#if QT_CONFIG(sharedmemory)
-
-class QSharedMemoryPrivate;
-
-class Q_CORE_EXPORT QSharedMemory : public QObject
-{
- Q_OBJECT
- Q_DECLARE_PRIVATE(QSharedMemory)
-
-public:
- enum AccessMode
- {
- ReadOnly,
- ReadWrite
- };
-
- enum SharedMemoryError
- {
- NoError,
- PermissionDenied,
- InvalidSize,
- KeyError,
- AlreadyExists,
- NotFound,
- LockError,
- OutOfResources,
- UnknownError
- };
-
- QSharedMemory(QObject *parent = nullptr);
- QSharedMemory(const QString &key, QObject *parent = nullptr);
- ~QSharedMemory();
-
- void setKey(const QString &key);
- QString key() const;
- void setNativeKey(const QString &key);
- QString nativeKey() const;
-
- bool create(qsizetype size, AccessMode mode = ReadWrite);
- qsizetype size() const;
-
- bool attach(AccessMode mode = ReadWrite);
- bool isAttached() const;
- bool detach();
-
- void *data();
- const void* constData() const;
- const void *data() const;
-
-#if QT_CONFIG(systemsemaphore)
- bool lock();
- bool unlock();
-#endif
-
- SharedMemoryError error() const;
- QString errorString() const;
-
-private:
- Q_DISABLE_COPY(QSharedMemory)
-};
-
-#endif // QT_CONFIG(sharedmemory)
-
-QT_END_NAMESPACE
-
-#endif // QSHAREDMEMORY_H
-
diff --git a/src/corelib/kernel/qsharedmemory_android.cpp b/src/corelib/kernel/qsharedmemory_android.cpp
deleted file mode 100644
index 0cee2ab3af..0000000000
--- a/src/corelib/kernel/qsharedmemory_android.cpp
+++ /dev/null
@@ -1,65 +0,0 @@
-// Copyright (C) 2012 Collabora Ltd, author <robin.burchell@collabora.co.uk>
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
-
-#include "qsharedmemory.h"
-#include "qsharedmemory_p.h"
-#include <qdebug.h>
-
-#if QT_CONFIG(sharedmemory)
-QT_BEGIN_NAMESPACE
-
-void QSharedMemoryPrivate::setErrorString(QLatin1StringView function)
-{
- Q_UNUSED(function);
- Q_UNIMPLEMENTED();
-}
-
-key_t QSharedMemoryPrivate::handle()
-{
- Q_UNIMPLEMENTED();
- return 0;
-}
-
-#endif // QT_CONFIG(sharedmemory)
-
-#if QT_CONFIG(sharedmemory) || QT_CONFIG(systemsemaphore)
-int QSharedMemoryPrivate::createUnixKeyFile(const QString &fileName)
-{
- Q_UNUSED(fileName);
- Q_UNIMPLEMENTED();
- return 0;
-}
-#endif // QT_CONFIG(sharedmemory) || QT_CONFIG(systemsemaphore)
-
-#if QT_CONFIG(sharedmemory)
-
-bool QSharedMemoryPrivate::cleanHandle()
-{
- Q_UNIMPLEMENTED();
- return true;
-}
-
-bool QSharedMemoryPrivate::create(qsizetype size)
-{
- Q_UNUSED(size);
- Q_UNIMPLEMENTED();
- return false;
-}
-
-bool QSharedMemoryPrivate::attach(QSharedMemory::AccessMode mode)
-{
- Q_UNUSED(mode);
- Q_UNIMPLEMENTED();
- return false;
-}
-
-bool QSharedMemoryPrivate::detach()
-{
- Q_UNIMPLEMENTED();
- return false;
-}
-
-
-QT_END_NAMESPACE
-
-#endif // QT_CONFIG(sharedmemory)
diff --git a/src/corelib/kernel/qsharedmemory_p.h b/src/corelib/kernel/qsharedmemory_p.h
deleted file mode 100644
index 6d7973faf8..0000000000
--- a/src/corelib/kernel/qsharedmemory_p.h
+++ /dev/null
@@ -1,140 +0,0 @@
-// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
-
-#ifndef QSHAREDMEMORY_P_H
-#define QSHAREDMEMORY_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 "qsharedmemory.h"
-
-#include <QtCore/qstring.h>
-
-#if !QT_CONFIG(sharedmemory)
-# if QT_CONFIG(systemsemaphore)
-
-QT_BEGIN_NAMESPACE
-
-namespace QSharedMemoryPrivate
-{
- int createUnixKeyFile(const QString &fileName);
- QString makePlatformSafeKey(const QString &key,
- const QString &prefix = QStringLiteral("qipc_sharedmemory_"));
-}
-
-QT_END_NAMESPACE
-
-# endif
-#else
-
-#include "qsystemsemaphore.h"
-#include "private/qobject_p.h"
-
-#if !defined(Q_OS_WIN) && !defined(Q_OS_ANDROID) && !defined(Q_OS_INTEGRITY) && !defined(Q_OS_RTEMS)
-# include <sys/sem.h>
-#endif
-
-QT_BEGIN_NAMESPACE
-
-#if QT_CONFIG(systemsemaphore)
-/*!
- Helper class
- */
-class QSharedMemoryLocker
-{
-
-public:
- inline QSharedMemoryLocker(QSharedMemory *sharedMemory) : q_sm(sharedMemory)
- {
- Q_ASSERT(q_sm);
- }
-
- inline ~QSharedMemoryLocker()
- {
- if (q_sm)
- q_sm->unlock();
- }
-
- inline bool lock()
- {
- if (q_sm && q_sm->lock())
- return true;
- q_sm = nullptr;
- return false;
- }
-
-private:
- QSharedMemory *q_sm;
-};
-#endif // QT_CONFIG(systemsemaphore)
-
-class Q_AUTOTEST_EXPORT QSharedMemoryPrivate : public QObjectPrivate
-{
- Q_DECLARE_PUBLIC(QSharedMemory)
-
-public:
- void *memory = nullptr;
- qsizetype size = 0;
- QString key;
- QString nativeKey;
- QSharedMemory::SharedMemoryError error = QSharedMemory::NoError;
- QString errorString;
-#if QT_CONFIG(systemsemaphore)
- QSystemSemaphore systemSemaphore{QString()};
- bool lockedByMe = false;
-#endif
-
- static int createUnixKeyFile(const QString &fileName);
- static QString makePlatformSafeKey(const QString &key,
- const QString &prefix = QStringLiteral("qipc_sharedmemory_"));
-#ifdef Q_OS_WIN
- Qt::HANDLE handle();
-#elif defined(QT_POSIX_IPC)
- int handle();
-#else
- key_t handle();
-#endif
- bool initKey();
- bool cleanHandle();
- bool create(qsizetype size);
- bool attach(QSharedMemory::AccessMode mode);
- bool detach();
-
- void setErrorString(QLatin1StringView function);
-
-#if QT_CONFIG(systemsemaphore)
- bool tryLocker(QSharedMemoryLocker *locker, const QString &function) {
- if (!locker->lock()) {
- errorString = QSharedMemory::tr("%1: unable to lock").arg(function);
- error = QSharedMemory::LockError;
- return false;
- }
- return true;
- }
-#endif // QT_CONFIG(systemsemaphore)
-
-private:
-#ifdef Q_OS_WIN
- Qt::HANDLE hand = nullptr;
-#elif defined(QT_POSIX_IPC)
- int hand = -1;
-#else
- key_t unix_key = 0;
-#endif
-};
-
-QT_END_NAMESPACE
-
-#endif // QT_CONFIG(sharedmemory)
-
-#endif // QSHAREDMEMORY_P_H
-
diff --git a/src/corelib/kernel/qsharedmemory_posix.cpp b/src/corelib/kernel/qsharedmemory_posix.cpp
deleted file mode 100644
index ac316b9d12..0000000000
--- a/src/corelib/kernel/qsharedmemory_posix.cpp
+++ /dev/null
@@ -1,200 +0,0 @@
-// Copyright (C) 2015 Konstantin Ritt <ritt.ks@gmail.com>
-// Copyright (C) 2016 The Qt Company Ltd.
-// Copyright (C) 2015 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Tobias Koenig <tobias.koenig@kdab.com>
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
-
-#include "qplatformdefs.h"
-
-#include "qsharedmemory.h"
-#include "qsharedmemory_p.h"
-#include "qsystemsemaphore.h"
-#include <qfile.h>
-
-#include <errno.h>
-
-#ifdef QT_POSIX_IPC
-
-#if QT_CONFIG(sharedmemory)
-#include <sys/types.h>
-#include <sys/mman.h>
-#include <sys/stat.h>
-#include <fcntl.h>
-#include <unistd.h>
-
-#include "private/qcore_unix_p.h"
-
-QT_BEGIN_NAMESPACE
-
-using namespace Qt::StringLiterals;
-
-int QSharedMemoryPrivate::handle()
-{
- // don't allow making handles on empty keys
- const QString safeKey = makePlatformSafeKey(key);
- if (safeKey.isEmpty()) {
- errorString = QSharedMemory::tr("%1: key is empty").arg("QSharedMemory::handle"_L1);
- error = QSharedMemory::KeyError;
- return 0;
- }
-
- return 1;
-}
-
-bool QSharedMemoryPrivate::cleanHandle()
-{
- qt_safe_close(hand);
- hand = -1;
-
- return true;
-}
-
-bool QSharedMemoryPrivate::create(qsizetype size)
-{
- if (!handle())
- return false;
-
- const QByteArray shmName = QFile::encodeName(makePlatformSafeKey(key));
-
- int fd;
-#ifdef O_CLOEXEC
- // First try with O_CLOEXEC flag, if that fails, fall back to normal flags
- EINTR_LOOP(fd, ::shm_open(shmName.constData(), O_RDWR | O_CREAT | O_EXCL | O_CLOEXEC, 0600));
- if (fd == -1)
- EINTR_LOOP(fd, ::shm_open(shmName.constData(), O_RDWR | O_CREAT | O_EXCL, 0600));
-#else
- EINTR_LOOP(fd, ::shm_open(shmName.constData(), O_RDWR | O_CREAT | O_EXCL, 0600));
-#endif
- if (fd == -1) {
- const int errorNumber = errno;
- const auto function = "QSharedMemory::attach (shm_open)"_L1;
- switch (errorNumber) {
- case EINVAL:
- errorString = QSharedMemory::tr("%1: bad name").arg(function);
- error = QSharedMemory::KeyError;
- break;
- default:
- setErrorString(function);
- }
- return false;
- }
-
- // the size may only be set once
- int ret;
- EINTR_LOOP(ret, QT_FTRUNCATE(fd, size));
- if (ret == -1) {
- setErrorString("QSharedMemory::create (ftruncate)"_L1);
- qt_safe_close(fd);
- return false;
- }
-
- qt_safe_close(fd);
-
- return true;
-}
-
-bool QSharedMemoryPrivate::attach(QSharedMemory::AccessMode mode)
-{
- const QByteArray shmName = QFile::encodeName(makePlatformSafeKey(key));
-
- const int oflag = (mode == QSharedMemory::ReadOnly ? O_RDONLY : O_RDWR);
- const mode_t omode = (mode == QSharedMemory::ReadOnly ? 0400 : 0600);
-
-#ifdef O_CLOEXEC
- // First try with O_CLOEXEC flag, if that fails, fall back to normal flags
- EINTR_LOOP(hand, ::shm_open(shmName.constData(), oflag | O_CLOEXEC, omode));
- if (hand == -1)
- EINTR_LOOP(hand, ::shm_open(shmName.constData(), oflag, omode));
-#else
- EINTR_LOOP(hand, ::shm_open(shmName.constData(), oflag, omode));
-#endif
- if (hand == -1) {
- const int errorNumber = errno;
- const auto function = "QSharedMemory::attach (shm_open)"_L1;
- switch (errorNumber) {
- case EINVAL:
- errorString = QSharedMemory::tr("%1: bad name").arg(function);
- error = QSharedMemory::KeyError;
- break;
- default:
- setErrorString(function);
- }
- hand = -1;
- return false;
- }
-
- // grab the size
- QT_STATBUF st;
- if (QT_FSTAT(hand, &st) == -1) {
- setErrorString("QSharedMemory::attach (fstat)"_L1);
- cleanHandle();
- return false;
- }
- size = qsizetype(st.st_size);
-
- // grab the memory
- const int mprot = (mode == QSharedMemory::ReadOnly ? PROT_READ : PROT_READ | PROT_WRITE);
- memory = QT_MMAP(0, size_t(size), mprot, MAP_SHARED, hand, 0);
- if (memory == MAP_FAILED || !memory) {
- setErrorString("QSharedMemory::attach (mmap)"_L1);
- cleanHandle();
- memory = 0;
- size = 0;
- return false;
- }
-
-#ifdef F_ADD_SEALS
- // Make sure the shared memory region will not shrink
- // otherwise someone could cause SIGBUS on us.
- // (see http://lwn.net/Articles/594919/)
- fcntl(hand, F_ADD_SEALS, F_SEAL_SHRINK);
-#endif
-
- return true;
-}
-
-bool QSharedMemoryPrivate::detach()
-{
- // detach from the memory segment
- if (::munmap(memory, size_t(size)) == -1) {
- setErrorString("QSharedMemory::detach (munmap)"_L1);
- return false;
- }
- memory = 0;
- size = 0;
-
-#ifdef Q_OS_QNX
- // On QNX the st_nlink field of struct stat contains the number of
- // active shm_open() connections to the shared memory file, so we
- // can use it to automatically clean up the file once the last
- // user has detached from it.
-
- // get the number of current attachments
- int shm_nattch = 0;
- QT_STATBUF st;
- if (QT_FSTAT(hand, &st) == 0) {
- // subtract 2 from linkcount: one for our own open and one for the dir entry
- shm_nattch = st.st_nlink - 2;
- }
-
- cleanHandle();
-
- // if there are no attachments then unlink the shared memory
- if (shm_nattch == 0) {
- const QByteArray shmName = QFile::encodeName(makePlatformSafeKey(key));
- if (::shm_unlink(shmName.constData()) == -1 && errno != ENOENT)
- setErrorString("QSharedMemory::detach (shm_unlink)"_L1);
- }
-#else
- // On non-QNX systems (tested Linux and Haiku), the st_nlink field is always 1,
- // so we'll simply leak the shared memory files.
- cleanHandle();
-#endif
-
- return true;
-}
-
-QT_END_NAMESPACE
-
-#endif // QT_CONFIG(sharedmemory)
-
-#endif // QT_POSIX_IPC
diff --git a/src/corelib/kernel/qsharedmemory_systemv.cpp b/src/corelib/kernel/qsharedmemory_systemv.cpp
deleted file mode 100644
index de6b40746c..0000000000
--- a/src/corelib/kernel/qsharedmemory_systemv.cpp
+++ /dev/null
@@ -1,226 +0,0 @@
-// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
-
-#include "qplatformdefs.h"
-
-#include "qsharedmemory.h"
-#include "qsharedmemory_p.h"
-#include "qsystemsemaphore.h"
-#include <qdir.h>
-#include <qdebug.h>
-
-#include <errno.h>
-
-#ifndef QT_POSIX_IPC
-
-#if QT_CONFIG(sharedmemory)
-#include <sys/types.h>
-#include <sys/ipc.h>
-#include <sys/shm.h>
-#include <sys/stat.h>
-#include <fcntl.h>
-#include <unistd.h>
-#endif // QT_CONFIG(sharedmemory)
-
-#include "private/qcore_unix_p.h"
-
-#if QT_CONFIG(sharedmemory)
-QT_BEGIN_NAMESPACE
-
-using namespace Qt::StringLiterals;
-
-/*!
- \internal
-
- If not already made create the handle used for accessing the shared memory.
-*/
-key_t QSharedMemoryPrivate::handle()
-{
- // already made
- if (unix_key)
- return unix_key;
-
- // don't allow making handles on empty keys
- if (nativeKey.isEmpty()) {
- errorString = QSharedMemory::tr("%1: key is empty").arg("QSharedMemory::handle:"_L1);
- error = QSharedMemory::KeyError;
- return 0;
- }
-
- // ftok requires that an actual file exists somewhere
- if (!QFile::exists(nativeKey)) {
- errorString = QSharedMemory::tr("%1: UNIX key file doesn't exist").arg("QSharedMemory::handle:"_L1);
- error = QSharedMemory::NotFound;
- return 0;
- }
-
- unix_key = ftok(QFile::encodeName(nativeKey).constData(), 'Q');
- if (-1 == unix_key) {
- errorString = QSharedMemory::tr("%1: ftok failed").arg("QSharedMemory::handle:"_L1);
- error = QSharedMemory::KeyError;
- unix_key = 0;
- }
- return unix_key;
-}
-
-#endif // QT_CONFIG(sharedmemory)
-
-#if QT_CONFIG(sharedmemory) || QT_CONFIG(systemsemaphore)
-/*!
- \internal
- Creates the unix file if needed.
- returns \c true if the unix file was created.
-
- -1 error
- 0 already existed
- 1 created
- */
-int QT_PREPEND_NAMESPACE(QSharedMemoryPrivate)::createUnixKeyFile(const QString &fileName)
-{
- int fd = qt_safe_open(QFile::encodeName(fileName).constData(),
- O_EXCL | O_CREAT | O_RDWR, 0640);
- if (-1 == fd) {
- if (errno == EEXIST)
- return 0;
- return -1;
- } else {
- close(fd);
- }
- return 1;
-}
-#endif // QT_CONFIG(sharedmemory) || QT_CONFIG(systemsemaphore)
-
-#if QT_CONFIG(sharedmemory)
-
-bool QSharedMemoryPrivate::cleanHandle()
-{
- unix_key = 0;
- return true;
-}
-
-bool QSharedMemoryPrivate::create(qsizetype size)
-{
- // build file if needed
- bool createdFile = false;
- int built = createUnixKeyFile(nativeKey);
- if (built == -1) {
- errorString = QSharedMemory::tr("%1: unable to make key").arg("QSharedMemory::handle:"_L1);
- error = QSharedMemory::KeyError;
- return false;
- }
- if (built == 1) {
- createdFile = true;
- }
-
- // get handle
- if (!handle()) {
- if (createdFile)
- QFile::remove(nativeKey);
- return false;
- }
-
- // create
- if (-1 == shmget(unix_key, size_t(size), 0600 | IPC_CREAT | IPC_EXCL)) {
- const auto function = "QSharedMemory::create"_L1;
- switch (errno) {
- case EINVAL:
- errorString = QSharedMemory::tr("%1: system-imposed size restrictions").arg("QSharedMemory::handle"_L1);
- error = QSharedMemory::InvalidSize;
- break;
- default:
- setErrorString(function);
- }
- if (createdFile && error != QSharedMemory::AlreadyExists)
- QFile::remove(nativeKey);
- return false;
- }
-
- return true;
-}
-
-bool QSharedMemoryPrivate::attach(QSharedMemory::AccessMode mode)
-{
- // grab the shared memory segment id
- int id = shmget(unix_key, 0, (mode == QSharedMemory::ReadOnly ? 0400 : 0600));
- if (-1 == id) {
- setErrorString("QSharedMemory::attach (shmget)"_L1);
- return false;
- }
-
- // grab the memory
- memory = shmat(id, nullptr, (mode == QSharedMemory::ReadOnly ? SHM_RDONLY : 0));
- if ((void *)-1 == memory) {
- memory = nullptr;
- setErrorString("QSharedMemory::attach (shmat)"_L1);
- return false;
- }
-
- // grab the size
- shmid_ds shmid_ds;
- if (!shmctl(id, IPC_STAT, &shmid_ds)) {
- size = (qsizetype)shmid_ds.shm_segsz;
- } else {
- setErrorString("QSharedMemory::attach (shmctl)"_L1);
- return false;
- }
-
- return true;
-}
-
-bool QSharedMemoryPrivate::detach()
-{
- // detach from the memory segment
- if (-1 == shmdt(memory)) {
- const auto function = "QSharedMemory::detach"_L1;
- switch (errno) {
- case EINVAL:
- errorString = QSharedMemory::tr("%1: not attached").arg(function);
- error = QSharedMemory::NotFound;
- break;
- default:
- setErrorString(function);
- }
- return false;
- }
- memory = nullptr;
- size = 0;
-
- // Get the number of current attachments
- int id = shmget(unix_key, 0, 0400);
- cleanHandle();
-
- struct shmid_ds shmid_ds;
- if (0 != shmctl(id, IPC_STAT, &shmid_ds)) {
- switch (errno) {
- case EINVAL:
- return true;
- default:
- return false;
- }
- }
- // If there are no attachments then remove it.
- if (shmid_ds.shm_nattch == 0) {
- // mark for removal
- if (-1 == shmctl(id, IPC_RMID, &shmid_ds)) {
- setErrorString("QSharedMemory::remove"_L1);
- switch (errno) {
- case EINVAL:
- return true;
- default:
- return false;
- }
- }
-
- // remove file
- if (!QFile::remove(nativeKey))
- return false;
- }
- return true;
-}
-
-
-QT_END_NAMESPACE
-
-#endif // QT_CONFIG(sharedmemory)
-
-#endif // QT_POSIX_IPC
diff --git a/src/corelib/kernel/qsharedmemory_unix.cpp b/src/corelib/kernel/qsharedmemory_unix.cpp
deleted file mode 100644
index 0696c3fe1d..0000000000
--- a/src/corelib/kernel/qsharedmemory_unix.cpp
+++ /dev/null
@@ -1,64 +0,0 @@
-// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
-
-#include "qplatformdefs.h"
-
-#include "qsharedmemory.h"
-#include "qsharedmemory_p.h"
-#include "qsystemsemaphore.h"
-#include <qdebug.h>
-
-#include <errno.h>
-
-#if QT_CONFIG(sharedmemory)
-#include <sys/types.h>
-#ifndef QT_POSIX_IPC
-#include <sys/ipc.h>
-#include <sys/shm.h>
-#else
-#include <sys/mman.h>
-#endif
-#include <sys/stat.h>
-#include <fcntl.h>
-#include <unistd.h>
-#endif // QT_CONFIG(sharedmemory)
-
-#include "private/qcore_unix_p.h"
-
-#if QT_CONFIG(sharedmemory)
-QT_BEGIN_NAMESPACE
-
-void QSharedMemoryPrivate::setErrorString(QLatin1StringView function)
-{
- // EINVAL is handled in functions so they can give better error strings
- switch (errno) {
- case EACCES:
- errorString = QSharedMemory::tr("%1: permission denied").arg(function);
- error = QSharedMemory::PermissionDenied;
- break;
- case EEXIST:
- errorString = QSharedMemory::tr("%1: already exists").arg(function);
- error = QSharedMemory::AlreadyExists;
- break;
- case ENOENT:
- errorString = QSharedMemory::tr("%1: doesn't exist").arg(function);
- error = QSharedMemory::NotFound;
- break;
- case EMFILE:
- case ENOMEM:
- case ENOSPC:
- errorString = QSharedMemory::tr("%1: out of resources").arg(function);
- error = QSharedMemory::OutOfResources;
- break;
- default:
- errorString = QSharedMemory::tr("%1: unknown error %2").arg(function).arg(errno);
- error = QSharedMemory::UnknownError;
-#if defined QSHAREDMEMORY_DEBUG
- qDebug() << errorString << "key" << key << "errno" << errno << EINVAL;
-#endif
- }
-}
-
-QT_END_NAMESPACE
-
-#endif // QT_CONFIG(sharedmemory)
diff --git a/src/corelib/kernel/qsharedmemory_win.cpp b/src/corelib/kernel/qsharedmemory_win.cpp
deleted file mode 100644
index bf8c625b39..0000000000
--- a/src/corelib/kernel/qsharedmemory_win.cpp
+++ /dev/null
@@ -1,147 +0,0 @@
-// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
-
-#include "qsharedmemory.h"
-#include "qsharedmemory_p.h"
-#include "qsystemsemaphore.h"
-#include <qdebug.h>
-#include <qt_windows.h>
-
-QT_BEGIN_NAMESPACE
-
-using namespace Qt::StringLiterals;
-
-#if QT_CONFIG(sharedmemory)
-
-void QSharedMemoryPrivate::setErrorString(QLatin1StringView function)
-{
- DWORD windowsError = GetLastError();
- if (windowsError == 0)
- return;
- switch (windowsError) {
- case ERROR_ALREADY_EXISTS:
- error = QSharedMemory::AlreadyExists;
- errorString = QSharedMemory::tr("%1: already exists").arg(function);
- break;
- case ERROR_FILE_NOT_FOUND:
- error = QSharedMemory::NotFound;
- errorString = QSharedMemory::tr("%1: doesn't exist").arg(function);
- break;
- case ERROR_COMMITMENT_LIMIT:
- error = QSharedMemory::InvalidSize;
- errorString = QSharedMemory::tr("%1: invalid size").arg(function);
- break;
- case ERROR_NO_SYSTEM_RESOURCES:
- case ERROR_NOT_ENOUGH_MEMORY:
- error = QSharedMemory::OutOfResources;
- errorString = QSharedMemory::tr("%1: out of resources").arg(function);
- break;
- case ERROR_ACCESS_DENIED:
- error = QSharedMemory::PermissionDenied;
- errorString = QSharedMemory::tr("%1: permission denied").arg(function);
- break;
- default:
- errorString = QSharedMemory::tr("%1: unknown error %2").arg(function).arg(windowsError);
- error = QSharedMemory::UnknownError;
-#if defined QSHAREDMEMORY_DEBUG
- qDebug() << errorString << "key" << key;
-#endif
- }
-}
-
-HANDLE QSharedMemoryPrivate::handle()
-{
- if (!hand) {
- const auto function = "QSharedMemory::handle"_L1;
- if (nativeKey.isEmpty()) {
- error = QSharedMemory::KeyError;
- errorString = QSharedMemory::tr("%1: unable to make key").arg(function);
- return 0;
- }
- hand = OpenFileMapping(FILE_MAP_ALL_ACCESS, false,
- reinterpret_cast<const wchar_t *>(nativeKey.utf16()));
- if (!hand) {
- setErrorString(function);
- return 0;
- }
- }
- return hand;
-}
-
-bool QSharedMemoryPrivate::cleanHandle()
-{
- if (hand != 0 && !CloseHandle(hand)) {
- hand = 0;
- setErrorString("QSharedMemory::cleanHandle"_L1);
- return false;
- }
- hand = 0;
- return true;
-}
-
-bool QSharedMemoryPrivate::create(qsizetype size)
-{
- const auto function = "QSharedMemory::create"_L1;
- if (nativeKey.isEmpty()) {
- error = QSharedMemory::KeyError;
- errorString = QSharedMemory::tr("%1: key error").arg(function);
- return false;
- }
-
- // Create the file mapping.
- DWORD high, low;
- if constexpr (sizeof(qsizetype) == 8)
- high = DWORD(quint64(size) >> 32);
- else
- high = 0;
- low = DWORD(size_t(size) & 0xffffffff);
- hand = CreateFileMapping(INVALID_HANDLE_VALUE, 0, PAGE_READWRITE, high, low,
- reinterpret_cast<const wchar_t *>(nativeKey.utf16()));
- setErrorString(function);
-
- // hand is valid when it already exists unlike unix so explicitly check
- return error != QSharedMemory::AlreadyExists && hand;
-}
-
-bool QSharedMemoryPrivate::attach(QSharedMemory::AccessMode mode)
-{
- // Grab a pointer to the memory block
- int permissions = (mode == QSharedMemory::ReadOnly ? FILE_MAP_READ : FILE_MAP_ALL_ACCESS);
- memory = (void *)MapViewOfFile(handle(), permissions, 0, 0, 0);
- if (0 == memory) {
- setErrorString("QSharedMemory::attach"_L1);
- cleanHandle();
- return false;
- }
-
- // Grab the size of the memory we have been given (a multiple of 4K on windows)
- MEMORY_BASIC_INFORMATION info;
- if (!VirtualQuery(memory, &info, sizeof(info))) {
- // Windows doesn't set an error code on this one,
- // it should only be a kernel memory error.
- error = QSharedMemory::UnknownError;
- errorString = QSharedMemory::tr("%1: size query failed").arg("QSharedMemory::attach: "_L1);
- return false;
- }
- size = qsizetype(info.RegionSize);
-
- return true;
-}
-
-bool QSharedMemoryPrivate::detach()
-{
- // umap memory
- if (!UnmapViewOfFile(memory)) {
- setErrorString("QSharedMemory::detach"_L1);
- return false;
- }
- memory = 0;
- size = 0;
-
- // close handle
- return cleanHandle();
-}
-
-#endif // QT_CONFIG(sharedmemory)
-
-QT_END_NAMESPACE
diff --git a/src/corelib/kernel/qsignalmapper.cpp b/src/corelib/kernel/qsignalmapper.cpp
index 2a3b8149d1..65d766db4a 100644
--- a/src/corelib/kernel/qsignalmapper.cpp
+++ b/src/corelib/kernel/qsignalmapper.cpp
@@ -13,11 +13,6 @@ class QSignalMapperPrivate : public QObjectPrivate
{
Q_DECLARE_PUBLIC(QSignalMapper)
public:
- void _q_senderDestroyed()
- {
- Q_Q(QSignalMapper);
- q->removeMappings(q->sender());
- }
template <class Signal, class Container>
void emitMappedValue(QObject *sender, Signal signal, const Container &mappedValues)
@@ -129,7 +124,7 @@ void QSignalMapper::setMapping(QObject *sender, int id)
{
Q_D(QSignalMapper);
d->intHash.insert(sender, id);
- connect(sender, SIGNAL(destroyed()), this, SLOT(_q_senderDestroyed()));
+ connect(sender, &QObject::destroyed, this, &QSignalMapper::removeMappings);
}
/*!
@@ -142,7 +137,7 @@ void QSignalMapper::setMapping(QObject *sender, const QString &text)
{
Q_D(QSignalMapper);
d->stringHash.insert(sender, text);
- connect(sender, SIGNAL(destroyed()), this, SLOT(_q_senderDestroyed()));
+ connect(sender, &QObject::destroyed, this, &QSignalMapper::removeMappings);
}
/*!
@@ -155,7 +150,7 @@ void QSignalMapper::setMapping(QObject *sender, QObject *object)
{
Q_D(QSignalMapper);
d->objectHash.insert(sender, object);
- connect(sender, SIGNAL(destroyed()), this, SLOT(_q_senderDestroyed()));
+ connect(sender, &QObject::destroyed, this, &QSignalMapper::removeMappings);
}
/*!
diff --git a/src/corelib/kernel/qsignalmapper.h b/src/corelib/kernel/qsignalmapper.h
index 3c3005dabd..af0be52ee5 100644
--- a/src/corelib/kernel/qsignalmapper.h
+++ b/src/corelib/kernel/qsignalmapper.h
@@ -38,7 +38,6 @@ public Q_SLOTS:
private:
Q_DISABLE_COPY(QSignalMapper)
- Q_PRIVATE_SLOT(d_func(), void _q_senderDestroyed())
};
QT_END_NAMESPACE
diff --git a/src/corelib/kernel/qsingleshottimer_p.h b/src/corelib/kernel/qsingleshottimer_p.h
new file mode 100644
index 0000000000..dd1402f63a
--- /dev/null
+++ b/src/corelib/kernel/qsingleshottimer_p.h
@@ -0,0 +1,141 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// Copyright (C) 2016 Intel Corporation.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QSINGLESHOTTIMER_P_H
+#define QSINGLESHOTTIMER_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 "qabstracteventdispatcher.h"
+#include "qcoreapplication.h"
+#include "qmetaobject_p.h"
+#include "private/qnumeric_p.h"
+
+#include <chrono>
+
+QT_BEGIN_NAMESPACE
+
+class QSingleShotTimer : public QObject
+{
+ Q_OBJECT
+
+ Qt::TimerId timerId = Qt::TimerId::Invalid;
+
+public:
+ // use the same duration type
+ using Duration = QAbstractEventDispatcher::Duration;
+
+ inline ~QSingleShotTimer();
+ inline QSingleShotTimer(Duration interval, Qt::TimerType timerType,
+ const QObject *r, const char *member);
+ inline QSingleShotTimer(Duration interval, Qt::TimerType timerType,
+ const QObject *r, QtPrivate::QSlotObjectBase *slotObj);
+
+ inline void startTimerForReceiver(Duration interval, Qt::TimerType timerType,
+ const QObject *receiver);
+
+ static Duration fromMsecs(std::chrono::milliseconds ms)
+ {
+ using namespace std::chrono;
+ using ratio = std::ratio_divide<std::milli, Duration::period>;
+ static_assert(ratio::den == 1);
+
+ Duration::rep r;
+ if (qMulOverflow<ratio::num>(ms.count(), &r)) {
+ qWarning("QTimer::singleShot(std::chrono::milliseconds, ...): "
+ "interval argument overflowed when converted to nanoseconds.");
+ return Duration::max();
+ }
+ return Duration{r};
+ }
+Q_SIGNALS:
+ void timeout();
+
+private:
+ inline void timerEvent(QTimerEvent *) override;
+};
+
+QSingleShotTimer::QSingleShotTimer(Duration interval, Qt::TimerType timerType,
+ const QObject *r, const char *member)
+ : QObject(QAbstractEventDispatcher::instance())
+{
+ connect(this, SIGNAL(timeout()), r, member);
+ startTimerForReceiver(interval, timerType, r);
+}
+
+QSingleShotTimer::QSingleShotTimer(Duration interval, Qt::TimerType timerType,
+ const QObject *r, QtPrivate::QSlotObjectBase *slotObj)
+ : QObject(QAbstractEventDispatcher::instance())
+{
+ int signal_index = QMetaObjectPrivate::signalOffset(&staticMetaObject);
+ Q_ASSERT(QMetaObjectPrivate::signal(&staticMetaObject, signal_index).name() == "timeout");
+ QObjectPrivate::connectImpl(this, signal_index, r ? r : this, nullptr, slotObj,
+ Qt::AutoConnection, nullptr, &staticMetaObject);
+
+ startTimerForReceiver(interval, timerType, r);
+}
+
+QSingleShotTimer::~QSingleShotTimer()
+{
+ if (timerId > Qt::TimerId::Invalid)
+ killTimer(timerId);
+}
+
+/*
+ Move the timer, and the dispatching and handling of the timer event, into
+ the same thread as where it will be handled, so that it fires reliably even
+ if the thread that set up the timer is busy.
+*/
+void QSingleShotTimer::startTimerForReceiver(Duration interval, Qt::TimerType timerType,
+ const QObject *receiver)
+{
+ if (receiver && receiver->thread() != thread()) {
+ // Avoid leaking the QSingleShotTimer instance in case the application exits before the
+ // timer fires
+ connect(QCoreApplication::instance(), &QCoreApplication::aboutToQuit, this,
+ &QObject::deleteLater);
+ setParent(nullptr);
+ moveToThread(receiver->thread());
+
+ QDeadlineTimer deadline(interval, timerType);
+ auto invokable = [this, deadline, timerType] {
+ if (deadline.hasExpired()) {
+ Q_EMIT timeout();
+ } else {
+ timerId = Qt::TimerId{startTimer(deadline.remainingTimeAsDuration(), timerType)};
+ }
+ };
+ QMetaObject::invokeMethod(this, invokable, Qt::QueuedConnection);
+ } else {
+ timerId = Qt::TimerId{startTimer(interval, timerType)};
+ }
+}
+
+void QSingleShotTimer::timerEvent(QTimerEvent *)
+{
+ // need to kill the timer _before_ we emit timeout() in case the
+ // slot connected to timeout calls processEvents()
+ if (timerId > Qt::TimerId::Invalid)
+ killTimer(std::exchange(timerId, Qt::TimerId::Invalid));
+
+ Q_EMIT timeout();
+
+ // we would like to use delete later here, but it feels like a
+ // waste to post a new event to handle this event, so we just unset the flag
+ // and explicitly delete...
+ delete this;
+}
+
+QT_END_NAMESPACE
+
+#endif // QSINGLESHOTTIMER_P_H
diff --git a/src/corelib/kernel/qsocketnotifier.cpp b/src/corelib/kernel/qsocketnotifier.cpp
index 4c5b3812a0..4e4cf3666b 100644
--- a/src/corelib/kernel/qsocketnotifier.cpp
+++ b/src/corelib/kernel/qsocketnotifier.cpp
@@ -16,6 +16,7 @@
#include <private/qthread_p.h>
#include <QtCore/QLoggingCategory>
+#include <QtCore/qpointer.h>
QT_BEGIN_NAMESPACE
diff --git a/src/corelib/kernel/qsocketnotifier.h b/src/corelib/kernel/qsocketnotifier.h
index 8288a6b2b5..ac9e577ebc 100644
--- a/src/corelib/kernel/qsocketnotifier.h
+++ b/src/corelib/kernel/qsocketnotifier.h
@@ -83,20 +83,20 @@ public:
Q_DECL_CONSTEXPR_NOT_WIN bool isValid() const noexcept { return *this != QSocketDescriptor(); }
- friend Q_DECL_CONSTEXPR_NOT_WIN bool operator==(QSocketDescriptor lhs,
- QSocketDescriptor rhs) noexcept
+private:
+ friend Q_DECL_CONSTEXPR_NOT_WIN bool comparesEqual(const QSocketDescriptor &lhs,
+ const QSocketDescriptor &rhs) noexcept
{
return lhs.sockfd == rhs.sockfd;
}
- friend Q_DECL_CONSTEXPR_NOT_WIN bool operator!=(QSocketDescriptor lhs,
- QSocketDescriptor rhs) noexcept
- {
- return lhs.sockfd != rhs.sockfd;
- }
+#if defined(Q_OS_WIN) || defined(Q_QDOC)
+ Q_DECLARE_EQUALITY_COMPARABLE(QSocketDescriptor)
+#else
+ Q_DECLARE_EQUALITY_COMPARABLE_LITERAL_TYPE(QSocketDescriptor)
+#endif
#undef Q_DECL_CONSTEXPR_NOT_WIN
-private:
DescriptorType sockfd;
};
diff --git a/src/corelib/kernel/qsystemerror_p.h b/src/corelib/kernel/qsystemerror_p.h
index 66c434cb13..72ced63dc5 100644
--- a/src/corelib/kernel/qsystemerror_p.h
+++ b/src/corelib/kernel/qsystemerror_p.h
@@ -40,12 +40,19 @@ public:
constexpr ErrorScope scope() const { return errorScope; }
constexpr int error() const { return errorCode; }
+ constexpr bool ok() const noexcept { return errorScope == NoError; }
+ static constexpr QSystemError stdError(int error)
+ { return QSystemError(error, StandardLibraryError); }
+
static Q_CORE_EXPORT QString string(ErrorScope errorScope, int errorCode);
static Q_CORE_EXPORT QString stdString(int errorCode = -1);
#ifdef Q_OS_WIN
static Q_CORE_EXPORT QString windowsString(int errorCode = -1);
using HRESULT = long;
static Q_CORE_EXPORT QString windowsComString(HRESULT hr);
+
+ static constexpr QSystemError nativeError(int error)
+ { return QSystemError(error, NativeError); }
#endif
// data members
diff --git a/src/corelib/kernel/qsystemsemaphore.cpp b/src/corelib/kernel/qsystemsemaphore.cpp
deleted file mode 100644
index c4505ea989..0000000000
--- a/src/corelib/kernel/qsystemsemaphore.cpp
+++ /dev/null
@@ -1,330 +0,0 @@
-// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
-
-#include "qsystemsemaphore.h"
-#include "qsystemsemaphore_p.h"
-#include <qglobal.h>
-
-QT_BEGIN_NAMESPACE
-
-#if QT_CONFIG(systemsemaphore)
-
-/*!
- \class QSystemSemaphore
- \inmodule QtCore
- \since 4.4
-
- \brief The QSystemSemaphore class provides a general counting system semaphore.
-
- A semaphore is a generalization of a mutex. While a mutex can be
- locked only once, a semaphore can be acquired multiple times.
- Typically, a semaphore is used to protect a certain number of
- identical resources.
-
- Like its lighter counterpart QSemaphore, a QSystemSemaphore can be
- accessed from multiple \l {QThread} {threads}. Unlike QSemaphore, a
- QSystemSemaphore can also be accessed from multiple \l {QProcess}
- {processes}. This means QSystemSemaphore is a much heavier class, so
- if your application doesn't need to access your semaphores across
- multiple processes, you will probably want to use QSemaphore.
-
- Semaphores support two fundamental operations, acquire() and release():
-
- acquire() tries to acquire one resource. If there isn't a resource
- available, the call blocks until a resource becomes available. Then
- the resource is acquired and the call returns.
-
- release() releases one resource so it can be acquired by another
- process. The function can also be called with a parameter n > 1,
- which releases n resources.
-
- A system semaphore is created with a string key that other processes
- can use to use the same semaphore.
-
- Example: Create a system semaphore
- \snippet code/src_corelib_kernel_qsystemsemaphore.cpp 0
-
- A typical application of system semaphores is for controlling access
- to a circular buffer shared by a producer process and a consumer
- processes.
-
- \section1 Platform-Specific Behavior
-
- When using this class, be aware of the following platform
- differences:
-
- \b{Windows:} QSystemSemaphore does not own its underlying system
- semaphore. Windows owns it. This means that when all instances of
- QSystemSemaphore for a particular key have been destroyed, either by
- having their destructors called, or because one or more processes
- crash, Windows removes the underlying system semaphore.
-
- \b{Unix:}
-
- \list
- \li QSystemSemaphore owns the underlying system semaphore
- in Unix systems. This means that the last process having an instance of
- QSystemSemaphore for a particular key must remove the underlying
- system semaphore in its destructor. If the last process crashes
- without running the QSystemSemaphore destructor, Unix does not
- automatically remove the underlying system semaphore, and the
- semaphore survives the crash. A subsequent process that constructs a
- QSystemSemaphore with the same key will then be given the existing
- system semaphore. In that case, if the QSystemSemaphore constructor
- has specified its \l {QSystemSemaphore::AccessMode} {access mode} as
- \l {QSystemSemaphore::} {Open}, its initial resource count will not
- be reset to the one provided but remain set to the value it received
- in the crashed process. To protect against this, the first process
- to create a semaphore for a particular key (usually a server), must
- pass its \l {QSystemSemaphore::AccessMode} {access mode} as \l
- {QSystemSemaphore::} {Create}, which will force Unix to reset the
- resource count in the underlying system semaphore.
-
- \li When a process using QSystemSemaphore terminates for
- any reason, Unix automatically reverses the effect of all acquire
- operations that were not released. Thus if the process acquires a
- resource and then exits without releasing it, Unix will release that
- resource.
-
- \endlist
-
- \b{Apple platforms:} Sandboxed applications (including apps
- shipped through the Apple App Store) require the key to
- be in the form \c {<application group identifier>/<custom identifier>},
- as documented \l {https://developer.apple.com/library/archive/documentation/Security/Conceptual/AppSandboxDesignGuide/AppSandboxInDepth/AppSandboxInDepth.html#//apple_ref/doc/uid/TP40011183-CH3-SW24}
- {here} and \l {https://developer.apple.com/documentation/bundleresources/entitlements/com_apple_security_application-groups}
- {here}, and the key length is limited to 30 characters.
-
- \sa QSharedMemory, QSemaphore
- */
-
-/*!
- Requests a system semaphore for the specified \a key. The parameters
- \a initialValue and \a mode are used according to the following
- rules, which are system dependent.
-
- In Unix, if the \a mode is \l {QSystemSemaphore::} {Open} and the
- system already has a semaphore identified by \a key, that semaphore
- is used, and the semaphore's resource count is not changed, i.e., \a
- initialValue is ignored. But if the system does not already have a
- semaphore identified by \a key, it creates a new semaphore for that
- key and sets its resource count to \a initialValue.
-
- In Unix, if the \a mode is \l {QSystemSemaphore::} {Create} and the
- system already has a semaphore identified by \a key, that semaphore
- is used, and its resource count is set to \a initialValue. If the
- system does not already have a semaphore identified by \a key, it
- creates a new semaphore for that key and sets its resource count to
- \a initialValue.
-
- In Windows, \a mode is ignored, and the system always tries to
- create a semaphore for the specified \a key. If the system does not
- already have a semaphore identified as \a key, it creates the
- semaphore and sets its resource count to \a initialValue. But if the
- system already has a semaphore identified as \a key it uses that
- semaphore and ignores \a initialValue.
-
- The \l {QSystemSemaphore::AccessMode} {mode} parameter is only used
- in Unix systems to handle the case where a semaphore survives a
- process crash. In that case, the next process to allocate a
- semaphore with the same \a key will get the semaphore that survived
- the crash, and unless \a mode is \l {QSystemSemaphore::} {Create},
- the resource count will not be reset to \a initialValue but will
- retain the initial value it had been given by the crashed process.
-
- \sa acquire(), key()
- */
-QSystemSemaphore::QSystemSemaphore(const QString &key, int initialValue, AccessMode mode)
- : d(new QSystemSemaphorePrivate)
-{
- setKey(key, initialValue, mode);
-}
-
-/*!
- The destructor destroys the QSystemSemaphore object, but the
- underlying system semaphore is not removed from the system unless
- this instance of QSystemSemaphore is the last one existing for that
- system semaphore.
-
- Two important side effects of the destructor depend on the system.
- In Windows, if acquire() has been called for this semaphore but not
- release(), release() will not be called by the destructor, nor will
- the resource be released when the process exits normally. This would
- be a program bug which could be the cause of a deadlock in another
- process trying to acquire the same resource. In Unix, acquired
- resources that are not released before the destructor is called are
- automatically released when the process exits.
-*/
-QSystemSemaphore::~QSystemSemaphore()
-{
- d->cleanHandle();
-}
-
-/*!
- \enum QSystemSemaphore::AccessMode
-
- This enum is used by the constructor and setKey(). Its purpose is to
- enable handling the problem in Unix implementations of semaphores
- that survive a crash. In Unix, when a semaphore survives a crash, we
- need a way to force it to reset its resource count, when the system
- reuses the semaphore. In Windows, where semaphores can't survive a
- crash, this enum has no effect.
-
- \value Open If the semaphore already exists, its initial resource
- count is not reset. If the semaphore does not already exist, it is
- created and its initial resource count set.
-
- \value Create QSystemSemaphore takes ownership of the semaphore and
- sets its resource count to the requested value, regardless of
- whether the semaphore already exists by having survived a crash.
- This value should be passed to the constructor, when the first
- semaphore for a particular key is constructed and you know that if
- the semaphore already exists it could only be because of a crash. In
- Windows, where a semaphore can't survive a crash, Create and Open
- have the same behavior.
-*/
-
-/*!
- This function works the same as the constructor. It reconstructs
- this QSystemSemaphore object. If the new \a key is different from
- the old key, calling this function is like calling the destructor of
- the semaphore with the old key, then calling the constructor to
- create a new semaphore with the new \a key. The \a initialValue and
- \a mode parameters are as defined for the constructor.
-
- \sa QSystemSemaphore(), key()
- */
-void QSystemSemaphore::setKey(const QString &key, int initialValue, AccessMode mode)
-{
- if (key == d->key && mode == Open)
- return;
- d->clearError();
-#if !defined(Q_OS_WIN) && !defined(QT_POSIX_IPC)
- // optimization to not destroy/create the file & semaphore
- if (key == d->key && mode == Create && d->createdSemaphore && d->createdFile) {
- d->initialValue = initialValue;
- d->unix_key = -1;
- d->handle(mode);
- return;
- }
-#endif
- d->cleanHandle();
- d->key = key;
- d->initialValue = initialValue;
- // cache the file name so it doesn't have to be generated all the time.
- d->fileName = d->makeKeyFileName();
- d->handle(mode);
-}
-
-/*!
- Returns the key assigned to this system semaphore. The key is the
- name by which the semaphore can be accessed from other processes.
-
- \sa setKey()
- */
-QString QSystemSemaphore::key() const
-{
- return d->key;
-}
-
-/*!
- Acquires one of the resources guarded by this semaphore, if there is
- one available, and returns \c true. If all the resources guarded by this
- semaphore have already been acquired, the call blocks until one of
- them is released by another process or thread having a semaphore
- with the same key.
-
- If false is returned, a system error has occurred. Call error()
- to get a value of QSystemSemaphore::SystemSemaphoreError that
- indicates which error occurred.
-
- \sa release()
- */
-bool QSystemSemaphore::acquire()
-{
- return d->modifySemaphore(-1);
-}
-
-/*!
- Releases \a n resources guarded by the semaphore. Returns \c true
- unless there is a system error.
-
- Example: Create a system semaphore having five resources; acquire
- them all and then release them all.
-
- \snippet code/src_corelib_kernel_qsystemsemaphore.cpp 1
-
- This function can also "create" resources. For example, immediately
- following the sequence of statements above, suppose we add the
- statement:
-
- \snippet code/src_corelib_kernel_qsystemsemaphore.cpp 2
-
- Ten new resources are now guarded by the semaphore, in addition to
- the five that already existed. You would not normally use this
- function to create more resources.
-
- \sa acquire()
- */
-bool QSystemSemaphore::release(int n)
-{
- if (n == 0)
- return true;
- if (n < 0) {
- qWarning("QSystemSemaphore::release: n is negative.");
- return false;
- }
- return d->modifySemaphore(n);
-}
-
-/*!
- Returns a value indicating whether an error occurred, and, if so,
- which error it was.
-
- \sa errorString()
- */
-QSystemSemaphore::SystemSemaphoreError QSystemSemaphore::error() const
-{
- return d->error;
-}
-
-/*!
- \enum QSystemSemaphore::SystemSemaphoreError
-
- \value NoError No error occurred.
-
- \value PermissionDenied The operation failed because the caller
- didn't have the required permissions.
-
- \value KeyError The operation failed because of an invalid key.
-
- \value AlreadyExists The operation failed because a system
- semaphore with the specified key already existed.
-
- \value NotFound The operation failed because a system semaphore
- with the specified key could not be found.
-
- \value OutOfResources The operation failed because there was
- not enough memory available to fill the request.
-
- \value UnknownError Something else happened and it was bad.
-*/
-
-/*!
- Returns a text description of the last error that occurred. If
- error() returns an \l {QSystemSemaphore::SystemSemaphoreError} {error
- value}, call this function to get a text string that describes the
- error.
-
- \sa error()
- */
-QString QSystemSemaphore::errorString() const
-{
- return d->errorString;
-}
-
-#endif // QT_CONFIG(systemsemaphore)
-
-QT_END_NAMESPACE
-
-#include "moc_qsystemsemaphore.cpp"
diff --git a/src/corelib/kernel/qsystemsemaphore.h b/src/corelib/kernel/qsystemsemaphore.h
deleted file mode 100644
index 7843ec8f04..0000000000
--- a/src/corelib/kernel/qsystemsemaphore.h
+++ /dev/null
@@ -1,63 +0,0 @@
-// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
-
-#ifndef QSYSTEMSEMAPHORE_H
-#define QSYSTEMSEMAPHORE_H
-
-#include <QtCore/qcoreapplication.h>
-#include <QtCore/qstring.h>
-#include <QtCore/qscopedpointer.h>
-
-QT_BEGIN_NAMESPACE
-
-
-#if QT_CONFIG(systemsemaphore)
-
-class QSystemSemaphorePrivate;
-
-class Q_CORE_EXPORT QSystemSemaphore
-{
- Q_GADGET
- Q_DECLARE_TR_FUNCTIONS(QSystemSemaphore)
-public:
- enum AccessMode
- {
- Open,
- Create
- };
- Q_ENUM(AccessMode)
-
- enum SystemSemaphoreError
- {
- NoError,
- PermissionDenied,
- KeyError,
- AlreadyExists,
- NotFound,
- OutOfResources,
- UnknownError
- };
-
- QSystemSemaphore(const QString &key, int initialValue = 0, AccessMode mode = Open);
- ~QSystemSemaphore();
-
- void setKey(const QString &key, int initialValue = 0, AccessMode mode = Open);
- QString key() const;
-
- bool acquire();
- bool release(int n = 1);
-
- SystemSemaphoreError error() const;
- QString errorString() const;
-
-private:
- Q_DISABLE_COPY(QSystemSemaphore)
- QScopedPointer<QSystemSemaphorePrivate> d;
-};
-
-#endif // QT_CONFIG(systemsemaphore)
-
-QT_END_NAMESPACE
-
-#endif // QSYSTEMSEMAPHORE_H
-
diff --git a/src/corelib/kernel/qsystemsemaphore_android.cpp b/src/corelib/kernel/qsystemsemaphore_android.cpp
deleted file mode 100644
index 5421dcbe8d..0000000000
--- a/src/corelib/kernel/qsystemsemaphore_android.cpp
+++ /dev/null
@@ -1,41 +0,0 @@
-// Copyright (C) 2012 Collabora Ltd, author <robin.burchell@collabora.co.uk>
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
-
-#include "qsystemsemaphore.h"
-#include "qsystemsemaphore_p.h"
-
-#include <qdebug.h>
-
-#if QT_CONFIG(systemsemaphore)
-
-QT_BEGIN_NAMESPACE
-
-void QSystemSemaphorePrivate::setErrorString(const QString &function)
-{
- Q_UNUSED(function);
- Q_UNIMPLEMENTED();
-}
-
-key_t QSystemSemaphorePrivate::handle(QSystemSemaphore::AccessMode mode)
-{
- Q_UNUSED(mode);
- Q_UNIMPLEMENTED();
- return -1;
-}
-
-void QSystemSemaphorePrivate::cleanHandle()
-{
- Q_UNIMPLEMENTED();
-}
-
-bool QSystemSemaphorePrivate::modifySemaphore(int count)
-{
- Q_UNUSED(count);
- Q_UNIMPLEMENTED();
- return false;
-}
-
-
-QT_END_NAMESPACE
-
-#endif // QT_CONFIG(systemsemaphore)
diff --git a/src/corelib/kernel/qsystemsemaphore_p.h b/src/corelib/kernel/qsystemsemaphore_p.h
deleted file mode 100644
index 47c9cdfe22..0000000000
--- a/src/corelib/kernel/qsystemsemaphore_p.h
+++ /dev/null
@@ -1,82 +0,0 @@
-// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
-
-#ifndef QSYSTEMSEMAPHORE_P_H
-#define QSYSTEMSEMAPHORE_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 "qsystemsemaphore.h"
-
-#if QT_CONFIG(systemsemaphore)
-
-#include "qcoreapplication.h"
-#include "qsharedmemory_p.h"
-#include <sys/types.h>
-#ifdef QT_POSIX_IPC
-# include <semaphore.h>
-#endif
-
-QT_BEGIN_NAMESPACE
-
-class QSystemSemaphorePrivate
-{
-
-public:
-
- QString makeKeyFileName()
- {
- return QSharedMemoryPrivate::makePlatformSafeKey(key, QLatin1StringView("qipc_systemsem_"));
- }
-
- inline void setError(QSystemSemaphore::SystemSemaphoreError e, const QString &message)
- { error = e; errorString = message; }
- inline void clearError()
- { setError(QSystemSemaphore::NoError, QString()); }
-
-#ifdef Q_OS_WIN
- Qt::HANDLE handle(QSystemSemaphore::AccessMode mode = QSystemSemaphore::Open);
- void setErrorString(const QString &function);
-#elif defined(QT_POSIX_IPC)
- bool handle(QSystemSemaphore::AccessMode mode = QSystemSemaphore::Open);
- void setErrorString(const QString &function);
-#else
- key_t handle(QSystemSemaphore::AccessMode mode = QSystemSemaphore::Open);
- void setErrorString(const QString &function);
-#endif
- void cleanHandle();
- bool modifySemaphore(int count);
-
- QString key;
- QString fileName;
- int initialValue;
-#ifdef Q_OS_WIN
- Qt::HANDLE semaphore = nullptr;
-#elif defined(QT_POSIX_IPC)
- sem_t *semaphore = SEM_FAILED;
- bool createdSemaphore = false;
-#else
- key_t unix_key = -1;
- int semaphore = -1;
- bool createdFile = false;
- bool createdSemaphore = false;
-#endif
- QString errorString;
- QSystemSemaphore::SystemSemaphoreError error = QSystemSemaphore::NoError;
-};
-
-QT_END_NAMESPACE
-
-#endif // QT_CONFIG(systemsemaphore)
-
-#endif // QSYSTEMSEMAPHORE_P_H
-
diff --git a/src/corelib/kernel/qsystemsemaphore_posix.cpp b/src/corelib/kernel/qsystemsemaphore_posix.cpp
deleted file mode 100644
index 4f3ad192b5..0000000000
--- a/src/corelib/kernel/qsystemsemaphore_posix.cpp
+++ /dev/null
@@ -1,152 +0,0 @@
-// Copyright (C) 2015 Konstantin Ritt <ritt.ks@gmail.com>
-// Copyright (C) 2016 The Qt Company Ltd.
-// Copyright (C) 2015 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Tobias Koenig <tobias.koenig@kdab.com>
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
-
-#include "qsystemsemaphore.h"
-#include "qsystemsemaphore_p.h"
-
-#include <qdebug.h>
-#include <qfile.h>
-#include <qcoreapplication.h>
-
-#ifdef QT_POSIX_IPC
-
-#if QT_CONFIG(systemsemaphore)
-
-#include <sys/types.h>
-#include <fcntl.h>
-#include <errno.h>
-
-#include "private/qcore_unix_p.h"
-
-// OpenBSD 4.2 doesn't define EIDRM, see BUGS section:
-// http://www.openbsd.org/cgi-bin/man.cgi?query=semop&manpath=OpenBSD+4.2
-#if defined(Q_OS_OPENBSD) && !defined(EIDRM)
-#define EIDRM EINVAL
-#endif
-
-QT_BEGIN_NAMESPACE
-
-using namespace Qt::StringLiterals;
-
-bool QSystemSemaphorePrivate::handle(QSystemSemaphore::AccessMode mode)
-{
- if (semaphore != SEM_FAILED)
- return true; // we already have a semaphore
-
- if (fileName.isEmpty()) {
- errorString = QSystemSemaphore::tr("%1: key is empty").arg("QSystemSemaphore::handle"_L1);
- error = QSystemSemaphore::KeyError;
- return false;
- }
-
- const QByteArray semName = QFile::encodeName(fileName);
-
- // Always try with O_EXCL so we know whether we created the semaphore.
- int oflag = O_CREAT | O_EXCL;
- for (int tryNum = 0, maxTries = 1; tryNum < maxTries; ++tryNum) {
- do {
- semaphore = ::sem_open(semName.constData(), oflag, 0600, initialValue);
- } while (semaphore == SEM_FAILED && errno == EINTR);
- if (semaphore == SEM_FAILED && errno == EEXIST) {
- if (mode == QSystemSemaphore::Create) {
- if (::sem_unlink(semName.constData()) == -1 && errno != ENOENT) {
- setErrorString("QSystemSemaphore::handle (sem_unlink)"_L1);
- return false;
- }
- // Race condition: the semaphore might be recreated before
- // we call sem_open again, so we'll retry several times.
- maxTries = 3;
- } else {
- // Race condition: if it no longer exists at the next sem_open
- // call, we won't realize we created it, so we'll leak it later.
- oflag &= ~O_EXCL;
- maxTries = 2;
- }
- } else {
- break;
- }
- }
-
- if (semaphore == SEM_FAILED) {
- setErrorString("QSystemSemaphore::handle"_L1);
- return false;
- }
-
- createdSemaphore = (oflag & O_EXCL) != 0;
-
- return true;
-}
-
-void QSystemSemaphorePrivate::cleanHandle()
-{
- if (semaphore != SEM_FAILED) {
- if (::sem_close(semaphore) == -1) {
- setErrorString("QSystemSemaphore::cleanHandle (sem_close)"_L1);
-#if defined QSYSTEMSEMAPHORE_DEBUG
- qDebug("QSystemSemaphore::cleanHandle sem_close failed.");
-#endif
- }
- semaphore = SEM_FAILED;
- }
-
- if (createdSemaphore) {
- if (::sem_unlink(QFile::encodeName(fileName).constData()) == -1 && errno != ENOENT) {
- setErrorString("QSystemSemaphore::cleanHandle (sem_unlink)"_L1);
-#if defined QSYSTEMSEMAPHORE_DEBUG
- qDebug("QSystemSemaphore::cleanHandle sem_unlink failed.");
-#endif
- }
- createdSemaphore = false;
- }
-}
-
-bool QSystemSemaphorePrivate::modifySemaphore(int count)
-{
- if (!handle())
- return false;
-
- if (count > 0) {
- int cnt = count;
- do {
- if (::sem_post(semaphore) == -1) {
- setErrorString("QSystemSemaphore::modifySemaphore (sem_post)"_L1);
-#if defined QSYSTEMSEMAPHORE_DEBUG
- qDebug("QSystemSemaphore::modify sem_post failed %d %d", count, errno);
-#endif
- // rollback changes to preserve the SysV semaphore behavior
- for ( ; cnt < count; ++cnt) {
- int res;
- EINTR_LOOP(res, ::sem_wait(semaphore));
- }
- return false;
- }
- --cnt;
- } while (cnt > 0);
- } else {
- int res;
- EINTR_LOOP(res, ::sem_wait(semaphore));
- if (res == -1) {
- // If the semaphore was removed be nice and create it and then modifySemaphore again
- if (errno == EINVAL || errno == EIDRM) {
- semaphore = SEM_FAILED;
- return modifySemaphore(count);
- }
- setErrorString("QSystemSemaphore::modifySemaphore (sem_wait)"_L1);
-#if defined QSYSTEMSEMAPHORE_DEBUG
- qDebug("QSystemSemaphore::modify sem_wait failed %d %d", count, errno);
-#endif
- return false;
- }
- }
-
- clearError();
- return true;
-}
-
-QT_END_NAMESPACE
-
-#endif // QT_CONFIG(systemsemaphore)
-
-#endif // QT_POSIX_IPC
diff --git a/src/corelib/kernel/qsystemsemaphore_systemv.cpp b/src/corelib/kernel/qsystemsemaphore_systemv.cpp
deleted file mode 100644
index 28992a0fb4..0000000000
--- a/src/corelib/kernel/qsystemsemaphore_systemv.cpp
+++ /dev/null
@@ -1,190 +0,0 @@
-// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
-
-#include "qsystemsemaphore.h"
-#include "qsystemsemaphore_p.h"
-
-#include <qdebug.h>
-#include <qfile.h>
-#include <qcoreapplication.h>
-
-#ifndef QT_POSIX_IPC
-
-#if QT_CONFIG(systemsemaphore)
-
-#include <sys/types.h>
-#include <sys/ipc.h>
-#include <sys/sem.h>
-#include <fcntl.h>
-#include <errno.h>
-
-#if defined(Q_OS_DARWIN)
-#include "qcore_mac_p.h"
-#endif
-
-#include "private/qcore_unix_p.h"
-
-// OpenBSD 4.2 doesn't define EIDRM, see BUGS section:
-// http://www.openbsd.org/cgi-bin/man.cgi?query=semop&manpath=OpenBSD+4.2
-#if defined(Q_OS_OPENBSD) && !defined(EIDRM)
-#define EIDRM EINVAL
-#endif
-
-QT_BEGIN_NAMESPACE
-
-using namespace Qt::StringLiterals;
-
-/*!
- \internal
-
- Setup unix_key
- */
-key_t QSystemSemaphorePrivate::handle(QSystemSemaphore::AccessMode mode)
-{
-#if defined(Q_OS_DARWIN)
- if (qt_apple_isSandboxed()) {
- errorString = QSystemSemaphore::tr("%1: System V semaphores are not available " \
- "for sandboxed applications. Please build Qt with -feature-ipc_posix")
- .arg("QSystemSemaphore::handle:"_L1);
- error = QSystemSemaphore::PermissionDenied;
- return -1;
- }
-#endif
-
- if (key.isEmpty()){
- errorString = QSystemSemaphore::tr("%1: key is empty")
- .arg("QSystemSemaphore::handle:"_L1);
- error = QSystemSemaphore::KeyError;
- return -1;
- }
-
- // ftok requires that an actual file exists somewhere
- if (-1 != unix_key)
- return unix_key;
-
- // Create the file needed for ftok
- int built = QSharedMemoryPrivate::createUnixKeyFile(fileName);
- if (-1 == built) {
- errorString = QSystemSemaphore::tr("%1: unable to make key")
- .arg("QSystemSemaphore::handle:"_L1);
- error = QSystemSemaphore::KeyError;
- return -1;
- }
- createdFile = (1 == built);
-
-#if QT_CONFIG(sharedmemory) && !defined(QT_POSIX_IPC) && !defined(Q_OS_ANDROID)
- // Get the unix key for the created file
- unix_key = ftok(QFile::encodeName(fileName).constData(), 'Q');
-#endif
- if (-1 == unix_key) {
- errorString = QSystemSemaphore::tr("%1: ftok failed")
- .arg("QSystemSemaphore::handle:"_L1);
- error = QSystemSemaphore::KeyError;
- return -1;
- }
-
- // Get semaphore
- semaphore = semget(unix_key, 1, 0600 | IPC_CREAT | IPC_EXCL);
- if (-1 == semaphore) {
- if (errno == EEXIST)
- semaphore = semget(unix_key, 1, 0600 | IPC_CREAT);
- if (-1 == semaphore) {
- setErrorString("QSystemSemaphore::handle"_L1);
- cleanHandle();
- return -1;
- }
- } else {
- createdSemaphore = true;
- // Force cleanup of file, it is possible that it can be left over from a crash
- createdFile = true;
- }
-
- if (mode == QSystemSemaphore::Create) {
- createdSemaphore = true;
- createdFile = true;
- }
-
- // Created semaphore so initialize its value.
- if (createdSemaphore && initialValue >= 0) {
- qt_semun init_op;
- init_op.val = initialValue;
- if (-1 == semctl(semaphore, 0, SETVAL, init_op)) {
- setErrorString("QSystemSemaphore::handle"_L1);
- cleanHandle();
- return -1;
- }
- }
-
- return unix_key;
-}
-
-/*!
- \internal
-
- Cleanup the unix_key
- */
-void QSystemSemaphorePrivate::cleanHandle()
-{
- unix_key = -1;
-
- // remove the file if we made it
- if (createdFile) {
- QFile::remove(fileName);
- createdFile = false;
- }
-
- if (createdSemaphore) {
- if (-1 != semaphore) {
- if (-1 == semctl(semaphore, 0, IPC_RMID, 0)) {
- setErrorString("QSystemSemaphore::cleanHandle"_L1);
-#if defined QSYSTEMSEMAPHORE_DEBUG
- qDebug("QSystemSemaphore::cleanHandle semctl failed.");
-#endif
- }
- semaphore = -1;
- }
- createdSemaphore = false;
- }
-}
-
-/*!
- \internal
- */
-bool QSystemSemaphorePrivate::modifySemaphore(int count)
-{
- if (-1 == handle())
- return false;
-
- struct sembuf operation;
- operation.sem_num = 0;
- operation.sem_op = count;
- operation.sem_flg = SEM_UNDO;
-
- int res;
- EINTR_LOOP(res, semop(semaphore, &operation, 1));
- if (-1 == res) {
- // If the semaphore was removed be nice and create it and then modifySemaphore again
- if (errno == EINVAL || errno == EIDRM) {
- semaphore = -1;
- cleanHandle();
- handle();
- return modifySemaphore(count);
- }
- setErrorString("QSystemSemaphore::modifySemaphore"_L1);
-#if defined QSYSTEMSEMAPHORE_DEBUG
- qDebug("QSystemSemaphore::modify failed %d %d %d %d %d",
- count, int(semctl(semaphore, 0, GETVAL)), int(errno), int(EIDRM), int(EINVAL);
-#endif
- return false;
- }
-
- clearError();
- return true;
-}
-
-
-QT_END_NAMESPACE
-
-#endif // QT_CONFIG(systemsemaphore)
-
-#endif // QT_POSIX_IPC
diff --git a/src/corelib/kernel/qsystemsemaphore_unix.cpp b/src/corelib/kernel/qsystemsemaphore_unix.cpp
deleted file mode 100644
index 5cb891129a..0000000000
--- a/src/corelib/kernel/qsystemsemaphore_unix.cpp
+++ /dev/null
@@ -1,63 +0,0 @@
-// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
-
-#include "qsystemsemaphore.h"
-#include "qsystemsemaphore_p.h"
-
-#include <qdebug.h>
-#include <qcoreapplication.h>
-
-#if QT_CONFIG(systemsemaphore)
-
-#include <sys/types.h>
-#ifndef QT_POSIX_IPC
-#include <sys/ipc.h>
-#include <sys/sem.h>
-#endif
-#include <fcntl.h>
-#include <errno.h>
-
-#include "private/qcore_unix_p.h"
-
-QT_BEGIN_NAMESPACE
-
-void QSystemSemaphorePrivate::setErrorString(const QString &function)
-{
- // EINVAL is handled in functions so they can give better error strings
- switch (errno) {
- case EPERM:
- case EACCES:
- errorString = QSystemSemaphore::tr("%1: permission denied").arg(function);
- error = QSystemSemaphore::PermissionDenied;
- break;
- case EEXIST:
- errorString = QSystemSemaphore::tr("%1: already exists").arg(function);
- error = QSystemSemaphore::AlreadyExists;
- break;
- case ENOENT:
- errorString = QSystemSemaphore::tr("%1: does not exist").arg(function);
- error = QSystemSemaphore::NotFound;
- break;
- case ERANGE:
- case ENOSPC:
- errorString = QSystemSemaphore::tr("%1: out of resources").arg(function);
- error = QSystemSemaphore::OutOfResources;
- break;
-#if defined(QT_POSIX_IPC)
- case ENAMETOOLONG:
- errorString = QSystemSemaphore::tr("%1: key too long").arg(function);
- error = QSystemSemaphore::KeyError;
- break;
-#endif
- default:
- errorString = QSystemSemaphore::tr("%1: unknown error %2").arg(function).arg(errno);
- error = QSystemSemaphore::UnknownError;
-#if defined QSYSTEMSEMAPHORE_DEBUG
- qDebug() << errorString << "key" << key << "errno" << errno << EINVAL;
-#endif
- }
-}
-
-QT_END_NAMESPACE
-
-#endif // QT_CONFIG(systemsemaphore)
diff --git a/src/corelib/kernel/qsystemsemaphore_win.cpp b/src/corelib/kernel/qsystemsemaphore_win.cpp
deleted file mode 100644
index f1b7e78ff7..0000000000
--- a/src/corelib/kernel/qsystemsemaphore_win.cpp
+++ /dev/null
@@ -1,97 +0,0 @@
-// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
-
-#include "qsystemsemaphore.h"
-#include "qsystemsemaphore_p.h"
-#include "qcoreapplication.h"
-#include <qdebug.h>
-#include <qt_windows.h>
-
-QT_BEGIN_NAMESPACE
-
-using namespace Qt::StringLiterals;
-
-#if QT_CONFIG(systemsemaphore)
-
-void QSystemSemaphorePrivate::setErrorString(const QString &function)
-{
- BOOL windowsError = GetLastError();
- if (windowsError == 0)
- return;
-
- switch (windowsError) {
- case ERROR_NO_SYSTEM_RESOURCES:
- case ERROR_NOT_ENOUGH_MEMORY:
- error = QSystemSemaphore::OutOfResources;
- errorString = QCoreApplication::translate("QSystemSemaphore", "%1: out of resources").arg(function);
- break;
- case ERROR_ACCESS_DENIED:
- error = QSystemSemaphore::PermissionDenied;
- errorString = QCoreApplication::translate("QSystemSemaphore", "%1: permission denied").arg(function);
- break;
- default:
- errorString = QCoreApplication::translate("QSystemSemaphore", "%1: unknown error %2").arg(function).arg(windowsError);
- error = QSystemSemaphore::UnknownError;
-#if defined QSYSTEMSEMAPHORE_DEBUG
- qDebug() << errorString << "key" << key;
-#endif
- }
-}
-
-HANDLE QSystemSemaphorePrivate::handle(QSystemSemaphore::AccessMode)
-{
- // don't allow making handles on empty keys
- if (key.isEmpty())
- return 0;
-
- // Create it if it doesn't already exists.
- if (semaphore == 0) {
- semaphore = CreateSemaphore(0, initialValue, MAXLONG,
- reinterpret_cast<const wchar_t*>(fileName.utf16()));
- if (semaphore == NULL)
- setErrorString("QSystemSemaphore::handle"_L1);
- }
-
- return semaphore;
-}
-
-void QSystemSemaphorePrivate::cleanHandle()
-{
- if (semaphore && !CloseHandle(semaphore)) {
-#if defined QSYSTEMSEMAPHORE_DEBUG
- qDebug("QSystemSemaphorePrivate::CloseHandle: sem failed");
-#endif
- }
- semaphore = 0;
-}
-
-bool QSystemSemaphorePrivate::modifySemaphore(int count)
-{
- if (0 == handle())
- return false;
-
- if (count > 0) {
- if (0 == ReleaseSemaphore(semaphore, count, 0)) {
- setErrorString("QSystemSemaphore::modifySemaphore"_L1);
-#if defined QSYSTEMSEMAPHORE_DEBUG
- qDebug("QSystemSemaphore::modifySemaphore ReleaseSemaphore failed");
-#endif
- return false;
- }
- } else {
- if (WAIT_OBJECT_0 != WaitForSingleObjectEx(semaphore, INFINITE, FALSE)) {
- setErrorString("QSystemSemaphore::modifySemaphore"_L1);
-#if defined QSYSTEMSEMAPHORE_DEBUG
- qDebug("QSystemSemaphore::modifySemaphore WaitForSingleObject failed");
-#endif
- return false;
- }
- }
-
- clearError();
- return true;
-}
-
-#endif // QT_CONFIG(systemsemaphore)
-
-QT_END_NAMESPACE
diff --git a/src/corelib/kernel/qt_attribution.json b/src/corelib/kernel/qt_attribution.json
index 6d8f4f2abc..86ca3a2664 100644
--- a/src/corelib/kernel/qt_attribution.json
+++ b/src/corelib/kernel/qt_attribution.json
@@ -3,9 +3,9 @@
"Name": "QEventDispatcher on macOS",
"QDocModule": "qtcore",
"QtUsage": "Used in Qt Core on macOS.",
- "Path": "qeventdispatcher_cf_p.h",
+ "Files": "qeventdispatcher_cf_p.h",
- "Description": "Treat as final version; no upstream known",
+ "Comment": "Treat as final version; no upstream known",
"Description": "Implementation of QAbstractEventDispatcher for macOS.",
"License": "BSD 3-clause \"New\" or \"Revised\" License",
"LicenseId": "BSD-3-Clause",
diff --git a/src/corelib/kernel/qtestsupport_core.cpp b/src/corelib/kernel/qtestsupport_core.cpp
index 3fa7f346be..2ac44bb13d 100644
--- a/src/corelib/kernel/qtestsupport_core.cpp
+++ b/src/corelib/kernel/qtestsupport_core.cpp
@@ -3,61 +3,101 @@
#include "qtestsupport_core.h"
-#ifdef Q_OS_WIN
-#include <qt_windows.h>
-#endif
+#include <thread>
+
+using namespace std::chrono_literals;
QT_BEGIN_NAMESPACE
/*!
- Sleeps for \a ms milliseconds, blocking execution of the
- test. qSleep() will not do any event processing and leave your test
- unresponsive. Network communication might time out while
- sleeping. Use \l {QTest::qWait()} to do non-blocking sleeping.
+ \overload
+
+ Sleeps for \a ms milliseconds, blocking execution of the test.
+
+ Equivalent to calling:
+ \code
+ QTest::qSleep(std::chrono::milliseconds{ms});
+ \endcode
+*/
+void QTest::qSleep(int ms)
+{
+ QTest::qSleep(std::chrono::milliseconds{ms});
+}
+
+/*!
+ \since 6.7
- \a ms must be greater than 0.
+ Sleeps for \a msecs, blocking execution of the test.
- \b {Note:} The qSleep() function calls either \c nanosleep() on
- unix or \c Sleep() on windows, so the accuracy of time spent in
- qSleep() depends on the operating system.
+ This method will not do any event processing and will leave your test
+ unresponsive. Network communication might time out while sleeping.
+ Use \l {QTest::qWait()} to do non-blocking sleeping.
+
+ \a msecs must be greater than 0ms.
+
+ \note Starting from Qt 6.7, this function is implemented using
+ \c {std::this_thread::sleep_for}, so the accuracy of time spent depends
+ on the Standard Library implementation. Before Qt 6.7 this function called
+ either \c nanosleep() on Unix or \c Sleep() on Windows, so the accuracy of
+ time spent in this function depended on the operating system.
Example:
\snippet code/src_qtestlib_qtestcase.cpp 23
\sa {QTest::qWait()}
*/
-Q_CORE_EXPORT void QTest::qSleep(int ms)
+void QTest::qSleep(std::chrono::milliseconds msecs)
{
- Q_ASSERT(ms > 0);
-
-#if defined(Q_OS_WIN)
- Sleep(uint(ms));
-#else
- struct timespec ts = { time_t(ms / 1000), (ms % 1000) * 1000 * 1000 };
- nanosleep(&ts, nullptr);
-#endif
+ Q_ASSERT(msecs > 0ms);
+ std::this_thread::sleep_for(msecs);
}
/*! \fn template <typename Functor> bool QTest::qWaitFor(Functor predicate, int timeout)
+ \since 5.10
+ \overload
+
Waits for \a timeout milliseconds or until the \a predicate returns true.
- Returns \c true if the \a predicate returned true at any point, otherwise returns \c false.
+ This is equivalent to calling:
+ \code
+ qWaitFor(predicate, QDeadlineTimer(timeout));
+ \endcode
+*/
+
+/*! \fn template <typename Functor> bool QTest::qWaitFor(Functor predicate, QDeadlineTimer deadline)
+ \since 6.7
+
+ Waits until \a deadline has expired, or until \a predicate returns true, whichever
+ happens first.
+
+ Returns \c true if \a predicate returned true at any point, otherwise returns \c false.
Example:
- \snippet code/src_corelib_kernel_qtestsupport_core_snippet.cpp 0
+ \snippet code/src_corelib_kernel_qtestsupport_core.cpp 2
The code above will wait for the object to become ready, for a
maximum of three seconds.
-
- \since 5.10
*/
+/*!
+ \overload
-/*! \fn void QTest::qWait(int ms)
+ Waits for \a msecs. Equivalent to calling:
+ \code
+ QTest::qWait(std::chrono::milliseconds{msecs});
+ \endcode
+*/
+Q_CORE_EXPORT void QTest::qWait(int msecs)
+{
+ qWait(std::chrono::milliseconds{msecs});
+}
- Waits for \a ms milliseconds. While waiting, events will be processed and
+/*!
+ \since 6.7
+
+ Waits for \a msecs. While waiting, events will be processed and
your test will stay responsive to user interface events or network communication.
Example:
@@ -69,7 +109,7 @@ Q_CORE_EXPORT void QTest::qSleep(int ms)
\sa QTest::qSleep(), QSignalSpy::wait()
*/
-Q_CORE_EXPORT void QTest::qWait(int ms)
+Q_CORE_EXPORT void QTest::qWait(std::chrono::milliseconds msecs)
{
// Ideally this method would be implemented in terms of qWaitFor(), with a
// predicate that always returns false, but qWaitFor() uses the 1-arg overload
@@ -79,17 +119,24 @@ Q_CORE_EXPORT void QTest::qWait(int ms)
Q_ASSERT(QCoreApplication::instance());
- QDeadlineTimer timer(ms, Qt::PreciseTimer);
- int remaining = ms;
+ using namespace std::chrono;
+
+ QDeadlineTimer deadline(msecs, Qt::PreciseTimer);
+
do {
- QCoreApplication::processEvents(QEventLoop::AllEvents, remaining);
+ QCoreApplication::processEvents(QEventLoop::AllEvents, deadline);
QCoreApplication::sendPostedEvents(nullptr, QEvent::DeferredDelete);
- remaining = timer.remainingTime();
- if (remaining <= 0)
+
+ // If dealine is Forever, processEvents() has already looped forever
+ if (deadline.isForever())
break;
- QTest::qSleep(qMin(10, remaining));
- remaining = timer.remainingTime();
- } while (remaining > 0);
+
+ msecs = ceil<milliseconds>(deadline.remainingTimeAsDuration());
+ if (msecs == 0ms)
+ break;
+
+ QTest::qSleep(std::min(10ms, msecs));
+ } while (!deadline.hasExpired());
}
QT_END_NAMESPACE
diff --git a/src/corelib/kernel/qtestsupport_core.h b/src/corelib/kernel/qtestsupport_core.h
index c9505196a9..27265903af 100644
--- a/src/corelib/kernel/qtestsupport_core.h
+++ b/src/corelib/kernel/qtestsupport_core.h
@@ -6,28 +6,27 @@
#include <QtCore/qcoreapplication.h>
#include <QtCore/qdeadlinetimer.h>
-#include <QtCore/qthread.h>
+
+#include <chrono>
QT_BEGIN_NAMESPACE
namespace QTest {
Q_CORE_EXPORT void qSleep(int ms);
+Q_CORE_EXPORT void qSleep(std::chrono::milliseconds msecs);
template <typename Functor>
-[[nodiscard]] static bool qWaitFor(Functor predicate, int timeout = 5000)
+[[nodiscard]] bool
+qWaitFor(Functor predicate, QDeadlineTimer deadline = QDeadlineTimer(std::chrono::seconds{5}))
{
// We should not spin the event loop in case the predicate is already true,
// otherwise we might send new events that invalidate the predicate.
if (predicate())
return true;
- // qWait() is expected to spin the event loop, even when called with a small
- // timeout like 1ms, so we we can't use a simple while-loop here based on
- // the deadline timer not having timed out. Use do-while instead.
-
- int remaining = timeout;
- QDeadlineTimer deadline(remaining, Qt::PreciseTimer);
+ // qWait() is expected to spin the event loop at least once, even when
+ // called with a small timeout like 1ns.
do {
// We explicitly do not pass the remaining time to processEvents, as
@@ -42,17 +41,26 @@ template <typename Functor>
if (predicate())
return true;
- remaining = int(deadline.remainingTime());
- if (remaining > 0)
- qSleep(qMin(10, remaining));
- remaining = int(deadline.remainingTime());
- } while (remaining > 0);
+ using namespace std::chrono;
+
+ if (const auto remaining = deadline.remainingTimeAsDuration(); remaining > 0ns)
+ qSleep((std::min)(10ms, ceil<milliseconds>(remaining)));
+
+ } while (!deadline.hasExpired());
return predicate(); // Last chance
}
+template <typename Functor>
+[[nodiscard]] bool qWaitFor(Functor predicate, int timeout)
+{
+ return qWaitFor(predicate, QDeadlineTimer{timeout, Qt::PreciseTimer});
+}
+
Q_CORE_EXPORT void qWait(int ms);
+Q_CORE_EXPORT void qWait(std::chrono::milliseconds msecs);
+
} // namespace QTest
QT_END_NAMESPACE
diff --git a/src/corelib/kernel/qtimer.cpp b/src/corelib/kernel/qtimer.cpp
index 8a94603488..294369c1b3 100644
--- a/src/corelib/kernel/qtimer.cpp
+++ b/src/corelib/kernel/qtimer.cpp
@@ -4,13 +4,18 @@
#include "qtimer.h"
#include "qtimer_p.h"
+#include "qsingleshottimer_p.h"
#include "qabstracteventdispatcher.h"
#include "qcoreapplication.h"
-#include "qobject_p.h"
-#include "qthread.h"
#include "qcoreapplication_p.h"
+#include "qdeadlinetimer.h"
+#include "qmetaobject_p.h"
+#include "qobject_p.h"
#include "qproperty_p.h"
+#include "qthread.h"
+
+using namespace std::chrono_literals;
QT_BEGIN_NAMESPACE
@@ -69,6 +74,13 @@ QT_BEGIN_NAMESPACE
more and more platforms, we expect that zero-millisecond
QTimer objects will gradually be replaced by \l{QThread}s.
+ \note Since Qt 6.7 this class is superseded by \l{QChronoTimer}.
+ The maximum interval QTimer supports is limited by the number of
+ milliseconds that would fit in an \c int (which is around 24 days);
+ whereas QChronoTimer stores its interval as \c std::chrono::nanoseconds
+ (which raises that limit to around 292 million years), that is, there is
+ less chance of integer overflow with QChronoTimer.
+
\section1 Accuracy and Timer Resolution
The accuracy of timers depends on the underlying operating system
@@ -105,7 +117,7 @@ QT_BEGIN_NAMESPACE
used; Qt tries to work around these limitations.
\sa QBasicTimer, QTimerEvent, QObject::timerEvent(), Timers,
- {Analog Clock Example}, {Wiggly Example}
+ {Analog Clock}
*/
/*!
@@ -113,8 +125,9 @@ QT_BEGIN_NAMESPACE
*/
QTimer::QTimer(QObject *parent)
- : QObject(*new QTimerPrivate, parent)
+ : QObject(*new QTimerPrivate(this), parent)
{
+ Q_ASSERT(d_func()->isQTimer);
}
@@ -124,7 +137,7 @@ QTimer::QTimer(QObject *parent)
QTimer::~QTimer()
{
- if (d_func()->id != QTimerPrivate::INV_TIMER) // stop running timer
+ if (d_func()->isActive()) // stop running timer
stop();
}
@@ -169,9 +182,21 @@ QBindable<bool> QTimer::bindableActive()
*/
int QTimer::timerId() const
{
- return d_func()->id;
+ auto v = qToUnderlying(id());
+ return v == 0 ? -1 : v;
}
+/*!
+ \since 6.8
+ Returns a Qt::TimerId representing the timer ID if the timer is running;
+ otherwise returns \c Qt::TimerId::Invalid.
+
+ \sa Qt::TimerId
+*/
+Qt::TimerId QTimer::id() const
+{
+ return d_func()->id;
+}
/*! \overload start()
@@ -185,10 +210,14 @@ int QTimer::timerId() const
void QTimer::start()
{
Q_D(QTimer);
- if (d->id != QTimerPrivate::INV_TIMER) // stop running timer
+ if (d->isActive()) // stop running timer
stop();
- d->id = QObject::startTimer(d->inter, d->type);
- d->isActiveData.notify();
+
+ Qt::TimerId newId{ QObject::startTimer(d->inter * 1ms, d->type) }; // overflow impossible
+ if (newId > Qt::TimerId::Invalid) {
+ d->id = newId;
+ d->isActiveData.notify();
+ }
}
/*!
@@ -198,14 +227,28 @@ void QTimer::start()
If the timer is already running, it will be
\l{QTimer::stop()}{stopped} and restarted.
- If \l singleShot is true, the timer will be activated only once.
+ If \l singleShot is true, the timer will be activated only once. This is
+ equivalent to:
+
+ \code
+ timer.setInterval(msec);
+ timer.start();
+ \endcode
\note Keeping the event loop busy with a zero-timer is bound to
cause trouble and highly erratic behavior of the UI.
*/
void QTimer::start(int msec)
{
+ start(msec * 1ms);
+}
+
+void QTimer::start(std::chrono::milliseconds interval)
+{
Q_D(QTimer);
+ // This could be narrowing as the interval is stored in an `int` QProperty,
+ // and the type can't be changed in Qt6.
+ const int msec = interval.count();
const bool intervalChanged = msec != d->inter;
d->inter.setValue(msec);
start();
@@ -224,9 +267,9 @@ void QTimer::start(int msec)
void QTimer::stop()
{
Q_D(QTimer);
- if (d->id != QTimerPrivate::INV_TIMER) {
+ if (d->isActive()) {
QObject::killTimer(d->id);
- d->id = QTimerPrivate::INV_TIMER;
+ d->id = Qt::TimerId::Invalid;
d->isActiveData.notify();
}
}
@@ -238,84 +281,13 @@ void QTimer::stop()
void QTimer::timerEvent(QTimerEvent *e)
{
Q_D(QTimer);
- if (e->timerId() == d->id) {
+ if (Qt::TimerId{e->timerId()} == d->id) {
if (d->single)
stop();
emit timeout(QPrivateSignal());
}
}
-class QSingleShotTimer : public QObject
-{
- Q_OBJECT
- int timerId;
- bool hasValidReceiver;
- QPointer<const QObject> receiver;
- QtPrivate::QSlotObjectBase *slotObj;
-public:
- ~QSingleShotTimer();
- QSingleShotTimer(int msec, Qt::TimerType timerType, const QObject *r, const char * m);
- QSingleShotTimer(int msec, Qt::TimerType timerType, const QObject *r, QtPrivate::QSlotObjectBase *slotObj);
-
-Q_SIGNALS:
- void timeout();
-protected:
- void timerEvent(QTimerEvent *) override;
-};
-
-QSingleShotTimer::QSingleShotTimer(int msec, Qt::TimerType timerType, const QObject *r, const char *member)
- : QObject(QAbstractEventDispatcher::instance()), hasValidReceiver(true), slotObj(nullptr)
-{
- timerId = startTimer(msec, timerType);
- connect(this, SIGNAL(timeout()), r, member);
-}
-
-QSingleShotTimer::QSingleShotTimer(int msec, Qt::TimerType timerType, const QObject *r, QtPrivate::QSlotObjectBase *slotObj)
- : QObject(QAbstractEventDispatcher::instance()), hasValidReceiver(r), receiver(r), slotObj(slotObj)
-{
- timerId = startTimer(msec, timerType);
- if (r && thread() != r->thread()) {
- // Avoid leaking the QSingleShotTimer instance in case the application exits before the timer fires
- connect(QCoreApplication::instance(), &QCoreApplication::aboutToQuit, this, &QObject::deleteLater);
- setParent(nullptr);
- moveToThread(r->thread());
- }
-}
-
-QSingleShotTimer::~QSingleShotTimer()
-{
- if (timerId > 0)
- killTimer(timerId);
- if (slotObj)
- slotObj->destroyIfLastRef();
-}
-
-void QSingleShotTimer::timerEvent(QTimerEvent *)
-{
- // need to kill the timer _before_ we emit timeout() in case the
- // slot connected to timeout calls processEvents()
- if (timerId > 0)
- killTimer(timerId);
- timerId = -1;
-
- if (slotObj) {
- // If the receiver was destroyed, skip this part
- if (Q_LIKELY(!receiver.isNull() || !hasValidReceiver)) {
- // We allocate only the return type - we previously checked the function had
- // no arguments.
- void *args[1] = { nullptr };
- slotObj->call(const_cast<QObject*>(receiver.data()), args);
- }
- } else {
- emit timeout();
- }
-
- // we would like to use delete later here, but it feels like a
- // waste to post a new event to handle this event, so we just unset the flag
- // and explicitly delete...
- qDeleteInEventHandler(this);
-}
-
/*!
\internal
@@ -325,14 +297,13 @@ void QSingleShotTimer::timerEvent(QTimerEvent *)
\a timerType is the timer type
\a receiver is the receiver object, can be null. In such a case, it will be the same
as the final sender class.
- \a slot a pointer only used when using Qt::UniqueConnection
\a slotObj the slot object
- */
-void QTimer::singleShotImpl(int msec, Qt::TimerType timerType,
+*/
+void QTimer::singleShotImpl(std::chrono::milliseconds msec, Qt::TimerType timerType,
const QObject *receiver,
QtPrivate::QSlotObjectBase *slotObj)
{
- if (msec == 0) {
+ if (msec == 0ms) {
bool deleteReceiver = false;
// Optimize: set a receiver context when none is given, such that we can use
// QMetaObject::invokeMethod which is more efficient than going through a timer.
@@ -351,19 +322,23 @@ void QTimer::singleShotImpl(int msec, Qt::TimerType timerType,
deleteReceiver = true;
}
+ auto h = QtPrivate::invokeMethodHelper({});
QMetaObject::invokeMethodImpl(const_cast<QObject *>(receiver), slotObj,
- Qt::QueuedConnection, nullptr);
+ Qt::QueuedConnection, h.parameterCount(), h.parameters.data(), h.typeNames.data(),
+ h.metaTypes.data());
if (deleteReceiver)
const_cast<QObject *>(receiver)->deleteLater();
return;
}
- new QSingleShotTimer(msec, timerType, receiver, slotObj);
+ new QSingleShotTimer(QSingleShotTimer::fromMsecs(msec), timerType, receiver, slotObj);
}
/*!
+ \fn void QTimer::singleShot(int msec, const QObject *receiver, const char *member)
\reentrant
+ \deprecated [6.8] Use the chrono overloads.
This static function calls a slot after a given time interval.
It is very convenient to use this function because you do not need
@@ -382,16 +357,11 @@ void QTimer::singleShotImpl(int msec, Qt::TimerType timerType,
\sa start()
*/
-void QTimer::singleShot(int msec, const QObject *receiver, const char *member)
-{
- // coarse timers are worst in their first firing
- // so we prefer a high precision timer for something that happens only once
- // unless the timeout is too big, in which case we go for coarse anyway
- singleShot(msec, msec >= 2000 ? Qt::CoarseTimer : Qt::PreciseTimer, receiver, member);
-}
-
-/*! \overload
+/*!
+ \fn void QTimer::singleShot(int msec, Qt::TimerType timerType, const QObject *receiver, const char *member)
+ \overload
\reentrant
+ \deprecated [6.8] Use the chrono overloads.
This static function calls a slot after a given time interval.
It is very convenient to use this function because you do not need
@@ -404,147 +374,54 @@ void QTimer::singleShot(int msec, const QObject *receiver, const char *member)
\sa start()
*/
-void QTimer::singleShot(int msec, Qt::TimerType timerType, const QObject *receiver, const char *member)
+
+void QTimer::singleShot(std::chrono::milliseconds msec, Qt::TimerType timerType,
+ const QObject *receiver, const char *member)
{
- if (Q_UNLIKELY(msec < 0)) {
+ if (Q_UNLIKELY(msec < 0ms)) {
qWarning("QTimer::singleShot: Timers cannot have negative timeouts");
return;
}
if (receiver && member) {
- if (msec == 0) {
+ if (msec == 0ms) {
// special code shortpath for 0-timers
const char* bracketPosition = strchr(member, '(');
if (!bracketPosition || !(member[0] >= '0' && member[0] <= '2')) {
qWarning("QTimer::singleShot: Invalid slot specification");
return;
}
- QByteArray methodName(member+1, bracketPosition - 1 - member); // extract method name
- QMetaObject::invokeMethod(const_cast<QObject *>(receiver), methodName.constData(), Qt::QueuedConnection);
+ const auto methodName = QByteArrayView(member + 1, // extract method name
+ bracketPosition - 1 - member).trimmed();
+ QMetaObject::invokeMethod(const_cast<QObject *>(receiver), methodName.toByteArray().constData(),
+ Qt::QueuedConnection);
return;
}
- (void) new QSingleShotTimer(msec, timerType, receiver, member);
+ (void) new QSingleShotTimer(QSingleShotTimer::fromMsecs(msec), timerType, receiver, member);
}
}
-/*! \fn template<typename PointerToMemberFunction> void QTimer::singleShot(int msec, const QObject *receiver, PointerToMemberFunction method)
-
- \since 5.4
-
- \overload
- \reentrant
- This static function calls a member function of a QObject after a given time interval.
-
- It is very convenient to use this function because you do not need
- to bother with a \l{QObject::timerEvent()}{timerEvent} or
- create a local QTimer object.
-
- The \a receiver is the receiving object and the \a method is the member function. The
- time interval is \a msec milliseconds.
-
- If \a receiver is destroyed before the interval occurs, the method will not be called.
- The function will be run in the thread of \a receiver. The receiver's thread must have
- a running Qt event loop.
-
- \sa start()
-*/
-
-/*! \fn template<typename PointerToMemberFunction> void QTimer::singleShot(int msec, Qt::TimerType timerType, const QObject *receiver, PointerToMemberFunction method)
-
- \since 5.4
-
- \overload
- \reentrant
- This static function calls a member function of a QObject after a given time interval.
-
- It is very convenient to use this function because you do not need
- to bother with a \l{QObject::timerEvent()}{timerEvent} or
- create a local QTimer object.
-
- The \a receiver is the receiving object and the \a method is the member function. The
- time interval is \a msec milliseconds. The \a timerType affects the
- accuracy of the timer.
-
- If \a receiver is destroyed before the interval occurs, the method will not be called.
- The function will be run in the thread of \a receiver. The receiver's thread must have
- a running Qt event loop.
-
- \sa start()
-*/
-
-/*! \fn template<typename Functor> void QTimer::singleShot(int msec, Functor functor)
-
- \since 5.4
-
- \overload
- \reentrant
- This static function calls \a functor after a given time interval.
-
- It is very convenient to use this function because you do not need
- to bother with a \l{QObject::timerEvent()}{timerEvent} or
- create a local QTimer object.
-
- The time interval is \a msec milliseconds.
-
- \sa start()
-*/
-
-/*! \fn template<typename Functor> void QTimer::singleShot(int msec, Qt::TimerType timerType, Functor functor)
-
+/*! \fn template<typename Duration, typename Functor> void QTimer::singleShot(Duration msec, const QObject *context, Functor &&functor)
+ \fn template<typename Duration, typename Functor> void QTimer::singleShot(Duration msec, Qt::TimerType timerType, const QObject *context, Functor &&functor)
+ \fn template<typename Duration, typename Functor> void QTimer::singleShot(Duration msec, Functor &&functor)
+ \fn template<typename Duration, typename Functor> void QTimer::singleShot(Duration msec, Qt::TimerType timerType, Functor &&functor)
\since 5.4
- \overload
\reentrant
- This static function calls \a functor after a given time interval.
+ This static function calls \a functor after \a msec milliseconds.
It is very convenient to use this function because you do not need
to bother with a \l{QObject::timerEvent()}{timerEvent} or
create a local QTimer object.
- The time interval is \a msec milliseconds. The \a timerType affects the
- accuracy of the timer.
-
- \sa start()
-*/
-
-/*! \fn template<typename Functor> void QTimer::singleShot(int msec, const QObject *context, Functor functor)
-
- \since 5.4
-
- \overload
- \reentrant
- This static function calls \a functor after a given time interval.
-
- It is very convenient to use this function because you do not need
- to bother with a \l{QObject::timerEvent()}{timerEvent} or
- create a local QTimer object.
+ If \a context is specified, then the \a functor will be called only if the
+ \a context object has not been destroyed before the interval occurs. The functor
+ will then be run the thread of \a context. The context's thread must have a
+ running Qt event loop.
- The time interval is \a msec milliseconds.
+ If \a functor is a member
+ function of \a context, then the function will be called on the object.
- If \a context is destroyed before the interval occurs, the method will not be called.
- The function will be run in the thread of \a context. The context's thread must have
- a running Qt event loop.
-
- \sa start()
-*/
-
-/*! \fn template<typename Functor> void QTimer::singleShot(int msec, Qt::TimerType timerType, const QObject *context, Functor functor)
-
- \since 5.4
-
- \overload
- \reentrant
- This static function calls \a functor after a given time interval.
-
- It is very convenient to use this function because you do not need
- to bother with a \l{QObject::timerEvent()}{timerEvent} or
- create a local QTimer object.
-
- The time interval is \a msec milliseconds. The \a timerType affects the
- accuracy of the timer.
-
- If \a context is destroyed before the interval occurs, the method will not be called.
- The function will be run in the thread of \a context. The context's thread must have
- a running Qt event loop.
+ The \a msec parameter can be an \c int or a \c std::chrono::milliseconds value.
\sa start()
*/
@@ -587,43 +464,35 @@ void QTimer::singleShot(int msec, Qt::TimerType timerType, const QObject *receiv
*/
/*!
- \fn template <typename Functor> QMetaObject::Connection QTimer::callOnTimeout(Functor slot, Qt::ConnectionType connectionType = Qt::AutoConnection)
+ \fn template <typename Functor> QMetaObject::Connection QTimer::callOnTimeout(Functor &&slot)
\since 5.12
- \overload
- Creates a connection of type \a connectionType from the timeout() signal
- to \a slot, and returns a handle to the connection.
+ Creates a connection from the timer's timeout() signal to \a slot.
+ Returns a handle to the connection.
+
+ This method is provided for convenience. It's equivalent to calling:
+ \code
+ QObject::connect(timer, &QTimer::timeout, timer, slot, Qt::DirectConnection);
+ \endcode
- This method is provided for convenience.
- It's equivalent to calling \c {QObject::connect(timer, &QTimer::timeout, timer, slot, connectionType)}.
+ \note This overload is not available when \c {QT_NO_CONTEXTLESS_CONNECT} is
+ defined, instead use the callOnTimeout() overload that takes a context object.
\sa QObject::connect(), timeout()
*/
/*!
- \fn template <typename Functor> QMetaObject::Connection QTimer::callOnTimeout(const QObject *context, Functor slot, Qt::ConnectionType connectionType = Qt::AutoConnection)
+ \fn template <typename Functor> QMetaObject::Connection QTimer::callOnTimeout(const QObject *context, Functor &&slot, Qt::ConnectionType connectionType = Qt::AutoConnection)
\since 5.12
\overload callOnTimeout()
Creates a connection from the timeout() signal to \a slot to be placed in a specific
event loop of \a context, and returns a handle to the connection.
- This method is provided for convenience. It's equivalent to calling
- \c {QObject::connect(timer, &QTimer::timeout, context, slot, connectionType)}.
-
- \sa QObject::connect(), timeout()
-*/
-
-/*!
- \fn template <typename MemberFunction> QMetaObject::Connection QTimer::callOnTimeout(const QObject *receiver, MemberFunction *slot, Qt::ConnectionType connectionType = Qt::AutoConnection)
- \since 5.12
- \overload callOnTimeout()
-
- Creates a connection from the timeout() signal to the \a slot in the \a receiver object. Returns
- a handle to the connection.
-
- This method is provided for convenience. It's equivalent to calling
- \c {QObject::connect(timer, &QTimer::timeout, receiver, slot, connectionType)}.
+ This method is provided for convenience. It's equivalent to calling:
+ \code
+ QObject::connect(timer, &QTimer::timeout, context, slot, connectionType);
+ \endcode
\sa QObject::connect(), timeout()
*/
@@ -638,7 +507,13 @@ void QTimer::singleShot(int msec, Qt::TimerType timerType, const QObject *receiv
If the timer is already running, it will be
\l{QTimer::stop()}{stopped} and restarted.
- If \l singleShot is true, the timer will be activated only once.
+ If \l singleShot is true, the timer will be activated only once. This is
+ equivalent to:
+
+ \code
+ timer.setInterval(msec);
+ timer.start();
+ \endcode
*/
/*!
@@ -703,14 +578,30 @@ QBindable<bool> QTimer::bindableSingleShot()
*/
void QTimer::setInterval(int msec)
{
+ setInterval(std::chrono::milliseconds{msec});
+}
+
+void QTimer::setInterval(std::chrono::milliseconds interval)
+{
Q_D(QTimer);
- const bool intervalChanged = msec != d->inter;
- d->inter.setValue(msec);
- if (d->id != QTimerPrivate::INV_TIMER) { // create new timer
+ // This could be narrowing as the interval is stored in an `int` QProperty,
+ // and the type can't be changed in Qt6.
+ const int msec = interval.count();
+ d->inter.removeBindingUnlessInWrapper();
+ const bool intervalChanged = msec != d->inter.valueBypassingBindings();
+ d->inter.setValueBypassingBindings(msec);
+ if (d->isActive()) { // create new timer
QObject::killTimer(d->id); // restart timer
- d->id = QObject::startTimer(msec, d->type);
- // No need to call markDirty() for d->isActiveData here,
- // as timer state actually does not change
+ Qt::TimerId newId{ QObject::startTimer(msec * 1ms, d->type) }; // overflow impossible
+ if (newId > Qt::TimerId::Invalid) {
+ // Restarted successfully. No need to update the active state.
+ d->id = newId;
+ } else {
+ // Failed to start the timer.
+ // Need to notify about active state change.
+ d->id = Qt::TimerId::Invalid;
+ d->isActiveData.notify();
+ }
}
if (intervalChanged)
d->inter.notify();
@@ -740,8 +631,10 @@ QBindable<int> QTimer::bindableInterval()
int QTimer::remainingTime() const
{
Q_D(const QTimer);
- if (d->id != QTimerPrivate::INV_TIMER) {
- return QAbstractEventDispatcher::instance()->remainingTime(d->id);
+ if (d->isActive()) {
+ using namespace std::chrono;
+ auto remaining = QAbstractEventDispatcher::instance()->remainingTime(d->id);
+ return ceil<milliseconds>(remaining).count();
}
return -1;
@@ -772,5 +665,4 @@ QBindable<Qt::TimerType> QTimer::bindableTimerType()
QT_END_NAMESPACE
-#include "qtimer.moc"
#include "moc_qtimer.cpp"
diff --git a/src/corelib/kernel/qtimer.h b/src/corelib/kernel/qtimer.h
index 5599b60bd7..854d9072f2 100644
--- a/src/corelib/kernel/qtimer.h
+++ b/src/corelib/kernel/qtimer.h
@@ -31,6 +31,7 @@ public:
bool isActive() const;
QBindable<bool> bindableActive();
int timerId() const;
+ Qt::TimerId id() const;
void setInterval(int msec);
int interval() const;
@@ -46,85 +47,56 @@ public:
bool isSingleShot() const;
QBindable<bool> bindableSingleShot();
+ QT_CORE_INLINE_SINCE(6, 8)
static void singleShot(int msec, const QObject *receiver, const char *member);
+
+ QT_CORE_INLINE_SINCE(6, 8)
static void singleShot(int msec, Qt::TimerType timerType, const QObject *receiver, const char *member);
+ // singleShot with context
#ifdef Q_QDOC
- template<typename PointerToMemberFunction>
- static void singleShot(int msec, const QObject *receiver, PointerToMemberFunction method);
- template<typename PointerToMemberFunction>
- static void singleShot(int msec, Qt::TimerType timerType, const QObject *receiver, PointerToMemberFunction method);
- template<typename Functor>
- static void singleShot(int msec, Functor functor);
- template<typename Functor>
- static void singleShot(int msec, Qt::TimerType timerType, Functor functor);
- template<typename Functor, int>
- static void singleShot(int msec, const QObject *context, Functor functor);
- template<typename Functor, int>
- static void singleShot(int msec, Qt::TimerType timerType, const QObject *context, Functor functor);
- template <typename Functor>
- QMetaObject::Connection callOnTimeout(Functor slot, Qt::ConnectionType connectionType = Qt::AutoConnection);
- template <typename Functor>
- QMetaObject::Connection callOnTimeout(const QObject *context, Functor slot, Qt::ConnectionType connectionType = Qt::AutoConnection);
- template <typename MemberFunction>
- QMetaObject::Connection callOnTimeout(const QObject *receiver, MemberFunction *slot, Qt::ConnectionType connectionType = Qt::AutoConnection);
+ template <typename Duration, typename Functor>
+ static inline void singleShot(Duration interval, const QObject *receiver, Functor &&slot);
+ template <typename Duration, typename Functor>
+ static inline void singleShot(Duration interval, Qt::TimerType timerType,
+ const QObject *receiver, Functor &&slot);
#else
- // singleShot to a QObject slot
- template <typename Duration, typename Func1>
- static inline void singleShot(Duration interval, const typename QtPrivate::FunctionPointer<Func1>::Object *receiver, Func1 slot)
+ template <typename Duration, typename Functor>
+ static inline void singleShot(Duration interval,
+ const typename QtPrivate::ContextTypeForFunctor<Functor>::ContextType *receiver,
+ Functor &&slot)
{
- singleShot(interval, defaultTypeFor(interval), receiver, slot);
+ singleShot(interval, defaultTypeFor(interval), receiver, std::forward<Functor>(slot));
}
- template <typename Duration, typename Func1>
- static inline void singleShot(Duration interval, Qt::TimerType timerType, const typename QtPrivate::FunctionPointer<Func1>::Object *receiver,
- Func1 slot)
+ template <typename Duration, typename Functor>
+ static inline void singleShot(Duration interval, Qt::TimerType timerType,
+ const typename QtPrivate::ContextTypeForFunctor<Functor>::ContextType *receiver,
+ Functor &&slot)
{
- typedef QtPrivate::FunctionPointer<Func1> SlotType;
-
- //compilation error if the slot has arguments.
- static_assert(int(SlotType::ArgumentCount) == 0,
- "The slot must not have any arguments.");
-
+ using Prototype = void(*)();
singleShotImpl(interval, timerType, receiver,
- new QtPrivate::QSlotObject<Func1, typename SlotType::Arguments, void>(slot));
- }
- // singleShot to a functor or function pointer (without context)
- 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, Func1 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, std::move(slot));
+ QtPrivate::makeCallableObject<Prototype>(std::forward<Functor>(slot)));
}
- // singleShot to a functor or function pointer (with context)
- 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, const QObject *context, Func1 slot)
+#endif
+
+ // singleShot without context
+ template <typename Duration, typename Functor>
+ static inline void singleShot(Duration interval, Functor &&slot)
{
- singleShot(interval, defaultTypeFor(interval), context, std::move(slot));
+ singleShot(interval, defaultTypeFor(interval), nullptr, std::forward<Functor>(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, const QObject *context, Func1 slot)
+ template <typename Duration, typename Functor>
+ static inline void singleShot(Duration interval, Qt::TimerType timerType, Functor &&slot)
{
- //compilation error if the slot has arguments.
- typedef QtPrivate::FunctionPointer<Func1> SlotType;
- static_assert(int(SlotType::ArgumentCount) <= 0, "The slot must not have any arguments.");
-
- singleShotImpl(interval, timerType, context,
- new QtPrivate::QFunctorSlotObject<Func1, 0,
- typename QtPrivate::List_Left<void, 0>::Value, void>(std::move(slot)));
+ singleShot(interval, timerType, nullptr, std::forward<Functor>(slot));
}
+#ifdef Q_QDOC
+ template <typename Functor>
+ QMetaObject::Connection callOnTimeout(Functor &&slot);
+ template <typename Functor>
+ QMetaObject::Connection callOnTimeout(const QObject *context, Functor &&slot, Qt::ConnectionType connectionType = Qt::AutoConnection);
+#else
template <typename ... Args>
QMetaObject::Connection callOnTimeout(Args && ...args)
{
@@ -143,10 +115,7 @@ Q_SIGNALS:
void timeout(QPrivateSignal);
public:
- void setInterval(std::chrono::milliseconds value)
- {
- setInterval(int(value.count()));
- }
+ void setInterval(std::chrono::milliseconds value);
std::chrono::milliseconds intervalAsDuration() const
{
@@ -160,18 +129,12 @@ public:
static void singleShot(std::chrono::milliseconds value, const QObject *receiver, const char *member)
{
- singleShot(int(value.count()), receiver, member);
+ singleShot(value, defaultTypeFor(value), receiver, member);
}
+ static void singleShot(std::chrono::milliseconds interval, Qt::TimerType timerType,
+ const QObject *receiver, const char *member);
- static void singleShot(std::chrono::milliseconds value, Qt::TimerType timerType, const QObject *receiver, const char *member)
- {
- singleShot(int(value.count()), timerType, receiver, member);
- }
-
- void start(std::chrono::milliseconds value)
- {
- start(int(value.count()));
- }
+ void start(std::chrono::milliseconds value);
protected:
void timerEvent(QTimerEvent *) override;
@@ -184,21 +147,40 @@ private:
inline void killTimer(int){}
static constexpr Qt::TimerType defaultTypeFor(int msecs) noexcept
- { return msecs >= 2000 ? Qt::CoarseTimer : Qt::PreciseTimer; }
+ { return defaultTypeFor(std::chrono::milliseconds{msecs}); }
+
+ static constexpr Qt::TimerType defaultTypeFor(std::chrono::milliseconds interval) noexcept
+ {
+ // coarse timers are worst in their first firing
+ // so we prefer a high precision timer for something that happens only once
+ // unless the timeout is too big, in which case we go for coarse anyway
+ using namespace std::chrono_literals;
+ return interval >= 2s ? Qt::CoarseTimer : Qt::PreciseTimer;
+ }
+
+ QT_CORE_INLINE_SINCE(6, 8)
static void singleShotImpl(int msec, Qt::TimerType timerType,
const QObject *receiver, QtPrivate::QSlotObjectBase *slotObj);
- static Qt::TimerType defaultTypeFor(std::chrono::milliseconds interval)
- { return defaultTypeFor(int(interval.count())); }
-
static void singleShotImpl(std::chrono::milliseconds interval, Qt::TimerType timerType,
- const QObject *receiver, QtPrivate::QSlotObjectBase *slotObj)
- {
- singleShotImpl(int(interval.count()),
- timerType, receiver, slotObj);
- }
+ const QObject *receiver, QtPrivate::QSlotObjectBase *slotObj);
};
+#if QT_CORE_INLINE_IMPL_SINCE(6, 8)
+void QTimer::singleShot(int msec, const QObject *receiver, const char *member)
+{ singleShot(std::chrono::milliseconds{msec}, receiver, member); }
+
+void QTimer::singleShot(int msec, Qt::TimerType timerType, const QObject *receiver,
+ const char *member)
+{ singleShot(std::chrono::milliseconds{msec}, timerType, receiver, member); }
+
+void QTimer::singleShotImpl(int msec, Qt::TimerType timerType,
+ const QObject *receiver, QtPrivate::QSlotObjectBase *slotObj)
+{
+ singleShotImpl(std::chrono::milliseconds{msec}, timerType, receiver, slotObj);
+}
+#endif
+
QT_END_NAMESPACE
#endif // QT_NO_QOBJECT
diff --git a/src/corelib/kernel/qtimer_p.h b/src/corelib/kernel/qtimer_p.h
index f283a264fa..9347f6c241 100644
--- a/src/corelib/kernel/qtimer_p.h
+++ b/src/corelib/kernel/qtimer_p.h
@@ -12,27 +12,61 @@
//
// We mean it.
//
+#include "qtimer.h"
+#include "qchronotimer.h"
+
#include "qobject_p.h"
#include "qproperty_p.h"
-#include "qtimer.h"
+#include "qttypetraits.h"
QT_BEGIN_NAMESPACE
class QTimerPrivate : public QObjectPrivate
{
- Q_DECLARE_PUBLIC(QTimer)
public:
+ QTimerPrivate(QTimer *qq)
+ : q(qq),
+ isQTimer(true)
+ {}
+
+ QTimerPrivate(std::chrono::nanoseconds nsec, QChronoTimer *qq)
+ : intervalDuration(nsec),
+ q(qq)
+ {
+ intervalDuration.notify();
+ }
+
static constexpr int INV_TIMER = -1; // invalid timer id
- void setInterval(int msec) { q_func()->setInterval(msec); }
- bool isActiveActualCalculation() const { return id >= 0; }
+ void setIntervalDuration(std::chrono::nanoseconds nsec)
+ {
+ if (isQTimer) {
+ const auto msec = std::chrono::ceil<std::chrono::milliseconds>(nsec);
+ static_cast<QTimer *>(q)->setInterval(msec);
+ } else {
+ static_cast<QChronoTimer *>(q)->setInterval(nsec);
+ }
+ }
+
+ void setInterval(int msec)
+ {
+ Q_ASSERT(isQTimer);
+ static_cast<QTimer *>(q)->setInterval(msec);
+ }
- int id = INV_TIMER;
+ bool isActive() const { return id > Qt::TimerId::Invalid; }
+
+ Qt::TimerId id = Qt::TimerId::Invalid;
Q_OBJECT_COMPAT_PROPERTY_WITH_ARGS(QTimerPrivate, int, inter, &QTimerPrivate::setInterval, 0)
+ Q_OBJECT_COMPAT_PROPERTY_WITH_ARGS(QTimerPrivate, std::chrono::nanoseconds, intervalDuration,
+ &QTimerPrivate::setIntervalDuration,
+ std::chrono::nanoseconds{0})
Q_OBJECT_BINDABLE_PROPERTY_WITH_ARGS(QTimerPrivate, bool, single, false)
Q_OBJECT_BINDABLE_PROPERTY_WITH_ARGS(QTimerPrivate, Qt::TimerType, type, Qt::CoarseTimer)
- Q_OBJECT_COMPUTED_PROPERTY(QTimerPrivate, bool, isActiveData,
- &QTimerPrivate::isActiveActualCalculation)
+ Q_OBJECT_COMPUTED_PROPERTY(QTimerPrivate, bool, isActiveData, &QTimerPrivate::isActive)
+
+ QObject *q;
+ const bool isQTimer = false; // true if q is a QTimer*
};
QT_END_NAMESPACE
diff --git a/src/corelib/kernel/qtimerinfo_unix.cpp b/src/corelib/kernel/qtimerinfo_unix.cpp
index 7ec23de488..b83f0194c2 100644
--- a/src/corelib/kernel/qtimerinfo_unix.cpp
+++ b/src/corelib/kernel/qtimerinfo_unix.cpp
@@ -10,13 +10,12 @@
#include "private/qobject_p.h"
#include "private/qabstracteventdispatcher_p.h"
-#ifdef QTIMERINFO_DEBUG
-# include <QDebug>
-# include <QThread>
-#endif
-
#include <sys/times.h>
+using namespace std::chrono;
+// Implied by "using namespace std::chrono", but be explicit about it, for grep-ability
+using namespace std::chrono_literals;
+
QT_BEGIN_NAMESPACE
Q_CORE_EXPORT bool qt_disable_lowpriority_timers=false;
@@ -26,175 +25,75 @@ Q_CORE_EXPORT bool qt_disable_lowpriority_timers=false;
* timerBitVec array is used for keeping track of timer identifiers.
*/
-QTimerInfoList::QTimerInfoList()
-{
-#if (_POSIX_MONOTONIC_CLOCK-0 <= 0) && !defined(Q_OS_MAC)
- if (!QElapsedTimer::isMonotonic()) {
- // not using monotonic timers, initialize the timeChanged() machinery
- previousTime = qt_gettime();
-
- tms unused;
- previousTicks = times(&unused);
-
- ticksPerSecond = sysconf(_SC_CLK_TCK);
- msPerTick = 1000/ticksPerSecond;
- } else {
- // detected monotonic timers
- previousTime.tv_sec = previousTime.tv_nsec = 0;
- previousTicks = 0;
- ticksPerSecond = 0;
- msPerTick = 0;
- }
-#endif
-
- firstTimerInfo = nullptr;
-}
-
-timespec QTimerInfoList::updateCurrentTime()
-{
- return (currentTime = qt_gettime());
-}
-
-#if ((_POSIX_MONOTONIC_CLOCK-0 <= 0) && !defined(Q_OS_MAC) && !defined(Q_OS_INTEGRITY)) || defined(QT_BOOTSTRAPPED)
+QTimerInfoList::QTimerInfoList() = default;
-timespec qAbsTimespec(const timespec &t)
+steady_clock::time_point QTimerInfoList::updateCurrentTime() const
{
- timespec tmp = t;
- if (tmp.tv_sec < 0) {
- tmp.tv_sec = -tmp.tv_sec - 1;
- tmp.tv_nsec -= 1000000000;
- }
- if (tmp.tv_sec == 0 && tmp.tv_nsec < 0) {
- tmp.tv_nsec = -tmp.tv_nsec;
- }
- return normalizedTimespec(tmp);
+ currentTime = steady_clock::now();
+ return currentTime;
}
-/*
- Returns \c true if the real time clock has changed by more than 10%
- relative to the processor time since the last time this function was
- called. This presumably means that the system time has been changed.
-
- If /a delta is nonzero, delta is set to our best guess at how much the system clock was changed.
-*/
-bool QTimerInfoList::timeChanged(timespec *delta)
-{
- struct tms unused;
- clock_t currentTicks = times(&unused);
-
- clock_t elapsedTicks = currentTicks - previousTicks;
- timespec elapsedTime = currentTime - previousTime;
-
- timespec elapsedTimeTicks;
- elapsedTimeTicks.tv_sec = elapsedTicks / ticksPerSecond;
- elapsedTimeTicks.tv_nsec = (((elapsedTicks * 1000) / ticksPerSecond) % 1000) * 1000 * 1000;
-
- timespec dummy;
- if (!delta)
- delta = &dummy;
- *delta = elapsedTime - elapsedTimeTicks;
-
- previousTicks = currentTicks;
- previousTime = currentTime;
-
- // If tick drift is more than 10% off compared to realtime, we assume that the clock has
- // been set. Of course, we have to allow for the tick granularity as well.
- timespec tickGranularity;
- tickGranularity.tv_sec = 0;
- tickGranularity.tv_nsec = msPerTick * 1000 * 1000;
- return elapsedTimeTicks < ((qAbsTimespec(*delta) - tickGranularity) * 10);
-}
+/*! \internal
+ Updates the currentTime member to the current time, and returns \c true if
+ the first timer's timeout is in the future (after currentTime).
-/*
- repair broken timer
+ The list is sorted by timeout, thus it's enough to check the first timer only.
*/
-void QTimerInfoList::timerRepair(const timespec &diff)
-{
- // repair all timers
- for (int i = 0; i < size(); ++i) {
- QTimerInfo *t = at(i);
- t->timeout = t->timeout + diff;
- }
-}
-
-void QTimerInfoList::repairTimersIfNeeded()
-{
- if (QElapsedTimer::isMonotonic())
- return;
- timespec delta;
- if (timeChanged(&delta))
- timerRepair(delta);
-}
-
-#else // !(_POSIX_MONOTONIC_CLOCK-0 <= 0) && !defined(QT_BOOTSTRAPPED)
-
-void QTimerInfoList::repairTimersIfNeeded()
+bool QTimerInfoList::hasPendingTimers()
{
+ if (timers.isEmpty())
+ return false;
+ return updateCurrentTime() < timers.at(0)->timeout;
}
-#endif
+static bool byTimeout(const QTimerInfo *a, const QTimerInfo *b)
+{ return a->timeout < b->timeout; };
/*
insert timer info into list
*/
void QTimerInfoList::timerInsert(QTimerInfo *ti)
{
- int index = size();
- while (index--) {
- const QTimerInfo * const t = at(index);
- if (!(ti->timeout < t->timeout))
- break;
- }
- insert(index+1, ti);
-}
-
-inline timespec &operator+=(timespec &t1, int ms)
-{
- t1.tv_sec += ms / 1000;
- t1.tv_nsec += ms % 1000 * 1000 * 1000;
- return normalizedTimespec(t1);
-}
-
-inline timespec operator+(const timespec &t1, int ms)
-{
- timespec t2 = t1;
- return t2 += ms;
+ timers.insert(std::upper_bound(timers.cbegin(), timers.cend(), ti, byTimeout),
+ ti);
}
-static constexpr timespec roundToMillisecond(timespec val)
+static constexpr milliseconds roundToMillisecond(nanoseconds val)
{
// always round up
// worst case scenario is that the first trigger of a 1-ms timer is 0.999 ms late
-
- int ns = val.tv_nsec % (1000 * 1000);
- if (ns)
- val.tv_nsec += 1000 * 1000 - ns;
- return normalizedTimespec(val);
-}
-static_assert(roundToMillisecond({0, 0}) == timespec{0, 0});
-static_assert(roundToMillisecond({0, 1}) == timespec{0, 1'000'000});
-static_assert(roundToMillisecond({0, 999'999}) == timespec{0, 1'000'000});
-static_assert(roundToMillisecond({0, 1'000'000}) == timespec{0, 1'000'000});
-static_assert(roundToMillisecond({0, 999'999'999}) == timespec{1, 0});
-static_assert(roundToMillisecond({1, 0}) == timespec{1, 0});
-
-#ifdef QTIMERINFO_DEBUG
-QDebug operator<<(QDebug s, timeval tv)
-{
- QDebugStateSaver saver(s);
- s.nospace() << tv.tv_sec << "." << qSetFieldWidth(6) << qSetPadChar(QChar(48)) << tv.tv_usec << Qt::reset;
- return s;
+ return ceil<milliseconds>(val);
}
-QDebug operator<<(QDebug s, Qt::TimerType t)
+
+static_assert(roundToMillisecond(0ns) == 0ms);
+static_assert(roundToMillisecond(1ns) == 1ms);
+static_assert(roundToMillisecond(999'999ns) == 1ms);
+static_assert(roundToMillisecond(1'000'000ns) == 1ms);
+static_assert(roundToMillisecond(999'000'000ns) == 999ms);
+static_assert(roundToMillisecond(999'000'001ns) == 1000ms);
+static_assert(roundToMillisecond(999'999'999ns) == 1000ms);
+static_assert(roundToMillisecond(1s) == 1s);
+
+static constexpr seconds roundToSecs(nanoseconds interval)
{
- QDebugStateSaver saver(s);
- s << (t == Qt::PreciseTimer ? "P" :
- t == Qt::CoarseTimer ? "C" : "VC");
- return s;
+ // The very coarse timer is based on full second precision, so we want to
+ // round the interval to the closest second, rounding 500ms up to 1s.
+ //
+ // std::chrono::round() wouldn't work with all multiples of 500 because for the
+ // middle point it would round to even:
+ // value round() wanted
+ // 500 0 1
+ // 1500 2 2
+ // 2500 2 3
+
+ auto secs = duration_cast<seconds>(interval);
+ const nanoseconds frac = interval - secs;
+ if (frac >= 500ms)
+ ++secs;
+ return secs;
}
-#endif
-static void calculateCoarseTimerTimeout(QTimerInfo *t, timespec currentTime)
+static void calculateCoarseTimerTimeout(QTimerInfo *t, steady_clock::time_point now)
{
// The coarse timer works like this:
// - interval under 40 ms: round to even
@@ -212,216 +111,186 @@ static void calculateCoarseTimerTimeout(QTimerInfo *t, timespec currentTime)
//
// The objective is to make most timers wake up at the same time, thereby reducing CPU wakeups.
- uint interval = uint(t->interval);
- Q_ASSERT(interval >= 20);
+ Q_ASSERT(t->interval >= 20ms);
+
+ const auto timeoutInSecs = time_point_cast<seconds>(t->timeout);
+
+ auto recalculate = [&](const milliseconds frac) {
+ t->timeout = timeoutInSecs + frac;
+ if (t->timeout < now)
+ t->timeout += t->interval;
+ };
+
// Calculate how much we can round and still keep within 5% error
- uint absMaxRounding = interval / 20;
+ milliseconds interval = roundToMillisecond(t->interval);
+ const milliseconds absMaxRounding = interval / 20;
- using namespace std::chrono;
- uint msec = duration_cast<milliseconds>(nanoseconds{t->timeout.tv_nsec}).count();
+ auto fracMsec = duration_cast<milliseconds>(t->timeout - timeoutInSecs);
- if (interval < 100 && interval != 25 && interval != 50 && interval != 75) {
+ if (interval < 100ms && interval != 25ms && interval != 50ms && interval != 75ms) {
+ auto fracCount = fracMsec.count();
// special mode for timers of less than 100 ms
- if (interval < 50) {
+ if (interval < 50ms) {
// round to even
// round towards multiples of 50 ms
- bool roundUp = (msec % 50) >= 25;
- msec >>= 1;
- msec |= uint(roundUp);
- msec <<= 1;
+ bool roundUp = (fracCount % 50) >= 25;
+ fracCount >>= 1;
+ fracCount |= roundUp;
+ fracCount <<= 1;
} else {
// round to multiple of 4
// round towards multiples of 100 ms
- bool roundUp = (msec % 100) >= 50;
- msec >>= 2;
- msec |= uint(roundUp);
- msec <<= 2;
- }
- } else {
- uint min = qMax<int>(0, msec - absMaxRounding);
- uint max = qMin(1000u, msec + absMaxRounding);
-
- // find the boundary that we want, according to the rules above
- // extra rules:
- // 1) whatever the interval, we'll take any round-to-the-second timeout
- if (min == 0) {
- msec = 0;
- goto recalculate;
- } else if (max == 1000) {
- msec = 1000;
- goto recalculate;
+ bool roundUp = (fracCount % 100) >= 50;
+ fracCount >>= 2;
+ fracCount |= roundUp;
+ fracCount <<= 2;
}
+ fracMsec = milliseconds{fracCount};
+ recalculate(fracMsec);
+ return;
+ }
- uint wantedBoundaryMultiple;
-
- // 2) if the interval is a multiple of 500 ms and > 5000 ms, we'll always round
- // towards a round-to-the-second
- // 3) if the interval is a multiple of 500 ms, we'll round towards the nearest
- // multiple of 500 ms
- if ((interval % 500) == 0) {
- if (interval >= 5000) {
- msec = msec >= 500 ? max : min;
- goto recalculate;
- } else {
- wantedBoundaryMultiple = 500;
- }
- } else if ((interval % 50) == 0) {
- // 4) same for multiples of 250, 200, 100, 50
- uint mult50 = interval / 50;
- if ((mult50 % 4) == 0) {
- // multiple of 200
- wantedBoundaryMultiple = 200;
- } else if ((mult50 % 2) == 0) {
- // multiple of 100
- wantedBoundaryMultiple = 100;
- } else if ((mult50 % 5) == 0) {
- // multiple of 250
- wantedBoundaryMultiple = 250;
- } else {
- // multiple of 50
- wantedBoundaryMultiple = 50;
- }
- } else {
- wantedBoundaryMultiple = 25;
- }
+ milliseconds min = std::max(0ms, fracMsec - absMaxRounding);
+ milliseconds max = std::min(1000ms, fracMsec + absMaxRounding);
- uint base = msec / wantedBoundaryMultiple * wantedBoundaryMultiple;
- uint middlepoint = base + wantedBoundaryMultiple / 2;
- if (msec < middlepoint)
- msec = qMax(base, min);
- else
- msec = qMin(base + wantedBoundaryMultiple, max);
+ // find the boundary that we want, according to the rules above
+ // extra rules:
+ // 1) whatever the interval, we'll take any round-to-the-second timeout
+ if (min == 0ms) {
+ fracMsec = 0ms;
+ recalculate(fracMsec);
+ return;
+ } else if (max == 1000ms) {
+ fracMsec = 1000ms;
+ recalculate(fracMsec);
+ return;
}
-recalculate:
- if (msec == 1000u) {
- ++t->timeout.tv_sec;
- t->timeout.tv_nsec = 0;
- } else {
- t->timeout.tv_nsec = nanoseconds{milliseconds{msec}}.count();
+ milliseconds wantedBoundaryMultiple{25};
+
+ // 2) if the interval is a multiple of 500 ms and > 5000 ms, we'll always round
+ // towards a round-to-the-second
+ // 3) if the interval is a multiple of 500 ms, we'll round towards the nearest
+ // multiple of 500 ms
+ if ((interval % 500) == 0ms) {
+ if (interval >= 5s) {
+ fracMsec = fracMsec >= 500ms ? max : min;
+ recalculate(fracMsec);
+ return;
+ } else {
+ wantedBoundaryMultiple = 500ms;
+ }
+ } else if ((interval % 50) == 0ms) {
+ // 4) same for multiples of 250, 200, 100, 50
+ milliseconds mult50 = interval / 50;
+ if ((mult50 % 4) == 0ms) {
+ // multiple of 200
+ wantedBoundaryMultiple = 200ms;
+ } else if ((mult50 % 2) == 0ms) {
+ // multiple of 100
+ wantedBoundaryMultiple = 100ms;
+ } else if ((mult50 % 5) == 0ms) {
+ // multiple of 250
+ wantedBoundaryMultiple = 250ms;
+ } else {
+ // multiple of 50
+ wantedBoundaryMultiple = 50ms;
+ }
}
- if (t->timeout < currentTime)
- t->timeout += interval;
+ milliseconds base = (fracMsec / wantedBoundaryMultiple) * wantedBoundaryMultiple;
+ milliseconds middlepoint = base + wantedBoundaryMultiple / 2;
+ if (fracMsec < middlepoint)
+ fracMsec = qMax(base, min);
+ else
+ fracMsec = qMin(base + wantedBoundaryMultiple, max);
+
+ recalculate(fracMsec);
}
-static void calculateNextTimeout(QTimerInfo *t, timespec currentTime)
+static void calculateNextTimeout(QTimerInfo *t, steady_clock::time_point now)
{
switch (t->timerType) {
case Qt::PreciseTimer:
case Qt::CoarseTimer:
t->timeout += t->interval;
- if (t->timeout < currentTime) {
- t->timeout = currentTime;
+ if (t->timeout < now) {
+ t->timeout = now;
t->timeout += t->interval;
}
-#ifdef QTIMERINFO_DEBUG
- t->expected += t->interval;
- if (t->expected < currentTime) {
- t->expected = currentTime;
- t->expected += t->interval;
- }
-#endif
if (t->timerType == Qt::CoarseTimer)
- calculateCoarseTimerTimeout(t, currentTime);
+ calculateCoarseTimerTimeout(t, now);
return;
case Qt::VeryCoarseTimer:
- // we don't need to take care of the microsecond component of t->interval
- t->timeout.tv_sec += t->interval;
- if (t->timeout.tv_sec <= currentTime.tv_sec)
- t->timeout.tv_sec = currentTime.tv_sec + t->interval;
-#ifdef QTIMERINFO_DEBUG
- t->expected.tv_sec += t->interval;
- if (t->expected.tv_sec <= currentTime.tv_sec)
- t->expected.tv_sec = currentTime.tv_sec + t->interval;
-#endif
- return;
+ // t->interval already rounded to full seconds in registerTimer()
+ t->timeout += t->interval;
+ if (t->timeout <= now)
+ t->timeout = time_point_cast<seconds>(now + t->interval);
+ break;
}
-
-#ifdef QTIMERINFO_DEBUG
- if (t->timerType != Qt::PreciseTimer)
- qDebug() << "timer" << t->timerType << Qt::hex << t->id << Qt::dec << "interval" << t->interval
- << "originally expected at" << t->expected << "will fire at" << t->timeout
- << "or" << (t->timeout - t->expected) << "s late";
-#endif
}
/*
- Returns the time to wait for the next timer, or null if no timers
- are waiting.
-*/
-bool QTimerInfoList::timerWait(timespec &tm)
+ Returns the time to wait for the first timer that has not been activated yet,
+ otherwise returns std::nullopt.
+ */
+std::optional<QTimerInfoList::Duration> QTimerInfoList::timerWait()
{
- timespec currentTime = updateCurrentTime();
- repairTimersIfNeeded();
+ steady_clock::time_point now = updateCurrentTime();
+ auto isWaiting = [](QTimerInfo *tinfo) { return !tinfo->activateRef; };
// Find first waiting timer not already active
- QTimerInfo *t = nullptr;
- for (QTimerInfoList::const_iterator it = constBegin(); it != constEnd(); ++it) {
- if (!(*it)->activateRef) {
- t = *it;
- break;
- }
- }
-
- if (!t)
- return false;
-
- if (currentTime < t->timeout) {
- // time to wait
- tm = roundToMillisecond(t->timeout - currentTime);
- } else {
- // no time to wait
- tm.tv_sec = 0;
- tm.tv_nsec = 0;
- }
-
- return true;
+ auto it = std::find_if(timers.cbegin(), timers.cend(), isWaiting);
+ if (it == timers.cend())
+ return std::nullopt;
+
+ Duration timeToWait = (*it)->timeout - now;
+ if (timeToWait > 0ns)
+ return roundToMillisecond(timeToWait);
+ return 0ms;
}
/*
Returns the timer's remaining time in milliseconds with the given timerId.
- If the timer id is not found in the list, the returned value will be -1.
+ If the timer id is not found in the list, the returned value will be \c{Duration::min()}.
If the timer is overdue, the returned value will be 0.
*/
-qint64 QTimerInfoList::timerRemainingTime(int timerId)
+QTimerInfoList::Duration QTimerInfoList::remainingDuration(Qt::TimerId timerId) const
{
- timespec currentTime = updateCurrentTime();
- repairTimersIfNeeded();
- timespec tm = {0, 0};
-
- for (const auto *t : std::as_const(*this)) {
- if (t->id == timerId) {
- if (currentTime < t->timeout) {
- // time to wait
- tm = roundToMillisecond(t->timeout - currentTime);
- using namespace std::chrono;
- const auto dur = duration_cast<milliseconds>(seconds{tm.tv_sec} + nanoseconds{tm.tv_nsec});
- return dur.count();
- } else {
- return 0;
- }
- }
- }
+ const steady_clock::time_point now = updateCurrentTime();
+ auto it = findTimerById(timerId);
+ if (it == timers.cend()) {
#ifndef QT_NO_DEBUG
- qWarning("QTimerInfoList::timerRemainingTime: timer id %i not found", timerId);
+ qWarning("QTimerInfoList::timerRemainingTime: timer id %i not found", int(timerId));
#endif
+ return Duration::min();
+ }
- return -1;
+ const QTimerInfo *t = *it;
+ if (now < t->timeout) // time to wait
+ return t->timeout - now;
+ return 0ms;
}
-void QTimerInfoList::registerTimer(int timerId, qint64 interval, Qt::TimerType timerType, QObject *object)
+void QTimerInfoList::registerTimer(Qt::TimerId timerId, QTimerInfoList::Duration interval,
+ Qt::TimerType timerType, QObject *object)
{
- QTimerInfo *t = new QTimerInfo;
- t->id = timerId;
- t->interval = interval;
- t->timerType = timerType;
- t->obj = object;
- t->activateRef = nullptr;
+ // correct the timer type first
+ if (timerType == Qt::CoarseTimer) {
+ // this timer has up to 5% coarseness
+ // so our boundaries are 20 ms and 20 s
+ // below 20 ms, 5% inaccuracy is below 1 ms, so we convert to high precision
+ // above 20 s, 5% inaccuracy is above 1 s, so we convert to VeryCoarseTimer
+ if (interval >= 20s)
+ timerType = Qt::VeryCoarseTimer;
+ else if (interval <= 20ms)
+ timerType = Qt::PreciseTimer;
+ }
- timespec expected = updateCurrentTime() + interval;
+ QTimerInfo *t = new QTimerInfo(timerId, interval, timerType, object);
+ QTimerInfo::TimePoint expected = updateCurrentTime() + interval;
switch (timerType) {
case Qt::PreciseTimer:
@@ -431,103 +300,69 @@ void QTimerInfoList::registerTimer(int timerId, qint64 interval, Qt::TimerType t
break;
case Qt::CoarseTimer:
- // this timer has up to 5% coarseness
- // so our boundaries are 20 ms and 20 s
- // below 20 ms, 5% inaccuracy is below 1 ms, so we convert to high precision
- // above 20 s, 5% inaccuracy is above 1 s, so we convert to VeryCoarseTimer
- if (interval >= 20000) {
- t->timerType = Qt::VeryCoarseTimer;
- } else {
- t->timeout = expected;
- if (interval <= 20) {
- t->timerType = Qt::PreciseTimer;
- // no adjustment is necessary
- } else if (interval <= 20000) {
- calculateCoarseTimerTimeout(t, currentTime);
- }
- break;
- }
- Q_FALLTHROUGH();
+ t->timeout = expected;
+ t->interval = roundToMillisecond(interval);
+ calculateCoarseTimerTimeout(t, currentTime);
+ break;
+
case Qt::VeryCoarseTimer:
- // the very coarse timer is based on full second precision,
- // so we keep the interval in seconds (round to closest second)
- t->interval /= 500;
- t->interval += 1;
- t->interval >>= 1;
- t->timeout.tv_sec = currentTime.tv_sec + t->interval;
- t->timeout.tv_nsec = 0;
-
- // if we're past the half-second mark, increase the timeout again
- using namespace std::chrono;
- if (currentTime.tv_nsec > nanoseconds{500ms}.count())
- ++t->timeout.tv_sec;
+ t->interval = roundToSecs(t->interval);
+ const auto currentTimeInSecs = floor<seconds>(currentTime);
+ t->timeout = currentTimeInSecs + t->interval;
+ // If we're past the half-second mark, increase the timeout again
+ if (currentTime - currentTimeInSecs > 500ms)
+ t->timeout += 1s;
}
timerInsert(t);
-
-#ifdef QTIMERINFO_DEBUG
- t->expected = expected;
- t->cumulativeError = 0;
- t->count = 0;
- if (t->timerType != Qt::PreciseTimer)
- qDebug() << "timer" << t->timerType << Qt::hex <<t->id << Qt::dec << "interval" << t->interval << "expected at"
- << t->expected << "will fire first at" << t->timeout;
-#endif
}
-bool QTimerInfoList::unregisterTimer(int timerId)
+bool QTimerInfoList::unregisterTimer(Qt::TimerId timerId)
{
+ auto it = findTimerById(timerId);
+ if (it == timers.cend())
+ return false; // id not found
+
// set timer inactive
- for (int i = 0; i < size(); ++i) {
- QTimerInfo *t = at(i);
- if (t->id == timerId) {
- // found it
- removeAt(i);
- if (t == firstTimerInfo)
- firstTimerInfo = nullptr;
- if (t->activateRef)
- *(t->activateRef) = nullptr;
- delete t;
- return true;
- }
- }
- // id not found
- return false;
+ QTimerInfo *t = *it;
+ if (t == firstTimerInfo)
+ firstTimerInfo = nullptr;
+ if (t->activateRef)
+ *(t->activateRef) = nullptr;
+ delete t;
+ timers.erase(it);
+ return true;
}
bool QTimerInfoList::unregisterTimers(QObject *object)
{
- if (isEmpty())
+ if (timers.isEmpty())
return false;
- for (int i = 0; i < size(); ++i) {
- QTimerInfo *t = at(i);
- if (t->obj == object) {
- // object found
- removeAt(i);
- if (t == firstTimerInfo)
- firstTimerInfo = nullptr;
- if (t->activateRef)
- *(t->activateRef) = nullptr;
- delete t;
- // move back one so that we don't skip the new current item
- --i;
- }
- }
- return true;
+
+ auto associatedWith = [this](QObject *o) {
+ return [this, o](auto &t) {
+ if (t->obj == o) {
+ if (t == firstTimerInfo)
+ firstTimerInfo = nullptr;
+ if (t->activateRef)
+ *(t->activateRef) = nullptr;
+ delete t;
+ return true;
+ }
+ return false;
+ };
+ };
+
+ qsizetype count = timers.removeIf(associatedWith(object));
+ return count > 0;
}
-QList<QAbstractEventDispatcher::TimerInfo> QTimerInfoList::registeredTimers(QObject *object) const
+auto QTimerInfoList::registeredTimers(QObject *object) const -> QList<TimerInfo>
{
- QList<QAbstractEventDispatcher::TimerInfo> list;
- for (int i = 0; i < size(); ++i) {
- const QTimerInfo * const t = at(i);
- if (t->obj == object) {
- list << QAbstractEventDispatcher::TimerInfo(t->id,
- (t->timerType == Qt::VeryCoarseTimer
- ? t->interval * 1000
- : t->interval),
- t->timerType);
- }
+ QList<TimerInfo> list;
+ for (const auto &t : timers) {
+ if (t->obj == object)
+ list.emplaceBack(TimerInfo{t->interval, t->id, t->timerType});
}
return list;
}
@@ -537,31 +372,27 @@ QList<QAbstractEventDispatcher::TimerInfo> QTimerInfoList::registeredTimers(QObj
*/
int QTimerInfoList::activateTimers()
{
- if (qt_disable_lowpriority_timers || isEmpty())
+ if (qt_disable_lowpriority_timers || timers.isEmpty())
return 0; // nothing to do
- int n_act = 0, maxCount = 0;
firstTimerInfo = nullptr;
- timespec currentTime = updateCurrentTime();
- // qDebug() << "Thread" << QThread::currentThreadId() << "woken up at" << currentTime;
- repairTimersIfNeeded();
-
-
+ const steady_clock::time_point now = updateCurrentTime();
+ // qDebug() << "Thread" << QThread::currentThreadId() << "woken up at" << now;
// Find out how many timer have expired
- for (QTimerInfoList::const_iterator it = constBegin(); it != constEnd(); ++it) {
- if (currentTime < (*it)->timeout)
- break;
- maxCount++;
- }
+ auto stillActive = [&now](const QTimerInfo *t) { return now < t->timeout; };
+ // Find first one still active (list is sorted by timeout)
+ auto it = std::find_if(timers.cbegin(), timers.cend(), stillActive);
+ auto maxCount = it - timers.cbegin();
+ int n_act = 0;
//fire the timers.
while (maxCount--) {
- if (isEmpty())
+ if (timers.isEmpty())
break;
- QTimerInfo *currentTimerInfo = constFirst();
- if (currentTime < currentTimerInfo->timeout)
+ QTimerInfo *currentTimerInfo = timers.constFirst();
+ if (now < currentTimerInfo->timeout)
break; // no timer has expired
if (!firstTimerInfo) {
@@ -574,42 +405,24 @@ int QTimerInfoList::activateTimers()
firstTimerInfo = currentTimerInfo;
}
- // remove from list
- removeFirst();
-
-#ifdef QTIMERINFO_DEBUG
- float diff;
- if (currentTime < currentTimerInfo->expected) {
- // early
- timeval early = currentTimerInfo->expected - currentTime;
- diff = -(early.tv_sec + early.tv_usec / 1000000.0);
- } else {
- timeval late = currentTime - currentTimerInfo->expected;
- diff = late.tv_sec + late.tv_usec / 1000000.0;
- }
- currentTimerInfo->cumulativeError += diff;
- ++currentTimerInfo->count;
- if (currentTimerInfo->timerType != Qt::PreciseTimer)
- qDebug() << "timer" << currentTimerInfo->timerType << Qt::hex << currentTimerInfo->id << Qt::dec << "interval"
- << currentTimerInfo->interval << "firing at" << currentTime
- << "(orig" << currentTimerInfo->expected << "scheduled at" << currentTimerInfo->timeout
- << ") off by" << diff << "activation" << currentTimerInfo->count
- << "avg error" << (currentTimerInfo->cumulativeError / currentTimerInfo->count);
-#endif
-
// determine next timeout time
- calculateNextTimeout(currentTimerInfo, currentTime);
+ calculateNextTimeout(currentTimerInfo, now);
+ if (timers.size() > 1) {
+ // Find where "currentTimerInfo" should be in the list so as
+ // to keep the list ordered by timeout
+ auto afterCurrentIt = timers.begin() + 1;
+ auto iter = std::upper_bound(afterCurrentIt, timers.end(), currentTimerInfo, byTimeout);
+ currentTimerInfo = *std::rotate(timers.begin(), afterCurrentIt, iter);
+ }
- // reinsert timer
- timerInsert(currentTimerInfo);
- if (currentTimerInfo->interval > 0)
+ if (currentTimerInfo->interval > 0ms)
n_act++;
// Send event, but don't allow it to recurse:
if (!currentTimerInfo->activateRef) {
currentTimerInfo->activateRef = &currentTimerInfo;
- QTimerEvent e(currentTimerInfo->id);
+ QTimerEvent e(qToUnderlying(currentTimerInfo->id));
QCoreApplication::sendEvent(currentTimerInfo->obj, &e);
// Storing currentTimerInfo's address in its activateRef allows the
diff --git a/src/corelib/kernel/qtimerinfo_unix_p.h b/src/corelib/kernel/qtimerinfo_unix_p.h
index c33bddbd2f..293e9c4d4e 100644
--- a/src/corelib/kernel/qtimerinfo_unix_p.h
+++ b/src/corelib/kernel/qtimerinfo_unix_p.h
@@ -17,65 +17,76 @@
#include <QtCore/private/qglobal_p.h>
-// #define QTIMERINFO_DEBUG
-
#include "qabstracteventdispatcher.h"
-#include <sys/time.h> // struct timeval
+#include <sys/time.h> // struct timespec
+#include <chrono>
QT_BEGIN_NAMESPACE
// internal timer info
-struct QTimerInfo {
- int id; // - timer identifier
- qint64 interval; // - timer interval in milliseconds
+struct QTimerInfo
+{
+ using Duration = QAbstractEventDispatcher::Duration;
+ using TimePoint = std::chrono::time_point<std::chrono::steady_clock, Duration>;
+ QTimerInfo(Qt::TimerId timerId, Duration interval, Qt::TimerType type, QObject *obj)
+ : interval(interval), id(timerId), timerType(type), obj(obj)
+ {
+ }
+
+ TimePoint timeout = {}; // - when to actually fire
+ Duration interval = Duration{-1}; // - timer interval
+ Qt::TimerId id = Qt::TimerId::Invalid; // - timer identifier
Qt::TimerType timerType; // - timer type
- timespec timeout; // - when to actually fire
- QObject *obj; // - object to receive event
- QTimerInfo **activateRef; // - ref from activateTimers
-
-#ifdef QTIMERINFO_DEBUG
- timeval expected; // when timer is expected to fire
- float cumulativeError;
- uint count;
-#endif
+ QObject *obj = nullptr; // - object to receive event
+ QTimerInfo **activateRef = nullptr; // - ref from activateTimers
};
-class Q_CORE_EXPORT QTimerInfoList : public QList<QTimerInfo*>
+class Q_CORE_EXPORT QTimerInfoList
{
-#if ((_POSIX_MONOTONIC_CLOCK-0 <= 0) && !defined(Q_OS_MAC)) || defined(QT_BOOTSTRAPPED)
- timespec previousTime;
- clock_t previousTicks;
- int ticksPerSecond;
- int msPerTick;
-
- bool timeChanged(timespec *delta);
- void timerRepair(const timespec &);
-#endif
-
- // state variables used by activateTimers()
- QTimerInfo *firstTimerInfo;
-
public:
+ using Duration = QAbstractEventDispatcher::Duration;
+ using TimerInfo = QAbstractEventDispatcher::TimerInfoV2;
QTimerInfoList();
- timespec currentTime;
- timespec updateCurrentTime();
-
- // must call updateCurrentTime() first!
- void repairTimersIfNeeded();
+ mutable std::chrono::steady_clock::time_point currentTime;
- bool timerWait(timespec &);
+ std::optional<Duration> timerWait();
void timerInsert(QTimerInfo *);
- qint64 timerRemainingTime(int timerId);
+ Duration remainingDuration(Qt::TimerId timerId) const;
- void registerTimer(int timerId, qint64 interval, Qt::TimerType timerType, QObject *object);
- bool unregisterTimer(int timerId);
+ void registerTimer(Qt::TimerId timerId, Duration interval,
+ Qt::TimerType timerType, QObject *object);
+ bool unregisterTimer(Qt::TimerId timerId);
bool unregisterTimers(QObject *object);
- QList<QAbstractEventDispatcher::TimerInfo> registeredTimers(QObject *object) const;
+ QList<TimerInfo> registeredTimers(QObject *object) const;
int activateTimers();
+ bool hasPendingTimers();
+
+ void clearTimers()
+ {
+ qDeleteAll(timers);
+ timers.clear();
+ }
+
+ bool isEmpty() const { return timers.empty(); }
+
+ qsizetype size() const { return timers.size(); }
+
+ auto findTimerById(Qt::TimerId timerId) const
+ {
+ auto matchesId = [timerId](const auto &t) { return t->id == timerId; };
+ return std::find_if(timers.cbegin(), timers.cend(), matchesId);
+ }
+
+private:
+ std::chrono::steady_clock::time_point updateCurrentTime() const;
+
+ // state variables used by activateTimers()
+ QTimerInfo *firstTimerInfo = nullptr;
+ QList<QTimerInfo *> timers;
};
QT_END_NAMESPACE
diff --git a/src/corelib/kernel/qtmetamacros.h b/src/corelib/kernel/qtmetamacros.h
index 1ce6a49bde..ae68abcfee 100644
--- a/src/corelib/kernel/qtmetamacros.h
+++ b/src/corelib/kernel/qtmetamacros.h
@@ -103,6 +103,8 @@ QT_BEGIN_NAMESPACE
# endif
#elif defined(Q_CC_GNU) && Q_CC_GNU >= 501
# define Q_OBJECT_NO_OVERRIDE_WARNING QT_WARNING_DISABLE_GCC("-Wsuggest-override")
+#elif defined(Q_CC_MSVC)
+# define Q_OBJECT_NO_OVERRIDE_WARNING QT_WARNING_DISABLE_MSVC(26433)
#else
# define Q_OBJECT_NO_OVERRIDE_WARNING
#endif
diff --git a/src/corelib/kernel/qtmochelpers.h b/src/corelib/kernel/qtmochelpers.h
index b6b7ca6cbd..9d75177645 100644
--- a/src/corelib/kernel/qtmochelpers.h
+++ b/src/corelib/kernel/qtmochelpers.h
@@ -26,37 +26,38 @@
QT_BEGIN_NAMESPACE
namespace QtMocHelpers {
-template <uint... Nx> struct StringData
-{
- static constexpr size_t calculateStringSize()
- {
- // same as:
- // return (0 + ... + Nx);
- // but not using the fold expression to avoid exceeding compiler limits
- size_t total = 0;
- uint sizes[] = { Nx... };
- for (uint n : sizes)
- total += n;
- return total;
- }
+// The maximum Size of a string literal is 2 GB on 32-bit and 4 GB on 64-bit
+// (but the compiler is likely to give up before you get anywhere near that much)
+static constexpr size_t MaxStringSize =
+ (std::min)(size_t((std::numeric_limits<uint>::max)()),
+ size_t((std::numeric_limits<qsizetype>::max)()));
- // The maximum Size of a string literal is 2 GB on 32-bit and 4 GB on 64-bit
- // (but the compiler is likely to give up before you get anywhere near that much)
- static constexpr size_t MaxStringSize =
- (std::min)(size_t((std::numeric_limits<uint>::max)()),
- size_t((std::numeric_limits<qsizetype>::max)()));
+template <uint... Nx> constexpr size_t stringDataSizeHelper(std::integer_sequence<uint, Nx...>)
+{
+ // same as:
+ // return (0 + ... + Nx);
+ // but not using the fold expression to avoid exceeding compiler limits
+ size_t total = 0;
+ uint sizes[] = { Nx... };
+ for (uint n : sizes)
+ total += n;
+ return total;
+}
- static constexpr size_t StringSize = calculateStringSize();
+template <int Count, size_t StringSize> struct StringData
+{
static_assert(StringSize <= MaxStringSize, "Meta Object data is too big");
-
- uint offsetsAndSizes[2 * sizeof...(Nx)] = {};
+ uint offsetsAndSizes[Count] = {};
char stringdata0[StringSize] = {};
constexpr StringData() = default;
};
template <uint... Nx> constexpr auto stringData(const char (&...strings)[Nx])
{
- StringData<Nx...> result;
+ constexpr size_t StringSize = stringDataSizeHelper<Nx...>({});
+ constexpr size_t Count = 2 * sizeof...(Nx);
+
+ StringData<Count, StringSize> result;
const char *inputs[] = { strings... };
uint sizes[] = { Nx... };
@@ -75,10 +76,7 @@ template <uint... Nx> constexpr auto stringData(const char (&...strings)[Nx])
return result;
}
-#if !defined(Q_CC_GNU_ONLY) || Q_CC_GNU_ONLY >= 1000
-// It looks like there's a bug in GCC 9
# define QT_MOC_HAS_STRINGDATA 1
-#endif
} // namespace QtMocHelpers
QT_END_NAMESPACE
diff --git a/src/corelib/kernel/qtranslator.cpp b/src/corelib/kernel/qtranslator.cpp
index 521503b96b..ec92404a15 100644
--- a/src/corelib/kernel/qtranslator.cpp
+++ b/src/corelib/kernel/qtranslator.cpp
@@ -22,7 +22,7 @@
#include "qendian.h"
#include "qresource.h"
-#if defined(Q_OS_UNIX) && !defined(Q_OS_NACL) && !defined(Q_OS_INTEGRITY)
+#if defined(Q_OS_UNIX) && !defined(Q_OS_INTEGRITY)
# define QT_USE_MMAP
# include "private/qcore_unix_p.h"
// for mmap
@@ -39,8 +39,11 @@
QT_BEGIN_NAMESPACE
+namespace {
enum Tag { Tag_End = 1, Tag_SourceText16, Tag_Translation, Tag_Context16, Tag_Obsolete1,
Tag_SourceText, Tag_Context, Tag_Comment, Tag_Obsolete2 };
+}
+
/*
$ mcookie
3cb86418caef9c95cd211cbf60a1bddd
@@ -627,7 +630,7 @@ static QString find_translation(const QLocale & locale,
// "prefix_en_us.qm" won't be found under the "en_US" locale. Note
// that the Qt resource system is always case-sensitive, even on
// Windows (in other words: this codepath is *not* UNIX-only).
- QStringList languages = locale.uiLanguages();
+ QStringList languages = locale.uiLanguages(QLocale::TagSeparator::Underscore);
for (int i = languages.size()-1; i >= 0; --i) {
QString lang = languages.at(i);
QString lowerLang = lang.toLower();
@@ -636,8 +639,6 @@ static QString find_translation(const QLocale & locale,
}
for (QString localeName : std::as_const(languages)) {
- localeName.replace(u'-', u'_');
-
// try the complete locale name first and progressively truncate from
// the end until a matching language tag is found (with or without suffix)
for (;;) {
@@ -914,7 +915,7 @@ end:
if (!tn)
return QString();
QString str(tn_length / 2, Qt::Uninitialized);
- qFromBigEndian<ushort>(tn, str.size(), str.data());
+ qFromBigEndian<char16_t>(tn, str.size(), str.data());
return str;
}
diff --git a/src/corelib/kernel/qvariant.cpp b/src/corelib/kernel/qvariant.cpp
index 75bdef2383..92a44c462b 100644
--- a/src/corelib/kernel/qvariant.cpp
+++ b/src/corelib/kernel/qvariant.cpp
@@ -3,7 +3,7 @@
// Copyright (C) 2015 Olivier Goffart <ogoffart@woboq.com>
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
-#include "qvariant.h"
+#include "qvariant_p.h"
#include "qbitarray.h"
#include "qbytearray.h"
#include "qdatastream.h"
@@ -47,6 +47,8 @@
#include "qline.h"
#endif
+#include <memory>
+
#include <cmath>
#include <float.h>
#include <cstring>
@@ -125,7 +127,7 @@ static std::optional<qlonglong> qConvertToNumber(const QVariant::Private *d, boo
if (s == "true"_L1 || s == "1"_L1)
return 1;
}
- return 0;
+ return std::nullopt;
}
case QMetaType::QChar:
return d->get<QChar>().unicode();
@@ -171,20 +173,20 @@ static std::optional<qlonglong> qConvertToNumber(const QVariant::Private *d, boo
return std::nullopt;
}
-static std::optional<qreal> qConvertToRealNumber(const QVariant::Private *d)
+static std::optional<double> qConvertToRealNumber(const QVariant::Private *d)
{
bool ok;
switch (d->typeInterface()->typeId) {
case QMetaType::QString:
if (double r = d->get<QString>().toDouble(&ok); ok)
- return qreal(r);
+ return r;
return std::nullopt;
case QMetaType::Double:
- return qreal(d->get<double>());
+ return d->get<double>();
case QMetaType::Float:
- return qreal(d->get<float>());
+ return double(d->get<float>());
case QMetaType::Float16:
- return qreal(d->get<qfloat16>());
+ return double(d->get<qfloat16>());
case QMetaType::ULongLong:
case QMetaType::UInt:
case QMetaType::UChar:
@@ -192,7 +194,7 @@ static std::optional<qreal> qConvertToRealNumber(const QVariant::Private *d)
case QMetaType::Char32:
case QMetaType::UShort:
case QMetaType::ULong:
- return qreal(qMetaTypeUNumber(d));
+ return double(qMetaTypeUNumber(d));
#ifndef QT_BOOTSTRAPPED
case QMetaType::QCborValue:
return d->get<QCborValue>().toDouble();
@@ -202,7 +204,7 @@ static std::optional<qreal> qConvertToRealNumber(const QVariant::Private *d)
default:
// includes enum conversion as well as invalid types
if (std::optional<qlonglong> l = qConvertToNumber(d))
- return qreal(*l);
+ return double(*l);
return std::nullopt;
}
}
@@ -231,24 +233,22 @@ static bool isValidMetaTypeForVariant(const QtPrivate::QMetaTypeInterface *iface
return true;
}
-template <typename F> static QVariant::PrivateShared *
-customConstructShared(size_t size, size_t align, F &&construct)
-{
- struct Deleter {
- void operator()(QVariant::PrivateShared *p) const
- { QVariant::PrivateShared::free(p); }
- };
+enum CustomConstructMoveOptions {
+ UseCopy, // custom construct uses the copy ctor unconditionally
+ // future option: TryMove: uses move ctor if available, else copy ctor
+ ForceMove, // custom construct use the move ctor (which must exist)
+};
- // this is exception-safe
- std::unique_ptr<QVariant::PrivateShared, Deleter> ptr;
- ptr.reset(QVariant::PrivateShared::create(size, align));
- construct(ptr->data());
- return ptr.release();
-}
+enum CustomConstructNullabilityOption {
+ MaybeNull, // copy might be null, might be non-null
+ NonNull, // copy is guarantueed to be non-null
+ // future option: AlwaysNull?
+};
// the type of d has already been set, but other field are not set
+template <CustomConstructMoveOptions moveOption = UseCopy, CustomConstructNullabilityOption nullability = MaybeNull>
static void customConstruct(const QtPrivate::QMetaTypeInterface *iface, QVariant::Private *d,
- const void *copy)
+ std::conditional_t<moveOption == ForceMove, void *, const void *> copy)
{
using namespace QtMetaTypePrivate;
Q_ASSERT(iface);
@@ -257,6 +257,10 @@ static void customConstruct(const QtPrivate::QMetaTypeInterface *iface, QVariant
Q_ASSERT(isCopyConstructible(iface));
Q_ASSERT(isDestructible(iface));
Q_ASSERT(copy || isDefaultConstructible(iface));
+ if constexpr (moveOption == ForceMove)
+ Q_ASSERT(isMoveConstructible(iface));
+ if constexpr (nullability == NonNull)
+ Q_ASSERT(copy != nullptr);
// need to check for nullptr_t here, as this can get called by fromValue(nullptr). fromValue() uses
// std::addressof(value) which in this case returns the address of the nullptr object.
@@ -266,11 +270,17 @@ static void customConstruct(const QtPrivate::QMetaTypeInterface *iface, QVariant
if (QVariant::Private::canUseInternalSpace(iface)) {
d->is_shared = false;
if (!copy && !iface->defaultCtr)
- return; // trivial default constructor, we've already memset
- construct(iface, d->data.data, copy);
+ return; // trivial default constructor and it's OK to build in 0-filled storage, which we've already done
+ if constexpr (moveOption == ForceMove && nullability == NonNull)
+ moveConstruct(iface, d->data.data, copy);
+ else
+ construct(iface, d->data.data, copy);
} else {
d->data.shared = customConstructShared(iface->size, iface->alignment, [=](void *where) {
- construct(iface, where, copy);
+ if constexpr (moveOption == ForceMove && nullability == NonNull)
+ moveConstruct(iface, where, copy);
+ else
+ construct(iface, where, copy);
});
d->is_shared = true;
}
@@ -306,59 +316,6 @@ static QVariant::Private clonePrivate(const QVariant::Private &other)
} // anonymous used to hide QVariant handlers
-inline QVariant::PrivateShared *QVariant::PrivateShared::create(size_t size, size_t align)
-{
- size += sizeof(PrivateShared);
- if (align > sizeof(PrivateShared)) {
- // The alignment is larger than the alignment we can guarantee for the pointer
- // directly following PrivateShared, so we need to allocate some additional
- // memory to be able to fit the object into the available memory with suitable
- // alignment.
- size += align - sizeof(PrivateShared);
- }
- void *data = operator new(size);
- auto *ps = new (data) QVariant::PrivateShared();
- ps->offset = int(((quintptr(ps) + sizeof(PrivateShared) + align - 1) & ~(align - 1)) - quintptr(ps));
- return ps;
-}
-
-inline void QVariant::PrivateShared::free(PrivateShared *p)
-{
- p->~PrivateShared();
- operator delete(p);
-}
-
-inline QVariant::Private::Private(const QtPrivate::QMetaTypeInterface *iface) noexcept
- : is_shared(false), is_null(false), packedType(quintptr(iface) >> 2)
-{
- Q_ASSERT((quintptr(iface) & 0x3) == 0);
-}
-
-template <typename T> inline
-QVariant::Private::Private(std::piecewise_construct_t, const T &t)
- : is_shared(!CanUseInternalSpace<T>), is_null(std::is_same_v<T, std::nullptr_t>)
-{
- // confirm noexceptness
- static constexpr bool isNothrowQVariantConstructible = noexcept(QVariant(t));
- static constexpr bool isNothrowCopyConstructible = std::is_nothrow_copy_constructible_v<T>;
- static constexpr bool isNothrowCopyAssignable = std::is_nothrow_copy_assignable_v<T>;
-
- const QtPrivate::QMetaTypeInterface *iface = QtPrivate::qMetaTypeInterfaceForType<T>();
- Q_ASSERT((quintptr(iface) & 0x3) == 0);
- packedType = quintptr(iface) >> 2;
-
- if constexpr (CanUseInternalSpace<T>) {
- static_assert(isNothrowQVariantConstructible == isNothrowCopyConstructible);
- static_assert(isNothrowQVariantConstructible == isNothrowCopyAssignable);
- new (data.data) T(t);
- } else {
- static_assert(!isNothrowQVariantConstructible); // we allocate memory, even if T doesn't
- data.shared = customConstructShared(sizeof(T), alignof(T), [=](void *where) {
- new (where) T(t);
- });
- }
-}
-
/*!
\class QVariant
\inmodule QtCore
@@ -367,6 +324,7 @@ QVariant::Private::Private(std::piecewise_construct_t, const T &t)
\ingroup objectmodel
\ingroup shared
+ \compares equality
Because C++ forbids unions from including types that have
non-default constructors or destructors, most interesting Qt
@@ -559,23 +517,18 @@ void QVariant::create(int type, const void *copy)
*/
void QVariant::create(QMetaType type, const void *copy)
{
- *this = QVariant(type, copy);
+ *this = QVariant::fromMetaType(type, copy);
}
/*!
\fn QVariant::~QVariant()
Destroys the QVariant and the contained object.
-
- Note that subclasses that reimplement clear() should reimplement
- the destructor to call clear(). This destructor calls clear(), but
- because it is the destructor, QVariant::clear() is called rather
- than a subclass's clear().
*/
QVariant::~QVariant()
{
- if ((d.is_shared && !d.data.shared->ref.deref()) || (!d.is_shared))
+ if (!d.is_shared || !d.data.shared->ref.deref())
customClear(&d);
}
@@ -592,6 +545,106 @@ QVariant::QVariant(const QVariant &p)
}
/*!
+ \fn template <typename T, typename... Args, QVariant::if_constructible<T, Args...> = true> QVariant::QVariant(std::in_place_type_t<T>, Args&&... args) noexcept(is_noexcept_constructible<q20::remove_cvref_t<T>, Args...>::value)
+
+ \since 6.6
+ Constructs a new variant containing a value of type \c T. The contained
+ value is is initialized with the arguments
+ \c{std::forward<Args>(args)...}.
+
+ This overload only participates in overload resolution if \c T can be
+ constructed from \a args.
+
+ This constructor is provided for STL/std::any compatibility.
+
+ \overload
+ */
+
+/*!
+
+ \fn template <typename T, typename U, typename... Args, QVariant::if_constructible<T, std::initializer_list<U> &, Args...> = true> explicit QVariant::QVariant(std::in_place_type_t<T>, std::initializer_list<U> il, Args&&... args) noexcept(is_noexcept_constructible<q20::remove_cvref_t<T>, std::initializer_list<U> &, Args... >::value)
+
+ \since 6.6
+ \overload
+ This overload exists to support types with constructors taking an
+ \c initializer_list. It behaves otherwise equivalent to the
+ non-initializer list \c{in_place_type_t} overload.
+*/
+
+
+/*!
+ \fn template <typename T, typename... Args, QVariant::if_constructible<T, Args...> = true> QVariant::emplace(Args&&... args)
+
+ \since 6.6
+ Replaces the object currently held in \c{*this} with an object of
+ type \c{T}, constructed from \a{args}\c{...}. If \c{*this} was non-null,
+ the previously held object is destroyed first.
+ If possible, this method will reuse memory allocated by the QVariant.
+ Returns a reference to the newly-created object.
+ */
+
+/*!
+ \fn template <typename T, typename U, typename... Args, QVariant::if_constructible<T, std::initializer_list<U> &, Args...> = true> QVariant::emplace(std::initializer_list<U> list, Args&&... args)
+
+ \since 6.6
+ \overload
+ This overload exists to support types with constructors taking an
+ \c initializer_list. It behaves otherwise equivalent to the
+ non-initializer list overload.
+*/
+
+QVariant::QVariant(std::in_place_t, QMetaType type) : d(type.iface())
+{
+ // we query the metatype instead of detecting it at compile time
+ // so that we can change relocatability of internal types
+ if (!Private::canUseInternalSpace(type.iface())) {
+ d.data.shared = PrivateShared::create(type.sizeOf(), type.alignOf());
+ d.is_shared = true;
+ }
+}
+
+/*!
+ \internal
+ Returns a pointer to data suitable for placement new
+ of an object of type \a type
+ Changes the variant's metatype to \a type
+ */
+void *QVariant::prepareForEmplace(QMetaType type)
+{
+ /* There are two cases where we can reuse the existing storage
+ (1) The new type fits in QVariant's SBO storage
+ (2) We are using the externally allocated storage, the variant is
+ detached, and the new type fits into the existing storage.
+ In all other cases (3), we cannot reuse the storage.
+ */
+ auto typeFits = [&] {
+ auto newIface = type.iface();
+ auto oldIface = d.typeInterface();
+ auto newSize = PrivateShared::computeAllocationSize(newIface->size, newIface->alignment);
+ auto oldSize = PrivateShared::computeAllocationSize(oldIface->size, oldIface->alignment);
+ return newSize <= oldSize;
+ };
+ if (Private::canUseInternalSpace(type.iface())) { // (1)
+ clear();
+ d.packedType = quintptr(type.iface()) >> 2;
+ return d.data.data;
+ } else if (d.is_shared && isDetached() && typeFits()) { // (2)
+ QtMetaTypePrivate::destruct(d.typeInterface(), d.data.shared->data());
+ // compare QVariant::PrivateShared::create
+ const auto ps = d.data.shared;
+ const auto align = type.alignOf();
+ ps->offset = PrivateShared::computeOffset(ps, align);
+ d.packedType = quintptr(type.iface()) >> 2;
+ return ps->data();
+ }
+ // (3)
+ QVariant newVariant(std::in_place, type);
+ swap(newVariant);
+ // const cast is safe, we're in a non-const method
+ return const_cast<void *>(d.storage());
+}
+
+/*!
\fn QVariant::QVariant(const QString &val) noexcept
Constructs a new variant with a string value, \a val.
@@ -600,7 +653,8 @@ QVariant::QVariant(const QVariant &p)
/*!
\fn QVariant::QVariant(QLatin1StringView val)
- Constructs a new variant with a string value, \a val.
+ Constructs a new variant with a QString value from the Latin-1
+ string viewed by \a val.
*/
/*!
@@ -857,26 +911,27 @@ QVariant::QVariant(const QVariant &p)
*/
/*!
- Constructs variant of type \a type, and initializes with
- \a copy if \a copy is not \nullptr.
+ Constructs a variant of type \a type, and initializes it with
+ a copy of \c{*copy} if \a copy is not \nullptr (in which case, \a copy
+ must point to an object of type \a type).
- Note that you have to pass the address of the variable you want stored.
+ Note that you have to pass the address of the object you want stored.
Usually, you never have to use this constructor, use QVariant::fromValue()
instead to construct variants from the pointer types represented by
\c QMetaType::VoidStar, and \c QMetaType::QObjectStar.
- If \a type does not support copy and default construction, the variant will
- be invalid.
+ If \a type does not support copy construction and \a copy is not \nullptr,
+ the variant will be invalid. Similarly, if \a copy is \nullptr and
+ \a type does not support default construction, the variant will be
+ invalid.
- \sa QVariant::fromValue(), QMetaType::Type
+ \sa QVariant::fromMetaType, QVariant::fromValue(), QMetaType::Type
*/
-QVariant::QVariant(QMetaType type, const void *copy) : d(type.iface())
+QVariant::QVariant(QMetaType type, const void *copy)
+ : d()
{
- if (isValidMetaTypeForVariant(type.iface(), copy))
- customConstruct(type.iface(), &d, copy);
- else
- d = {};
+ *this = fromMetaType(type, copy);
}
QVariant::QVariant(int val) noexcept : d(std::piecewise_construct_t{}, val) {}
@@ -888,7 +943,9 @@ QVariant::QVariant(double val) noexcept : d(std::piecewise_construct_t{}, val) {
QVariant::QVariant(float val) noexcept : d(std::piecewise_construct_t{}, val) {}
QVariant::QVariant(const QByteArray &val) noexcept : d(std::piecewise_construct_t{}, val) {}
+#ifndef QT_BOOTSTRAPPED
QVariant::QVariant(const QBitArray &val) noexcept : d(std::piecewise_construct_t{}, val) {}
+#endif
QVariant::QVariant(const QString &val) noexcept : d(std::piecewise_construct_t{}, val) {}
QVariant::QVariant(QChar val) noexcept : d(std::piecewise_construct_t{}, val) {}
QVariant::QVariant(const QStringList &val) noexcept : d(std::piecewise_construct_t{}, val) {}
@@ -1035,7 +1092,8 @@ void QVariant::detach()
Q_ASSERT(isValidMetaTypeForVariant(d.typeInterface(), constData()));
Private dd(d.typeInterface());
- customConstruct(d.typeInterface(), &dd, constData());
+ // null variant is never shared; anything else is NonNull
+ customConstruct<UseCopy, NonNull>(d.typeInterface(), &dd, constData());
if (!d.data.shared->ref.deref())
customClear(&d);
d.data.shared = dd.data.shared;
@@ -1064,7 +1122,7 @@ const char *QVariant::typeName() const
*/
void QVariant::clear()
{
- if ((d.is_shared && !d.data.shared->ref.deref()) || (!d.is_shared))
+ if (!d.is_shared || !d.data.shared->ref.deref())
customClear(&d);
d = {};
}
@@ -1360,12 +1418,14 @@ QDataStream &operator<<(QDataStream &s, const QVariant &p)
}
/*! \fn QDataStream& operator>>(QDataStream &s, QVariant::Type &p)
+ \relates QVariant
\deprecated [6.0] Stream QMetaType::Type instead.
Reads a variant type \a p in enum representation from the stream \a s.
*/
/*! \fn QDataStream& operator<<(QDataStream &s, const QVariant::Type p)
+ \relates QVariant
\deprecated [6.0] Stream QMetaType::Type instead.
Writes a variant type \a p to the stream \a s.
@@ -1786,6 +1846,7 @@ QChar QVariant::toChar() const
return qvariant_cast<QChar>(*this);
}
+#ifndef QT_BOOTSTRAPPED
/*!
Returns the variant as a QBitArray if the variant has userType()
\l QMetaType::QBitArray; otherwise returns an empty bit array.
@@ -1796,6 +1857,7 @@ QBitArray QVariant::toBitArray() const
{
return qvariant_cast<QBitArray>(*this);
}
+#endif // QT_BOOTSTRAPPED
template <typename T>
inline T qNumVariantToHelper(const QVariant::Private &d, bool *ok)
@@ -2091,9 +2153,9 @@ bool QVariant::view(int type, void *ptr)
}
/*!
- \fn bool QVariant::operator==(const QVariant &v1, const QVariant &v2)
+ \fn bool QVariant::operator==(const QVariant &lhs, const QVariant &rhs)
- Returns \c true if \a v1 and \a v2 are equal; otherwise returns \c false.
+ Returns \c true if \a lhs and \a rhs are equal; otherwise returns \c false.
QVariant uses the equality operator of the type() contained to check for
equality.
@@ -2117,9 +2179,9 @@ bool QVariant::view(int type, void *ptr)
*/
/*!
- \fn bool QVariant::operator!=(const QVariant &v1, const QVariant &v2)
+ \fn bool QVariant::operator!=(const QVariant &lhs, const QVariant &rhs)
- Returns \c false if \a v1 and \a v2 are equal; otherwise returns \c true.
+ Returns \c false if \a lhs and \a rhs are equal; otherwise returns \c true.
QVariant uses the equality operator of the type() contained to check for
equality.
@@ -2299,19 +2361,19 @@ static QPartialOrdering numericCompare(const QVariant::Private *d1, const QVaria
if (promotedType != QMetaType::QReal)
return integralCompare(promotedType, d1, d2);
- // qreal comparisons
- std::optional<qreal> r1 = qConvertToRealNumber(d1);
- std::optional<qreal> r2 = qConvertToRealNumber(d2);
+ // floating point comparison
+ const auto r1 = qConvertToRealNumber(d1);
+ const auto r2 = qConvertToRealNumber(d2);
if (!r1 || !r2)
return QPartialOrdering::Unordered;
if (*r1 == *r2)
return QPartialOrdering::Equivalent;
- return spaceShip<qreal>(*r1, *r2);
+ return spaceShip(*r1, *r2);
}
#ifndef QT_BOOTSTRAPPED
-static bool canConvertMetaObject(QMetaType fromType, QMetaType toType)
+static bool qvCanConvertMetaObject(QMetaType fromType, QMetaType toType)
{
if ((fromType.flags() & QMetaType::PointerToQObject)
&& (toType.flags() & QMetaType::PointerToQObject)) {
@@ -2341,7 +2403,7 @@ bool QVariant::equals(const QVariant &v) const
return numericCompare(&d, &v.d) == QPartialOrdering::Equivalent;
#ifndef QT_BOOTSTRAPPED
// if both types are related pointers to QObjects, check if they point to the same object
- if (canConvertMetaObject(metatype, v.metaType()))
+ if (qvCanConvertMetaObject(metatype, v.metaType()))
return pointerCompare(&d, &v.d) == QPartialOrdering::Equivalent;
#endif
return false;
@@ -2384,7 +2446,7 @@ QPartialOrdering QVariant::compare(const QVariant &lhs, const QVariant &rhs)
if (canBeNumericallyCompared(lhs.d.type().iface(), rhs.d.type().iface()))
return numericCompare(&lhs.d, &rhs.d);
#ifndef QT_BOOTSTRAPPED
- if (canConvertMetaObject(lhs.metaType(), rhs.metaType()))
+ if (qvCanConvertMetaObject(lhs.metaType(), rhs.metaType()))
return pointerCompare(&lhs.d, &rhs.d);
#endif
return QPartialOrdering::Unordered;
@@ -2399,7 +2461,7 @@ QPartialOrdering QVariant::compare(const QVariant &lhs, const QVariant &rhs)
Returns a pointer to the contained object as a generic void* that cannot be
written to.
- \sa QMetaType
+ \sa get_if(), QMetaType
*/
/*!
@@ -2409,7 +2471,7 @@ QPartialOrdering QVariant::compare(const QVariant &lhs, const QVariant &rhs)
This function detaches the QVariant. When called on a \l{isNull}{null-QVariant},
the QVariant will not be null after the call.
- \sa QMetaType
+ \sa get_if(), QMetaType
*/
void *QVariant::data()
{
@@ -2420,6 +2482,42 @@ void *QVariant::data()
}
/*!
+ \since 6.6
+ \fn template <typename T> const T* QVariant::get_if(const QVariant *v)
+ \fn template <typename T> T* QVariant::get_if(QVariant *v)
+
+ If \a v contains an object of type \c T, returns a pointer to the contained
+ object, otherwise returns \nullptr.
+
+ The overload taking a mutable \a v detaches \a v: When called on a
+ \l{isNull()}{null} \a v with matching type \c T, \a v will not be null
+ after the call.
+
+ These functions are provided for compatibility with \c{std::variant}.
+
+ \sa data()
+*/
+
+/*!
+ \since 6.6
+ \fn template <typename T> T &QVariant::get(QVariant &v)
+ \fn template <typename T> const T &QVariant::get(const QVariant &v)
+ \fn template <typename T> T &&QVariant::get(QVariant &&v)
+ \fn template <typename T> const T &&QVariant::get(const QVariant &&v)
+
+ If \a v contains an object of type \c T, returns a reference to the contained
+ object, otherwise the call has undefined behavior.
+
+ The overloads taking a mutable \a v detach \a v: When called on a
+ \l{isNull()}{null} \a v with matching type \c T, \a v will not be null
+ after the call.
+
+ These functions are provided for compatibility with \c{std::variant}.
+
+ \sa get_if(), data()
+*/
+
+/*!
Returns \c true if this is a null variant, false otherwise.
A variant is considered null if it contains no initialized value or a null pointer.
@@ -2457,6 +2555,22 @@ QDebug QVariant::qdebugHelper(QDebug dbg) const
return dbg;
}
+QVariant QVariant::moveConstruct(QMetaType type, void *data)
+{
+ QVariant var;
+ var.d = QVariant::Private(type.d_ptr);
+ customConstruct<ForceMove, NonNull>(type.d_ptr, &var.d, data);
+ return var;
+}
+
+QVariant QVariant::copyConstruct(QMetaType type, const void *data)
+{
+ QVariant var;
+ var.d = QVariant::Private(type.d_ptr);
+ customConstruct<UseCopy, NonNull>(type.d_ptr, &var.d, data);
+ return var;
+}
+
#if QT_DEPRECATED_SINCE(6, 0)
QT_WARNING_PUSH
QT_WARNING_DISABLE_DEPRECATED
@@ -2476,7 +2590,7 @@ QT_WARNING_POP
#endif
-/*! \fn template<typename T> void QVariant::setValue(T &&value)
+/*! \fn template<typename T, typename = std::enable_if_t<!std::is_same_v<std::decay_t<T>, QVariant>>> void QVariant::setValue(T &&value)
Stores a copy of \a value. If \c{T} is a type that QVariant
doesn't support, QMetaType is used to store the value. A compile
@@ -2489,19 +2603,19 @@ QT_WARNING_POP
\sa value(), fromValue(), canConvert()
*/
-/*! \fn template<typename T> void QVariant::setValue(const QVariant &value)
+/*! \fn void QVariant::setValue(const QVariant &value)
Copies \a value over this QVariant. It is equivalent to simply
assigning \a value to this QVariant.
*/
-/*! \fn template<typename T> void QVariant::setValue(QVariant &&value)
+/*! \fn void QVariant::setValue(QVariant &&value)
Moves \a value over this QVariant. It is equivalent to simply
move assigning \a value to this QVariant.
*/
-/*! \fn template<typename T> T QVariant::value() const
+/*! \fn template<typename T> T QVariant::value() const &
Returns the stored value converted to the template type \c{T}.
Call canConvert() to find out whether a type can be converted.
@@ -2541,7 +2655,7 @@ QT_WARNING_POP
\sa canView(), Q_DECLARE_SEQUENTIAL_CONTAINER_METATYPE()
*/
-/*! \fn bool QVariant::canConvert() const
+/*! \fn template<typename T> bool QVariant::canConvert() const
Returns \c true if the variant can be converted to the template type \c{T},
otherwise false.
@@ -2557,7 +2671,7 @@ QT_WARNING_POP
\sa convert()
*/
-/*! \fn bool QVariant::canView() const
+/*! \fn template<typename T> bool QVariant::canView() const
Returns \c true if a mutable view of the template type \c{T} can be created on this variant,
otherwise \c false.
@@ -2577,6 +2691,12 @@ QT_WARNING_POP
\sa setValue(), value()
*/
+/*! \fn template<typename T, QVariant::if_rvalue<T> = true> static QVariant QVariant::fromValue(T &&value)
+
+ \since 6.6
+ \overload
+*/
+
/*! \fn template<typename... Types> QVariant QVariant::fromStdVariant(const std::variant<Types...> &value)
\since 5.11
@@ -2591,6 +2711,47 @@ QT_WARNING_POP
*/
/*!
+ \fn template<typename... Types> QVariant QVariant::fromStdVariant(std::variant<Types...> &&value)
+ \since 6.6
+ \overload
+*/
+
+
+/*!
+ \since 6.7
+
+ Creates a variant of type \a type, and initializes it with
+ a copy of \c{*copy} if \a copy is not \nullptr (in which case, \a copy
+ must point to an object of type \a type).
+
+ Note that you have to pass the address of the object you want stored.
+
+ Usually, you never have to use this constructor, use QVariant::fromValue()
+ instead to construct variants from the pointer types represented by
+ \c QMetaType::VoidStar, and \c QMetaType::QObjectStar.
+
+ If \a type does not support copy construction and \a copy is not \nullptr,
+ the variant will be invalid. Similarly, if \a copy is \nullptr and
+ \a type does not support default construction, the variant will be
+ invalid.
+
+ Returns the QVariant created as described above.
+
+ \sa QVariant::fromValue(), QMetaType::Type
+*/
+QVariant QVariant::fromMetaType(QMetaType type, const void *copy)
+{
+ QVariant result;
+ type.registerType();
+ const auto iface = type.iface();
+ if (isValidMetaTypeForVariant(iface, copy)) {
+ result.d = Private(iface);
+ customConstruct(iface, &result.d, copy);
+ }
+ return result;
+}
+
+/*!
\fn template<typename T> T qvariant_cast(const QVariant &value)
\relates QVariant
@@ -2601,6 +2762,14 @@ QT_WARNING_POP
\sa QVariant::value()
*/
+/*!
+ \fn template<typename T> T QVariant::qvariant_cast(QVariant &&value)
+ \overload
+ \since 6.7
+
+ Returns the given \a value converted to the template type \c{T}.
+*/
+
/*! \fn template<typename T> T qVariantValue(const QVariant &value)
\relates QVariant
\deprecated
diff --git a/src/corelib/kernel/qvariant.h b/src/corelib/kernel/qvariant.h
index 844ad3cd65..306e5b3a38 100644
--- a/src/corelib/kernel/qvariant.h
+++ b/src/corelib/kernel/qvariant.h
@@ -5,14 +5,18 @@
#define QVARIANT_H
#include <QtCore/qatomic.h>
+#include <QtCore/qcompare.h>
#include <QtCore/qcontainerfwd.h>
#include <QtCore/qmetatype.h>
#ifndef QT_NO_DEBUG_STREAM
#include <QtCore/qdebug.h>
#endif
+
#include <memory>
-#include <type_traits>
+#include <QtCore/q20type_traits.h>
+#include <QtCore/q23utility.h>
#include <variant>
+
#if !defined(QT_LEAN_HEADERS) || QT_LEAN_HEADERS < 1
# include <QtCore/qlist.h>
# include <QtCore/qstringlist.h>
@@ -24,6 +28,9 @@
QT_BEGIN_NAMESPACE
+QT_ENABLE_P0846_SEMANTICS_FOR(get_if)
+QT_ENABLE_P0846_SEMANTICS_FOR(get)
+
class QBitArray;
class QDataStream;
class QDate;
@@ -51,9 +58,23 @@ class QVariant;
template<typename T>
inline T qvariant_cast(const QVariant &);
+namespace QtPrivate {
template<> constexpr inline bool qIsRelocatable<QVariant> = true;
+}
class Q_CORE_EXPORT QVariant
{
+ template <typename T, typename... Args>
+ using if_constructible = std::enable_if_t<
+ std::conjunction_v<
+ std::is_copy_constructible<q20::remove_cvref_t<T>>,
+ std::is_destructible<q20::remove_cvref_t<T>>,
+ std::is_constructible<q20::remove_cvref_t<T>, Args...>
+ >,
+ bool>;
+
+ template <typename T>
+ using if_rvalue = std::enable_if_t<!std::is_reference_v<T>, bool>;
+
struct CborValueStandIn { qint64 n; void *c; int t; };
public:
struct PrivateShared
@@ -61,6 +82,8 @@ public:
private:
inline PrivateShared() : ref(1) { }
public:
+ static int computeOffset(PrivateShared *ps, size_t align);
+ static size_t computeAllocationSize(size_t size, size_t align);
static PrivateShared *create(size_t size, size_t align);
static void free(PrivateShared *p);
@@ -70,6 +93,7 @@ public:
const void *data() const { return reinterpret_cast<const uchar *>(this) + offset; }
void *data() { return reinterpret_cast<uchar *>(this) + offset; }
};
+
struct Private
{
static constexpr size_t MaxInternalSize = 3 * sizeof(void *);
@@ -196,6 +220,37 @@ public:
explicit QVariant(QMetaType type, const void *copy = nullptr);
QVariant(const QVariant &other);
+private:
+ template <typename T, typename ...Args>
+ using is_noexcept_constructible = std::conjunction<
+ std::bool_constant<Private::CanUseInternalSpace<T>>,
+ std::is_nothrow_constructible<T, Args...>
+ >;
+
+public:
+ template <typename T, typename... Args,
+ if_constructible<T, Args...> = true>
+ explicit QVariant(std::in_place_type_t<T>, Args&&... args)
+ noexcept(is_noexcept_constructible<q20::remove_cvref_t<T>, Args...>::value)
+ : QVariant(std::in_place, QMetaType::fromType<q20::remove_cvref_t<T>>() )
+ {
+ void *data = const_cast<void *>(constData());
+ new (data) T(std::forward<Args>(args)...);
+ }
+
+ template <typename T, typename U, typename... Args,
+ if_constructible<T, std::initializer_list<U> &, Args...> = true>
+ explicit QVariant(std::in_place_type_t<T>, std::initializer_list<U> il, Args&&... args)
+ noexcept(is_noexcept_constructible<q20::remove_cvref_t<T>,
+ std::initializer_list<U> &,
+ Args...
+ >::value)
+ : QVariant(std::in_place, QMetaType::fromType<q20::remove_cvref_t<T>>())
+ {
+ char *data = static_cast<char *>(const_cast<void *>(constData()));
+ new (data) T(il, std::forward<Args>(args)...);
+ }
+
// primitives
QVariant(int i) noexcept;
QVariant(uint ui) noexcept;
@@ -209,7 +264,9 @@ public:
QVariant(QChar qchar) noexcept;
QVariant(QDate date) noexcept;
QVariant(QTime time) noexcept;
+#ifndef QT_BOOTSTRAPPED
QVariant(const QBitArray &bitarray) noexcept;
+#endif
QVariant(const QByteArray &bytearray) noexcept;
QVariant(const QDateTime &datetime) noexcept;
QVariant(const QHash<QString, QVariant> &hash) noexcept;
@@ -318,7 +375,9 @@ public:
float toFloat(bool *ok = nullptr) const;
qreal toReal(bool *ok = nullptr) const;
QByteArray toByteArray() const;
+#ifndef QT_BOOTSTRAPPED
QBitArray toBitArray() const;
+#endif
QString toString() const;
QStringList toStringList() const;
QChar toChar() const;
@@ -393,6 +452,43 @@ public:
{ return d.storage(); }
inline const void *data() const { return constData(); }
+private:
+ template <typename T>
+ void verifySuitableForEmplace()
+ {
+ static_assert(!std::is_reference_v<T>,
+ "QVariant does not support reference types");
+ static_assert(!std::is_const_v<T>,
+ "QVariant does not support const types");
+ static_assert(std::is_copy_constructible_v<T>,
+ "QVariant requires that the type is copyable");
+ static_assert(std::is_destructible_v<T>,
+ "QVariant requires that the type is destructible");
+ }
+
+ template <typename T, typename... Args>
+ T &emplaceImpl(Args&&... args)
+ {
+ verifySuitableForEmplace<T>();
+ auto data = static_cast<T *>(prepareForEmplace(QMetaType::fromType<T>()));
+ return *q20::construct_at(data, std::forward<Args>(args)...);
+ }
+
+public:
+ template <typename T, typename... Args,
+ if_constructible<T, Args...> = true>
+ T &emplace(Args&&... args)
+ {
+ return emplaceImpl<T>(std::forward<Args>(args)...);
+ }
+
+ template <typename T, typename U, typename... Args,
+ if_constructible<T, std::initializer_list<U> &, Args...> = true>
+ T &emplace(std::initializer_list<U> list, Args&&... args)
+ {
+ return emplaceImpl<T>(list, std::forward<Args>(args)...);
+ }
+
template<typename T, typename = std::enable_if_t<!std::is_same_v<std::decay_t<T>, QVariant>>>
void setValue(T &&avalue)
{
@@ -401,6 +497,7 @@ public:
// If possible we reuse the current QVariant private.
if (isDetached() && d.type() == metaType) {
*reinterpret_cast<VT *>(const_cast<void *>(constData())) = std::forward<T>(avalue);
+ d.is_null = false;
} else {
*this = QVariant::fromValue<VT>(std::forward<T>(avalue));
}
@@ -417,7 +514,7 @@ public:
}
template<typename T>
- inline T value() const
+ inline T value() const &
{ return qvariant_cast<T>(*this); }
template<typename T>
@@ -429,6 +526,43 @@ public:
}
template<typename T>
+ inline T value() &&
+ { return qvariant_cast<T>(std::move(*this)); }
+
+ template<typename T, if_rvalue<T> = true>
+#ifndef Q_QDOC
+ /* needs is_copy_constructible for variants semantics, is_move_constructible so that moveConstruct works
+ (but copy_constructible implies move_constructble, so don't bother checking)
+ */
+ static inline auto fromValue(T &&value)
+ noexcept(std::is_nothrow_copy_constructible_v<T> && Private::CanUseInternalSpace<T>)
+ -> std::enable_if_t<std::conjunction_v<std::is_copy_constructible<T>,
+ std::is_destructible<T>>, QVariant>
+#else
+ static inline QVariant fromValue(T &&value)
+#endif
+ {
+ // handle special cases
+ using Type = std::remove_cv_t<T>;
+ if constexpr (std::is_null_pointer_v<Type>)
+ return QVariant::fromMetaType(QMetaType::fromType<std::nullptr_t>());
+ else if constexpr (std::is_same_v<Type, QVariant>)
+ return std::forward<T>(value);
+ else if constexpr (std::is_same_v<Type, std::monostate>)
+ return QVariant();
+ QMetaType mt = QMetaType::fromType<Type>();
+ mt.registerType(); // we want the type stored in QVariant to always be registered
+ // T is a forwarding reference, so if T satifies the enable-ifery,
+ // we get this overload even if T is an lvalue reference and thus must check here
+ // Moreover, we only try to move if the type is actually moveable and not if T is const
+ // as in const int i; QVariant::fromValue(std::move(i));
+ if constexpr (std::conjunction_v<std::is_move_constructible<Type>, std::negation<std::is_const<T>>>)
+ return moveConstruct(QMetaType::fromType<Type>(), std::addressof(value));
+ else
+ return copyConstruct(mt, std::addressof(value));
+ }
+
+ template<typename T>
#ifndef Q_QDOC
static inline auto fromValue(const T &value)
noexcept(std::is_nothrow_copy_constructible_v<T> && Private::CanUseInternalSpace<T>)
@@ -439,17 +573,27 @@ public:
{
if constexpr (std::is_null_pointer_v<T>)
return QVariant(QMetaType::fromType<std::nullptr_t>());
+ else if constexpr (std::is_same_v<T, QVariant>)
+ return value;
+ else if constexpr (std::is_same_v<T, std::monostate>)
+ return QVariant();
return QVariant(QMetaType::fromType<T>(), std::addressof(value));
}
template<typename... Types>
static inline QVariant fromStdVariant(const std::variant<Types...> &value)
{
- if (value.valueless_by_exception())
- return QVariant();
- return std::visit([](const auto &arg) { return fromValue(arg); }, value);
+ return fromStdVariantImpl(value);
+ }
+
+ template<typename... Types>
+ static QVariant fromStdVariant(std::variant<Types...> &&value)
+ {
+ return fromStdVariantImpl(std::move(value));
}
+ static QVariant fromMetaType(QMetaType type, const void *copy = nullptr);
+
template<typename T>
bool canConvert() const
{ return canConvert(QMetaType::fromType<T>()); }
@@ -461,10 +605,21 @@ public:
static QPartialOrdering compare(const QVariant &lhs, const QVariant &rhs);
private:
- friend inline bool operator==(const QVariant &a, const QVariant &b)
+ template <typename StdVariant>
+ static QVariant fromStdVariantImpl(StdVariant &&v)
+ {
+ if (Q_UNLIKELY(v.valueless_by_exception()))
+ return QVariant();
+ auto visitor = [](auto &&arg) {
+ return QVariant::fromValue(q23::forward_like<StdVariant>(arg));
+ };
+ return std::visit(visitor, std::forward<StdVariant>(v));
+ }
+
+ friend bool comparesEqual(const QVariant &a, const QVariant &b)
{ return a.equals(b); }
- friend inline bool operator!=(const QVariant &a, const QVariant &b)
- { return !a.equals(b); }
+ Q_DECLARE_EQUALITY_COMPARABLE(QVariant)
+
#ifndef QT_NO_DEBUG_STREAM
template <typename T>
friend auto operator<<(const QDebug &debug, const T &variant) -> std::enable_if_t<std::is_same_v<T, QVariant>, QDebug> {
@@ -472,8 +627,48 @@ private:
}
QDebug qdebugHelper(QDebug) const;
#endif
+
+ template <typename T>
+ friend T *get_if(QVariant *v) noexcept
+ {
+ // data() will detach from is_null, returning non-nullptr
+ if (!v || v->d.type() != QMetaType::fromType<T>())
+ return nullptr;
+ return static_cast<T*>(v->data());
+ }
+ template <typename T>
+ friend const T *get_if(const QVariant *v) noexcept
+ {
+ // (const) data() will not detach from is_null, return nullptr
+ if (!v || v->d.is_null || v->d.type() != QMetaType::fromType<T>())
+ return nullptr;
+ return static_cast<const T*>(v->data());
+ }
+
+#define Q_MK_GET(cvref) \
+ template <typename T> \
+ friend T cvref get(QVariant cvref v) \
+ { \
+ if constexpr (std::is_const_v<T cvref>) \
+ Q_ASSERT(!v.d.is_null); \
+ Q_ASSERT(v.d.type() == QMetaType::fromType<q20::remove_cvref_t<T>>()); \
+ return static_cast<T cvref>(*get_if<T>(&v)); \
+ } \
+ /* end */
+ Q_MK_GET(&)
+ Q_MK_GET(const &)
+ Q_MK_GET(&&)
+ Q_MK_GET(const &&)
+#undef Q_MK_GET
+
+ static QVariant moveConstruct(QMetaType type, void *data);
+ static QVariant copyConstruct(QMetaType type, const void *data);
+
template<typename T>
friend inline T qvariant_cast(const QVariant &);
+ template<typename T>
+ friend inline T qvariant_cast(QVariant &&);
+
protected:
Private d;
void create(int type, const void *copy);
@@ -493,6 +688,11 @@ private:
// int variant, so delete this constructor:
QVariant(QMetaType::Type) = delete;
+ // used to setup the QVariant internals for the "real" inplace ctor
+ QVariant(std::in_place_t, QMetaType type);
+ // helper for emplace
+ void *prepareForEmplace(QMetaType type);
+
// These constructors don't create QVariants of the type associated
// with the enum, as expected, but they would create a QVariant of
// type int with the value of the enum value.
@@ -512,18 +712,6 @@ public:
inline const DataPtr &data_ptr() const { return d; }
};
-template<>
-inline QVariant QVariant::fromValue(const QVariant &value)
-{
- return value;
-}
-
-template<>
-inline QVariant QVariant::fromValue(const std::monostate &) noexcept
-{
- return QVariant();
-}
-
inline bool QVariant::isValid() const
{
return d.type().isValid();
@@ -580,14 +768,45 @@ template<typename T> inline T qvariant_cast(const QVariant &v)
return t;
}
+template<typename T> inline T qvariant_cast(QVariant &&v)
+{
+ QMetaType targetType = QMetaType::fromType<T>();
+ if (v.d.type() == targetType) {
+ if constexpr (QVariant::Private::CanUseInternalSpace<T>) {
+ return std::move(*reinterpret_cast<T *>(v.d.data.data));
+ } else {
+ if (v.d.data.shared->ref.loadRelaxed() == 1)
+ return std::move(*reinterpret_cast<T *>(v.d.data.shared->data()));
+ else
+ return v.d.get<T>();
+ }
+ }
+ if constexpr (std::is_same_v<T, QVariant>) {
+ // if the metatype doesn't match, but we want a QVariant, just return the current variant
+ return v;
+ } if constexpr (std::is_same_v<T,std::remove_const_t<std::remove_pointer_t<T>> const *>) {
+ // moving a pointer is pointless, just do the same as the const & overload
+ using nonConstT = std::remove_const_t<std::remove_pointer_t<T>> *;
+ QMetaType nonConstTargetType = QMetaType::fromType<nonConstT>();
+ if (v.d.type() == nonConstTargetType)
+ return v.d.get<nonConstT>();
+ }
+
+ T t{};
+ QMetaType::convert(v.metaType(), v.constData(), targetType, &t);
+ return t;
+}
+
+# ifndef QT_NO_VARIANT
template<> inline QVariant qvariant_cast<QVariant>(const QVariant &v)
{
if (v.metaType().id() == QMetaType::QVariant)
return *reinterpret_cast<const QVariant *>(v.constData());
return v;
}
+# endif
-#endif
+#endif // QT_MOC
#ifndef QT_NO_DEBUG_STREAM
#if QT_DEPRECATED_SINCE(6, 0)
diff --git a/src/corelib/kernel/qvariant_p.h b/src/corelib/kernel/qvariant_p.h
new file mode 100644
index 0000000000..d2a7390938
--- /dev/null
+++ b/src/corelib/kernel/qvariant_p.h
@@ -0,0 +1,109 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QVARIANT_P_H
+#define QVARIANT_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 "qvariant.h"
+
+QT_BEGIN_NAMESPACE
+
+inline auto customConstructSharedImpl(size_t size, size_t align)
+{
+ struct Deleter {
+ void operator()(QVariant::PrivateShared *p) const
+ { QVariant::PrivateShared::free(p); }
+ };
+
+ // this is exception-safe
+ std::unique_ptr<QVariant::PrivateShared, Deleter> ptr;
+ ptr.reset(QVariant::PrivateShared::create(size, align));
+ return ptr;
+}
+
+template <typename F> static QVariant::PrivateShared *
+customConstructShared(size_t size, size_t align, F &&construct)
+{
+ auto ptr = customConstructSharedImpl(size, align);
+ construct(ptr->data());
+ return ptr.release();
+}
+
+inline int QVariant::PrivateShared::computeOffset(PrivateShared *ps, size_t align)
+{
+ return int(((quintptr(ps) + sizeof(PrivateShared) + align - 1) & ~(align - 1)) - quintptr(ps));
+}
+
+inline size_t QVariant::PrivateShared::computeAllocationSize(size_t size, size_t align)
+{
+ size += sizeof(PrivateShared);
+ if (align > sizeof(PrivateShared)) {
+ // The alignment is larger than the alignment we can guarantee for the pointer
+ // directly following PrivateShared, so we need to allocate some additional
+ // memory to be able to fit the object into the available memory with suitable
+ // alignment.
+ size += align - sizeof(PrivateShared);
+ }
+ return size;
+}
+
+inline QVariant::PrivateShared *QVariant::PrivateShared::create(size_t size, size_t align)
+{
+ size = computeAllocationSize(size, align);
+ void *data = operator new(size);
+ auto *ps = new (data) QVariant::PrivateShared();
+ ps->offset = computeOffset(ps, align);
+ return ps;
+}
+
+inline void QVariant::PrivateShared::free(PrivateShared *p)
+{
+ p->~PrivateShared();
+ operator delete(p);
+}
+
+inline QVariant::Private::Private(const QtPrivate::QMetaTypeInterface *iface) noexcept
+ : is_shared(false), is_null(false), packedType(quintptr(iface) >> 2)
+{
+ Q_ASSERT((quintptr(iface) & 0x3) == 0);
+}
+
+template <typename T> inline
+QVariant::Private::Private(std::piecewise_construct_t, const T &t)
+ : is_shared(!CanUseInternalSpace<T>), is_null(std::is_same_v<T, std::nullptr_t>)
+{
+ // confirm noexceptness
+ static constexpr bool isNothrowQVariantConstructible = noexcept(QVariant(t));
+ static constexpr bool isNothrowCopyConstructible = std::is_nothrow_copy_constructible_v<T>;
+ static constexpr bool isNothrowCopyAssignable = std::is_nothrow_copy_assignable_v<T>;
+
+ const QtPrivate::QMetaTypeInterface *iface = QtPrivate::qMetaTypeInterfaceForType<T>();
+ Q_ASSERT((quintptr(iface) & 0x3) == 0);
+ packedType = quintptr(iface) >> 2;
+
+ if constexpr (CanUseInternalSpace<T>) {
+ static_assert(isNothrowQVariantConstructible == isNothrowCopyConstructible);
+ static_assert(isNothrowQVariantConstructible == isNothrowCopyAssignable);
+ new (data.data) T(t);
+ } else {
+ static_assert(!isNothrowQVariantConstructible); // we allocate memory, even if T doesn't
+ data.shared = customConstructShared(sizeof(T), alignof(T), [=](void *where) {
+ new (where) T(t);
+ });
+ }
+}
+
+QT_END_NAMESPACE
+
+#endif // QVARIANT_P_H
diff --git a/src/corelib/kernel/qwineventnotifier.cpp b/src/corelib/kernel/qwineventnotifier.cpp
index cac29b68bb..afbf4227dc 100644
--- a/src/corelib/kernel/qwineventnotifier.cpp
+++ b/src/corelib/kernel/qwineventnotifier.cpp
@@ -248,3 +248,5 @@ void QWinEventNotifierPrivate::waitCallback(PTP_CALLBACK_INSTANCE instance, PVOI
}
QT_END_NAMESPACE
+
+#include "moc_qwineventnotifier.cpp"
diff --git a/src/corelib/kernel/qwinregistry.cpp b/src/corelib/kernel/qwinregistry.cpp
index dc5252f9d1..d237316577 100644
--- a/src/corelib/kernel/qwinregistry.cpp
+++ b/src/corelib/kernel/qwinregistry.cpp
@@ -125,10 +125,10 @@ QString QWinRegistryKey::stringValue(QStringView subKey) const
return value<QString>(subKey).value_or(QString());
}
-QPair<DWORD, bool> QWinRegistryKey::dwordValue(QStringView subKey) const
+std::pair<DWORD, bool> QWinRegistryKey::dwordValue(QStringView subKey) const
{
const std::optional<DWORD> val = value<DWORD>(subKey);
- return qMakePair(val.value_or(0), val.has_value());
+ return {val.value_or(0), val.has_value()};
}
QT_END_NAMESPACE
diff --git a/src/corelib/kernel/qwinregistry_p.h b/src/corelib/kernel/qwinregistry_p.h
index 0de1bfe0f5..20b2d10dd7 100644
--- a/src/corelib/kernel/qwinregistry_p.h
+++ b/src/corelib/kernel/qwinregistry_p.h
@@ -58,7 +58,7 @@ public:
// ### TODO: Remove once all usages are migrated to new interface.
QString stringValue(QStringView subKey) const;
- QPair<DWORD, bool> dwordValue(QStringView subKey) const;
+ std::pair<DWORD, bool> dwordValue(QStringView subKey) const;
private:
HKEY m_key = nullptr;