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.h9
-rw-r--r--src/corelib/kernel/qapplicationstatic.qdoc7
-rw-r--r--src/corelib/kernel/qbasictimer.cpp20
-rw-r--r--src/corelib/kernel/qchronotimer.cpp452
-rw-r--r--src/corelib/kernel/qchronotimer.h149
-rw-r--r--src/corelib/kernel/qcore_foundation.mm15
-rw-r--r--src/corelib/kernel/qcore_mac.mm52
-rw-r--r--src/corelib/kernel/qcore_mac_p.h67
-rw-r--r--src/corelib/kernel/qcore_unix.cpp91
-rw-r--r--src/corelib/kernel/qcore_unix_p.h71
-rw-r--r--src/corelib/kernel/qcore_wasm.cpp8
-rw-r--r--src/corelib/kernel/qcoreapplication.cpp354
-rw-r--r--src/corelib/kernel/qcoreapplication.h30
-rw-r--r--src/corelib/kernel/qcoreapplication_p.h6
-rw-r--r--src/corelib/kernel/qcoreapplication_platform.h4
-rw-r--r--src/corelib/kernel/qcoreevent.cpp38
-rw-r--r--src/corelib/kernel/qcoreevent.h23
-rw-r--r--src/corelib/kernel/qcoreevent_p.h39
-rw-r--r--src/corelib/kernel/qdeadlinetimer.cpp56
-rw-r--r--src/corelib/kernel/qdeadlinetimer.h31
-rw-r--r--src/corelib/kernel/qelapsedtimer.cpp27
-rw-r--r--src/corelib/kernel/qelapsedtimer.h39
-rw-r--r--src/corelib/kernel/qeventdispatcher_cf.mm54
-rw-r--r--src/corelib/kernel/qeventdispatcher_cf_p.h37
-rw-r--r--src/corelib/kernel/qeventdispatcher_glib.cpp58
-rw-r--r--src/corelib/kernel/qeventdispatcher_glib_p.h14
-rw-r--r--src/corelib/kernel/qeventdispatcher_unix.cpp115
-rw-r--r--src/corelib/kernel/qeventdispatcher_unix_p.h22
-rw-r--r--src/corelib/kernel/qeventdispatcher_wasm.cpp323
-rw-r--r--src/corelib/kernel/qeventdispatcher_wasm_p.h28
-rw-r--r--src/corelib/kernel/qeventdispatcher_win.cpp15
-rw-r--r--src/corelib/kernel/qeventdispatcher_win_p.h8
-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.cpp1
-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.h468
-rw-r--r--src/corelib/kernel/qjnienvironment.cpp106
-rw-r--r--src/corelib/kernel/qjnienvironment.h16
-rw-r--r--src/corelib/kernel/qjnihelpers.cpp157
-rw-r--r--src/corelib/kernel/qjnihelpers_p.h33
-rw-r--r--src/corelib/kernel/qjniobject.cpp608
-rw-r--r--src/corelib/kernel/qjniobject.h790
-rw-r--r--src/corelib/kernel/qjnitypes.h467
-rw-r--r--src/corelib/kernel/qjnitypes_impl.h373
-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.cpp436
-rw-r--r--src/corelib/kernel/qmetaobject.h26
-rw-r--r--src/corelib/kernel/qmetaobject_p.h25
-rw-r--r--src/corelib/kernel/qmetaobjectbuilder.cpp4
-rw-r--r--src/corelib/kernel/qmetatype.cpp195
-rw-r--r--src/corelib/kernel/qmetatype.h99
-rw-r--r--src/corelib/kernel/qmetatype_p.h14
-rw-r--r--src/corelib/kernel/qmimedata.cpp84
-rw-r--r--src/corelib/kernel/qobject.cpp667
-rw-r--r--src/corelib/kernel/qobject.h87
-rw-r--r--src/corelib/kernel/qobject_p.h64
-rw-r--r--src/corelib/kernel/qobject_p_p.h13
-rw-r--r--src/corelib/kernel/qobjectcleanuphandler.cpp4
-rw-r--r--src/corelib/kernel/qobjectdefs.h147
-rw-r--r--src/corelib/kernel/qobjectdefs_impl.h197
-rw-r--r--src/corelib/kernel/qpermissions.cpp11
-rw-r--r--src/corelib/kernel/qpermissions_p.h2
-rw-r--r--src/corelib/kernel/qpermissions_wasm.cpp10
-rw-r--r--src/corelib/kernel/qpointer.h81
-rw-r--r--src/corelib/kernel/qpointer.qdoc (renamed from src/corelib/kernel/qpointer.cpp)77
-rw-r--r--src/corelib/kernel/qpoll.cpp4
-rw-r--r--src/corelib/kernel/qproperty.cpp40
-rw-r--r--src/corelib/kernel/qproperty.h31
-rw-r--r--src/corelib/kernel/qproperty_p.h74
-rw-r--r--src/corelib/kernel/qpropertyprivate.h53
-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/qtestsupport_core.cpp117
-rw-r--r--src/corelib/kernel/qtestsupport_core.h34
-rw-r--r--src/corelib/kernel/qtimer.cpp255
-rw-r--r--src/corelib/kernel/qtimer.h80
-rw-r--r--src/corelib/kernel/qtimer_p.h48
-rw-r--r--src/corelib/kernel/qtimerinfo_unix.cpp361
-rw-r--r--src/corelib/kernel/qtimerinfo_unix_p.h81
-rw-r--r--src/corelib/kernel/qtmetamacros.h2
-rw-r--r--src/corelib/kernel/qtmochelpers.h3
-rw-r--r--src/corelib/kernel/qtranslator.cpp9
-rw-r--r--src/corelib/kernel/qvariant.cpp260
-rw-r--r--src/corelib/kernel/qvariant.h214
-rw-r--r--src/corelib/kernel/qvariant_p.h25
-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
103 files changed, 6792 insertions, 3619 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 1eadeb20e2..345a880748 100644
--- a/src/corelib/kernel/qapplicationstatic.h
+++ b/src/corelib/kernel/qapplicationstatic.h
@@ -11,6 +11,10 @@
#include <new>
+#if 0
+#pragma qt_class(QApplicationStatic)
+#endif
+
QT_BEGIN_NAMESPACE
namespace QtGlobalStatic {
@@ -50,7 +54,10 @@ template <typename QAS> struct ApplicationHolder
QMutexLocker locker(&mutex);
if (guard.loadRelaxed() == QtGlobalStatic::Uninitialized) {
QAS::innerFunction(&storage);
- QObject::connect(QCoreApplication::instance(), &QObject::destroyed, reset);
+ 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();
diff --git a/src/corelib/kernel/qapplicationstatic.qdoc b/src/corelib/kernel/qapplicationstatic.qdoc
index 5cbac65df9..1d13fbd84a 100644
--- a/src/corelib/kernel/qapplicationstatic.qdoc
+++ b/src/corelib/kernel/qapplicationstatic.qdoc
@@ -2,9 +2,14 @@
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
/*!
+ \headerfile <QApplicationStatic>
+ \inmodule QtCore
+*/
+
+/*!
\macro Q_APPLICATION_STATIC(Type, VariableName, ...)
\since 6.3
- \relates QGlobalStatic
+ \relates <QApplicationStatic>
This macro extends Q_GLOBAL_STATIC and creates a global and static object
of type \l QGlobalStatic, of name \a VariableName, initialized by the
diff --git a/src/corelib/kernel/qbasictimer.cpp b/src/corelib/kernel/qbasictimer.cpp
index b843ec1ecf..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/tetrix}{Tetrix} example uses QBasicTimer to control
- the rate at which pieces fall.
-
- \sa QTimer, QTimerEvent, QObject::timerEvent(), Timers, {Affine Transformations}
+ \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.
*/
@@ -159,7 +165,7 @@ void QBasicTimer::start(std::chrono::milliseconds duration, Qt::TimerType timerT
}
stop();
if (obj)
- id = eventDispatcher->registerTimer(duration.count(), timerType, obj);
+ id = int(eventDispatcher->registerTimer(duration, timerType, obj));
}
/*!
@@ -171,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/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 cf15e24917..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.
diff --git a/src/corelib/kernel/qcore_mac.mm b/src/corelib/kernel/qcore_mac.mm
index 2ad835b754..00b0d078d7 100644
--- a/src/corelib/kernel/qcore_mac.mm
+++ b/src/corelib/kernel/qcore_mac.mm
@@ -25,7 +25,6 @@
#include "qendian.h"
#include "qhash.h"
-#include "qpair.h"
#include "qmutex.h"
#include "qvarlengtharray.h"
#include "private/qlocking_p.h"
@@ -53,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,
@@ -65,6 +65,7 @@ static void initializeStandardUserDefaults()
Q_UNUSED(NSUserDefaults.standardUserDefaults);
}
Q_CONSTRUCTOR_FUNCTION(initializeStandardUserDefaults);
+#endif
// --------------------------------------------------------------------------
@@ -86,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);
@@ -137,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)
@@ -327,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 {};
@@ -487,7 +506,7 @@ bool qt_apple_isSandboxed()
}
QT_END_NAMESPACE
-@implementation NSObject (QtSandboxHelpers)
+@implementation NSObject (QtExtras)
- (id)qt_valueForPrivateKey:(NSString *)key
{
if (qt_apple_isSandboxed())
@@ -522,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];
@@ -548,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;
@@ -697,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 39ffe831bb..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;
@@ -201,7 +205,7 @@ Q_CORE_EXPORT bool qt_apple_isSandboxed();
#if defined(__OBJC__)
QT_END_NAMESPACE
-@interface NSObject (QtSandboxHelpers)
+@interface NSObject (QtExtras)
- (id)qt_valueForPrivateKey:(NSString *)key;
@end
QT_BEGIN_NAMESPACE
@@ -233,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);
@@ -430,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();
@@ -438,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 78d99a382d..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>
@@ -20,6 +20,21 @@
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
@@ -65,55 +80,19 @@ int qt_open64(const char *pathname, int flags, mode_t mode)
#ifndef QT_BOOTSTRAPPED
-static inline void do_gettime(qint64 *sec, qint64 *frac)
-{
- timespec ts;
- clockid_t clk = CLOCK_REALTIME;
-#if defined(CLOCK_MONOTONIC_RAW)
- clk = CLOCK_MONOTONIC_RAW;
-#elif defined(CLOCK_MONOTONIC)
- clk = CLOCK_MONOTONIC;
-#endif
-
- clock_gettime(clk, &ts);
- *sec = ts.tv_sec;
- *frac = ts.tv_nsec;
-}
-
-// also used in 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;
-}
-
#if QT_CONFIG(poll_pollts)
# 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);
@@ -139,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 ed64a5d86b..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"
@@ -44,10 +44,6 @@
#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
@@ -60,7 +56,7 @@
struct sockaddr;
-#define EINTR_LOOP(var, cmd) \
+#define QT_EINTR_LOOP(var, cmd) \
do { \
var = cmd; \
} while (var == -1 && errno == EINTR)
@@ -184,21 +180,7 @@ inline timespec qAbsTimespec(timespec ts)
return normalizedTimespec(ts);
}
-inline void qt_ignore_sigpipe()
-{
- // 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);
- }
-}
+Q_CORE_EXPORT void qt_ignore_sigpipe() noexcept;
#if defined(Q_PROCESSOR_X86_32) && defined(__GLIBC__)
# if !__GLIBC_PREREQ(2, 22)
@@ -208,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)
@@ -216,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)
@@ -288,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;
@@ -304,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
@@ -313,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
@@ -328,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
@@ -340,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)
@@ -370,8 +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;
QByteArray qt_readlink(const char *path);
/* non-static */
@@ -389,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 79b5b01cc1..fb12ae50c3 100644
--- a/src/corelib/kernel/qcore_wasm.cpp
+++ b/src/corelib/kernel/qcore_wasm.cpp
@@ -49,9 +49,9 @@ emscripten::val QRectF::toDOMRect() const
\since 6.6
\ingroup platform-type-conversions
- \sa toJsString()
+ \sa toEcmaString()
*/
-QString QString::fromJsString(emscripten::val jsString)
+QString QString::fromEcmaString(emscripten::val jsString)
{
Q_ASSERT_X(jsString.isString(), Q_FUNC_INFO, "Passed object is not a string");
@@ -86,9 +86,9 @@ QString QString::fromJsString(emscripten::val jsString)
\since 6.6
\ingroup platform-type-conversions
- \sa fromJsString()
+ \sa fromEcmaString()
*/
-emscripten::val QString::toJsString() const
+emscripten::val QString::toEcmaString() const
{
static const emscripten::val UTF16ToString(emscripten::val::module_property("UTF16ToString"));
return UTF16ToString(emscripten::val(quintptr(utf16())));
diff --git a/src/corelib/kernel/qcoreapplication.cpp b/src/corelib/kernel/qcoreapplication.cpp
index 36892b6f6c..4262c14405 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"
@@ -34,8 +35,8 @@
#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>
@@ -56,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"
@@ -104,9 +107,14 @@
#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;
Q_TRACE_PREFIX(qtcore,
@@ -233,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()
{
}
@@ -293,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();
}
@@ -513,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);
@@ -576,7 +589,9 @@ void QCoreApplicationPrivate::initConsole()
return;
consoleAllocated = true;
} else if (env.compare(u"attach"_s, Qt::CaseInsensitive) == 0) {
- if (AttachConsole(ATTACH_PARENT_PROCESS) == FALSE)
+ // 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.
@@ -621,9 +636,13 @@ void QCoreApplicationPrivate::initLocale()
# 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
- const char *charEncoding = nl_langinfo(CODESET);
- if (Q_UNLIKELY(qstricmp(charEncoding, "UTF-8") != 0 && qstricmp(charEncoding, "utf8") != 0)) {
+ // 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;
@@ -658,14 +677,14 @@ void QCoreApplicationPrivate::initLocale()
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(), nl_langinfo(CODESET));
+ "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(), nl_langinfo(CODESET), newLocale.constData());
+ oldLocale.constData(), oldEncoding.data(), newLocale.constData());
}
}
# endif // Platform choice
@@ -745,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}
*/
/*!
@@ -984,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)
{
@@ -1004,7 +1027,6 @@ bool QCoreApplication::isSetuidAllowed()
return QCoreApplicationPrivate::setuidAllowed;
}
-
/*!
Sets the attribute \a attribute if \a on is true;
otherwise clears the attribute.
@@ -1017,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
@@ -1065,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
@@ -1092,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
@@ -1112,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);
}
@@ -1170,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.
@@ -1188,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;
@@ -1235,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)
@@ -1266,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;
@@ -1334,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)
{
@@ -1345,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
@@ -1366,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
@@ -1376,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;
}
}
@@ -1397,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
@@ -1452,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);
}
@@ -1635,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);
@@ -1686,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
@@ -1792,6 +1825,8 @@ void QCoreApplicationPrivate::sendPostedEvents(QObject *receiver, int event_type
// Exception-safe cleaning up without the need for a try/catch block
struct CleanUp {
+ Q_DISABLE_COPY_MOVE(CleanUp)
+
QObject *receiver;
int event_type;
QThreadData *data;
@@ -1846,14 +1881,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
@@ -1870,6 +1928,8 @@ void QCoreApplicationPrivate::sendPostedEvents(QObject *receiver, int event_type
data->postEventList.addEvent(pe_copy);
}
continue;
+ } else {
+ qCDebug(lcDeleteLater) << "Sending deferred delete to" << pe.receiver;
}
}
@@ -2044,7 +2104,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;
@@ -2132,6 +2198,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()
*/
@@ -2155,7 +2227,7 @@ 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.
@@ -2440,10 +2512,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]);
}
}
@@ -2587,7 +2659,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
@@ -2627,7 +2699,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
@@ -2663,11 +2735,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()
*/
@@ -2767,7 +2843,7 @@ Qt::PermissionStatus QCoreApplication::checkPermission(const QPermission &permis
}
/*!
- \fn template<typename Functor> void QCoreApplication::requestPermission(
+ \fn template <typename Functor> void QCoreApplication::requestPermission(
const QPermission &permission, Functor &&functor)
Requests the given \a permission.
@@ -2837,86 +2913,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);
-
- // Used as the signalID in the metacall event and only used to
- // verify that we are not processing an unrelated event, not to
- // emit the right signal. So using a value that can never clash
- // with any signal index. Clang doesn't like this to be a static
- // member of the PermissionReceiver.
- static constexpr ushort PermissionReceivedID = 0xffff;
-
- // If we have a context object, then we dispatch the permission response
- // asynchronously through a received object that lives in the same thread
- // as the context object. Otherwise we call the functor synchronously when
- // we get a response (which might still be asynchronous for the caller).
class PermissionReceiver : public QObject
{
public:
- PermissionReceiver(QtPrivate::QSlotObjectBase *slotObject, const QObject *context)
- : slotObject(slotObject), context(context)
- {}
+ explicit PermissionReceiver(QtPrivate::SlotObjUniquePtr &&slotObject, const QObject *context)
+ : slotObject(std::move(slotObject)), context(context ? context : this)
+ {
+ Q_ASSERT(this->context);
+ moveToThread(this->context->thread());
+ }
- protected:
- bool event(QEvent *event) override {
- if (event->type() == QEvent::MetaCall) {
- auto metaCallEvent = static_cast<QMetaCallEvent *>(event);
- if (metaCallEvent->id() == PermissionReceivedID) {
- Q_ASSERT(slotObject);
- // only execute if context object is still alive
- if (context)
- slotObject->call(const_cast<QObject*>(context.data()), metaCallEvent->args());
- slotObject->destroyIfLastRef();
- deleteLater();
-
- return true;
- }
+ 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);
}
- return QObject::event(event);
+ deleteLater();
}
+
private:
- QtPrivate::QSlotObjectBase *slotObject;
+ QtPrivate::SlotObjSharedPtr slotObject;
QPointer<const QObject> context;
};
- PermissionReceiver *receiver = nullptr;
- if (context) {
- receiver = new PermissionReceiver(slotObj, context);
- receiver->moveToThread(context->thread());
- }
+
+ 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;
-
- if (receiver) {
- auto metaCallEvent = QMetaCallEvent::create(slotObj, qApp,
- PermissionReceivedID, permission);
- qApp->postEvent(receiver, metaCallEvent);
- } else {
- void *argv[] = { nullptr, &permission };
- slotObj->call(const_cast<QObject*>(context), argv);
- }
+ QMetaObject::invokeMethod(receiver,
+ &PermissionReceiver::finalizePermissionRequest,
+ Qt::QueuedConnection,
+ permission);
}
-
- if (!receiver)
- slotObj->destroyIfLastRef();
});
}
@@ -2952,11 +3005,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}
*/
@@ -3205,7 +3253,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 37ed9890ed..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);
@@ -120,25 +128,35 @@ public:
# else
// 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>
+ template <typename Functor,
+ std::enable_if_t<
+ QtPrivate::AreFunctionsCompatible<RequestPermissionPrototype, Functor>::value,
+ bool> = true>
void requestPermission(const QPermission &permission,
const typename QtPrivate::ContextTypeForFunctor<Functor>::ContextType *receiver,
Functor &&func)
{
- using Prototype = void(*)(QPermission);
- QtPrivate::AssertCompatibleFunctions<Prototype, Functor>();
requestPermission(permission,
- QtPrivate::makeCallableObject<Prototype>(std::forward<Functor>(func)),
+ QtPrivate::makeCallableObject<RequestPermissionPrototype>(std::forward<Functor>(func)),
receiver);
}
# endif // Q_QDOC
- // requestPermission to a functor or function pointer (without context)
+#ifndef QT_NO_CONTEXTLESS_CONNECT
+ #ifdef Q_QDOC
template <typename Functor>
+ #else
+ // requestPermission to a functor or function pointer (without context)
+ 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::forward<Functor>(func));
}
+#endif // QT_NO_CONTEXTLESS_CONNECT
private:
// ### Qt 7: rename to requestPermissionImpl to avoid ambiguity
diff --git a/src/corelib/kernel/qcoreapplication_p.h b/src/corelib/kernel/qcoreapplication_p.h
index 56d726cff5..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;
@@ -103,6 +108,7 @@ public:
virtual void quit();
static QBasicAtomicPointer<QThread> theMainThread;
+ static QBasicAtomicPointer<void> theMainThreadId;
static QThread *mainThread();
static bool threadRequiresCoreApplication();
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/qcoreevent.cpp b/src/corelib/kernel/qcoreevent.cpp
index b01f1c2a69..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"
@@ -78,6 +79,8 @@ Q_TRACE_POINT(qtcore, QEvent_dtor, QEvent *event, QEvent::Type type);
\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).
@@ -85,7 +88,8 @@ Q_TRACE_POINT(qtcore, QEvent_dtor, QEvent *event, QEvent::Type type);
\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 DevicePixelRatioChange The devicePixelRatio has changed for this widget's or window's underlying backing store
+ \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).
@@ -158,8 +162,12 @@ Q_TRACE_POINT(qtcore, QEvent_dtor, QEvent *event, QEvent::Type type);
\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
@@ -432,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'
@@ -510,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()
*/
@@ -633,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 0c83919bc5..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
@@ -286,6 +286,11 @@ public:
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
@@ -349,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
@@ -395,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 4e15b3b80f..f99e68f990 100644
--- a/src/corelib/kernel/qdeadlinetimer.cpp
+++ b/src/corelib/kernel/qdeadlinetimer.cpp
@@ -51,6 +51,8 @@ static qint64 add_saturate(qint64 t1, Duration1 dur, Durations... extra)
\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
@@ -84,11 +86,12 @@ static qint64 add_saturate(qint64 t1, Duration1 dur, Durations... extra)
\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
@@ -120,7 +123,7 @@ static qint64 add_saturate(qint64 t1, Duration1 dur, Durations... extra)
\snippet code/src_corelib_kernel_qdeadlinetimer.cpp 2
- \sa QTime, QTimer, QDeadlineTimer, Qt::TimerType
+ \sa QTime, QChronoTimer, QDeadlineTimer, Qt::TimerType
*/
/*!
@@ -131,10 +134,12 @@ static qint64 add_saturate(qint64 t1, Duration1 dur, Durations... extra)
*/
/*!
+ \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
@@ -588,9 +593,9 @@ QDeadlineTimer QDeadlineTimer::current(Qt::TimerType timerType) noexcept
}
/*!
- \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:
@@ -601,9 +606,9 @@ QDeadlineTimer QDeadlineTimer::current(Qt::TimerType timerType) noexcept
*/
/*!
- \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:
@@ -614,10 +619,10 @@ QDeadlineTimer QDeadlineTimer::current(Qt::TimerType timerType) noexcept
*/
/*!
- \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
@@ -627,10 +632,10 @@ QDeadlineTimer QDeadlineTimer::current(Qt::TimerType timerType) noexcept
*/
/*!
- \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
@@ -640,10 +645,10 @@ QDeadlineTimer QDeadlineTimer::current(Qt::TimerType timerType) noexcept
*/
/*!
- \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
@@ -653,10 +658,10 @@ QDeadlineTimer QDeadlineTimer::current(Qt::TimerType timerType) noexcept
*/
/*!
- \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
@@ -746,9 +751,4 @@ QDeadlineTimer operator+(QDeadlineTimer dt, qint64 msecs)
Returns the time remaining before the deadline.
*/
-/*!
- \fn QPair<qint64, unsigned> QDeadlineTimer::_q_data() const
- \internal
-*/
-
QT_END_NAMESPACE
diff --git a/src/corelib/kernel/qdeadlinetimer.h b/src/corelib/kernel/qdeadlinetimer.h
index b872422add..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()
@@ -26,7 +25,8 @@ public:
enum class ForeverConstant { Forever };
static constexpr ForeverConstant Forever = ForeverConstant::Forever;
- constexpr QDeadlineTimer(Qt::TimerType type_ = Qt::CoarseTimer) noexcept
+ 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)()), type(type_) {}
@@ -58,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; }
- friend bool operator!=(QDeadlineTimer d1, QDeadlineTimer d2) noexcept
- { return !(d1 == d2); }
- friend bool operator<(QDeadlineTimer d1, QDeadlineTimer d2) noexcept
- { return d1.t1 < d2.t1; }
- 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; }
@@ -138,11 +125,23 @@ public:
{ return dt = dt + value; }
private:
+ 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 t1 = 0;
#if QT_VERSION < QT_VERSION_CHECK(7, 0, 0)
unsigned t2 = 0;
#endif
- unsigned type;
+ unsigned type = Qt::CoarseTimer;
qint64 rawRemainingTimeNSecs() const noexcept;
};
diff --git a/src/corelib/kernel/qelapsedtimer.cpp b/src/corelib/kernel/qelapsedtimer.cpp
index 66ae82811f..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.
@@ -202,6 +203,16 @@ bool QElapsedTimer::isMonotonic() noexcept
}
/*!
+ \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:
@@ -403,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
*/
diff --git a/src/corelib/kernel/qelapsedtimer.h b/src/corelib/kernel/qelapsedtimer.h
index 7d8b889f61..e71573456d 100644
--- a/src/corelib/kernel/qelapsedtimer.h
+++ b/src/corelib/kernel/qelapsedtimer.h
@@ -4,6 +4,7 @@
#ifndef QELAPSEDTIMER_H
#define QELAPSEDTIMER_H
+#include <QtCore/qcompare.h>
#include <QtCore/qglobal.h>
#include <chrono>
@@ -45,15 +46,41 @@ public:
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:
+ 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);
};
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..95bd328c5e 100644
--- a/src/corelib/kernel/qeventdispatcher_cf_p.h
+++ b/src/corelib/kernel/qeventdispatcher_cf_p.h
@@ -63,8 +63,8 @@ Q_FORWARD_DECLARE_OBJC_CLASS(QT_MANGLE_NAMESPACE(RunLoopModeTracker));
QT_BEGIN_NAMESPACE
namespace QtPrivate {
-Q_DECLARE_EXPORTED_LOGGING_CATEGORY(lcEventDispatcher, Q_CORE_EXPORT)
-Q_DECLARE_EXPORTED_LOGGING_CATEGORY(lcEventDispatcherTimers, Q_CORE_EXPORT)
+QT_DECLARE_EXPORTED_QT_LOGGING_CATEGORY(lcEventDispatcher, Q_CORE_EXPORT)
+QT_DECLARE_EXPORTED_QT_LOGGING_CATEGORY(lcEventDispatcherTimers, Q_CORE_EXPORT)
}
class QEventDispatcherCoreFoundation;
@@ -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 85403bdabb..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);
@@ -475,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()) {
@@ -488,14 +491,13 @@ void QEventDispatcherGlib::registerTimer(int timerId, qint64 interval, Qt::Timer
#endif
Q_D(QEventDispatcherGlib);
- d->timerSource->timerList.registerTimer(timerId, std::chrono::milliseconds{ interval },
- timerType, object);
+ 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 c75b59acfe..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()) {
@@ -299,16 +283,16 @@ void QEventDispatcherUNIX::registerTimer(int timerId, qint64 interval, Qt::Timer
#endif
Q_D(QEventDispatcherUNIX);
- d->timerList.registerTimer(timerId, std::chrono::milliseconds{ interval }, timerType, obj);
+ d->timerList.registerTimer(timerId, interval, timerType, obj);
}
/*!
\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 6818a5eab8..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,6 +43,50 @@ 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;
@@ -66,6 +113,7 @@ EM_JS(void, qt_asyncify_resume_js, (), {
wakeUp();
});
});
+// clang-format on
#else
@@ -76,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();
@@ -103,15 +173,15 @@ 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;
@@ -124,7 +194,6 @@ 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:
@@ -149,6 +218,12 @@ QEventDispatcherWasm::QEventDispatcherWasm()
#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);
@@ -203,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
@@ -212,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)
@@ -226,28 +297,36 @@ bool QEventDispatcherWasm::processEvents(QEventLoop::ProcessEventsFlags flags)
handleApplicationExec();
}
- if (!hasPendingEvents && (flags & QEventLoop::WaitForMoreEvents))
- wait();
+#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();
+
+ // 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();
}
- QCoreApplication::sendPostedEvents();
- processWindowSystemEvents(flags);
-
- return qGlobalPostedEventsCount() > 0;
-}
-
-void QEventDispatcherWasm::processWindowSystemEvents(QEventLoop::ProcessEventsFlags flags)
-{
- Q_UNUSED(flags);
+ return false;
}
void QEventDispatcherWasm::registerSocketNotifier(QSocketNotifier *notifier)
@@ -257,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)
@@ -273,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()) {
@@ -288,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()) {
@@ -307,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();
@@ -334,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()
@@ -365,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)
@@ -373,7 +454,7 @@ void QEventDispatcherWasm::wakeUp()
m_pendingProcessEvents = true;
}
runOnMainThreadAsync([this](){
- QEventDispatcherWasm::callProcessEvents(this);
+ QEventDispatcherWasm::callProcessPostedEvents(this);
});
}
}
@@ -388,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()
@@ -417,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;
@@ -429,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 {
@@ -456,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());
@@ -473,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)
@@ -483,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()
@@ -507,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 timespecToMsec = [](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 = timespecToMsec(m_timerInfo->currentTime);
- uint64_t toWaitDuration = timespecToMsec(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;
};
@@ -548,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();
});
}
@@ -565,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;
}
@@ -575,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();
}
@@ -792,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){
@@ -810,26 +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);
- g_proxyingQueue.proxyAsync(g_mainThread, [context]{
- 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,
@@ -849,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 f253a1a95f..7b257e02ad 100644
--- a/src/corelib/kernel/qeventdispatcher_wasm_p.h
+++ b/src/corelib/kernel/qeventdispatcher_wasm_p.h
@@ -20,6 +20,7 @@
#include <QtCore/qloggingcategory.h>
#include <QtCore/qwaitcondition.h>
+#include <chrono>
#include <mutex>
#include <optional>
#include <tuple>
@@ -31,7 +32,7 @@ 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:
@@ -43,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);
+
+ static void registerStartupTask();
+ static void completeStarupTask();
+ static void callOnLoadedIfRequired();
+ virtual void onLoaded();
+
protected:
- virtual void processWindowSystemEvents(QEventLoop::ProcessEventsFlags flags);
+ virtual bool processPostedEvents();
private:
bool isMainThreadEventDispatcher();
@@ -66,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();
@@ -88,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;
@@ -99,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;
diff --git a/src/corelib/kernel/qeventdispatcher_win.cpp b/src/corelib/kernel/qeventdispatcher_win.cpp
index f7fd2a7b51..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"
@@ -136,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 {
@@ -409,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
@@ -558,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) {
@@ -582,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;
@@ -626,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;
@@ -643,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);
@@ -906,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 e6ea6b28c3..558490a85e 100644
--- a/src/corelib/kernel/qeventdispatcher_win_p.h
+++ b/src/corelib/kernel/qeventdispatcher_win_p.h
@@ -71,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;
@@ -82,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;
@@ -135,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 d5ce3e5894..048fdbc934 100644
--- a/src/corelib/kernel/qfunctions_win.cpp
+++ b/src/corelib/kernel/qfunctions_win.cpp
@@ -28,6 +28,7 @@ QComHelper::QComHelper(COINIT concurrencyModel)
QComHelper::~QComHelper()
{
+ Q_ASSERT(m_threadId == GetCurrentThreadId());
if (SUCCEEDED(m_initResult))
CoUninitialize();
}
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..e3e9d0c4dc
--- /dev/null
+++ b/src/corelib/kernel/qjniarray.h
@@ -0,0 +1,468 @@
+// 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 C, typename = void> struct IsContiguousContainerHelper : std::false_type {};
+ template <typename C>
+ struct IsContiguousContainerHelper<C, std::void_t<decltype(std::data(std::declval<C>())),
+ decltype(std::size(std::declval<C>())),
+ typename C::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 C>
+ static constexpr bool isContiguousContainer = IsContiguousContainerHelper<q20::remove_cvref_t<C>>::value;
+ template <typename C>
+ using if_contiguous_container = std::enable_if_t<isContiguousContainer<C>, bool>;
+
+ template <typename Container, if_contiguous_container<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, if_contiguous_container<Container> = true>
+ explicit QJniArray(Container &&container)
+ : QJniArrayBase(QJniArrayBase::fromContainer(std::forward<Container>(container)))
+ {
+ }
+
+ Q_IMPLICIT inline QJniArray(std::initializer_list<T> list)
+ : QJniArrayBase(QJniArrayBase::fromContainer(list))
+ {
+ }
+
+ template <typename Other>
+ using if_convertible = std::enable_if_t<std::is_convertible_v<Other, T>, bool>;
+
+ template <typename Other, if_convertible<Other> = 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;
+ }
+ }
+};
+
+// Deduction guide so that we can construct as 'QJniArray list(Container<T>)'. Since
+// fromContainer() maps several C++ types to the same JNI type (e.g. both jboolean and
+// bool become QJniArray<jboolean>), we have to deduce to what fromContainer() would
+// give us.
+template <typename Container, QJniArrayBase::if_contiguous_container<Container> = true>
+QJniArray(Container) -> QJniArray<typename decltype(QJniArrayBase::fromContainer(std::declval<Container>()))::value_type>;
+
+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 7fda44a21e..b4f8497ddd 100644
--- a/src/corelib/kernel/qjnienvironment.cpp
+++ b/src/corelib/kernel/qjnienvironment.cpp
@@ -29,8 +29,6 @@ QT_BEGIN_NAMESPACE
It has not been tested for other platforms.
*/
-static const char qJniThreadName[] = "QtThread";
-
class QJniEnvironmentPrivate
{
public:
@@ -46,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;
}
/*!
@@ -111,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
@@ -121,8 +112,6 @@ JNIEnv *QJniEnvironment::operator->() const
}
/*!
- \fn JNIEnv &QJniEnvironment::operator*() const
-
Returns the JNI Environment's \c JNIEnv object.
*/
JNIEnv &QJniEnvironment::operator*() const
@@ -131,8 +120,6 @@ JNIEnv &QJniEnvironment::operator*() const
}
/*!
- \fn JNIEnv *QJniEnvironment::jniEnv() const
-
Returns the JNI Environment's \c JNIEnv pointer.
*/
JNIEnv *QJniEnvironment::jniEnv() const
@@ -323,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.
@@ -335,6 +320,30 @@ JavaVM *QJniEnvironment::javaVM()
}
/*!
+ \fn template <typename Class> bool QJniEnvironment::registerNativeMethods(std::initializer_list<JNINativeMethod> 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.
@@ -366,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
@@ -421,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
@@ -448,7 +474,7 @@ bool QJniEnvironment::checkAndClearExceptions(QJniEnvironment::OutputMode output
namespace {
// Any pending exception need to be cleared before calling this
- QString exceptionMessage(JNIEnv *env, const jthrowable &exception)
+ QString exceptionMessage(JNIEnv *env, jthrowable exception)
{
if (!exception)
return {};
@@ -496,8 +522,6 @@ namespace {
} // 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.
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 78d05261e5..4d5ccd7d9b 100644
--- a/src/corelib/kernel/qjnihelpers.cpp
+++ b/src/corelib/kernel/qjnihelpers.cpp
@@ -18,6 +18,10 @@
QT_BEGIN_NAMESPACE
+Q_DECLARE_JNI_CLASS(QtInputDelegate, "org/qtproject/qt/android/QtInputDelegate");
+Q_DECLARE_JNI_CLASS(MotionEvent, "android/view/MotionEvent");
+Q_DECLARE_JNI_CLASS(KeyEvent, "android/view/KeyEvent");
+
namespace QtAndroidPrivate {
// *Listener virtual function implementations.
// Defined out-of-line to pin the vtable/type_info.
@@ -26,8 +30,6 @@ namespace QtAndroidPrivate {
ResumePauseListener::~ResumePauseListener() {}
void ResumePauseListener::handlePause() {}
void ResumePauseListener::handleResume() {}
- GenericMotionEventListener::~GenericMotionEventListener() {}
- KeyEventListener::~KeyEventListener() {}
}
static JavaVM *g_javaVM = nullptr;
@@ -42,40 +44,6 @@ Q_CONSTINIT static QBasicAtomicInt g_serviceSetupLockers = Q_BASIC_ATOMIC_INITIA
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)
{
@@ -177,6 +145,68 @@ void QtAndroidPrivate::handleNewIntent(JNIEnv *env, jobject intent)
}
}
+QtAndroidPrivate::GenericMotionEventListener::~GenericMotionEventListener() {}
+namespace {
+struct GenericMotionEventListeners {
+ QMutex mutex;
+ QList<QtAndroidPrivate::GenericMotionEventListener *> listeners;
+};
+}
+Q_GLOBAL_STATIC(GenericMotionEventListeners, g_genericMotionEventListeners)
+
+static jboolean dispatchGenericMotionEvent(JNIEnv *, jclass, QtJniTypes::MotionEvent event)
+{
+ jboolean ret = JNI_FALSE;
+ QMutexLocker locker(&g_genericMotionEventListeners()->mutex);
+ for (auto *listener : std::as_const(g_genericMotionEventListeners()->listeners))
+ ret |= listener->handleGenericMotionEvent(event.object());
+ return ret;
+}
+Q_DECLARE_JNI_NATIVE_METHOD(dispatchGenericMotionEvent);
+
+QtAndroidPrivate::KeyEventListener::~KeyEventListener() {}
+namespace {
+struct KeyEventListeners {
+ QMutex mutex;
+ QList<QtAndroidPrivate::KeyEventListener *> listeners;
+};
+}
+Q_GLOBAL_STATIC(KeyEventListeners, g_keyEventListeners)
+
+static jboolean dispatchKeyEvent(JNIEnv *, jclass, QtJniTypes::KeyEvent event)
+{
+ jboolean ret = JNI_FALSE;
+ QMutexLocker locker(&g_keyEventListeners()->mutex);
+ for (auto *listener : std::as_const(g_keyEventListeners()->listeners))
+ ret |= listener->handleKeyEvent(event.object());
+ return ret;
+}
+Q_DECLARE_JNI_NATIVE_METHOD(dispatchKeyEvent);
+
+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);
+}
+
namespace {
class ResumePauseListeners
{
@@ -272,8 +302,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) },
};
@@ -282,21 +310,46 @@ jint QtAndroidPrivate::initJNI(JavaVM *vm, JNIEnv *env)
if (!regOk && QJniEnvironment::checkAndClearExceptions(env))
return JNI_ERR;
- if (!registerPermissionNatives())
+ QJniEnvironment qJniEnv;
+ using namespace QtJniTypes;
+ if (!QtInputDelegate::registerNativeMethods(
+ { Q_JNI_NATIVE_METHOD(dispatchGenericMotionEvent),
+ Q_JNI_NATIVE_METHOD(dispatchKeyEvent) })) {
+ qCritical() << "Failed to register natives methods for"
+ << Traits<QtInputDelegate>::className();
+ return JNI_ERR;
+ }
+
+ 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;
@@ -331,30 +384,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();
diff --git a/src/corelib/kernel/qjnihelpers_p.h b/src/corelib/kernel/qjnihelpers_p.h
index bce2b782de..a7b879d9e1 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,6 +49,13 @@ namespace QtAndroidPrivate
virtual void handleResume();
};
+ class Q_CORE_EXPORT OnBindListener
+ {
+ public:
+ virtual ~OnBindListener() {}
+ virtual jobject onBind(jobject intent) = 0;
+ };
+
class Q_CORE_EXPORT GenericMotionEventListener
{
public:
@@ -63,13 +70,6 @@ namespace QtAndroidPrivate
virtual bool handleKeyEvent(jobject event) = 0;
};
- class Q_CORE_EXPORT OnBindListener
- {
- public:
- virtual ~OnBindListener() {}
- virtual jobject onBind(jobject intent) = 0;
- };
-
Q_CORE_EXPORT QtJniTypes::Activity activity();
Q_CORE_EXPORT QtJniTypes::Service service();
Q_CORE_EXPORT QtJniTypes::Context context();
@@ -79,8 +79,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);
@@ -90,17 +91,17 @@ namespace QtAndroidPrivate
Q_CORE_EXPORT void registerNewIntentListener(NewIntentListener *listener);
Q_CORE_EXPORT void unregisterNewIntentListener(NewIntentListener *listener);
- Q_CORE_EXPORT void handlePause();
- Q_CORE_EXPORT void handleResume();
- 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 handlePause();
+ Q_CORE_EXPORT void handleResume();
+ Q_CORE_EXPORT void registerResumePauseListener(ResumePauseListener *listener);
+ Q_CORE_EXPORT void unregisterResumePauseListener(ResumePauseListener *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 df4335092e..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()
-{
- return "%1%2:%3"_L1;
-}
-
-static QString qt_convertJString(jstring string)
+template <typename ...Args>
+static inline QByteArray cacheKey(Args &&...args)
{
- QJniEnvironment env;
- int strLength = env->GetStringLength(string);
- QString res(strLength, Qt::Uninitialized);
- env->GetStringRegion(string, 0, strLength, reinterpret_cast<jchar *>(res.data()));
- return res;
+ return (QByteArrayView(":") + ... + QByteArrayView(args));
}
-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)
@@ -393,13 +462,8 @@ void QJniObject::callVoidMethodV(JNIEnv *env, jmethodID id, ...) const
{
va_list args;
va_start(args, id);
- callVoidMethodV(env, id, args);
- va_end(args);
-}
-
-void QJniObject::callVoidMethodV(JNIEnv *env, jmethodID id, va_list args) const
-{
env->CallVoidMethodV(d->m_jobject, id, args);
+ va_end(args);
}
jmethodID QJniObject::getCachedMethodID(JNIEnv *env,
@@ -412,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);
@@ -443,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)
@@ -472,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);
@@ -505,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()
@@ -562,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();
}
/*!
@@ -595,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);
}
/*!
@@ -630,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.
@@ -662,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);
}
}
@@ -705,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);
- }
- }
- }
- }
}
/*!
@@ -759,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));
@@ -782,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.
@@ -810,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.
@@ -861,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;
- }
- }
-
- 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;
- }
- }
+ 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(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;
}
/*!
@@ -1028,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
@@ -1041,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
@@ -1054,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;
}
@@ -1083,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;
}
@@ -1110,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;
}
@@ -1142,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;
}
@@ -1192,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.
*/
@@ -1213,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.
@@ -1224,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.
*/
@@ -1280,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);
}
/*!
@@ -1309,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);
}
/*!
@@ -1331,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.
@@ -1353,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());
}
/*!
@@ -1375,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.
@@ -1385,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.
@@ -1409,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;
}
/*!
@@ -1433,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;
}
/*!
@@ -1454,7 +1369,7 @@ bool QJniObject::isClassAvailable(const char *className)
if (!env.jniEnv())
return false;
- return loadClass(className, env.jniEnv());;
+ return loadClass(className, env.jniEnv());
}
/*!
@@ -1490,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
@@ -1506,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 2e1ed4add2..f9fc5cb03a 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,17 +580,6 @@ private:
const char *signature, bool isStatic = false);
void callVoidMethodV(JNIEnv *env, jmethodID id, ...) const;
- // ### Qt 7: merge into ... overload
- void callVoidMethodV(JNIEnv *env, jmethodID id, va_list args) const;
- // ### Qt 7: remove unused
- QJniObject callObjectMethodV(const char *methodName, const char *signature,
- va_list args) const;
- // ### Qt 7: remove unused
- static QJniObject callStaticObjectMethodV(const char *className, const char *methodName,
- const char *signature, va_list args);
- // ### Qt 7: remove unused
- 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;
@@ -455,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);
}
@@ -486,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);
@@ -519,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;
@@ -632,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::isContiguousContainer<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::isContiguousContainer<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.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 0ca1c67249..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"
@@ -130,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];
@@ -145,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)
@@ -170,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
{
@@ -334,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);
}
/*!
@@ -346,7 +346,7 @@ static inline const char *objectClassName(const QMetaObject *m)
*/
const char *QMetaObject::className() const
{
- return objectClassName(this);
+ return objectClassName(this).constData();
}
/*!
@@ -401,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
@@ -833,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
@@ -1018,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 {
@@ -1032,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);
@@ -1061,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 (strcmp(name, prop) == 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 (strcmp(name, prop) == 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;
}
@@ -1391,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)
{
@@ -1399,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()) {
@@ -1426,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
@@ -1437,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:
@@ -1584,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;
@@ -1611,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();
@@ -1631,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) {
@@ -1641,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 {
@@ -1736,6 +1746,31 @@ bool QMetaObject::invokeMethodImpl(QObject *object, QtPrivate::QSlotObjectBase *
*/
/*!
+ \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
+
+ 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.
+*/
+
+/*!
\fn QMetaObject::Connection &QMetaObject::Connection::operator=(Connection &&other)
Move-assigns \a other to this object, and returns a reference.
@@ -1763,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
@@ -1790,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.
*/
@@ -1988,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
@@ -1998,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;
}
@@ -2237,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
{
@@ -2354,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:
@@ -2382,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:
@@ -2598,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
@@ -2607,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)
@@ -2615,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]);
@@ -2822,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
@@ -2833,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
@@ -3047,15 +3085,14 @@ const char *QMetaEnum::enumName() const
/*!
Returns the meta type of the enum.
- If the QMetaObject this enum is part of was generated with Qt 6.5 or
- earlier this will be the invalid metatype.
+ 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
- numeric type. You can retrieve the meta type of the underlying type of the
+ integral type. You can retrieve the meta type of the underlying type of the
enum using \l{QMetaType::underlyingType()}.
\since 6.6
- \sa QMetaType::underlyingType()
*/
QMetaType QMetaEnum::metaType() const
{
@@ -3151,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;
}
/*!
@@ -3171,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];
@@ -3210,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;
}
/*!
@@ -3240,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;
@@ -3297,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) {
@@ -3326,7 +3417,7 @@ int QMetaEnum::Data::index(const QMetaObject *mobj) const
}
/*!
- \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.
@@ -3412,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
@@ -3565,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));
}
/*!
@@ -3888,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
@@ -4062,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
*/
diff --git a/src/corelib/kernel/qmetaobject.h b/src/corelib/kernel/qmetaobject.h
index c51922e659..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; }
@@ -251,10 +252,11 @@ protected:
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);
diff --git a/src/corelib/kernel/qmetaobject_p.h b/src/corelib/kernel/qmetaobject_p.h
index 224c9d5354..d5dc9a356a 100644
--- a/src/corelib/kernel/qmetaobject_p.h
+++ b/src/corelib/kernel/qmetaobject_p.h
@@ -111,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;
};
@@ -212,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)
{
diff --git a/src/corelib/kernel/qmetaobjectbuilder.cpp b/src/corelib/kernel/qmetaobjectbuilder.cpp
index b471bf6546..c2b44a4f00 100644
--- a/src/corelib/kernel/qmetaobjectbuilder.cpp
+++ b/src/corelib/kernel/qmetaobjectbuilder.cpp
@@ -1162,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 =
diff --git a/src/corelib/kernel/qmetatype.cpp b/src/corelib/kernel/qmetatype.cpp
index dbdd5b21ef..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>
@@ -151,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;
@@ -269,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
@@ -448,7 +441,7 @@ const char *QtMetaTypePrivate::typedefNameForType(const QtPrivate::QMetaTypeInte
The enum describes attributes of a type supported by QMetaType.
\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 construtcor. If the flag is not set, instances can be copied with memcpy.
+ \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 RelocatableType An instance of a type having this attribute can be safely moved to a different memory location using memcpy.
@@ -481,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
@@ -907,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);
}
/*!
@@ -923,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 },
@@ -954,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 *>>
@@ -1001,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;)
@@ -1155,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);
@@ -1192,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;);
@@ -1254,8 +1277,11 @@ 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 (const auto &v: source)
@@ -1281,7 +1307,6 @@ static const struct : QMetaTypeModuleHelper
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)
@@ -1293,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,
@@ -1693,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.
@@ -1755,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;
@@ -1788,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;
@@ -1856,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
@@ -2075,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;
@@ -2107,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;
@@ -2233,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) {
@@ -2303,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)) {
@@ -2314,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)
@@ -2355,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);
@@ -2372,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;
@@ -2385,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);
@@ -2411,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);
@@ -2454,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;
@@ -2570,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;
@@ -2580,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)
@@ -2616,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
@@ -2629,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);
}
/*!
- \fn bool QMetaType::hasRegisteredMutableViewFunction()
+ \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 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
@@ -2646,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);
}
/*!
@@ -3061,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
@@ -3095,7 +3172,7 @@ QMetaType QMetaType::fromName(QByteArrayView typeName)
*/
/*!
- \fn int qRegisterMetaType()
+ \fn template <typename T> int qRegisterMetaType()
\relates QMetaType
\threadsafe
\since 4.2
@@ -3151,7 +3228,7 @@ QMetaType QMetaType::fromName(QByteArrayView typeName)
*/
/*!
- \fn int qMetaTypeId()
+ \fn template <typename T> int qMetaTypeId()
\relates QMetaType
\threadsafe
\since 4.1
diff --git a/src/corelib/kernel/qmetatype.h b/src/corelib/kernel/qmetatype.h
index 02ecaf9831..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>
@@ -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)\
@@ -497,20 +519,20 @@ public:
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);
@@ -1729,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);
}
@@ -1765,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);
}
@@ -1775,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);
}
@@ -1809,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);
}
@@ -1819,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);
}
@@ -2235,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()
{
@@ -2276,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)
@@ -2310,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>
@@ -2366,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:
//
diff --git a/src/corelib/kernel/qmetatype_p.h b/src/corelib/kernel/qmetatype_p.h
index e649394832..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;
@@ -178,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 9a3ec434c5..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;
@@ -554,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 668c1592dd..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,7 +22,6 @@
#include <qthread.h>
#include <private/qthread_p.h>
#include <qdebug.h>
-#include <qpair.h>
#include <qvarlengtharray.h>
#include <qscopeguard.h>
#include <qset.h>
@@ -178,6 +178,7 @@ QObjectPrivate::QObjectPrivate(int version)
isQuickItem = false;
willBeWidget = false;
wasWidget = false;
+ receiveParentEvents = false; // If object wants ParentAboutToChange and ParentChange
}
QObjectPrivate::~QObjectPrivate()
@@ -190,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");
}
@@ -226,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()) {
@@ -267,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);
}
/*!
@@ -451,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();
@@ -474,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();
@@ -551,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_)
@@ -561,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.
*/
@@ -585,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_)
@@ -595,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()
{
@@ -607,8 +609,6 @@ QMetaCallEvent::~QMetaCallEvent()
if (reinterpret_cast<void *>(d.args_) != reinterpret_cast<void *>(prealloc_))
free(d.args_);
}
- if (d.slotObj_)
- d.slotObj_->destroyIfLastRef();
}
/*!
@@ -626,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().
@@ -713,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.
@@ -753,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
@@ -836,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.
@@ -966,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
@@ -1016,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();
@@ -1024,7 +1051,7 @@ QObject::~QObject()
}
QBasicMutex *signalSlotMutex = signalSlotLock(this);
- QBasicMutexLocker locker(signalSlotMutex);
+ QMutexLocker locker(signalSlotMutex);
// disconnect all receivers
int receiverCount = cd->signalVectorCount();
@@ -1202,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.
@@ -1273,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
}
@@ -1291,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
}
@@ -1374,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;
@@ -1396,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;
@@ -1423,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()
*/
@@ -1564,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
@@ -1603,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();
@@ -1642,7 +1679,7 @@ void QObject::moveToThread(QThread *targetThread)
"DYLD_PRINT_LIBRARIES=1 and check that only one set of binaries are being loaded.");
#endif
- return;
+ return false;
}
// prepare to move
@@ -1676,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()
@@ -1718,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();
@@ -1757,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
@@ -1786,18 +1811,19 @@ void QObjectPrivate::_q_reregisterTimers(void *pointer)
startTimer(std::chrono::milliseconds{interval}, timerType);
\endcode
- \sa timerEvent(), killTimer(), QTimer::singleShot()
+ \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
- \fn int QObject::startTimer(std::chrono::milliseconds interval, Qt::TimerType timerType)
Starts a timer and returns a timer identifier, or returns zero if
it could not start a timer.
@@ -1818,27 +1844,41 @@ int QObject::startTimer(int interval, Qt::TimerType timerType)
\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(), QChronoTimer::singleShot()
+
+ \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
- \sa timerEvent(), killTimer(), QTimer::singleShot()
*/
-int QObject::startTimer(std::chrono::milliseconds interval, Qt::TimerType timerType)
+int QObject::startTimer(std::chrono::nanoseconds interval, Qt::TimerType timerType)
{
Q_D(QObject);
using namespace std::chrono_literals;
- if (Q_UNLIKELY(interval < 0ms)) {
+ if (Q_UNLIKELY(interval < 0ns)) {
qWarning("QObject::startTimer: Timers cannot have negative intervals");
return 0;
}
@@ -1854,10 +1894,10 @@ int QObject::startTimer(std::chrono::milliseconds interval, Qt::TimerType timerT
}
auto dispatcher = thisThreadData->eventDispatcher.loadRelaxed();
- int timerId = dispatcher->registerTimer(interval.count(), timerType, this);
+ Qt::TimerId timerId = dispatcher->registerTimer(interval, timerType, this);
d->ensureExtraData();
d->extraData->runningTimers.append(timerId);
- return timerId;
+ return int(timerId);
}
/*!
@@ -1871,17 +1911,26 @@ int QObject::startTimer(std::chrono::milliseconds interval, Qt::TimerType timerT
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()));
@@ -1897,7 +1946,6 @@ void QObject::killTimer(int id)
}
}
-
/*!
\fn QObject *QObject::parent() const
@@ -1931,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
@@ -1964,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.
@@ -1990,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()
*/
@@ -2007,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
@@ -2051,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);
}
}
@@ -2117,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()) {
@@ -2206,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()) {
@@ -2222,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);
+ }
}
/*!
@@ -2240,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:
@@ -2278,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);
}
@@ -2301,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;
+ }
}
}
}
@@ -2332,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.
@@ -2343,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
*/
@@ -2355,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));
}
/*!
@@ -2373,8 +2511,7 @@ void QObject::deleteLater()
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:
@@ -2522,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;
@@ -2564,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;
@@ -2628,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) {
@@ -2676,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);
}
@@ -3582,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)
@@ -3763,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)
@@ -3771,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;
};
/*!
@@ -3809,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
@@ -3890,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;
@@ -3950,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 ?
@@ -3967,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};
@@ -4016,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);
@@ -4094,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
@@ -4114,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;
@@ -4135,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;
}
}
@@ -4155,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);
}
/*!
@@ -4255,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");
@@ -4317,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
@@ -4335,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++}
*/
/*!
@@ -4357,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}
*/
@@ -4544,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
@@ -4561,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}
*/
@@ -4573,8 +4736,9 @@ 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.
@@ -4598,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
@@ -4838,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()
@@ -4942,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.
*/
@@ -5007,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();
}
@@ -5026,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,
@@ -5055,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");
@@ -5074,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();
}
}
@@ -5111,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;
@@ -5262,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);
}
/*!
@@ -5364,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 852f5c7053..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,23 +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);
+
+#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);
@@ -147,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)
@@ -161,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);
@@ -207,6 +237,7 @@ public:
"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;
@@ -226,14 +257,20 @@ public:
types = QtPrivate::ConnectionTypes<typename SignalType::Arguments>::types();
void **pSlot = nullptr;
- if constexpr (std::is_member_function_pointer_v<std::decay_t<Func2>>)
+ 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);
}
+#ifndef QT_NO_CONTEXTLESS_CONNECT
//connect without context
template <typename Func1, typename Func2>
static inline QMetaObject::Connection
@@ -241,6 +278,7 @@ public:
{
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,
@@ -292,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; }
@@ -345,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,
@@ -363,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");
@@ -375,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");
@@ -424,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)
@@ -497,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_p.h b/src/corelib/kernel/qobject_p.h
index de014a5525..0ab9bf02ed 100644
--- a/src/corelib/kernel/qobject_p.h
+++ b/src/corelib/kernel/qobject_p.h
@@ -91,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,
@@ -140,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);
@@ -189,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);
@@ -375,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,
@@ -384,27 +388,29 @@ 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, Args ...argv)
+ int signal_index, const Args &...argv)
{
- auto metaCallEvent = std::make_unique<QMetaCallEvent>(slotObj, sender,
- signal_index, int(1 + sizeof...(Args)));
-
- void **args = metaCallEvent->args();
- QMetaType *types = metaCallEvent->types();
- const std::array<void *, sizeof...(Args) + 1> argp{ nullptr, std::addressof(argv)... };
- const std::array metaTypes{ QMetaType::fromType<void>(), QMetaType::fromType<Args>()... };
- for (size_t i = 0; i < sizeof...(Args) + 1; ++i) {
- types[i] = metaTypes[i];
- args[i] = types[i].create(argp[i]);
- Q_CHECK_PTR(!i || args[i]);
- }
-
- return metaCallEvent.release();
+ 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_; }
@@ -416,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_;
@@ -434,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:
@@ -442,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 a9bd290d37..2277af0497 100644
--- a/src/corelib/kernel/qobject_p_p.h
+++ b/src/corelib/kernel/qobject_p_p.h
@@ -224,19 +224,18 @@ struct QObjectPrivate::ConnectionData
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()
{
@@ -246,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 5c1d2fd8ae..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
@@ -142,6 +143,12 @@ 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)
@@ -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) };
}
@@ -217,7 +225,7 @@ template <typename... Args> inline auto invokeMethodHelper(QMetaMethodReturnArgu
} // namespace QtPrivate
template <typename T> void qReturnArg(const T &&) = delete;
-template <typename T> inline QMetaMethodReturnArgument qReturnArg(T &data)
+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,7 +409,7 @@ 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)...);
}
@@ -409,33 +418,101 @@ struct Q_CORE_EXPORT QMetaObject
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);
+
+ 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
template <typename Func>
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 = Qt::AutoConnection,
- typename QtPrivate::Callable<Func>::ReturnType *ret = nullptr)
+ Func &&function, Qt::ConnectionType type,
+ typename QtPrivate::Callable<Func>::ReturnType *ret)
{
- static_assert(QtPrivate::Callable<Func>::ArgumentCount <= 0,
- "QMetaObject::invokeMethod cannot call functions with arguments!");
- using Prototype = typename QtPrivate::Callable<Func>::Function;
- return invokeMethodImpl(object, QtPrivate::makeCallableObject<Prototype>(std::forward<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 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)
+ Func &&function, typename QtPrivate::Callable<Func>::ReturnType *ret)
{
return invokeMethod(object, std::forward<Func>(function), Qt::AutoConnection, 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,
+ QTemplatedMetaMethodReturnArgument<
+ typename QtPrivate::Callable<Func, Args...>::ReturnType>
+ ret,
+ Args &&...args)
+ {
+ return invokeMethodCallableHelper(object, std::forward<Func>(function), type, ret,
+ std::forward<Args>(args)...);
+ }
+
+ 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)
+ {
+ 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)...);
+ }
+
+ 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 invokeMethod(object, std::forward<Func>(function), Qt::AutoConnection, ret,
+ std::forward<Args>(args)...);
+ }
+
+ 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)
+ {
+ 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)
@@ -523,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 {
@@ -568,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 6f2dac783b..1e953f29b6 100644
--- a/src/corelib/kernel/qobjectdefs_impl.h
+++ b/src/corelib/kernel/qobjectdefs_impl.h
@@ -14,6 +14,8 @@
#include <QtCore/qfunctionaltools_impl.h>
+#include <memory>
+
QT_BEGIN_NAMESPACE
class QObject;
class QObjectPrivate;
@@ -49,25 +51,27 @@ namespace QtPrivate {
template <typename L> struct List_Left<L, 0> { typedef List<> 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 built-in 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.
@@ -130,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]))...);
+ });
}
};
@@ -330,28 +350,64 @@ namespace QtPrivate {
typedef decltype(std::declval<Functor>().operator()((std::declval<ArgList>())...)) Value;
};
- template<typename Function, int N> struct Functor
+ 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(Function &f, void *, void **arg) {
- FunctorCall<typename Indexes<N>::Value, SignalArgs, R, Function>::call(f, arg);
+ static void call(Func &f, void *, void **arg) {
+ FunctorCall<typename Indexes<ArgumentCount>::Value, SignalArgs, R, Func>::call(f, arg);
}
};
- template<typename Func>
- struct ZeroArgFunctor : Functor<Func, 0>
+ template <typename Functor, typename... Args>
+ struct HasCallOperatorAcceptingArgs
{
- using ReturnType = decltype(std::declval<Func>()());
- using Function = ReturnType(*)();
- enum {ArgumentCount = 0};
- using Arguments = QtPrivate::List<>;
+ 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>
- using Callable = std::conditional_t<FunctionPointer<std::decay_t<Func>>::ArgumentCount == -1,
- ZeroArgFunctor<std::decay_t<Func>>,
- FunctionPointer<std::decay_t<Func>>
- >;
+ 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,
@@ -413,7 +469,16 @@ namespace QtPrivate {
public:
explicit QSlotObjectBase(ImplFn fn) : m_impl(fn) {}
- inline int ref() noexcept { return m_ref.ref(); }
+ // 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); }
+ };
+
+ 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); }
@@ -439,6 +504,44 @@ namespace QtPrivate {
Q_DISABLE_COPY_MOVE(QSlotObjectBase)
};
+ using SlotObjUniquePtr = std::unique_ptr<QSlotObjectBase,
+ QSlotObjectBase::Deleter>;
+ inline SlotObjUniquePtr copy(const SlotObjUniquePtr &other) noexcept
+ {
+ 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))
+ {
+ // does NOT ref() (takes unique_ptr by value)
+ // (that's why (QSlotObjectBase*) ctor doesn't exisit: don't know whether that one _should_)
+ }
+ 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 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, typename Args, typename R>
@@ -447,10 +550,7 @@ namespace QtPrivate {
{
using FunctorValue = std::decay_t<Func>;
using Storage = QtPrivate::CompactStorage<FunctorValue>;
- using FuncType = std::conditional_t<std::is_member_function_pointer_v<FunctorValue>,
- QtPrivate::FunctionPointer<FunctorValue>,
- QtPrivate::Functor<FunctorValue, Args::size>
- >;
+ 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)
@@ -540,6 +640,7 @@ namespace QtPrivate {
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));
}
diff --git a/src/corelib/kernel/qpermissions.cpp b/src/corelib/kernel/qpermissions.cpp
index 46b989c50c..ae9cc6161d 100644
--- a/src/corelib/kernel/qpermissions.cpp
+++ b/src/corelib/kernel/qpermissions.cpp
@@ -85,7 +85,8 @@ Q_LOGGING_CATEGORY(lcPermissions, "qt.permissions", QtWarningMsg);
\target apple-usage-description
Each permission you request must be accompanied by a so called
- \e {usage description} string in the application's \c Info.plist
+ \e {usage description} string in the application's
+ \l{Information Property List Files}{\c Info.plist}
file, describing why the application needs to access the given
permission. For example:
@@ -97,6 +98,10 @@ Q_LOGGING_CATEGORY(lcPermissions, "qt.permissions", QtWarningMsg);
The relevant usage description keys are described in the documentation
for each permission type.
+ To ensure the relevant permission backend is included with your
+ application, please \l{Information Property List Files}
+ {point the build system to your custom \c Info.plist}.
+
\sa {Information Property List Files}.
\section3 Android
@@ -226,7 +231,7 @@ Q_LOGGING_CATEGORY(lcPermissions, "qt.permissions", QtWarningMsg);
*/
/*!
- \fn template <typename T, if_permission<T>> QPermission::QPermission(const T &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.
@@ -240,7 +245,7 @@ Q_LOGGING_CATEGORY(lcPermissions, "qt.permissions", QtWarningMsg);
*/
/*!
- \fn template <typename T, if_permission<T>> std::optional<T> QPermission::value() 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.
diff --git a/src/corelib/kernel/qpermissions_p.h b/src/corelib/kernel/qpermissions_p.h
index 36f497f198..8838b36d9a 100644
--- a/src/corelib/kernel/qpermissions_p.h
+++ b/src/corelib/kernel/qpermissions_p.h
@@ -28,7 +28,7 @@ QT_REQUIRE_CONFIG(permissions);
QT_BEGIN_NAMESPACE
-Q_DECLARE_EXPORTED_LOGGING_CATEGORY(lcPermissions, Q_CORE_EXPORT)
+QT_DECLARE_EXPORTED_QT_LOGGING_CATEGORY(lcPermissions, Q_CORE_EXPORT)
namespace QPermissions::Private
{
diff --git a/src/corelib/kernel/qpermissions_wasm.cpp b/src/corelib/kernel/qpermissions_wasm.cpp
index 7e885a4382..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,7 +135,7 @@ namespace
return cb(Qt::PermissionStatus::Denied);
qstdweb::PromiseCallbacks queryCallbacks;
- queryCallbacks.thenFunc = [device, cb](val mediaStream)
+ queryCallbacks.thenFunc = [device, cb](val)
{
updatePermission(device, wapiGranted, cb);
};
diff --git a/src/corelib/kernel/qpointer.h b/src/corelib/kernel/qpointer.h
index 66088054ef..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>
@@ -27,18 +28,39 @@ class QPointer
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();
@@ -49,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);
@@ -98,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 c884844db3..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,8 +105,8 @@
*/
/*!
- \fn template <class T> template <class X> QPointer<T>::QPointer(QPointer<X> &&other)
- \fn template <class T> template <class X> QPointer<T>::QPointer(const QPointer<X> &other)
+ \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
@@ -112,6 +119,28 @@
*/
/*!
+ \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
@@ -179,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
@@ -218,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 b6513a0b71..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));
}
}
@@ -310,7 +310,7 @@ void QPropertyBindingPrivate::unlinkAndDeref()
{
clearDependencyObservers();
propertyDataPtr = nullptr;
- if (--ref == 0)
+ if (!deref())
destroyAndFreeMemory(this);
}
@@ -455,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
{
@@ -678,8 +678,10 @@ QPropertyObserver::QPropertyObserver(ChangeHandler 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
@@ -751,13 +753,6 @@ 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 setBindingToNotify, but assumes that the tag is already correct.
@@ -1190,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)
*/
/*!
@@ -1278,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.
*/
diff --git a/src/corelib/kernel/qproperty.h b/src/corelib/kernel/qproperty.h
index e45d532fb9..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,10 +54,11 @@ Q_CORE_EXPORT void beginPropertyUpdateGroup();
Q_CORE_EXPORT void endPropertyUpdateGroup();
}
-class [[nodiscard]] QScopedPropertyUpdateGroup
+class QScopedPropertyUpdateGroup
{
Q_DISABLE_COPY_MOVE(QScopedPropertyUpdateGroup)
public:
+ Q_NODISCARD_CTOR
QScopedPropertyUpdateGroup()
{ Qt::beginPropertyUpdateGroup(); }
~QScopedPropertyUpdateGroup() noexcept(false)
@@ -228,7 +230,7 @@ public:
ObserverNotifiesChangeHandler, // observer is a change handler, which runs on every change
ObserverIsPlaceholder, // the observer before this one is currently evaluated in QPropertyObserver::notifyObservers.
#if QT_DEPRECATED_SINCE(6, 6)
- ObserverIsAlias
+ ObserverIsAlias QT_DEPRECATED_VERSION_X_6_6("Use QProperty and add a binding to the target.")
#endif
};
protected:
@@ -262,7 +264,7 @@ 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);
@@ -270,7 +272,8 @@ public:
protected:
QPropertyObserver(ChangeHandler changeHandler);
#if QT_DEPRECATED_SINCE(6, 6)
- QT_DEPRECATED QPropertyObserver(QUntypedPropertyData *aliasedPropertyPtr);
+ 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
@@ -286,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);
@@ -299,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);
@@ -311,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);
@@ -326,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);
@@ -883,7 +892,7 @@ public:
#if QT_DEPRECATED_SINCE(6, 6)
template<typename T>
-class QT_DEPRECATED_X("Class was only meant for internal use, use a QProperty and add a binding to the target")
+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)
@@ -899,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)
@@ -1008,7 +1017,7 @@ public:
}
QT_WARNING_POP
};
-#endif
+#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 5b1d0eb273..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 {
@@ -94,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
@@ -125,20 +127,30 @@ struct QPropertyObserverPointer
{
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);
@@ -161,7 +173,7 @@ struct QPropertyObserverPointer
{
Q_ASSERT(ptr->next.tag() == QPropertyObserver::ObserverNotifiesBinding);
return ptr->binding;
- };
+ }
private:
void unlink_common()
@@ -220,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
@@ -254,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;
@@ -488,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;
@@ -883,8 +925,10 @@ inline void QPropertyObserverPointer::notify(QUntypedPropertyData *propertyDataP
// 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();
}
@@ -904,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(); }
diff --git a/src/corelib/kernel/qpropertyprivate.h b/src/corelib/kernel/qpropertyprivate.h
index aca6d14c96..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;
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/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 a1abe71052..8d3ea7865d 100644
--- a/src/corelib/kernel/qtimer.cpp
+++ b/src/corelib/kernel/qtimer.cpp
@@ -4,6 +4,7 @@
#include "qtimer.h"
#include "qtimer_p.h"
+#include "qsingleshottimer_p.h"
#include "qabstracteventdispatcher.h"
#include "qcoreapplication.h"
@@ -14,6 +15,8 @@
#include "qproperty_p.h"
#include "qthread.h"
+using namespace std::chrono_literals;
+
QT_BEGIN_NAMESPACE
/*!
@@ -71,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.8 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
@@ -107,7 +117,7 @@ QT_BEGIN_NAMESPACE
used; Qt tries to work around these limitations.
\sa QBasicTimer, QTimerEvent, QObject::timerEvent(), Timers,
- {Analog Clock}, {Tetrix Example}
+ {Analog Clock}
*/
/*!
@@ -115,8 +125,9 @@ QT_BEGIN_NAMESPACE
*/
QTimer::QTimer(QObject *parent)
- : QObject(*new QTimerPrivate, parent)
+ : QObject(*new QTimerPrivate(this), parent)
{
+ Q_ASSERT(d_func()->isQTimer);
}
@@ -126,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();
}
@@ -171,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()
@@ -187,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(std::chrono::milliseconds{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();
+ }
}
/*!
@@ -200,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();
@@ -226,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();
}
}
@@ -240,97 +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 = -1;
-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);
-
- void startTimerForReceiver(int msec, Qt::TimerType timerType, const QObject *receiver);
-
-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())
-{
- connect(this, SIGNAL(timeout()), r, member);
-
- startTimerForReceiver(msec, timerType, r);
-}
-
-QSingleShotTimer::QSingleShotTimer(int msec, 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(msec, timerType, r);
-}
-
-QSingleShotTimer::~QSingleShotTimer()
-{
- if (timerId > 0)
- 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(int msec, 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(std::chrono::milliseconds{msec}, timerType);
- QMetaObject::invokeMethod(this, [this, deadline, timerType]{
- if (deadline.hasExpired())
- emit timeout();
- else
- timerId = startTimer(std::chrono::milliseconds{deadline.remainingTime()}, timerType);
- }, Qt::QueuedConnection);
- } else {
- timerId = startTimer(std::chrono::milliseconds{msec}, 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 > 0)
- killTimer(timerId);
- timerId = -1;
-
- 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
@@ -340,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.
@@ -366,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
@@ -397,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
@@ -419,25 +374,29 @@ 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);
}
}
@@ -505,14 +464,19 @@ 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
- 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 \c {QObject::connect(timer, &QTimer::timeout, timer, slot, connectionType)}.
+ This method is provided for convenience. It's equivalent to calling:
+ \code
+ QObject::connect(timer, &QTimer::timeout, timer, slot, Qt::DirectConnection);
+ \endcode
+
+ \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()
*/
@@ -525,8 +489,10 @@ void QTimer::singleShot(int msec, Qt::TimerType timerType, const QObject *receiv
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)}.
+ 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()
*/
@@ -541,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
*/
/*!
@@ -606,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(std::chrono::milliseconds{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();
@@ -643,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;
@@ -675,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 e77db27049..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,35 +47,38 @@ 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
- template <typename Duration, typename Functor>
- static inline void singleShot(Duration interval,
#ifdef Q_QDOC
- const QObject *receiver,
+ 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
+ template <typename Duration, typename Functor>
+ static inline void singleShot(Duration interval,
const typename QtPrivate::ContextTypeForFunctor<Functor>::ContextType *receiver,
-#endif
-
Functor &&slot)
{
singleShot(interval, defaultTypeFor(interval), receiver, std::forward<Functor>(slot));
}
template <typename Duration, typename Functor>
static inline void singleShot(Duration interval, Qt::TimerType timerType,
-#ifdef Q_QDOC
- const QObject *receiver,
-#else
const typename QtPrivate::ContextTypeForFunctor<Functor>::ContextType *receiver,
-#endif
Functor &&slot)
{
using Prototype = void(*)();
singleShotImpl(interval, timerType, receiver,
QtPrivate::makeCallableObject<Prototype>(std::forward<Functor>(slot)));
}
+#endif
+
// singleShot without context
template <typename Duration, typename Functor>
static inline void singleShot(Duration interval, Functor &&slot)
@@ -89,7 +93,7 @@ public:
#ifdef Q_QDOC
template <typename Functor>
- QMetaObject::Connection callOnTimeout(Functor &&slot, Qt::ConnectionType connectionType = Qt::AutoConnection);
+ QMetaObject::Connection callOnTimeout(Functor &&slot);
template <typename Functor>
QMetaObject::Connection callOnTimeout(const QObject *context, Functor &&slot, Qt::ConnectionType connectionType = Qt::AutoConnection);
#else
@@ -111,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
{
@@ -128,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;
@@ -152,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 01238d8d0e..b83f0194c2 100644
--- a/src/corelib/kernel/qtimerinfo_unix.cpp
+++ b/src/corelib/kernel/qtimerinfo_unix.cpp
@@ -10,14 +10,11 @@
#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
@@ -28,48 +25,56 @@ Q_CORE_EXPORT bool qt_disable_lowpriority_timers=false;
* timerBitVec array is used for keeping track of timer identifiers.
*/
-QTimerInfoList::QTimerInfoList()
+QTimerInfoList::QTimerInfoList() = default;
+
+steady_clock::time_point QTimerInfoList::updateCurrentTime() const
{
- firstTimerInfo = nullptr;
+ currentTime = steady_clock::now();
+ return currentTime;
}
-timespec QTimerInfoList::updateCurrentTime()
+/*! \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).
+
+ The list is sorted by timeout, thus it's enough to check the first timer only.
+*/
+bool QTimerInfoList::hasPendingTimers()
{
- return (currentTime = qt_gettime());
+ if (timers.isEmpty())
+ return false;
+ return updateCurrentTime() < timers.at(0)->timeout;
}
+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);
+ 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);
+ return ceil<milliseconds>(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});
-
-static constexpr seconds roundToSecs(milliseconds msecs)
+
+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)
{
// 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.
@@ -81,30 +86,14 @@ static constexpr seconds roundToSecs(milliseconds msecs)
// 1500 2 2
// 2500 2 3
- auto secs = duration_cast<seconds>(msecs);
- const milliseconds frac = msecs - secs;
+ auto secs = duration_cast<seconds>(interval);
+ const nanoseconds frac = interval - secs;
if (frac >= 500ms)
++secs;
return secs;
}
-#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;
-}
-QDebug operator<<(QDebug s, Qt::TimerType t)
-{
- QDebugStateSaver saver(s);
- s << (t == Qt::PreciseTimer ? "P" :
- t == Qt::CoarseTimer ? "C" : "VC");
- return s;
-}
-#endif
-
-static void calculateCoarseTimerTimeout(QTimerInfo *t, timespec now)
+static void calculateCoarseTimerTimeout(QTimerInfo *t, steady_clock::time_point now)
{
// The coarse timer works like this:
// - interval under 40 ms: round to even
@@ -124,27 +113,24 @@ static void calculateCoarseTimerTimeout(QTimerInfo *t, timespec now)
Q_ASSERT(t->interval >= 20ms);
- auto recalculate = [&](const milliseconds fracMsec) {
- if (fracMsec == 1000ms) {
- ++t->timeout.tv_sec;
- t->timeout.tv_nsec = 0;
- } else {
- t->timeout.tv_nsec = nanoseconds{fracMsec}.count();
- }
+ 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
- const milliseconds absMaxRounding = t->interval / 20;
+ milliseconds interval = roundToMillisecond(t->interval);
+ const milliseconds absMaxRounding = interval / 20;
- auto fracMsec = duration_cast<milliseconds>(nanoseconds{t->timeout.tv_nsec});
+ auto fracMsec = duration_cast<milliseconds>(t->timeout - timeoutInSecs);
- if (t->interval < 100ms && t->interval != 25ms && t->interval != 50ms && t->interval != 75ms) {
+ if (interval < 100ms && interval != 25ms && interval != 50ms && interval != 75ms) {
auto fracCount = fracMsec.count();
// special mode for timers of less than 100 ms
- if (t->interval < 50ms) {
+ if (interval < 50ms) {
// round to even
// round towards multiples of 50 ms
bool roundUp = (fracCount % 50) >= 25;
@@ -186,17 +172,17 @@ static void calculateCoarseTimerTimeout(QTimerInfo *t, timespec now)
// 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 ((t->interval % 500) == 0ms) {
- if (t->interval >= 5s) {
+ if ((interval % 500) == 0ms) {
+ if (interval >= 5s) {
fracMsec = fracMsec >= 500ms ? max : min;
recalculate(fracMsec);
return;
} else {
wantedBoundaryMultiple = 500ms;
}
- } else if ((t->interval % 50) == 0ms) {
+ } else if ((interval % 50) == 0ms) {
// 4) same for multiples of 250, 200, 100, 50
- milliseconds mult50 = t->interval / 50;
+ milliseconds mult50 = interval / 50;
if ((mult50 % 4) == 0ms) {
// multiple of 200
wantedBoundaryMultiple = 200ms;
@@ -222,7 +208,7 @@ static void calculateCoarseTimerTimeout(QTimerInfo *t, timespec now)
recalculate(fracMsec);
}
-static void calculateNextTimeout(QTimerInfo *t, timespec now)
+static void calculateNextTimeout(QTimerInfo *t, steady_clock::time_point now)
{
switch (t->timerType) {
case Qt::PreciseTimer:
@@ -232,107 +218,79 @@ static void calculateNextTimeout(QTimerInfo *t, timespec 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, now);
return;
case Qt::VeryCoarseTimer:
// t->interval already rounded to full seconds in registerTimer()
- const auto secs = duration_cast<seconds>(t->interval).count();
- t->timeout.tv_sec += secs;
- if (t->timeout.tv_sec <= now.tv_sec)
- t->timeout.tv_sec = now.tv_sec + secs;
-#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->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 now = updateCurrentTime();
+ steady_clock::time_point now = updateCurrentTime();
auto isWaiting = [](QTimerInfo *tinfo) { return !tinfo->activateRef; };
// Find first waiting timer not already active
- auto it = std::find_if(cbegin(), cend(), isWaiting);
- if (it == cend())
- return false;
-
- QTimerInfo *t = *it;
- if (now < t->timeout) // Time to wait
- tm = roundToMillisecond(t->timeout - now);
- else // No time to wait
- tm = {0, 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)
-{
- return remainingDuration(timerId).count();
-}
-
-milliseconds QTimerInfoList::remainingDuration(int timerId)
+QTimerInfoList::Duration QTimerInfoList::remainingDuration(Qt::TimerId timerId) const
{
- timespec now = updateCurrentTime();
+ const steady_clock::time_point now = updateCurrentTime();
auto it = findTimerById(timerId);
- if (it == cend()) {
+ 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 milliseconds{-1};
+ return Duration::min();
}
const QTimerInfo *t = *it;
if (now < t->timeout) // time to wait
- return timespecToChronoMs(roundToMillisecond(t->timeout - now));
- else
- return milliseconds{0};
-}
-
-void QTimerInfoList::registerTimer(int timerId, qint64 interval, Qt::TimerType timerType, QObject *object)
-{
- registerTimer(timerId, milliseconds{interval}, timerType, object);
+ return t->timeout - now;
+ return 0ms;
}
-void QTimerInfoList::registerTimer(int timerId, milliseconds interval,
+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:
@@ -342,50 +300,27 @@ void QTimerInfoList::registerTimer(int timerId, milliseconds interval,
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 >= 20s) {
- t->timerType = Qt::VeryCoarseTimer;
- } else {
- t->timeout = expected;
- if (interval <= 20ms) {
- t->timerType = Qt::PreciseTimer;
- // no adjustment is necessary
- } else if (interval <= 20s) {
- calculateCoarseTimerTimeout(t, currentTime);
- }
- break;
- }
- Q_FALLTHROUGH();
+ t->timeout = expected;
+ t->interval = roundToMillisecond(interval);
+ calculateCoarseTimerTimeout(t, currentTime);
+ break;
+
case Qt::VeryCoarseTimer:
- const seconds secs = roundToSecs(t->interval);
- t->interval = secs;
- t->timeout.tv_sec = currentTime.tv_sec + secs.count();
- t->timeout.tv_nsec = 0;
-
- // if we're past the half-second mark, increase the timeout again
- 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 == cend())
+ if (it == timers.cend())
return false; // id not found
// set timer inactive
@@ -395,37 +330,39 @@ bool QTimerInfoList::unregisterTimer(int timerId)
if (t->activateRef)
*(t->activateRef) = nullptr;
delete t;
- erase(it);
+ 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 (const QTimerInfo *const t : std::as_const(*this)) {
+ QList<TimerInfo> list;
+ for (const auto &t : timers) {
if (t->obj == object)
- list.emplaceBack(t->id, t->interval.count(), t->timerType);
+ list.emplaceBack(TimerInfo{t->interval, t->id, t->timerType});
}
return list;
}
@@ -435,26 +372,26 @@ 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
firstTimerInfo = nullptr;
- timespec now = updateCurrentTime();
+ const steady_clock::time_point now = updateCurrentTime();
// qDebug() << "Thread" << QThread::currentThreadId() << "woken up at" << now;
// Find out how many timer have expired
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(cbegin(), cend(), stillActive);
- auto maxCount = it - cbegin();
+ 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();
+ QTimerInfo *currentTimerInfo = timers.constFirst();
if (now < currentTimerInfo->timeout)
break; // no timer has expired
@@ -468,34 +405,16 @@ 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, 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 > 0ms)
n_act++;
@@ -503,7 +422,7 @@ int QTimerInfoList::activateTimers()
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 fe3257b57b..293e9c4d4e 100644
--- a/src/corelib/kernel/qtimerinfo_unix_p.h
+++ b/src/corelib/kernel/qtimerinfo_unix_p.h
@@ -15,65 +15,78 @@
// We mean it.
//
-#include "qplatformdefs.h" // _POSIX_MONOTONIC_CLOCK-0
-
#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
+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
- std::chrono::milliseconds interval; // - timer interval
- 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
{
- // state variables used by activateTimers()
- QTimerInfo *firstTimerInfo;
-
public:
+ using Duration = QAbstractEventDispatcher::Duration;
+ using TimerInfo = QAbstractEventDispatcher::TimerInfoV2;
QTimerInfoList();
- timespec currentTime;
- timespec updateCurrentTime();
+ mutable std::chrono::steady_clock::time_point currentTime;
- bool timerWait(timespec &);
+ std::optional<Duration> timerWait();
void timerInsert(QTimerInfo *);
- qint64 timerRemainingTime(int timerId);
- std::chrono::milliseconds remainingDuration(int timerId);
+ Duration remainingDuration(Qt::TimerId timerId) const;
- void registerTimer(int timerId, qint64 interval, Qt::TimerType timerType, QObject *object);
- void registerTimer(int timerId, std::chrono::milliseconds 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();
- QList::const_iterator findTimerById(int timerId) const
+ void clearTimers()
{
- auto matchesId = [timerId](const QTimerInfo *t) { return t->id == timerId; };
- return std::find_if(cbegin(), cend(), matchesId);
+ 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 9e905ace20..9d75177645 100644
--- a/src/corelib/kernel/qtmochelpers.h
+++ b/src/corelib/kernel/qtmochelpers.h
@@ -76,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 9f9575f517..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 (;;) {
diff --git a/src/corelib/kernel/qvariant.cpp b/src/corelib/kernel/qvariant.cpp
index d4a7c91640..92a44c462b 100644
--- a/src/corelib/kernel/qvariant.cpp
+++ b/src/corelib/kernel/qvariant.cpp
@@ -47,6 +47,8 @@
#include "qline.h"
#endif
+#include <memory>
+
#include <cmath>
#include <float.h>
#include <cstring>
@@ -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,9 +233,22 @@ static bool isValidMetaTypeForVariant(const QtPrivate::QMetaTypeInterface *iface
return true;
}
+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)
+};
+
+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);
@@ -242,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.
@@ -251,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; // default constructor and it's OK to build in 0-filled storage, which we've already done
- 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;
}
@@ -299,6 +324,7 @@ static QVariant::Private clonePrivate(const QVariant::Private &other)
\ingroup objectmodel
\ingroup shared
+ \compares equality
Because C++ forbids unions from including types that have
non-default constructors or destructors, most interesting Qt
@@ -491,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);
}
@@ -524,14 +545,14 @@ QVariant::QVariant(const QVariant &p)
}
/*!
- \fn template <typename Type, typename... Args, if_constructible<Type, Args...> = true> QVariant::QVariant(std::in_place_type_t<Type>, Args&&... args) noexcept(is_noexcept_constructible<q20::remove_cvref_t<Type>, Args...>::value)
+ \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 Type. The contained
+ 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 Type can be
+ 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.
@@ -541,7 +562,7 @@ QVariant::QVariant(const QVariant &p)
/*!
- \fn template <typename Type, typename List, typename... Args, if_constructible<Type, std::initializer_list<List> &, Args...> = true> explicit QVariant::QVariant(std::in_place_type_t<Type>, std::initializer_list<List> il, Args&&... args) noexcept(is_noexcept_constructible<q20::remove_cvref_t<Type>, std::initializer_list<List> &, Args... >::value)
+ \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
@@ -550,6 +571,28 @@ QVariant::QVariant(const QVariant &p)
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
@@ -561,6 +604,47 @@ QVariant::QVariant(std::in_place_t, QMetaType type) : d(type.iface())
}
/*!
+ \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.
@@ -827,27 +911,27 @@ QVariant::QVariant(std::in_place_t, QMetaType type) : d(type.iface())
*/
/*!
- 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()
{
- type.registerType();
- 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) {}
@@ -859,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) {}
@@ -1006,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;
@@ -1035,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 = {};
}
@@ -1759,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.
@@ -1769,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)
@@ -2064,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.
@@ -2090,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.
@@ -2272,15 +2361,15 @@ 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
@@ -2466,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
@@ -2485,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
@@ -2498,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.
@@ -2550,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.
@@ -2566,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.
@@ -2586,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
@@ -2600,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
@@ -2610,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 1fd304e772..306e5b3a38 100644
--- a/src/corelib/kernel/qvariant.h
+++ b/src/corelib/kernel/qvariant.h
@@ -5,6 +5,7 @@
#define QVARIANT_H
#include <QtCore/qatomic.h>
+#include <QtCore/qcompare.h>
#include <QtCore/qcontainerfwd.h>
#include <QtCore/qmetatype.h>
#ifndef QT_NO_DEBUG_STREAM
@@ -62,15 +63,18 @@ template<> constexpr inline bool qIsRelocatable<QVariant> = true;
}
class Q_CORE_EXPORT QVariant
{
- template <typename Type, typename... Args>
+ template <typename T, typename... Args>
using if_constructible = std::enable_if_t<
std::conjunction_v<
- std::is_copy_constructible<q20::remove_cvref_t<Type>>,
- std::is_destructible<q20::remove_cvref_t<Type>>,
- std::is_constructible<q20::remove_cvref_t<Type>, Args...>
+ 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
@@ -78,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);
@@ -87,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 *);
@@ -214,34 +221,34 @@ public:
QVariant(const QVariant &other);
private:
- template<typename Type, typename ...Args>
+ template <typename T, typename ...Args>
using is_noexcept_constructible = std::conjunction<
- std::bool_constant<Private::CanUseInternalSpace<Type>>,
- std::is_nothrow_constructible<Type, Args...>
+ std::bool_constant<Private::CanUseInternalSpace<T>>,
+ std::is_nothrow_constructible<T, Args...>
>;
public:
- template <typename Type, typename... Args,
- if_constructible<Type, Args...> = true>
- explicit QVariant(std::in_place_type_t<Type>, Args&&... args)
- noexcept(is_noexcept_constructible<q20::remove_cvref_t<Type>, Args...>::value)
- : QVariant(std::in_place, QMetaType::fromType<q20::remove_cvref_t<Type>>() )
+ 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) Type(std::forward<Args>(args)...);
+ new (data) T(std::forward<Args>(args)...);
}
- template <typename Type, typename List, typename... Args,
- if_constructible<Type, std::initializer_list<List> &, Args...> = true>
- explicit QVariant(std::in_place_type_t<Type>, std::initializer_list<List> il, Args&&... args)
- noexcept(is_noexcept_constructible<q20::remove_cvref_t<Type>,
- std::initializer_list<List> &,
+ 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<Type>>())
+ : QVariant(std::in_place, QMetaType::fromType<q20::remove_cvref_t<T>>())
{
char *data = static_cast<char *>(const_cast<void *>(constData()));
- new (data) Type(il, std::forward<Args>(args)...);
+ new (data) T(il, std::forward<Args>(args)...);
}
// primitives
@@ -257,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;
@@ -366,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;
@@ -441,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)
{
@@ -449,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));
}
@@ -465,7 +514,7 @@ public:
}
template<typename T>
- inline T value() const
+ inline T value() const &
{ return qvariant_cast<T>(*this); }
template<typename T>
@@ -477,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>)
@@ -487,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>()); }
@@ -509,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> {
@@ -554,8 +661,14 @@ private:
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);
@@ -577,6 +690,8 @@ private:
// 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
@@ -597,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();
@@ -665,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
index f5f4d0fb8a..d2a7390938 100644
--- a/src/corelib/kernel/qvariant_p.h
+++ b/src/corelib/kernel/qvariant_p.h
@@ -19,8 +19,7 @@
QT_BEGIN_NAMESPACE
-template <typename F> static QVariant::PrivateShared *
-customConstructShared(size_t size, size_t align, F &&construct)
+inline auto customConstructSharedImpl(size_t size, size_t align)
{
struct Deleter {
void operator()(QVariant::PrivateShared *p) const
@@ -30,11 +29,23 @@ customConstructShared(size_t size, size_t align, F &&construct)
// 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 QVariant::PrivateShared *QVariant::PrivateShared::create(size_t size, size_t align)
+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)) {
@@ -44,9 +55,15 @@ inline QVariant::PrivateShared *QVariant::PrivateShared::create(size_t size, siz
// 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 = int(((quintptr(ps) + sizeof(PrivateShared) + align - 1) & ~(align - 1)) - quintptr(ps));
+ ps->offset = computeOffset(ps, align);
return ps;
}
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;