summaryrefslogtreecommitdiffstats
path: root/src/corelib/kernel
diff options
context:
space:
mode:
Diffstat (limited to 'src/corelib/kernel')
-rw-r--r--src/corelib/kernel/qabstracteventdispatcher.cpp332
-rw-r--r--src/corelib/kernel/qabstracteventdispatcher.h103
-rw-r--r--src/corelib/kernel/qabstracteventdispatcher_p.h57
-rw-r--r--src/corelib/kernel/qabstractnativeeventfilter.cpp43
-rw-r--r--src/corelib/kernel/qabstractnativeeventfilter.h40
-rw-r--r--src/corelib/kernel/qapplicationstatic.h86
-rw-r--r--src/corelib/kernel/qapplicationstatic.qdoc109
-rw-r--r--src/corelib/kernel/qassociativeiterable.cpp40
-rw-r--r--src/corelib/kernel/qassociativeiterable.h40
-rw-r--r--src/corelib/kernel/qbasictimer.cpp86
-rw-r--r--src/corelib/kernel/qbasictimer.h63
-rw-r--r--src/corelib/kernel/qbindingstorage.h94
-rw-r--r--src/corelib/kernel/qcfsocketnotifier.cpp42
-rw-r--r--src/corelib/kernel/qcfsocketnotifier_p.h40
-rw-r--r--src/corelib/kernel/qchronotimer.cpp452
-rw-r--r--src/corelib/kernel/qchronotimer.h149
-rw-r--r--src/corelib/kernel/qcore_foundation.mm52
-rw-r--r--src/corelib/kernel/qcore_mac.mm424
-rw-r--r--src/corelib/kernel/qcore_mac_p.h155
-rw-r--r--src/corelib/kernel/qcore_unix.cpp107
-rw-r--r--src/corelib/kernel/qcore_unix_p.h201
-rw-r--r--src/corelib/kernel/qcore_wasm.cpp97
-rw-r--r--src/corelib/kernel/qcoreapplication.cpp756
-rw-r--r--src/corelib/kernel/qcoreapplication.h101
-rw-r--r--src/corelib/kernel/qcoreapplication_mac.cpp40
-rw-r--r--src/corelib/kernel/qcoreapplication_p.h52
-rw-r--r--src/corelib/kernel/qcoreapplication_platform.h66
-rw-r--r--src/corelib/kernel/qcoreapplication_win.cpp111
-rw-r--r--src/corelib/kernel/qcorecmdlineargs_p.h40
-rw-r--r--src/corelib/kernel/qcoreevent.cpp120
-rw-r--r--src/corelib/kernel/qcoreevent.h110
-rw-r--r--src/corelib/kernel/qcoreevent_p.h39
-rw-r--r--src/corelib/kernel/qcoreglobaldata.cpp59
-rw-r--r--src/corelib/kernel/qcoreglobaldata_p.h75
-rw-r--r--src/corelib/kernel/qdeadlinetimer.cpp555
-rw-r--r--src/corelib/kernel/qdeadlinetimer.h147
-rw-r--r--src/corelib/kernel/qdeadlinetimer_p.h70
-rw-r--r--src/corelib/kernel/qelapsedtimer.cpp329
-rw-r--r--src/corelib/kernel/qelapsedtimer.h62
-rw-r--r--src/corelib/kernel/qelapsedtimer_generic.cpp205
-rw-r--r--src/corelib/kernel/qelapsedtimer_mac.cpp166
-rw-r--r--src/corelib/kernel/qelapsedtimer_unix.cpp259
-rw-r--r--src/corelib/kernel/qelapsedtimer_win.cpp172
-rw-r--r--src/corelib/kernel/qeventdispatcher_cf.mm98
-rw-r--r--src/corelib/kernel/qeventdispatcher_cf_p.h77
-rw-r--r--src/corelib/kernel/qeventdispatcher_glib.cpp120
-rw-r--r--src/corelib/kernel/qeventdispatcher_glib_p.h54
-rw-r--r--src/corelib/kernel/qeventdispatcher_unix.cpp163
-rw-r--r--src/corelib/kernel/qeventdispatcher_unix_p.h63
-rw-r--r--src/corelib/kernel/qeventdispatcher_wasm.cpp881
-rw-r--r--src/corelib/kernel/qeventdispatcher_wasm_p.h118
-rw-r--r--src/corelib/kernel/qeventdispatcher_win.cpp128
-rw-r--r--src/corelib/kernel/qeventdispatcher_win_p.h49
-rw-r--r--src/corelib/kernel/qeventloop.cpp215
-rw-r--r--src/corelib/kernel/qeventloop.h84
-rw-r--r--src/corelib/kernel/qeventloop_p.h40
-rw-r--r--src/corelib/kernel/qfunctions_p.h44
-rw-r--r--src/corelib/kernel/qfunctions_vxworks.cpp208
-rw-r--r--src/corelib/kernel/qfunctions_vxworks.h195
-rw-r--r--src/corelib/kernel/qfunctions_win.cpp66
-rw-r--r--src/corelib/kernel/qfunctions_win_p.h51
-rw-r--r--src/corelib/kernel/qfunctions_winrt_p.h42
-rw-r--r--src/corelib/kernel/qiterable.cpp69
-rw-r--r--src/corelib/kernel/qiterable.h94
-rw-r--r--src/corelib/kernel/qiterable_p.h42
-rw-r--r--src/corelib/kernel/qjniarray.h464
-rw-r--r--src/corelib/kernel/qjnienvironment.cpp253
-rw-r--r--src/corelib/kernel/qjnienvironment.h85
-rw-r--r--src/corelib/kernel/qjnihelpers.cpp194
-rw-r--r--src/corelib/kernel/qjnihelpers_p.h78
-rw-r--r--src/corelib/kernel/qjniobject.cpp779
-rw-r--r--src/corelib/kernel/qjniobject.h1175
-rw-r--r--src/corelib/kernel/qjnitypes.h218
-rw-r--r--src/corelib/kernel/qjnitypes_impl.h373
-rw-r--r--src/corelib/kernel/qmath.cpp42
-rw-r--r--src/corelib/kernel/qmath.h56
-rw-r--r--src/corelib/kernel/qmath.qdoc41
-rw-r--r--src/corelib/kernel/qmetacontainer.cpp40
-rw-r--r--src/corelib/kernel/qmetacontainer.h46
-rw-r--r--src/corelib/kernel/qmetaobject.cpp1414
-rw-r--r--src/corelib/kernel/qmetaobject.h136
-rw-r--r--src/corelib/kernel/qmetaobject_moc_p.h41
-rw-r--r--src/corelib/kernel/qmetaobject_p.h93
-rw-r--r--src/corelib/kernel/qmetaobjectbuilder.cpp136
-rw-r--r--src/corelib/kernel/qmetaobjectbuilder_p.h43
-rw-r--r--src/corelib/kernel/qmetatype.cpp794
-rw-r--r--src/corelib/kernel/qmetatype.h525
-rw-r--r--src/corelib/kernel/qmetatype_p.h209
-rw-r--r--src/corelib/kernel/qmimedata.cpp159
-rw-r--r--src/corelib/kernel/qmimedata.h40
-rw-r--r--src/corelib/kernel/qobject.cpp1102
-rw-r--r--src/corelib/kernel/qobject.h287
-rw-r--r--src/corelib/kernel/qobject_impl.h64
-rw-r--r--src/corelib/kernel/qobject_p.h386
-rw-r--r--src/corelib/kernel/qobject_p_p.h257
-rw-r--r--src/corelib/kernel/qobjectcleanuphandler.cpp44
-rw-r--r--src/corelib/kernel/qobjectcleanuphandler.h40
-rw-r--r--src/corelib/kernel/qobjectdefs.h450
-rw-r--r--src/corelib/kernel/qobjectdefs_impl.h494
-rw-r--r--src/corelib/kernel/qpermissions.cpp693
-rw-r--r--src/corelib/kernel/qpermissions.h219
-rw-r--r--src/corelib/kernel/qpermissions_android.cpp189
-rw-r--r--src/corelib/kernel/qpermissions_darwin.mm88
-rw-r--r--src/corelib/kernel/qpermissions_p.h55
-rw-r--r--src/corelib/kernel/qpermissions_wasm.cpp275
-rw-r--r--src/corelib/kernel/qpointer.h100
-rw-r--r--src/corelib/kernel/qpointer.qdoc (renamed from src/corelib/kernel/qpointer.cpp)89
-rw-r--r--src/corelib/kernel/qpoll.cpp49
-rw-r--r--src/corelib/kernel/qpoll_p.h42
-rw-r--r--src/corelib/kernel/qproperty.cpp860
-rw-r--r--src/corelib/kernel/qproperty.h261
-rw-r--r--src/corelib/kernel/qproperty_p.h302
-rw-r--r--src/corelib/kernel/qpropertyprivate.h94
-rw-r--r--src/corelib/kernel/qsequentialiterable.cpp40
-rw-r--r--src/corelib/kernel/qsequentialiterable.h40
-rw-r--r--src/corelib/kernel/qsharedmemory.cpp681
-rw-r--r--src/corelib/kernel/qsharedmemory.h137
-rw-r--r--src/corelib/kernel/qsharedmemory_android.cpp110
-rw-r--r--src/corelib/kernel/qsharedmemory_p.h186
-rw-r--r--src/corelib/kernel/qsharedmemory_posix.cpp234
-rw-r--r--src/corelib/kernel/qsharedmemory_systemv.cpp260
-rw-r--r--src/corelib/kernel/qsharedmemory_unix.cpp116
-rw-r--r--src/corelib/kernel/qsharedmemory_win.cpp191
-rw-r--r--src/corelib/kernel/qsignalmapper.cpp51
-rw-r--r--src/corelib/kernel/qsignalmapper.h41
-rw-r--r--src/corelib/kernel/qsingleshottimer_p.h126
-rw-r--r--src/corelib/kernel/qsocketnotifier.cpp44
-rw-r--r--src/corelib/kernel/qsocketnotifier.h45
-rw-r--r--src/corelib/kernel/qsystemerror.cpp60
-rw-r--r--src/corelib/kernel/qsystemerror_p.h49
-rw-r--r--src/corelib/kernel/qsystemsemaphore.cpp364
-rw-r--r--src/corelib/kernel/qsystemsemaphore.h97
-rw-r--r--src/corelib/kernel/qsystemsemaphore_android.cpp83
-rw-r--r--src/corelib/kernel/qsystemsemaphore_p.h120
-rw-r--r--src/corelib/kernel/qsystemsemaphore_posix.cpp186
-rw-r--r--src/corelib/kernel/qsystemsemaphore_systemv.cpp224
-rw-r--r--src/corelib/kernel/qsystemsemaphore_unix.cpp109
-rw-r--r--src/corelib/kernel/qsystemsemaphore_win.cpp136
-rw-r--r--src/corelib/kernel/qt_attribution.json4
-rw-r--r--src/corelib/kernel/qtestsupport_core.cpp165
-rw-r--r--src/corelib/kernel/qtestsupport_core.h74
-rw-r--r--src/corelib/kernel/qtimer.cpp445
-rw-r--r--src/corelib/kernel/qtimer.h206
-rw-r--r--src/corelib/kernel/qtimer_p.h73
-rw-r--r--src/corelib/kernel/qtimerinfo_unix.cpp713
-rw-r--r--src/corelib/kernel/qtimerinfo_unix_p.h129
-rw-r--r--src/corelib/kernel/qtmetamacros.h78
-rw-r--r--src/corelib/kernel/qtmochelpers.h86
-rw-r--r--src/corelib/kernel/qtranslator.cpp76
-rw-r--r--src/corelib/kernel/qtranslator.h40
-rw-r--r--src/corelib/kernel/qtranslator_p.h40
-rw-r--r--src/corelib/kernel/qvariant.cpp1156
-rw-r--r--src/corelib/kernel/qvariant.h610
-rw-r--r--src/corelib/kernel/qvariant_p.h133
-rw-r--r--src/corelib/kernel/qvarianthash.h20
-rw-r--r--src/corelib/kernel/qvariantlist.h19
-rw-r--r--src/corelib/kernel/qvariantmap.h20
-rw-r--r--src/corelib/kernel/qwineventnotifier.cpp42
-rw-r--r--src/corelib/kernel/qwineventnotifier.h42
-rw-r--r--src/corelib/kernel/qwineventnotifier_p.h40
-rw-r--r--src/corelib/kernel/qwinregistry.cpp172
-rw-r--r--src/corelib/kernel/qwinregistry_p.h68
162 files changed, 15767 insertions, 15535 deletions
diff --git a/src/corelib/kernel/qabstracteventdispatcher.cpp b/src/corelib/kernel/qabstracteventdispatcher.cpp
index 358a59d05b..f3056a399c 100644
--- a/src/corelib/kernel/qabstracteventdispatcher.cpp
+++ b/src/corelib/kernel/qabstracteventdispatcher.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtCore module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// 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 "qabstracteventdispatcher.h"
#include "qabstracteventdispatcher_p.h"
@@ -45,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
{
@@ -76,7 +43,7 @@ enum {
Size5 = QtTimerIdFreeListConstants::MaxIndex - Offset5
};
-const int QtTimerIdFreeListConstants::Sizes[QtTimerIdFreeListConstants::BlockCount] = {
+Q_CONSTINIT const int QtTimerIdFreeListConstants::Sizes[QtTimerIdFreeListConstants::BlockCount] = {
Size0,
Size1,
Size2,
@@ -88,6 +55,41 @@ const int QtTimerIdFreeListConstants::Sizes[QtTimerIdFreeListConstants::BlockCou
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;
+
int QAbstractEventDispatcherPrivate::allocateTimerId()
{
// This function may be called after timerIdFreeList() has been destructed
@@ -145,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.
@@ -238,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)
@@ -265,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
@@ -291,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
@@ -332,6 +408,7 @@ void QAbstractEventDispatcher::closingDown()
/*!
\class QAbstractEventDispatcher::TimerInfo
+ \deprecated [6.8] Use TimerInfoV2
\inmodule QtCore
This struct represents information about a timer:
@@ -339,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)
@@ -365,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()}
@@ -415,7 +523,7 @@ void QAbstractEventDispatcher::installNativeEventFilter(QAbstractNativeEventFilt
void QAbstractEventDispatcher::removeNativeEventFilter(QAbstractNativeEventFilter *filter)
{
Q_D(QAbstractEventDispatcher);
- for (int i = 0; i < d->eventFilters.count(); ++i) {
+ for (int i = 0; i < d->eventFilters.size(); ++i) {
if (d->eventFilters.at(i) == filter) {
d->eventFilters[i] = nullptr;
break;
@@ -448,7 +556,7 @@ bool QAbstractEventDispatcher::filterNativeEvent(const QByteArray &eventType, vo
if (!d->eventFilters.isEmpty()) {
// Raise the loopLevel so that deleteLater() calls in or triggered
// by event_filter() will be processed from the main event loop.
- QScopedScopeLevelCounter scopeLevelCounter(d->threadData);
+ QScopedScopeLevelCounter scopeLevelCounter(d->threadData.loadAcquire());
for (int i = 0; i < d->eventFilters.size(); ++i) {
QAbstractNativeEventFilter *filter = d->eventFilters.at(i);
if (!filter)
@@ -476,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 717fdc8984..ad97a93ba2 100644
--- a/src/corelib/kernel/qabstracteventdispatcher.h
+++ b/src/corelib/kernel/qabstracteventdispatcher.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2020 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtCore module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2020 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 QABSTRACTEVENTDISPATCHER_H
#define QABSTRACTEVENTDISPATCHER_H
@@ -55,6 +19,7 @@ class Q_CORE_EXPORT QAbstractEventDispatcher : public QObject
Q_DECLARE_PRIVATE(QAbstractEventDispatcher)
public:
+ using Duration = std::chrono::nanoseconds;
struct TimerInfo
{
int timerId;
@@ -62,8 +27,13 @@ public:
Qt::TimerType timerType;
inline TimerInfo(int id, int i, Qt::TimerType t)
- : timerId(id), interval(i), timerType(t)
- { }
+ : timerId(id), interval(i), timerType(t) { }
+ };
+ struct TimerInfoV2
+ {
+ Duration interval;
+ Qt::TimerId timerId;
+ Qt::TimerType timerType;
};
explicit QAbstractEventDispatcher(QObject *parent = nullptr);
@@ -76,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;
@@ -104,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 5679cae076..2576027d52 100644
--- a/src/corelib/kernel/qabstracteventdispatcher_p.h
+++ b/src/corelib/kernel/qabstracteventdispatcher_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtCore module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// 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 QABSTRACTEVENTDISPATCHER_P_H
#define QABSTRACTEVENTDISPATCHER_P_H
@@ -52,23 +16,34 @@
//
#include "QtCore/qabstracteventdispatcher.h"
+#include "QtCore/qnamespace.h"
#include "private/qobject_p.h"
+#include "QtCore/qttypetraits.h"
QT_BEGIN_NAMESPACE
-Q_CORE_EXPORT uint qGlobalPostedEventsCount();
+Q_AUTOTEST_EXPORT qsizetype qGlobalPostedEventsCount();
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/qabstractnativeeventfilter.cpp b/src/corelib/kernel/qabstractnativeeventfilter.cpp
index 392098a2f5..7b31c0fac5 100644
--- a/src/corelib/kernel/qabstractnativeeventfilter.cpp
+++ b/src/corelib/kernel/qabstractnativeeventfilter.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtCore module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// 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 "qabstractnativeeventfilter.h"
#include "qabstracteventdispatcher.h"
@@ -105,6 +69,9 @@ QAbstractNativeEventFilter::~QAbstractNativeEventFilter()
\b {Linux example}
\snippet code/src_corelib_kernel_qabstractnativeeventfilter.cpp 0
+ \b {Windows example}
+ \snippet code/src_corelib_kernel_qabstractnativeeventfilter_win.cpp 0
+
\b {macOS example}
mycocoaeventfilter.h:
diff --git a/src/corelib/kernel/qabstractnativeeventfilter.h b/src/corelib/kernel/qabstractnativeeventfilter.h
index b9a8863bb5..3660a31dba 100644
--- a/src/corelib/kernel/qabstractnativeeventfilter.h
+++ b/src/corelib/kernel/qabstractnativeeventfilter.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtCore module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// 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 QABSTRACTNATIVEEVENTFILTER_H
#define QABSTRACTNATIVEEVENTFILTER_H
diff --git a/src/corelib/kernel/qapplicationstatic.h b/src/corelib/kernel/qapplicationstatic.h
new file mode 100644
index 0000000000..bf5e79b8bf
--- /dev/null
+++ b/src/corelib/kernel/qapplicationstatic.h
@@ -0,0 +1,86 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// Copyright (C) 2021 Intel Corporation.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QAPPLICATIONSTATIC_H
+#define QAPPLICATIONSTATIC_H
+
+#include <QtCore/QMutex>
+#include <QtCore/qcoreapplication.h>
+#include <QtCore/qglobalstatic.h>
+
+#include <new>
+
+QT_BEGIN_NAMESPACE
+
+namespace QtGlobalStatic {
+template <typename QAS> struct ApplicationHolder
+{
+ using Type = typename QAS::QAS_Type;
+ using PlainType = std::remove_cv_t<Type>;
+
+ Q_CONSTINIT static inline struct { alignas(Type) unsigned char data[sizeof(Type)]; } storage = {};
+ Q_CONSTINIT static inline QBasicAtomicInteger<qint8> guard = { QtGlobalStatic::Uninitialized };
+ Q_CONSTINIT static inline QBasicMutex mutex {};
+
+ static constexpr bool MutexLockIsNoexcept = noexcept(mutex.lock());
+ static constexpr bool ConstructionIsNoexcept = noexcept(QAS::innerFunction(nullptr));
+
+ ApplicationHolder() = default;
+ Q_DISABLE_COPY_MOVE(ApplicationHolder)
+ ~ApplicationHolder()
+ {
+ if (guard.loadAcquire() == QtGlobalStatic::Initialized) {
+ // No mutex! Up to external code to ensure no race happens.
+ guard.storeRelease(QtGlobalStatic::Destroyed);
+ realPointer()->~PlainType();
+ }
+ }
+
+ static PlainType *realPointer()
+ {
+ return std::launder(reinterpret_cast<PlainType *>(&storage));
+ }
+
+ // called from QGlobalStatic::instance()
+ PlainType *pointer() noexcept(MutexLockIsNoexcept && ConstructionIsNoexcept)
+ {
+ if (guard.loadAcquire() == QtGlobalStatic::Initialized)
+ return realPointer();
+ QMutexLocker locker(&mutex);
+ if (guard.loadRelaxed() == QtGlobalStatic::Uninitialized) {
+ QAS::innerFunction(&storage);
+ const auto *app = QCoreApplication::instance();
+ Q_ASSERT_X(app, Q_FUNC_INFO,
+ "The application static was used without a QCoreApplication instance");
+ QObject::connect(app, &QObject::destroyed, app, reset, Qt::DirectConnection);
+ guard.storeRelease(QtGlobalStatic::Initialized);
+ }
+ return realPointer();
+ }
+
+ static void reset()
+ {
+ // we only synchronize using the mutex here, not the guard
+ QMutexLocker locker(&mutex);
+ realPointer()->~PlainType();
+ guard.storeRelaxed(QtGlobalStatic::Uninitialized);
+ }
+};
+} // namespace QtGlobalStatic
+
+#define Q_APPLICATION_STATIC(TYPE, NAME, ...) \
+ namespace { struct Q_QAS_ ## NAME { \
+ typedef TYPE QAS_Type; \
+ static void innerFunction(void *pointer) \
+ noexcept(noexcept(std::remove_cv_t<QAS_Type>(__VA_ARGS__))) \
+ { \
+ new (pointer) QAS_Type(__VA_ARGS__); \
+ } \
+ }; } \
+ static QGlobalStatic<QtGlobalStatic::ApplicationHolder<Q_QAS_ ## NAME>> NAME;\
+ /**/
+
+QT_END_NAMESPACE
+
+#endif // QAPPLICATIONSTATIC_H
diff --git a/src/corelib/kernel/qapplicationstatic.qdoc b/src/corelib/kernel/qapplicationstatic.qdoc
new file mode 100644
index 0000000000..5cbac65df9
--- /dev/null
+++ b/src/corelib/kernel/qapplicationstatic.qdoc
@@ -0,0 +1,109 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+
+/*!
+ \macro Q_APPLICATION_STATIC(Type, VariableName, ...)
+ \since 6.3
+ \relates QGlobalStatic
+
+ This macro extends Q_GLOBAL_STATIC and creates a global and static object
+ of type \l QGlobalStatic, of name \a VariableName, initialized by the
+ variadic arguments, and that behaves as a pointer to \a Type, where the
+ actual lifetime of the type is bound to the QCoreApplication. The object
+ created by Q_APPLICATION_STATIC initializes itself on the first use, which
+ means that it will not increase the application or the library's load time.
+ Additionally, the object is initialized in a thread-safe manner on all
+ platforms.
+
+ In contrast to Q_GLOBAL_STATIC where the type is only meant to be destroyed at
+ program exit, here the actual lifetime of the type is bound to the lifetime of
+ the QCoreApplication. This makes it ideal to store semi-static QObjects, which
+ should also be destroyed once the QCoreApplication is destroyed. This means the
+ type will get deleted once the QCoreApplication emits the destroyed signal.
+ It is permitted for the object to be recreated when it's accessed again, if
+ a new QCoreApplication has also been created.
+
+ Since the value is bound to the QCoreApplication, it should only ever be
+ accessed if there is a valid QCoreApplication::instance(). Accessing this
+ object before QCoreApplication is created or after it's destroyed will
+ produce warnings and may have unpredictable behavior.
+
+ The typical use of this macro is as follows, in a global context (that is,
+ outside of any function bodies):
+
+ \code
+ Q_APPLICATION_STATIC(MyQObjectType, staticType, "some string", function())
+ \endcode
+
+ Do note that the arguments passed in variadic fashion to this macro are
+ evaluated every time the object is constructed, so in the above example,
+ the function \c{function} will be called more than once if the object is
+ recreated.
+
+ Aside from the value also being bound to the lifetime of the QCoreApplication,
+ this macro behaves identically to Q_GLOBAL_STATIC(). Please see that macro's
+ documentation for more information.
+
+ \section1 Threading guarantees
+
+ The Q_APPLICATION_STATIC macro ensures that the object is initialized only
+ once (per lifetime of a QCoreApplication), even if multiple threads try to
+ concurrently access the object. This is done by providing a per-object
+ mutex; application and library developers need to be aware that their
+ object will be constructed with this mutex locked and therefore must not
+ reenter the same object's initialization, or a deadlock will occur.
+
+ There is no thread-safety on the destruction of the object: user code must
+ not access this object once the QCoreApplication destructor starts to run.
+ User code must arrange to ensure this does not happen, such as by not
+ accessing it once the main thread's event loop has exited.
+
+ Like Q_GLOBAL_STATIC, Q_APPLICATION_STATIC provides no thread-safety
+ guarantees for accesses to the object once creation is finished. It is up
+ to user code to ensure that no racy data accesses happen.
+
+ In case the object created by this operation is a QObject, its associated
+ thread will be the one that succeeded in creating it. It will be destroyed
+ by the main thread, so a \l{QObject::}{moveToThread()} to the main thread
+ or to no thread before destruction is adviseable. Doing so from the
+ constructor of the class in question is a sensible solution if one can't
+ guarantee that the main thread will be the one to initialize the object.
+
+ \omit
+ \section1 Implementation details
+ See \l Q_GLOBAL_STATIC implementation details for an introduction.
+
+ Q_APPLICATION_STATIC uses the same \l QGlobalStatic public class that
+ Q_GLOBAL_STATIC does, but instead uses a QtGlobalStatic::ApplicationHolder
+ template class as the template parameter. The differences to
+ QtGlobalStatic::Holder are:
+
+ \list
+ \li The ApplicationHolder class is empty. Unlike Holder, the storage is
+ provided as a \c {static inline} member, simply so that the static
+ member reset() function can access it without having to save the
+ pointer in a lambda.
+
+ \li The ApplicationHolder constructor is trivial; initialization of the
+ type is instead deferred to the \c pointer() function. This means the
+ C++11 thread-safe initialization of statics does not protect the
+ object.
+
+ \li Instead, ApplicationHolder provides a mutex (implemented as a \c
+ {static inline} member of type \l QBasicMutex) and locks it before
+ constructing or destructing the object.
+
+ \li After constructing the object, it will QObject::connect() the
+ QCoreApplication::destroyed() signal to a function that will in turn
+ destroy the object.
+
+ \li The destructor will destroy the object if the application is
+ exiting without first destroying the QCoreApplication object (i.e., a
+ call to \c ::exit) or this Q_APPLICATION_STATIC is part of a plugin
+ that is being unloaded.
+ \endlist
+
+ \endomit
+
+ \sa Q_GLOBAL_STATIC, QGlobalStatic
+*/
diff --git a/src/corelib/kernel/qassociativeiterable.cpp b/src/corelib/kernel/qassociativeiterable.cpp
index f978acbd8b..db17c392a2 100644
--- a/src/corelib/kernel/qassociativeiterable.cpp
+++ b/src/corelib/kernel/qassociativeiterable.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2020 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtCore module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2020 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 <QtCore/qassociativeiterable.h>
#include <QtCore/qvariant.h>
diff --git a/src/corelib/kernel/qassociativeiterable.h b/src/corelib/kernel/qassociativeiterable.h
index 6fe0ff670c..f3963d350e 100644
--- a/src/corelib/kernel/qassociativeiterable.h
+++ b/src/corelib/kernel/qassociativeiterable.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2020 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtCore module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2020 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 QASSOCIATIVEITERABLE_H
#define QASSOCIATIVEITERABLE_H
diff --git a/src/corelib/kernel/qbasictimer.cpp b/src/corelib/kernel/qbasictimer.cpp
index 60de171e14..17711a355e 100644
--- a/src/corelib/kernel/qbasictimer.cpp
+++ b/src/corelib/kernel/qbasictimer.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2020 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtCore module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2020 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 "qbasictimer.h"
#include "qabstracteventdispatcher.h"
@@ -69,10 +33,8 @@ QT_BEGIN_NAMESPACE
can maintain a list of basic timers by holding them in container
that supports move-only types, e.g. std::vector.
- The \l{widgets/wiggly}{Wiggly} example uses QBasicTimer to repaint
- a widget at regular intervals.
-
- \sa QTimer, QTimerEvent, QObject::timerEvent(), Timers, {Wiggly Example}
+ \sa QTimer, QChronoTimer, QTimerEvent, QObject::timerEvent(),
+ Timers, {Affine Transformations}
*/
@@ -122,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.
*/
@@ -140,7 +110,13 @@ QT_BEGIN_NAMESPACE
/*!
\fn void QBasicTimer::start(int msec, QObject *object)
- Starts (or restarts) the timer with a \a msec milliseconds timeout. The
+ \obsolete Use chrono overload instead.
+*/
+
+/*!
+ \since 6.5
+
+ Starts (or restarts) the timer with a \a duration timeout. The
timer will be a Qt::CoarseTimer. See Qt::TimerType for information on the
different timer types.
@@ -148,15 +124,23 @@ QT_BEGIN_NAMESPACE
\sa stop(), isActive(), QObject::timerEvent(), Qt::CoarseTimer
*/
-void QBasicTimer::start(int msec, QObject *obj)
+void QBasicTimer::start(std::chrono::milliseconds duration, QObject *object)
{
- start(msec, Qt::CoarseTimer, obj);
+ start(duration, Qt::CoarseTimer, object);
}
/*!
+ \fn QBasicTimer::start(int msec, Qt::TimerType timerType, QObject *obj)
\overload
+ \obsolete
+
+ Use chrono overload instead.
+*/
+
+/*!
+ \since 6.5
- Starts (or restarts) the timer with a \a msec milliseconds timeout and the
+ Starts (or restarts) the timer with a \a duration timeout and the
given \a timerType. See Qt::TimerType for information on the different
timer types.
@@ -164,10 +148,10 @@ void QBasicTimer::start(int msec, QObject *obj)
\sa stop(), isActive(), QObject::timerEvent(), Qt::TimerType
*/
-void QBasicTimer::start(int msec, Qt::TimerType timerType, QObject *obj)
+void QBasicTimer::start(std::chrono::milliseconds duration, Qt::TimerType timerType, QObject *obj)
{
QAbstractEventDispatcher *eventDispatcher = QAbstractEventDispatcher::instance();
- if (Q_UNLIKELY(msec < 0)) {
+ if (Q_UNLIKELY(duration.count() < 0)) {
qWarning("QBasicTimer::start: Timers cannot have negative timeouts");
return;
}
@@ -181,7 +165,7 @@ void QBasicTimer::start(int msec, Qt::TimerType timerType, QObject *obj)
}
stop();
if (obj)
- id = eventDispatcher->registerTimer(msec, timerType, obj);
+ id = int(eventDispatcher->registerTimer(duration, timerType, obj));
}
/*!
@@ -193,7 +177,7 @@ void QBasicTimer::stop()
{
if (id) {
QAbstractEventDispatcher *eventDispatcher = QAbstractEventDispatcher::instance();
- if (eventDispatcher && !eventDispatcher->unregisterTimer(id)) {
+ if (eventDispatcher && !eventDispatcher->unregisterTimer(Qt::TimerId(id))) {
qWarning("QBasicTimer::stop: Failed. Possibly trying to stop from a different thread");
return;
}
diff --git a/src/corelib/kernel/qbasictimer.h b/src/corelib/kernel/qbasictimer.h
index 6a2728dd56..ccc93f6e9b 100644
--- a/src/corelib/kernel/qbasictimer.h
+++ b/src/corelib/kernel/qbasictimer.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2020 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtCore module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2020 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 QBASICTIMER_H
#define QBASICTIMER_H
@@ -43,6 +7,8 @@
#include <QtCore/qglobal.h>
#include <QtCore/qnamespace.h>
+#include <chrono>
+
QT_BEGIN_NAMESPACE
@@ -58,22 +24,37 @@ public:
inline ~QBasicTimer() { if (id) stop(); }
QBasicTimer(QBasicTimer &&other) noexcept
- : id{qExchange(other.id, 0)}
+ : id{std::exchange(other.id, 0)}
{}
QT_MOVE_ASSIGNMENT_OPERATOR_IMPL_VIA_MOVE_AND_SWAP(QBasicTimer)
- void swap(QBasicTimer &other) noexcept { qSwap(id, other.id); }
+ void swap(QBasicTimer &other) noexcept { std::swap(id, other.id); }
bool isActive() const noexcept { return id != 0; }
int timerId() const noexcept { return id; }
-
+ QT_CORE_INLINE_SINCE(6, 5)
void start(int msec, QObject *obj);
+ QT_CORE_INLINE_SINCE(6, 5)
void start(int msec, Qt::TimerType timerType, QObject *obj);
+ void start(std::chrono::milliseconds duration, QObject *obj);
+ void start(std::chrono::milliseconds duration, Qt::TimerType timerType, QObject *obj);
void stop();
};
Q_DECLARE_TYPEINFO(QBasicTimer, Q_RELOCATABLE_TYPE);
+#if QT_CORE_INLINE_IMPL_SINCE(6, 5)
+void QBasicTimer::start(int msec, QObject *obj)
+{
+ start(std::chrono::milliseconds{msec}, obj);
+}
+
+void QBasicTimer::start(int msec, Qt::TimerType t, QObject *obj)
+{
+ start(std::chrono::milliseconds{msec}, t, obj);
+}
+#endif
+
inline void swap(QBasicTimer &lhs, QBasicTimer &rhs) noexcept { lhs.swap(rhs); }
QT_END_NAMESPACE
diff --git a/src/corelib/kernel/qbindingstorage.h b/src/corelib/kernel/qbindingstorage.h
new file mode 100644
index 0000000000..1c03b23bfa
--- /dev/null
+++ b/src/corelib/kernel/qbindingstorage.h
@@ -0,0 +1,94 @@
+// Copyright (C) 2021 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 QBINDINGSTORAGE_H
+#define QBINDINGSTORAGE_H
+
+#include <QtCore/qglobal.h>
+#include <QtCore/qnamespace.h>
+
+QT_BEGIN_NAMESPACE
+
+template <typename Class, typename T, auto Offset, auto Setter, auto Signal, auto Getter>
+class QObjectCompatProperty;
+struct QPropertyDelayedNotifications;
+class QUntypedPropertyData;
+
+namespace QtPrivate {
+
+class QPropertyBindingData;
+struct BindingEvaluationState;
+struct CompatPropertySafePoint;
+}
+
+struct QBindingStatus
+{
+ QtPrivate::BindingEvaluationState *currentlyEvaluatingBinding = nullptr;
+ QtPrivate::CompatPropertySafePoint *currentCompatProperty = nullptr;
+ Qt::HANDLE threadId = nullptr;
+ QPropertyDelayedNotifications *groupUpdateData = nullptr;
+};
+
+namespace QtPrivate {
+struct QBindingStatusAccessToken;
+Q_AUTOTEST_EXPORT QBindingStatus *getBindingStatus(QBindingStatusAccessToken);
+}
+
+
+struct QBindingStorageData;
+class Q_CORE_EXPORT QBindingStorage
+{
+ mutable QBindingStorageData *d = nullptr;
+ QBindingStatus *bindingStatus = nullptr;
+
+ template<typename Class, typename T, auto Offset, auto Setter, auto Signal, auto Getter>
+ friend class QObjectCompatProperty;
+ friend class QObjectPrivate;
+ friend class QtPrivate::QPropertyBindingData;
+public:
+ QBindingStorage();
+ ~QBindingStorage();
+
+ bool isEmpty() { return !d; }
+ bool isValid() const noexcept { return bindingStatus; }
+
+ const QBindingStatus *status(QtPrivate::QBindingStatusAccessToken) const;
+
+ void registerDependency(const QUntypedPropertyData *data) const
+ {
+ if (!bindingStatus || !bindingStatus->currentlyEvaluatingBinding)
+ return;
+ registerDependency_helper(data);
+ }
+ QtPrivate::QPropertyBindingData *bindingData(const QUntypedPropertyData *data) const
+ {
+ if (!d)
+ return nullptr;
+ return bindingData_helper(data);
+ }
+
+#if QT_CORE_REMOVED_SINCE(6, 2)
+ void maybeUpdateBindingAndRegister(const QUntypedPropertyData *data) const { registerDependency(data); }
+#endif
+
+ QtPrivate::QPropertyBindingData *bindingData(QUntypedPropertyData *data, bool create)
+ {
+ if (!d && !create)
+ return nullptr;
+ return bindingData_helper(data, create);
+ }
+private:
+ void reinitAfterThreadMove();
+ void clear();
+ void registerDependency_helper(const QUntypedPropertyData *data) const;
+#if QT_CORE_REMOVED_SINCE(6, 2)
+ // ### Unused, but keep for BC
+ void maybeUpdateBindingAndRegister_helper(const QUntypedPropertyData *data) const;
+#endif
+ QtPrivate::QPropertyBindingData *bindingData_helper(const QUntypedPropertyData *data) const;
+ QtPrivate::QPropertyBindingData *bindingData_helper(QUntypedPropertyData *data, bool create);
+};
+
+QT_END_NAMESPACE
+
+#endif // QBINDINGSTORAGE_H
diff --git a/src/corelib/kernel/qcfsocketnotifier.cpp b/src/corelib/kernel/qcfsocketnotifier.cpp
index 9b1ed8cbf9..21a22a7439 100644
--- a/src/corelib/kernel/qcfsocketnotifier.cpp
+++ b/src/corelib/kernel/qcfsocketnotifier.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtCore module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// 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 "qcfsocketnotifier_p.h"
#include <QtCore/qcoreapplication.h>
@@ -249,7 +213,7 @@ void QCFSocketNotifier::unregisterSocketNotifier(QSocketNotifier *notifier)
void QCFSocketNotifier::removeSocketNotifiers()
{
// Remove CFSockets from the runloop.
- for (MacSocketInfo *socketInfo : qAsConst(macSockets)) {
+ for (MacSocketInfo *socketInfo : std::as_const(macSockets)) {
unregisterSocketInfo(socketInfo);
delete socketInfo;
}
diff --git a/src/corelib/kernel/qcfsocketnotifier_p.h b/src/corelib/kernel/qcfsocketnotifier_p.h
index 12c5bf6334..9bffe132dd 100644
--- a/src/corelib/kernel/qcfsocketnotifier_p.h
+++ b/src/corelib/kernel/qcfsocketnotifier_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtCore module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// 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 QCFSOCKETNOTIFIER_P_H
#define QCFSOCKETNOTIFIER_P_H
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 958f0e2bc7..a31040944f 100644
--- a/src/corelib/kernel/qcore_foundation.mm
+++ b/src/corelib/kernel/qcore_foundation.mm
@@ -1,43 +1,7 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Copyright (C) 2014 Samuel Gaist <samuel.gaist@edeltech.ch>
-** Copyright (C) 2014 Petroules Corporation.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtCore module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// Copyright (C) 2014 Samuel Gaist <samuel.gaist@edeltech.ch>
+// Copyright (C) 2014 Petroules Corporation.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include <QtCore/qstring.h>
#include <QtCore/qurl.h>
@@ -46,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>
@@ -502,7 +466,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.
@@ -531,9 +495,9 @@ QTimeZone QTimeZone::fromCFTimeZone(CFTimeZoneRef timeZone)
CFTimeZoneRef QTimeZone::toCFTimeZone() const
{
#ifndef QT_NO_DYNAMIC_CAST
- Q_ASSERT(dynamic_cast<const QMacTimeZonePrivate *>(d.data()));
+ Q_ASSERT(dynamic_cast<const QMacTimeZonePrivate *>(d.d));
#endif
- const QMacTimeZonePrivate *p = static_cast<const QMacTimeZonePrivate *>(d.data());
+ const QMacTimeZonePrivate *p = static_cast<const QMacTimeZonePrivate *>(d.d);
return reinterpret_cast<CFTimeZoneRef>([p->nsTimeZone() copy]);
}
diff --git a/src/corelib/kernel/qcore_mac.mm b/src/corelib/kernel/qcore_mac.mm
index 435b2a6150..54c4373aed 100644
--- a/src/corelib/kernel/qcore_mac.mm
+++ b/src/corelib/kernel/qcore_mac.mm
@@ -1,42 +1,6 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Copyright (C) 2014 Petroules Corporation.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtCore module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// Copyright (C) 2014 Petroules Corporation.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include <private/qcore_mac_p.h>
@@ -55,19 +19,40 @@
#include <objc/runtime.h>
#include <mach-o/dyld.h>
#include <sys/sysctl.h>
+#include <spawn.h>
#include <qdebug.h>
+#include "qendian.h"
#include "qhash.h"
-#include "qpair.h"
#include "qmutex.h"
#include "qvarlengtharray.h"
#include "private/qlocking_p.h"
+#if !defined(QT_BOOTSTRAPPED)
+#include <thread>
+#endif
+
+#if !defined(QT_APPLE_NO_PRIVATE_APIS)
+extern "C" {
+typedef uint32_t csr_config_t;
+extern int csr_get_active_config(csr_config_t *) __attribute__((weak_import));
+
+#ifdef QT_BUILD_INTERNAL
+int responsibility_spawnattrs_setdisclaim(posix_spawnattr_t attrs, int disclaim)
+__attribute__((availability(macos,introduced=10.14),weak_import));
+pid_t responsibility_get_pid_responsible_for_pid(pid_t) __attribute__((weak_import));
+char *** _NSGetArgv();
+extern char **environ;
+#endif
+}
+#endif
+
QT_BEGIN_NAMESPACE
// --------------------------------------------------------------------------
+#if defined(Q_OS_MACOS)
static void initializeStandardUserDefaults()
{
// The standard user defaults are initialized from an ordered list of domains,
@@ -80,6 +65,7 @@ static void initializeStandardUserDefaults()
Q_UNUSED(NSUserDefaults.standardUserDefaults);
}
Q_CONSTRUCTOR_FUNCTION(initializeStandardUserDefaults);
+#endif
// --------------------------------------------------------------------------
@@ -101,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);
@@ -132,7 +136,7 @@ bool AppleUnifiedLogger::messageHandler(QtMsgType msgType, const QMessageLogCont
const bool isDefault = !context.category || !strcmp(context.category, "default");
os_log_t log = isDefault ? OS_LOG_DEFAULT :
- cachedLog(subsystem, QString::fromLatin1(context.category));
+ os_log_create(subsystem.toLatin1().constData(), context.category);
os_log_type_t logType = logTypeForMessageType(msgType);
if (!os_log_type_enabled(log, logType))
@@ -152,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)
@@ -168,33 +172,31 @@ os_log_type_t AppleUnifiedLogger::logTypeForMessageType(QtMsgType msgType)
return OS_LOG_TYPE_DEFAULT;
}
-os_log_t AppleUnifiedLogger::cachedLog(const QString &subsystem, const QString &category)
-{
- static QBasicMutex mutex;
- const auto locker = qt_scoped_lock(mutex);
+#endif // QT_USE_APPLE_UNIFIED_LOGGING
- static QHash<QPair<QString, QString>, os_log_t> logs;
- const auto cacheKey = qMakePair(subsystem, category);
- os_log_t log = logs.value(cacheKey);
+// -------------------------------------------------------------------------
- if (!log) {
- log = os_log_create(subsystem.toLatin1().constData(),
- category.toLatin1().constData());
- logs.insert(cacheKey, log);
+QDebug operator<<(QDebug dbg, id obj)
+{
+ if (!obj) {
+ // Match NSLog
+ dbg << "(null)";
+ return dbg;
+ }
- // Technically we should release the os_log_t resource when done
- // with it, but since we don't know when a category is disabled
- // we keep all cached os_log_t instances until shutdown, where
- // the OS will clean them up for us.
+ for (Class cls = object_getClass(obj); cls; cls = class_getSuperclass(cls)) {
+ if (cls == NSObject.class) {
+ dbg << static_cast<NSObject*>(obj);
+ return dbg;
+ }
}
- return log;
+ // Match NSObject.debugDescription
+ const QDebugStateSaver saver(dbg);
+ dbg.nospace() << '<' << object_getClassName(obj) << ": " << static_cast<void*>(obj) << '>';
+ return dbg;
}
-#endif // QT_USE_APPLE_UNIFIED_LOGGING
-
-// -------------------------------------------------------------------------
-
QDebug operator<<(QDebug dbg, const NSObject *nsObject)
{
return dbg << (nsObject ?
@@ -231,63 +233,42 @@ QT_FOR_EACH_MUTABLE_CORE_GRAPHICS_TYPE(QT_DECLARE_WEAK_QDEBUG_OPERATOR_FOR_CF_TY
QT_END_NAMESPACE
QT_USE_NAMESPACE
+
+#ifdef QT_DEBUG
@interface QT_MANGLE_NAMESPACE(QMacAutoReleasePoolTracker) : NSObject
@end
-@implementation QT_MANGLE_NAMESPACE(QMacAutoReleasePoolTracker) {
- NSAutoreleasePool **m_pool;
-}
-
-- (instancetype)initWithPool:(NSAutoreleasePool **)pool
-{
- if ((self = [self init]))
- m_pool = pool;
- return self;
-}
-
-- (void)dealloc
-{
- if (*m_pool) {
- // The pool is still valid, which means we're not being drained from
- // the corresponding QMacAutoReleasePool (see below).
-
- // QMacAutoReleasePool has only a single member, the NSAutoreleasePool*
- // so the address of that member is also the QMacAutoReleasePool itself.
- QMacAutoReleasePool *pool = reinterpret_cast<QMacAutoReleasePool *>(m_pool);
- qWarning() << "Premature drain of" << pool << "This can happen if you've allocated"
- << "the pool on the heap, or as a member of a heap-allocated object. This is not a"
- << "supported use of QMacAutoReleasePool, and might result in crashes when objects"
- << "in the pool are deallocated and then used later on under the assumption they"
- << "will be valid until" << pool << "has been drained.";
+@implementation QT_MANGLE_NAMESPACE(QMacAutoReleasePoolTracker)
+@end
+QT_NAMESPACE_ALIAS_OBJC_CLASS(QMacAutoReleasePoolTracker);
+#endif // QT_DEBUG
- // Reset the pool so that it's not drained again later on
- *m_pool = nullptr;
- }
+// Use the direct runtime interface to manage autorelease pools, as it
+// has less overhead then allocating NSAutoreleasePools, and allows for
+// a future where we use ARC (where NSAutoreleasePool is not allowed).
+// https://clang.llvm.org/docs/AutomaticReferenceCounting.html#runtime-support
- [super dealloc];
+extern "C" {
+void *objc_autoreleasePoolPush(void);
+void objc_autoreleasePoolPop(void *pool);
}
-@end
-QT_NAMESPACE_ALIAS_OBJC_CLASS(QMacAutoReleasePoolTracker);
QT_BEGIN_NAMESPACE
QMacAutoReleasePool::QMacAutoReleasePool()
- : pool([[NSAutoreleasePool alloc] init])
+ : pool(objc_autoreleasePoolPush())
{
+#ifdef QT_DEBUG
+ static const bool debugAutoReleasePools = qEnvironmentVariableIsSet("QT_DARWIN_DEBUG_AUTORELEASEPOOLS");
+ if (!debugAutoReleasePools)
+ return;
+
Class trackerClass = [QMacAutoReleasePoolTracker class];
-#ifdef QT_DEBUG
void *poolFrame = nullptr;
- if (__builtin_available(macOS 10.14, iOS 12.0, tvOS 12.0, watchOS 5.0, *)) {
- void *frame;
- if (backtrace_from_fp(__builtin_frame_address(0), &frame, 1))
- poolFrame = frame;
- } else {
- static const int maxFrames = 3;
- void *callstack[maxFrames];
- if (backtrace(callstack, maxFrames) == maxFrames)
- poolFrame = callstack[maxFrames - 1];
- }
+ void *frames[2];
+ if (backtrace_from_fp(__builtin_frame_address(0), frames, 2))
+ poolFrame = frames[1];
if (poolFrame) {
Dl_info info;
@@ -313,30 +294,14 @@ QMacAutoReleasePool::QMacAutoReleasePool()
free((char*)symbolName);
}
}
-#endif
- [[[trackerClass alloc] initWithPool:
- reinterpret_cast<NSAutoreleasePool **>(&pool)] autorelease];
+ [[trackerClass new] autorelease];
+#endif // QT_DEBUG
}
QMacAutoReleasePool::~QMacAutoReleasePool()
{
- if (!pool) {
- qWarning() << "Prematurely drained pool" << this << "finally drained. Any objects belonging"
- << "to this pool have already been released, and have potentially been invalid since the"
- << "premature drain earlier on.";
- return;
- }
-
- // Save and reset pool before draining, so that the pool tracker can know
- // that it's being drained by its owning pool.
- NSAutoreleasePool *savedPool = static_cast<NSAutoreleasePool*>(pool);
- pool = nullptr;
-
- // Drain behaves the same as release, with the advantage that
- // if we're ever used in a garbage-collected environment, the
- // drain acts as a hint to the garbage collector to collect.
- [savedPool drain];
+ objc_autoreleasePoolPop(pool);
}
#ifndef QT_NO_DEBUG_STREAM
@@ -358,14 +323,9 @@ QDebug operator<<(QDebug debug, const QCFString &string)
#ifdef Q_OS_MACOS
bool qt_mac_applicationIsInDarkMode()
{
-#if QT_MACOS_PLATFORM_SDK_EQUAL_OR_ABOVE(__MAC_10_14)
- if (__builtin_available(macOS 10.14, *)) {
- auto appearance = [NSApp.effectiveAppearance bestMatchFromAppearancesWithNames:
- @[ NSAppearanceNameAqua, NSAppearanceNameDarkAqua ]];
- return [appearance isEqualToString:NSAppearanceNameDarkAqua];
- }
-#endif
- return false;
+ auto appearance = [NSApp.effectiveAppearance bestMatchFromAppearancesWithNames:
+ @[ NSAppearanceNameAqua, NSAppearanceNameDarkAqua ]];
+ return [appearance isEqualToString:NSAppearanceNameDarkAqua];
}
bool qt_mac_runningUnderRosetta()
@@ -376,6 +336,89 @@ bool qt_mac_runningUnderRosetta()
return translated;
return false;
}
+
+std::optional<uint32_t> qt_mac_sipConfiguration()
+{
+ static auto configuration = []() -> std::optional<uint32_t> {
+#if !defined(QT_APPLE_NO_PRIVATE_APIS)
+ csr_config_t config;
+ if (csr_get_active_config && csr_get_active_config(&config) == 0)
+ return config;
+#endif
+
+ QIOType<io_registry_entry_t> nvram = IORegistryEntryFromPath(kIOMainPortDefault, "IODeviceTree:/options");
+ if (!nvram) {
+ qWarning("Failed to locate NVRAM entry in IO registry");
+ return {};
+ }
+
+ QCFType<CFTypeRef> csrConfig = IORegistryEntryCreateCFProperty(nvram,
+ CFSTR("csr-active-config"), kCFAllocatorDefault, IOOptionBits{});
+ if (!csrConfig)
+ return {}; // SIP config is not available
+
+ if (auto type = CFGetTypeID(csrConfig); type != CFDataGetTypeID()) {
+ qWarning() << "Unexpected SIP config type" << CFCopyTypeIDDescription(type);
+ return {};
+ }
+
+ QByteArray data = QByteArray::fromRawCFData(csrConfig.as<CFDataRef>());
+ if (data.size() != sizeof(uint32_t)) {
+ qWarning() << "Unexpected SIP config size" << data.size();
+ return {};
+ }
+
+ return qFromLittleEndian<uint32_t>(data.constData());
+ }();
+ return configuration;
+}
+
+#define CHECK_SPAWN(expr) \
+ if (int err = (expr)) { \
+ posix_spawnattr_destroy(&attr); \
+ return; \
+ }
+
+#ifdef QT_BUILD_INTERNAL
+void qt_mac_ensureResponsible()
+{
+#if !defined(QT_APPLE_NO_PRIVATE_APIS)
+ if (!responsibility_get_pid_responsible_for_pid || !responsibility_spawnattrs_setdisclaim)
+ return;
+
+ auto pid = getpid();
+ if (responsibility_get_pid_responsible_for_pid(pid) == pid)
+ return; // Already responsible
+
+ posix_spawnattr_t attr = {};
+ CHECK_SPAWN(posix_spawnattr_init(&attr));
+
+ // Behave as exec
+ short flags = POSIX_SPAWN_SETEXEC;
+
+ // Reset signal mask
+ sigset_t no_signals;
+ sigemptyset(&no_signals);
+ CHECK_SPAWN(posix_spawnattr_setsigmask(&attr, &no_signals));
+ flags |= POSIX_SPAWN_SETSIGMASK;
+
+ // Reset all signals to their default handlers
+ sigset_t all_signals;
+ sigfillset(&all_signals);
+ CHECK_SPAWN(posix_spawnattr_setsigdefault(&attr, &all_signals));
+ flags |= POSIX_SPAWN_SETSIGDEF;
+
+ CHECK_SPAWN(posix_spawnattr_setflags(&attr, flags));
+
+ CHECK_SPAWN(responsibility_spawnattrs_setdisclaim(&attr, 1));
+
+ char **argv = *_NSGetArgv();
+ posix_spawnp(&pid, argv[0], nullptr, &attr, argv, environ);
+ posix_spawnattr_destroy(&attr);
+#endif
+}
+#endif // QT_BUILD_INTERNAL
+
#endif
bool qt_apple_isApplicationExtension()
@@ -406,36 +449,64 @@ AppleApplication *qt_apple_sharedApplication()
}
#endif
+#if !defined(QT_BOOTSTRAPPED)
+
+#if defined(Q_OS_MACOS)
+namespace {
+struct SandboxChecker
+{
+ SandboxChecker() : m_thread([this]{
+ m_isSandboxed = []{
+ QCFType<SecStaticCodeRef> staticCode = nullptr;
+ NSURL *executableUrl = NSBundle.mainBundle.executableURL;
+ if (SecStaticCodeCreateWithPath((__bridge CFURLRef)executableUrl,
+ kSecCSDefaultFlags, &staticCode) != errSecSuccess)
+ return false;
+
+ QCFType<SecRequirementRef> sandboxRequirement;
+ if (SecRequirementCreateWithString(CFSTR("entitlement[\"com.apple.security.app-sandbox\"] exists"),
+ kSecCSDefaultFlags, &sandboxRequirement) != errSecSuccess)
+ return false;
+
+ if (SecStaticCodeCheckValidityWithErrors(staticCode,
+ kSecCSBasicValidateOnly, sandboxRequirement, nullptr) != errSecSuccess)
+ return false;
+
+ return true;
+ }();
+ })
+ {}
+ ~SandboxChecker() {
+ std::scoped_lock lock(m_mutex);
+ if (m_thread.joinable())
+ m_thread.detach();
+ }
+ bool isSandboxed() const {
+ std::scoped_lock lock(m_mutex);
+ if (m_thread.joinable())
+ m_thread.join();
+ return m_isSandboxed;
+ }
+private:
+ bool m_isSandboxed;
+ mutable std::thread m_thread;
+ mutable std::mutex m_mutex;
+};
+} // namespace
+static SandboxChecker sandboxChecker;
+#endif // Q_OS_MACOS
+
bool qt_apple_isSandboxed()
{
#if defined(Q_OS_MACOS)
- static bool isSandboxed = []() {
- QCFType<SecStaticCodeRef> staticCode = nullptr;
- NSURL *executableUrl = NSBundle.mainBundle.executableURL;
- if (SecStaticCodeCreateWithPath((__bridge CFURLRef)executableUrl,
- kSecCSDefaultFlags, &staticCode) != errSecSuccess)
- return false;
-
- QCFType<SecRequirementRef> sandboxRequirement;
- if (SecRequirementCreateWithString(CFSTR("entitlement[\"com.apple.security.app-sandbox\"] exists"),
- kSecCSDefaultFlags, &sandboxRequirement) != errSecSuccess)
- return false;
-
- if (SecStaticCodeCheckValidityWithErrors(staticCode,
- kSecCSBasicValidateOnly, sandboxRequirement, nullptr) != errSecSuccess)
- return false;
-
- return true;
- }();
- return isSandboxed;
+ return sandboxChecker.isSandboxed();
#else
return true; // All other Apple platforms
#endif
}
-#if !defined(QT_BOOTSTRAPPED)
QT_END_NAMESPACE
-@implementation NSObject (QtSandboxHelpers)
+@implementation NSObject (QtExtras)
- (id)qt_valueForPrivateKey:(NSString *)key
{
if (qt_apple_isSandboxed())
@@ -445,7 +516,7 @@ QT_END_NAMESPACE
}
@end
QT_BEGIN_NAMESPACE
-#endif
+#endif // !QT_BOOTSTRAPPED
#ifdef Q_OS_MACOS
/*
@@ -496,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;
@@ -503,10 +577,18 @@ void qt_apple_check_os_version()
const char *os = "macOS";
const int version = __MAC_OS_X_VERSION_MIN_REQUIRED;
#endif
- const NSOperatingSystemVersion required = (NSOperatingSystemVersion){
- version / 10000, version / 100 % 100, version % 100};
- const NSOperatingSystemVersion current = NSProcessInfo.processInfo.operatingSystemVersion;
- if (![NSProcessInfo.processInfo isOperatingSystemAtLeastVersion:required]) {
+
+ const auto required = QVersionNumber(version / 10000, version / 100 % 100, version % 100);
+ const auto current = QOperatingSystemVersion::current().version();
+
+#if defined(Q_OS_MACOS)
+ // Check for compatibility version, in which case we can't do a
+ // comparison to the deployment target, which might be e.g. 11.0
+ if (current.majorVersion() == 10 && current.minorVersion() >= 16)
+ return;
+#endif
+
+ if (current < required) {
NSDictionary *plist = NSBundle.mainBundle.infoDictionary;
NSString *applicationName = plist[@"CFBundleDisplayName"];
if (!applicationName)
@@ -517,8 +599,8 @@ void qt_apple_check_os_version()
fprintf(stderr, "Sorry, \"%s\" cannot be run on this version of %s. "
"Qt requires %s %ld.%ld.%ld or later, you have %s %ld.%ld.%ld.\n",
applicationName.UTF8String, os,
- os, long(required.majorVersion), long(required.minorVersion), long(required.patchVersion),
- os, long(current.majorVersion), long(current.minorVersion), long(current.patchVersion));
+ os, long(required.majorVersion()), long(required.minorVersion()), long(required.microVersion()),
+ os, long(current.majorVersion()), long(current.minorVersion()), long(current.microVersion()));
exit(1);
}
@@ -637,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)
);
@@ -652,11 +734,9 @@ QMacVersion::VersionTuple QMacVersion::versionsForImage(const mach_header *machH
|| loadCommand->cmd == LC_VERSION_MIN_TVOS || loadCommand->cmd == LC_VERSION_MIN_WATCHOS) {
auto versionCommand = reinterpret_cast<version_min_command *>(loadCommand);
return makeVersionTuple(versionCommand->version, versionCommand->sdk, osForLoadCommand(loadCommand->cmd));
-#if QT_DARWIN_PLATFORM_SDK_EQUAL_OR_ABOVE(__MAC_10_13, __IPHONE_11_0, __TVOS_11_0, __WATCHOS_4_0)
} else if (loadCommand->cmd == LC_BUILD_VERSION) {
auto versionCommand = reinterpret_cast<build_version_command *>(loadCommand);
return makeVersionTuple(versionCommand->minos, versionCommand->sdk, osForPlatform(versionCommand->platform));
-#endif
}
commandCursor += loadCommand->cmdsize;
}
diff --git a/src/corelib/kernel/qcore_mac_p.h b/src/corelib/kernel/qcore_mac_p.h
index 7b503d10cc..e63c320805 100644
--- a/src/corelib/kernel/qcore_mac_p.h
+++ b/src/corelib/kernel/qcore_mac_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtCore module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// 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 QCORE_MAC_P_H
#define QCORE_MAC_P_H
@@ -54,7 +18,17 @@
#include "private/qglobal_p.h"
#include <QtCore/qoperatingsystemversion.h>
+
+#ifdef Q_OS_MACOS
+#include <mach/port.h>
struct mach_header;
+typedef int kern_return_t;
+typedef mach_port_t io_object_t;
+extern "C" {
+kern_return_t IOObjectRetain(io_object_t object);
+kern_return_t IOObjectRelease(io_object_t object);
+}
+#endif
#ifndef __IMAGECAPTURE__
# define __IMAGECAPTURE__
@@ -103,20 +77,26 @@ struct mach_header;
Q_FORWARD_DECLARE_OBJC_CLASS(NSObject);
Q_FORWARD_DECLARE_OBJC_CLASS(NSString);
+// @compatibility_alias doesn't work with categories or their methods
+#define QtExtras QT_MANGLE_NAMESPACE(QtExtras)
+
QT_BEGIN_NAMESPACE
-template <typename T, typename U, U (*RetainFunction)(U), void (*ReleaseFunction)(U)>
+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(qExchange(other.value, T())) {}
- QAppleRefCounted(const QAppleRefCounted &other) : value(other.value) { if (value) RetainFunction(value); }
+ : value(std::exchange(other.value, T())) {}
+ 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)))
@@ -132,12 +112,21 @@ protected:
T value;
};
+class QMacAutoReleasePool
+{
+public:
+ Q_NODISCARD_CTOR Q_CORE_EXPORT QMacAutoReleasePool();
+ Q_CORE_EXPORT ~QMacAutoReleasePool();
+private:
+ Q_DISABLE_COPY(QMacAutoReleasePool)
+ void *pool;
+};
#ifdef Q_OS_MACOS
class QMacRootLevelAutoReleasePool
{
public:
- QMacRootLevelAutoReleasePool();
+ Q_NODISCARD_CTOR QMacRootLevelAutoReleasePool();
~QMacRootLevelAutoReleasePool();
private:
QScopedPointer<QMacAutoReleasePool> pool;
@@ -162,7 +151,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)
{
@@ -172,15 +161,23 @@ public:
}
};
-class Q_CORE_EXPORT QCFString : public QCFType<CFStringRef>
+#ifdef Q_OS_MACOS
+template <typename T>
+class QIOType : public QAppleRefCounted<T, io_object_t, IOObjectRetain, IOObjectRelease>
+{
+ using QAppleRefCounted<T, io_object_t, IOObjectRetain, IOObjectRelease>::QAppleRefCounted;
+};
+#endif
+
+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;
@@ -189,6 +186,10 @@ private:
#ifdef Q_OS_MACOS
Q_CORE_EXPORT bool qt_mac_applicationIsInDarkMode();
Q_CORE_EXPORT bool qt_mac_runningUnderRosetta();
+Q_CORE_EXPORT std::optional<uint32_t> qt_mac_sipConfiguration();
+#ifdef QT_BUILD_INTERNAL
+Q_AUTOTEST_EXPORT void qt_mac_ensureResponsible();
+#endif
#endif
#ifndef QT_NO_DEBUG_STREAM
@@ -197,15 +198,18 @@ Q_CORE_EXPORT QDebug operator<<(QDebug debug, const QCFString &string);
#endif
Q_CORE_EXPORT bool qt_apple_isApplicationExtension();
+
+#if !defined(QT_BOOTSTRAPPED)
Q_CORE_EXPORT bool qt_apple_isSandboxed();
-#if !defined(QT_BOOTSTRAPPED) && defined(__OBJC__)
+#if defined(__OBJC__)
QT_END_NAMESPACE
-@interface NSObject (QtSandboxHelpers)
+@interface NSObject (QtExtras)
- (id)qt_valueForPrivateKey:(NSString *)key;
@end
QT_BEGIN_NAMESPACE
#endif
+#endif // !QT_BOOTSTRAPPED
#if !defined(QT_BOOTSTRAPPED) && !defined(Q_OS_WATCHOS)
QT_END_NAMESPACE
@@ -232,9 +236,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);
@@ -262,7 +269,7 @@ public:
Q_DISABLE_COPY(QAppleLogActivity)
QAppleLogActivity(QAppleLogActivity &&other)
- : activity(qExchange(other.activity, nullptr)), state(other.state)
+ : activity(std::exchange(other.activity, nullptr)), state(other.state)
{
}
@@ -288,8 +295,8 @@ public:
void swap(QAppleLogActivity &other)
{
- qSwap(activity, other.activity);
- qSwap(state, other.state);
+ activity.swap(other.activity);
+ std::swap(state, other.state);
}
private:
@@ -337,7 +344,7 @@ public:
QMacNotificationObserver(const QMacNotificationObserver &other) = delete;
QMacNotificationObserver(QMacNotificationObserver &&other)
- : observer(qExchange(other.observer, nullptr))
+ : observer(std::exchange(other.observer, nullptr))
{
}
@@ -346,7 +353,7 @@ public:
void swap(QMacNotificationObserver &other) noexcept
{
- qSwap(observer, other.observer);
+ qt_ptr_swap(observer, other.observer);
}
void remove();
@@ -396,9 +403,9 @@ public:
void swap(QMacKeyValueObserver &other) noexcept
{
- qSwap(object, other.object);
- qSwap(keyPath, other.keyPath);
- qSwap(callback, other.callback);
+ qt_ptr_swap(object, other.object);
+ qt_ptr_swap(keyPath, other.keyPath);
+ callback.swap(other.callback);
}
private:
@@ -429,7 +436,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();
@@ -437,6 +444,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 ff0c957770..6861251bc2 100644
--- a/src/corelib/kernel/qcore_unix.cpp
+++ b/src/corelib/kernel/qcore_unix.cpp
@@ -1,46 +1,10 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Copyright (C) 2016 Intel Corporation.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtCore module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// Copyright (C) 2016 Intel Corporation.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include <QtCore/private/qglobal_p.h>
+#include <QtCore/qbasicatomic.h>
#include "qcore_unix_p.h"
-#include "qelapsedtimer.h"
#include <stdlib.h>
@@ -50,12 +14,27 @@
# include <unistd.h>
#endif
-#ifdef Q_OS_MAC
+#ifdef Q_OS_DARWIN
#include <mach/mach_time.h>
#endif
QT_BEGIN_NAMESPACE
+void qt_ignore_sigpipe() noexcept // noexcept: sigaction(2) is not a Posix Cancellation Point
+{
+ // Set to ignore SIGPIPE once only.
+ Q_CONSTINIT static QBasicAtomicInt atom = Q_BASIC_ATOMIC_INITIALIZER(0);
+ if (!atom.loadRelaxed()) {
+ // More than one thread could turn off SIGPIPE at the same time
+ // But that's acceptable because they all would be doing the same
+ // action
+ struct sigaction noaction = {};
+ noaction.sa_handler = SIG_IGN;
+ ::sigaction(SIGPIPE, &noaction, nullptr);
+ atom.storeRelaxed(1);
+ }
+}
+
QByteArray qt_readlink(const char *path)
{
#ifndef PATH_MAX
@@ -105,23 +84,15 @@ int qt_open64(const char *pathname, int flags, mode_t mode)
# define ppoll pollts
#endif
-static inline bool time_update(struct timespec *tv, const struct timespec &start,
- const struct timespec &timeout)
-{
- // clock source is (hopefully) monotonic, so we can recalculate how much timeout is left;
- // if it isn't monotonic, we'll simply hope that it hasn't jumped, because we have no alternative
- struct timespec now = qt_gettime();
- *tv = timeout + start - now;
- return tv->tv_sec >= 0;
-}
-
-#if QT_CONFIG(poll_poll)
+[[maybe_unused]]
static inline int timespecToMillisecs(const struct timespec *ts)
{
- return (ts == NULL) ? -1 :
- (ts->tv_sec * 1000) + (ts->tv_nsec / 1000000);
+ using namespace std::chrono;
+ if (!ts)
+ return -1;
+ auto ms = ceil<milliseconds>(timespecToChrono<nanoseconds>(*ts));
+ return int(ms.count());
}
-#endif
// defined in qpoll.cpp
int qt_poll(struct pollfd *fds, nfds_t nfds, const struct timespec *timeout_ts);
@@ -147,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 abe9bb3738..fd834cb2d9 100644
--- a/src/corelib/kernel/qcore_unix_p.h
+++ b/src/corelib/kernel/qcore_unix_p.h
@@ -1,42 +1,6 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Copyright (C) 2016 Intel Corporation.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtCore module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// Copyright (C) 2016 Intel Corporation.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QCORE_UNIX_P_H
#define QCORE_UNIX_P_H
@@ -54,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"
@@ -75,14 +39,11 @@
# include <selectLib.h>
#endif
+#include <chrono>
#include <sys/wait.h>
#include <errno.h>
#include <fcntl.h>
-#if !defined(QT_POSIX_IPC) && !defined(QT_NO_SHAREDMEMORY) && !defined(Q_OS_ANDROID)
-# include <sys/ipc.h>
-#endif
-
#if defined(Q_OS_VXWORKS)
# include <ioLib.h>
#endif
@@ -95,7 +56,7 @@
struct sockaddr;
-#define EINTR_LOOP(var, cmd) \
+#define QT_EINTR_LOOP(var, cmd) \
do { \
var = cmd; \
} while (var == -1 && errno == EINTR)
@@ -104,53 +65,78 @@ QT_BEGIN_NAMESPACE
Q_DECLARE_TYPEINFO(pollfd, Q_PRIMITIVE_TYPE);
+static constexpr auto OneSecAsNsecs = std::chrono::nanoseconds(std::chrono::seconds{ 1 }).count();
+
+inline timespec durationToTimespec(std::chrono::nanoseconds timeout) noexcept
+{
+ using namespace std::chrono;
+ const seconds secs = duration_cast<seconds>(timeout);
+ const nanoseconds frac = timeout - secs;
+ struct timespec ts;
+ ts.tv_sec = secs.count();
+ ts.tv_nsec = frac.count();
+ return ts;
+}
+
+template <typename Duration>
+inline Duration timespecToChrono(timespec ts) noexcept
+{
+ using namespace std::chrono;
+ return duration_cast<Duration>(seconds{ts.tv_sec} + nanoseconds{ts.tv_nsec});
+}
+
+inline std::chrono::milliseconds timespecToChronoMs(timespec ts) noexcept
+{
+ return timespecToChrono<std::chrono::milliseconds>(ts);
+}
+
// Internal operator functions for timespecs
-inline timespec &normalizedTimespec(timespec &t)
+constexpr inline timespec &normalizedTimespec(timespec &t)
{
- while (t.tv_nsec >= 1000000000) {
+ while (t.tv_nsec >= OneSecAsNsecs) {
++t.tv_sec;
- t.tv_nsec -= 1000000000;
+ t.tv_nsec -= OneSecAsNsecs;
}
while (t.tv_nsec < 0) {
--t.tv_sec;
- t.tv_nsec += 1000000000;
+ t.tv_nsec += OneSecAsNsecs;
}
return t;
}
-inline bool operator<(const timespec &t1, const timespec &t2)
+constexpr inline bool operator<(const timespec &t1, const timespec &t2)
{ return t1.tv_sec < t2.tv_sec || (t1.tv_sec == t2.tv_sec && t1.tv_nsec < t2.tv_nsec); }
-inline bool operator==(const timespec &t1, const timespec &t2)
+constexpr inline bool operator==(const timespec &t1, const timespec &t2)
{ return t1.tv_sec == t2.tv_sec && t1.tv_nsec == t2.tv_nsec; }
-inline bool operator!=(const timespec &t1, const timespec &t2)
+constexpr inline bool operator!=(const timespec &t1, const timespec &t2)
{ return !(t1 == t2); }
-inline timespec &operator+=(timespec &t1, const timespec &t2)
+constexpr inline timespec &operator+=(timespec &t1, const timespec &t2)
{
t1.tv_sec += t2.tv_sec;
t1.tv_nsec += t2.tv_nsec;
return normalizedTimespec(t1);
}
-inline timespec operator+(const timespec &t1, const timespec &t2)
+constexpr inline timespec operator+(const timespec &t1, const timespec &t2)
{
- timespec tmp;
+ timespec tmp = {};
tmp.tv_sec = t1.tv_sec + t2.tv_sec;
tmp.tv_nsec = t1.tv_nsec + t2.tv_nsec;
return normalizedTimespec(tmp);
}
-inline timespec operator-(const timespec &t1, const timespec &t2)
+constexpr inline timespec operator-(const timespec &t1, const timespec &t2)
{
- timespec tmp;
+ timespec tmp = {};
tmp.tv_sec = t1.tv_sec - (t2.tv_sec - 1);
- tmp.tv_nsec = t1.tv_nsec - (t2.tv_nsec + 1000000000);
+ tmp.tv_nsec = t1.tv_nsec - (t2.tv_nsec + OneSecAsNsecs);
return normalizedTimespec(tmp);
}
-inline timespec operator*(const timespec &t1, int mul)
+constexpr inline timespec operator*(const timespec &t1, int mul)
{
- timespec tmp;
+ timespec tmp = {};
tmp.tv_sec = t1.tv_sec * mul;
tmp.tv_nsec = t1.tv_nsec * mul;
return normalizedTimespec(tmp);
}
-inline timeval timespecToTimeval(const timespec &ts)
+inline timeval timespecToTimeval(timespec ts)
{
timeval tv;
tv.tv_sec = ts.tv_sec;
@@ -158,23 +144,44 @@ inline timeval timespecToTimeval(const timespec &ts)
return tv;
}
+inline timespec &operator+=(timespec &t1, std::chrono::milliseconds msecs)
+{
+ t1 += durationToTimespec(msecs);
+ return t1;
+}
-inline void qt_ignore_sigpipe()
+inline timespec &operator+=(timespec &t1, int ms)
{
- // Set to ignore SIGPIPE once only.
- static QBasicAtomicInt atom = Q_BASIC_ATOMIC_INITIALIZER(0);
- if (!atom.loadRelaxed()) {
- // More than one thread could turn off SIGPIPE at the same time
- // But that's acceptable because they all would be doing the same
- // action
- struct sigaction noaction;
- memset(&noaction, 0, sizeof(noaction));
- noaction.sa_handler = SIG_IGN;
- ::sigaction(SIGPIPE, &noaction, nullptr);
- atom.storeRelaxed(1);
+ t1 += std::chrono::milliseconds{ms};
+ return t1;
+}
+
+inline timespec operator+(const timespec &t1, std::chrono::milliseconds msecs)
+{
+ timespec tmp = t1;
+ tmp += msecs;
+ return tmp;
+}
+
+inline timespec operator+(const timespec &t1, int ms)
+{
+ return t1 + std::chrono::milliseconds{ms};
+}
+
+inline timespec qAbsTimespec(timespec ts)
+{
+ if (ts.tv_sec < 0) {
+ ts.tv_sec = -ts.tv_sec - 1;
+ ts.tv_nsec -= OneSecAsNsecs;
+ }
+ if (ts.tv_sec == 0 && ts.tv_nsec < 0) {
+ ts.tv_nsec = -ts.tv_nsec;
}
+ return normalizedTimespec(ts);
}
+Q_CORE_EXPORT void qt_ignore_sigpipe() noexcept;
+
#if defined(Q_PROCESSOR_X86_32) && defined(__GLIBC__)
# if !__GLIBC_PREREQ(2, 22)
Q_CORE_EXPORT int qt_open64(const char *pathname, int flags, mode_t);
@@ -183,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)
@@ -191,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)
@@ -263,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;
@@ -279,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
@@ -288,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
@@ -303,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
@@ -315,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)
@@ -345,9 +362,6 @@ static inline pid_t qt_safe_waitpid(pid_t pid, int *status, int options)
# define _POSIX_MONOTONIC_CLOCK -1
#endif
-// in qelapsedtimer_mac.cpp or qtimestamp_unix.cpp
-timespec qt_gettime() noexcept;
-void qt_nanosleep(timespec amount);
QByteArray qt_readlink(const char *path);
/* non-static */
@@ -365,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
new file mode 100644
index 0000000000..fb12ae50c3
--- /dev/null
+++ b/src/corelib/kernel/qcore_wasm.cpp
@@ -0,0 +1,97 @@
+// 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
+
+#include <QtCore/qrect.h>
+
+#include <emscripten/val.h>
+
+#if !defined(Q_OS_WASM)
+#error This is a wasm-only file.
+#endif // !defined(Q_OS_WASM)
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ Converts the DOMRect (https://www.w3.org/TR/geometry-1/) \a domRect to QRectF. The behavior is
+ undefined if the provided parameter is not a DOMRect.
+
+ \since 6.5
+ \ingroup platform-type-conversions
+
+ \sa toDOMRect()
+*/
+QRectF QRectF::fromDOMRect(emscripten::val domRect)
+{
+ Q_ASSERT_X(domRect["constructor"]["name"].as<std::string>() == "DOMRect", Q_FUNC_INFO,
+ "Passed object is not a DOMRect");
+
+ return QRectF(domRect["left"].as<qreal>(), domRect["top"].as<qreal>(),
+ domRect["width"].as<qreal>(), domRect["height"].as<qreal>());
+}
+
+/*!
+ Converts this object to a DOMRect (https://www.w3.org/TR/geometry-1/).
+
+ \since 6.5
+ \ingroup platform-type-conversions
+
+ \sa fromDOMRect()
+*/
+emscripten::val QRectF::toDOMRect() const
+{
+ return emscripten::val::global("DOMRect").new_(left(), top(), width(), height());
+}
+
+/*!
+ Converts the \l {https://262.ecma-international.org/#sec-string-object}{ECMAScript string} \a
+ jsString to QString. Behavior is undefined if the provided parameter is not a string.
+
+ \since 6.6
+ \ingroup platform-type-conversions
+
+ \sa toEcmaString()
+*/
+QString QString::fromEcmaString(emscripten::val jsString)
+{
+ Q_ASSERT_X(jsString.isString(), Q_FUNC_INFO, "Passed object is not a string");
+
+ const double length = jsString["length"].as<double>();
+
+ Q_ASSERT_X((double(uint64_t(length)) != double(uint64_t(length) - 1)
+ && double(uint64_t(length)) != double(uint64_t(length) + 1))
+ || !std::numeric_limits<double>::is_iec559,
+ Q_FUNC_INFO, "The floating-point length cannot precisely represent an integer");
+
+ constexpr int zeroTerminatorLength = 1;
+ const auto lengthOfUtf16 = (length + zeroTerminatorLength) * 2;
+
+ Q_ASSERT_X((double(uint64_t(lengthOfUtf16)) != double(uint64_t(lengthOfUtf16) - 1)
+ && double(uint64_t(lengthOfUtf16)) != double(uint64_t(lengthOfUtf16) + 1))
+ || !std::numeric_limits<double>::is_iec559,
+ Q_FUNC_INFO,
+ "The floating-point lengthOfUtf16 cannot precisely represent an integer");
+
+ const QString result(uint64_t(length), Qt::Uninitialized);
+
+ static const emscripten::val stringToUTF16(emscripten::val::module_property("stringToUTF16"));
+ stringToUTF16(jsString, emscripten::val(quintptr(result.data())),
+ emscripten::val(lengthOfUtf16));
+ return result;
+}
+
+/*!
+ Converts this object to an
+ \l {https://262.ecma-international.org/#sec-string-object}{ECMAScript string}.
+
+ \since 6.6
+ \ingroup platform-type-conversions
+
+ \sa fromEcmaString()
+*/
+emscripten::val QString::toEcmaString() const
+{
+ static const emscripten::val UTF16ToString(emscripten::val::module_property("UTF16ToString"));
+ return UTF16ToString(emscripten::val(quintptr(utf16())));
+}
+
+QT_END_NAMESPACE
diff --git a/src/corelib/kernel/qcoreapplication.cpp b/src/corelib/kernel/qcoreapplication.cpp
index c980e70dbf..13108cecea 100644
--- a/src/corelib/kernel/qcoreapplication.cpp
+++ b/src/corelib/kernel/qcoreapplication.cpp
@@ -1,42 +1,6 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Copyright (C) 2021 Intel Corporation.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtCore module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// Copyright (C) 2021 Intel Corporation.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qcoreapplication.h"
#include "qcoreapplication_p.h"
@@ -44,6 +8,7 @@
#ifndef QT_NO_QOBJECT
#include "qabstracteventdispatcher.h"
#include "qcoreevent.h"
+#include "qcoreevent_p.h"
#include "qeventloop.h"
#endif
#include "qmetaobject.h"
@@ -67,10 +32,11 @@
#include <private/qthread_p.h>
#if QT_CONFIG(thread)
#include <qthreadpool.h>
+#include <private/qthreadpool_p.h>
#endif
#endif
-#include <qelapsedtimer.h>
#include <qlibraryinfo.h>
+#include <qpointer.h>
#include <qvarlengtharray.h>
#include <private/qfactoryloader_p.h>
#include <private/qfunctions_p.h>
@@ -78,6 +44,10 @@
#include <private/qlocking_p.h>
#include <private/qhooks_p.h>
+#if QT_CONFIG(permissions)
+#include <private/qpermissions_p.h>
+#endif
+
#ifndef QT_NO_QOBJECT
#if defined(Q_OS_UNIX)
# if defined(Q_OS_DARWIN)
@@ -87,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"
@@ -98,7 +70,7 @@
#include <QtCore/qjniobject.h>
#endif
-#ifdef Q_OS_MAC
+#ifdef Q_OS_DARWIN
# include "qcore_mac_p.h"
#endif
@@ -134,20 +106,35 @@
#endif
#include <algorithm>
+#include <memory>
+#include <string>
QT_BEGIN_NAMESPACE
-#if defined(Q_OS_WIN) || defined(Q_OS_MAC)
-extern QString qAppFileName();
+#ifndef QT_NO_QOBJECT
+Q_LOGGING_CATEGORY(lcDeleteLater, "qt.core.qobject.deletelater")
#endif
-#if QT_VERSION >= QT_VERSION_CHECK(7, 0, 0)
-# error "Bump QCoreApplicatoinPrivate::app_compile_version to QT_VERSION_CHECK(7, 0, 0)"
+using namespace Qt::StringLiterals;
+
+Q_TRACE_PREFIX(qtcore,
+ "#include <qcoreevent.h>"
+);
+Q_TRACE_METADATA(qtcore, "ENUM { AUTO, RANGE User ... MaxUser } QEvent::Type;");
+Q_TRACE_POINT(qtcore, QCoreApplication_postEvent_entry, QObject *receiver, QEvent *event, QEvent::Type type);
+Q_TRACE_POINT(qtcore, QCoreApplication_postEvent_exit);
+Q_TRACE_POINT(qtcore, QCoreApplication_postEvent_event_compressed, QObject *receiver, QEvent *event);
+Q_TRACE_POINT(qtcore, QCoreApplication_postEvent_event_posted, QObject *receiver, QEvent *event, QEvent::Type type);
+Q_TRACE_POINT(qtcore, QCoreApplication_sendEvent, QObject *receiver, QEvent *event, QEvent::Type type);
+Q_TRACE_POINT(qtcore, QCoreApplication_sendSpontaneousEvent, QObject *receiver, QEvent *event, QEvent::Type type);
+Q_TRACE_POINT(qtcore, QCoreApplication_notify_entry, QObject *receiver, QEvent *event, QEvent::Type type);
+Q_TRACE_POINT(qtcore, QCoreApplication_notify_exit, bool consumed, bool filtered);
+
+#if defined(Q_OS_WIN) || defined(Q_OS_DARWIN)
+extern QString qAppFileName();
#endif
-// We don't know exactly, but it's at least 6.0.0:
-int QCoreApplicationPrivate::app_compile_version = QT_VERSION_CHECK(6, 0, 0);
-bool QCoreApplicationPrivate::setuidAllowed = false;
+Q_CONSTINIT bool QCoreApplicationPrivate::setuidAllowed = false;
#if !defined(Q_OS_WIN)
#ifdef Q_OS_DARWIN
@@ -204,7 +191,7 @@ QString QCoreApplicationPrivate::appVersion() const
}
#endif // !Q_OS_WIN
-QString *QCoreApplicationPrivate::cachedApplicationFilePath = nullptr;
+Q_CONSTINIT QString *QCoreApplicationPrivate::cachedApplicationFilePath = nullptr;
bool QCoreApplicationPrivate::checkInstance(const char *function)
{
@@ -254,7 +241,11 @@ void QCoreApplicationPrivate::processCommandLineArguments()
// Support for introspection
-extern "C" void Q_CORE_EXPORT qt_startup_hook()
+extern "C" void
+#ifdef QT_SHARED
+Q_DECL_EXPORT_OVERRIDABLE
+#endif
+qt_startup_hook()
{
}
@@ -262,8 +253,8 @@ typedef QList<QtStartUpFunction> QStartUpFuncList;
Q_GLOBAL_STATIC(QStartUpFuncList, preRList)
typedef QList<QtCleanUpFunction> QVFuncList;
Q_GLOBAL_STATIC(QVFuncList, postRList)
-static QBasicMutex globalRoutinesMutex;
-static bool preRoutinesCalled = false;
+Q_CONSTINIT static QBasicMutex globalRoutinesMutex;
+Q_CONSTINIT static bool preRoutinesCalled = false;
/*!
\internal
@@ -314,16 +305,16 @@ 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 (int i = 0; i < list.count(); ++i)
- list.at(i)();
+ return *preRList;
+ }();
+
+ for (QtStartUpFunction f : list)
+ f();
}
void Q_CORE_EXPORT qt_call_post_routines()
@@ -341,7 +332,7 @@ void Q_CORE_EXPORT qt_call_post_routines()
if (list.isEmpty())
break;
- for (QtCleanUpFunction f : qAsConst(list))
+ for (QtCleanUpFunction f : std::as_const(list))
f();
}
}
@@ -350,22 +341,22 @@ void Q_CORE_EXPORT qt_call_post_routines()
#ifndef QT_NO_QOBJECT
// app starting up if false
-bool QCoreApplicationPrivate::is_app_running = false;
+Q_CONSTINIT bool QCoreApplicationPrivate::is_app_running = false;
// app closing down if true
-bool QCoreApplicationPrivate::is_app_closing = false;
+Q_CONSTINIT bool QCoreApplicationPrivate::is_app_closing = false;
-Q_CORE_EXPORT uint qGlobalPostedEventsCount()
+qsizetype qGlobalPostedEventsCount()
{
- QThreadData *currentThreadData = QThreadData::current();
- return currentThreadData->postEventList.size() - currentThreadData->postEventList.startOffset;
+ const QPostEventList &l = QThreadData::current()->postEventList;
+ return l.size() - l.startOffset;
}
-QAbstractEventDispatcher *QCoreApplicationPrivate::eventDispatcher = nullptr;
+Q_CONSTINIT QAbstractEventDispatcher *QCoreApplicationPrivate::eventDispatcher = nullptr;
#endif // QT_NO_QOBJECT
-QCoreApplication *QCoreApplication::self = nullptr;
-uint QCoreApplicationPrivate::attribs =
+Q_CONSTINIT QCoreApplication *QCoreApplication::self = nullptr;
+Q_CONSTINIT uint QCoreApplicationPrivate::attribs =
(1 << Qt::AA_SynthesizeMouseForUnhandledTouchEvents) |
(1 << Qt::AA_SynthesizeMouseForUnhandledTabletEvents);
@@ -391,8 +382,8 @@ struct QCoreApplicationData {
bool applicationVersionSet; // true if setApplicationVersion was called
#if QT_CONFIG(library)
- QScopedPointer<QStringList> app_libpaths;
- QScopedPointer<QStringList> manual_libpaths;
+ std::unique_ptr<QStringList> app_libpaths;
+ std::unique_ptr<QStringList> manual_libpaths;
#endif
};
@@ -400,7 +391,7 @@ struct QCoreApplicationData {
Q_GLOBAL_STATIC(QCoreApplicationData, coreappdata)
#ifndef QT_NO_QOBJECT
-static bool quitLockEnabled = true;
+Q_CONSTINIT static bool quitLockEnabled = true;
#endif
#if defined(Q_OS_WIN)
@@ -432,7 +423,7 @@ static inline bool contains(int argc, char **argv, const char *needle)
}
#endif // Q_OS_WIN
-QCoreApplicationPrivate::QCoreApplicationPrivate(int &aargc, char **aargv, uint flags)
+QCoreApplicationPrivate::QCoreApplicationPrivate(int &aargc, char **aargv)
:
#ifndef QT_NO_QOBJECT
QObjectPrivate(),
@@ -452,7 +443,6 @@ QCoreApplicationPrivate::QCoreApplicationPrivate(int &aargc, char **aargv, uint
, q_ptr(nullptr)
#endif
{
- app_compile_version = flags & 0xffffff;
static const char *const empty = "";
if (argc == 0 || argv == nullptr) {
argc = 0;
@@ -487,6 +477,8 @@ QCoreApplicationPrivate::~QCoreApplicationPrivate()
#endif
#if defined(Q_OS_WIN)
delete [] origArgv;
+ if (consoleAllocated)
+ FreeConsole();
#endif
QCoreApplicationPrivate::clearApplicationFilePath();
}
@@ -505,8 +497,7 @@ void QCoreApplicationPrivate::cleanupThreadData()
// need to clear the state of the mainData, just in case a new QCoreApplication comes along.
const auto locker = qt_scoped_lock(thisThreadData->postEventList.mutex);
- for (int i = 0; i < thisThreadData->postEventList.size(); ++i) {
- const QPostEvent &pe = thisThreadData->postEventList.at(i);
+ for (const QPostEvent &pe : std::as_const(thisThreadData->postEventList)) {
if (pe.event) {
--pe.receiver->d_func()->postedEvents;
pe.event->m_posted = false;
@@ -533,7 +524,8 @@ void QCoreApplicationPrivate::eventDispatcherReady()
{
}
-QBasicAtomicPointer<QThread> QCoreApplicationPrivate::theMainThread = Q_BASIC_ATOMIC_INITIALIZER(nullptr);
+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);
@@ -568,11 +560,11 @@ void QCoreApplicationPrivate::checkReceiverThread(QObject *receiver)
void QCoreApplicationPrivate::appendApplicationPathToLibraryPaths()
{
#if QT_CONFIG(library)
- QStringList *app_libpaths = coreappdata()->app_libpaths.data();
+ QStringList *app_libpaths = coreappdata()->app_libpaths.get();
if (!app_libpaths)
coreappdata()->app_libpaths.reset(app_libpaths = new QStringList);
QString app_location = QCoreApplication::applicationFilePath();
- app_location.truncate(app_location.lastIndexOf(QLatin1Char('/')));
+ app_location.truncate(app_location.lastIndexOf(u'/'));
app_location = QDir(app_location).canonicalPath();
if (QFile::exists(app_location) && !app_libpaths->contains(app_location))
app_libpaths->append(app_location);
@@ -586,50 +578,117 @@ QString qAppName()
return QCoreApplication::instance()->d_func()->appName();
}
+void QCoreApplicationPrivate::initConsole()
+{
+#ifdef Q_OS_WINDOWS
+ const QString env = qEnvironmentVariable("QT_WIN_DEBUG_CONSOLE");
+ if (env.isEmpty())
+ return;
+ if (env.compare(u"new"_s, Qt::CaseInsensitive) == 0) {
+ if (AllocConsole() == FALSE)
+ return;
+ consoleAllocated = true;
+ } else if (env.compare(u"attach"_s, Qt::CaseInsensitive) == 0) {
+ // If the calling process is already attached to a console,
+ // the error code returned is ERROR_ACCESS_DENIED.
+ if (!::AttachConsole(ATTACH_PARENT_PROCESS) && ::GetLastError() != ERROR_ACCESS_DENIED)
+ return;
+ } else {
+ // Unknown input, don't make any decision for the user.
+ return;
+ }
+ // The std{in,out,err} handles are read-only, so we need to pass in dummies.
+ FILE *in = nullptr;
+ FILE *out = nullptr;
+ FILE *err = nullptr;
+ freopen_s(&in, "CONIN$", "r", stdin);
+ freopen_s(&out, "CONOUT$", "w", stdout);
+ freopen_s(&err, "CONOUT$", "w", stderr);
+ // However, things wouldn't work if the runtime did not preserve the pointers.
+ Q_ASSERT(in == stdin);
+ Q_ASSERT(out == stdout);
+ Q_ASSERT(err == stderr);
+#endif
+}
+
void QCoreApplicationPrivate::initLocale()
{
-#if defined(Q_OS_UNIX) && !defined(QT_BOOTSTRAPPED)
- static bool qt_locale_initialized = false;
+#if defined(QT_BOOTSTRAPPED)
+ // Don't try to control bootstrap library locale or encoding.
+#elif defined(Q_OS_UNIX)
+ Q_CONSTINIT static bool qt_locale_initialized = false;
if (qt_locale_initialized)
return;
qt_locale_initialized = true;
-#ifdef Q_OS_INTEGRITY
+ // By default the portable "C"/POSIX locale is selected and active.
+ // Apply the locale from the environment, via setlocale(), which will
+ // read LC_ALL, LC_<category>, and LANG, in order (for each category).
+ setlocale(LC_ALL, "");
+
+ // Next, let's ensure that LC_CTYPE is UTF-8, since QStringConverter's
+ // QLocal8Bit hard-codes this, and we need to be consistent.
+# if defined(Q_OS_INTEGRITY)
setlocale(LC_CTYPE, "UTF-8");
-#else
- // Android's Bionic didn't get nl_langinfo until NDK 15 (Android 8.0),
- // which is too new for Qt, so we just assume it's always UTF-8.
- auto nl_langinfo = [](int) { return "UTF-8"; };
-
- const char *locale = setlocale(LC_ALL, "");
- const char *codec = nl_langinfo(CODESET);
- if (Q_UNLIKELY(strcmp(codec, "UTF-8") != 0 && strcmp(codec, "utf8") != 0)) {
- QByteArray oldLocale = locale;
- QByteArray newLocale = setlocale(LC_CTYPE, nullptr);
- if (int dot = newLocale.indexOf('.'); dot != -1)
+# elif defined(Q_OS_QNX)
+ // QNX has no nl_langinfo, so we can't check.
+ // FIXME: Shouldn't we still setlocale("UTF-8")?
+# elif defined(Q_OS_ANDROID) && __ANDROID_API__ < __ANDROID_API_O__
+ // Android 6 still lacks nl_langinfo(), so we can't check.
+ // FIXME: Shouldn't we still setlocale("UTF-8")?
+# elif defined(Q_OS_VXWORKS)
+ // VxWorks has no nl_langinfo, so we can't check.
+# else
+ // std::string's SSO usually saves this the need to allocate:
+ const std::string oldEncoding = nl_langinfo(CODESET);
+ if (!Q_LIKELY(qstricmp(oldEncoding.data(), "UTF-8") == 0
+ || qstricmp(oldEncoding.data(), "utf8") == 0)) {
+ const QByteArray oldLocale = setlocale(LC_ALL, nullptr);
+ QByteArray newLocale;
+ bool warnOnOverride = true;
+# if defined(Q_OS_DARWIN)
+ // Don't warn unless the char encoding has been changed from the
+ // default "C" encoding, or the user touched any of the locale
+ // environment variables to force the "C" char encoding.
+ warnOnOverride = qstrcmp(setlocale(LC_CTYPE, nullptr), "C") != 0
+ || getenv("LC_ALL") || getenv("LC_CTYPE") || getenv("LANG");
+
+ // No need to try language or region specific CTYPEs, as they
+ // all point back to the same generic UTF-8 CTYPE.
+ newLocale = setlocale(LC_CTYPE, "UTF-8");
+# else
+ newLocale = setlocale(LC_CTYPE, nullptr);
+ if (qsizetype dot = newLocale.indexOf('.'); dot != -1)
newLocale.truncate(dot); // remove encoding, if any
- if (int at = newLocale.indexOf('@'); at != -1)
+ if (qsizetype at = newLocale.indexOf('@'); at != -1)
newLocale.truncate(at); // remove variant, as the old de_DE@euro
newLocale += ".UTF-8";
newLocale = setlocale(LC_CTYPE, newLocale);
- // if locale doesn't exist, try some fallbacks
-# ifdef Q_OS_DARWIN
- if (newLocale.isEmpty())
- newLocale = setlocale(LC_CTYPE, "UTF-8");
-# endif
+ // If that locale doesn't exist, try some fallbacks:
if (newLocale.isEmpty())
newLocale = setlocale(LC_CTYPE, "C.UTF-8");
if (newLocale.isEmpty())
newLocale = setlocale(LC_CTYPE, "C.utf8");
-
- qWarning("Detected system locale encoding (%s, locale \"%s\") is not UTF-8.\n"
- "Qt shall use a UTF-8 locale (\"%s\") instead. If this causes problems,\n"
- "reconfigure your locale. See the locale(1) manual for more information.",
- codec, oldLocale.constData(), newLocale.constData());
+# endif
+
+ if (newLocale.isEmpty()) {
+ // Failed to set a UTF-8 locale.
+ qWarning("Detected locale \"%s\" with character encoding \"%s\", which is not UTF-8.\n"
+ "Qt depends on a UTF-8 locale, but has failed to switch to one.\n"
+ "If this causes problems, reconfigure your locale. See the locale(1) manual\n"
+ "for more information.", oldLocale.constData(), oldEncoding.data());
+ } else if (warnOnOverride) {
+ // Let the user know we over-rode their configuration.
+ qWarning("Detected locale \"%s\" with character encoding \"%s\", which is not UTF-8.\n"
+ "Qt depends on a UTF-8 locale, and has switched to \"%s\" instead.\n"
+ "If this causes problems, reconfigure your locale. See the locale(1) manual\n"
+ "for more information.",
+ oldLocale.constData(), oldEncoding.data(), newLocale.constData());
+ }
}
-#endif
-#endif
+# endif // Platform choice
+#endif // Unix
}
@@ -705,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}
*/
/*!
@@ -748,13 +808,13 @@ QCoreApplication::QCoreApplication(QCoreApplicationPrivate &p)
*/
QCoreApplication::QCoreApplication(int &argc, char **argv
#ifndef Q_QDOC
- , int _internal
+ , int
#endif
)
#ifdef QT_NO_QOBJECT
- : d_ptr(new QCoreApplicationPrivate(argc, argv, _internal))
+ : d_ptr(new QCoreApplicationPrivate(argc, argv))
#else
- : QObject(*new QCoreApplicationPrivate(argc, argv, _internal))
+ : QObject(*new QCoreApplicationPrivate(argc, argv))
#endif
{
d_func()->q_ptr = this;
@@ -771,7 +831,7 @@ QCoreApplication::QCoreApplication(int &argc, char **argv
\value ApplicationFlags QT_VERSION
*/
-void QCoreApplicationPrivate::init()
+void Q_TRACE_INSTRUMENT(qtcore) QCoreApplicationPrivate::init()
{
Q_TRACE_SCOPE(QCoreApplicationPrivate_init);
@@ -781,6 +841,8 @@ void QCoreApplicationPrivate::init()
Q_Q(QCoreApplication);
+ initConsole();
+
initLocale();
Q_ASSERT_X(!QCoreApplication::self, "QCoreApplication", "there should be only one application object");
@@ -788,7 +850,11 @@ void QCoreApplicationPrivate::init()
#if QT_CONFIG(thread)
#ifdef Q_OS_WASM
- QThreadPrivate::idealThreadCount = emscripten::val::global("navigator")["hardwareConcurrency"].as<int>();
+ emscripten::val hardwareConcurrency = emscripten::val::global("navigator")["hardwareConcurrency"];
+ if (hardwareConcurrency.isUndefined())
+ QThreadPrivate::idealThreadCount = 2;
+ else
+ QThreadPrivate::idealThreadCount = hardwareConcurrency.as<int>();
#endif
#endif
@@ -811,8 +877,8 @@ void QCoreApplicationPrivate::init()
// Reset the lib paths, so that they will be recomputed, taking the availability of argv[0]
// into account. If necessary, recompute right away and replay the manual changes on top of the
// new lib paths.
- QStringList *appPaths = coreappdata()->app_libpaths.take();
- QStringList *manualPaths = coreappdata()->manual_libpaths.take();
+ QStringList *appPaths = coreappdata()->app_libpaths.release();
+ QStringList *manualPaths = coreappdata()->manual_libpaths.release();
if (appPaths) {
if (manualPaths) {
// Replay the delta. As paths can only be prepended to the front or removed from
@@ -820,7 +886,7 @@ void QCoreApplicationPrivate::init()
// have been removed. Once the original list is exhausted we know all the remaining
// items have been added.
QStringList newPaths(q->libraryPaths());
- for (int i = manualPaths->length(), j = appPaths->length(); i > 0 || j > 0; qt_noop()) {
+ for (qsizetype i = manualPaths->size(), j = appPaths->size(); i > 0 || j > 0; qt_noop()) {
if (--j < 0) {
newPaths.prepend((*manualPaths)[--i]);
} else if (--i < 0) {
@@ -890,8 +956,10 @@ QCoreApplication::~QCoreApplication()
#if QT_CONFIG(thread)
// Synchronize and stop the global thread pool threads.
QThreadPool *globalThreadPool = nullptr;
+ QThreadPool *guiThreadPool = nullptr;
QT_TRY {
globalThreadPool = QThreadPool::globalInstance();
+ guiThreadPool = QThreadPoolPrivate::qtGuiInstance();
} QT_CATCH (...) {
// swallow the exception, since destructors shouldn't throw
}
@@ -899,6 +967,10 @@ QCoreApplication::~QCoreApplication()
globalThreadPool->waitForDone();
delete globalThreadPool;
}
+ if (guiThreadPool) {
+ guiThreadPool->waitForDone();
+ delete guiThreadPool;
+ }
#endif
#ifndef QT_NO_QOBJECT
@@ -932,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)
{
@@ -952,7 +1027,6 @@ bool QCoreApplication::isSetuidAllowed()
return QCoreApplicationPrivate::setuidAllowed;
}
-
/*!
Sets the attribute \a attribute if \a on is true;
otherwise clears the attribute.
@@ -965,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
@@ -1040,7 +1118,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
@@ -1056,10 +1134,15 @@ bool QCoreApplication::notifyInternal2(QObject *receiver, QEvent *event)
// equivalent to QThreadData::current(), just without the function
// call overhead.
QObjectPrivate *d = receiver->d_func();
- QThreadData *threadData = d->threadData;
+ QThreadData *threadData = d->threadData.loadAcquire();
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);
}
@@ -1118,7 +1201,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.
@@ -1136,6 +1219,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;
@@ -1162,15 +1250,15 @@ static bool doNotify(QObject *receiver, QEvent *event)
bool QCoreApplicationPrivate::sendThroughApplicationEventFilters(QObject *receiver, QEvent *event)
{
// We can't access the application event filters outside of the main thread (race conditions)
- Q_ASSERT(receiver->d_func()->threadData.loadRelaxed()->thread.loadAcquire() == mainThread());
+ Q_ASSERT(receiver->d_func()->threadData.loadAcquire()->thread.loadRelaxed() == mainThread());
if (extraData) {
// application event filters are only called for objects in the GUI thread
- for (int i = 0; i < extraData->eventFilters.size(); ++i) {
+ for (qsizetype i = 0; i < extraData->eventFilters.size(); ++i) {
QObject *obj = extraData->eventFilters.at(i);
if (!obj)
continue;
- if (obj->d_func()->threadData != threadData) {
+ if (obj->d_func()->threadData.loadRelaxed() != threadData.loadRelaxed()) {
qWarning("QCoreApplication: Application event filter cannot be in a different thread.");
continue;
}
@@ -1183,12 +1271,14 @@ bool QCoreApplicationPrivate::sendThroughApplicationEventFilters(QObject *receiv
bool QCoreApplicationPrivate::sendThroughObjectEventFilters(QObject *receiver, QEvent *event)
{
- if (receiver != QCoreApplication::instance() && receiver->d_func()->extraData) {
- for (int i = 0; i < receiver->d_func()->extraData->eventFilters.size(); ++i) {
+ 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)
continue;
- if (obj->d_func()->threadData != receiver->d_func()->threadData) {
+ if (obj->d_func()->threadData.loadRelaxed() != receiver->d_func()->threadData.loadRelaxed()) {
qWarning("QCoreApplication: Object event filter cannot be in a different thread.");
continue;
}
@@ -1214,8 +1304,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;
@@ -1282,7 +1372,8 @@ bool QCoreApplication::closingDown()
\threadsafe
- \sa exec(), QTimer, QEventLoop::processEvents(), sendPostedEvents()
+ \sa exec(), QTimer, QChronoTimer, QEventLoop::processEvents(),
+ sendPostedEvents()
*/
void QCoreApplication::processEvents(QEventLoop::ProcessEventsFlags flags)
{
@@ -1293,12 +1384,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
@@ -1314,9 +1422,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
@@ -1324,10 +1432,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;
}
}
@@ -1345,10 +1452,10 @@ void QCoreApplication::processEvents(QEventLoop::ProcessEventsFlags flags, int m
main event loop receives events from the window system and
dispatches these to the application widgets.
- To make your application perform idle processing (by executing a
- special function whenever there are no pending events), use a
- QTimer with 0 timeout. More advanced idle processing schemes can
- be achieved using processEvents().
+ To make your application perform idle processing (by executing a special
+ function whenever there are no pending events), use a QChronoTimer
+ with 0ns timeout. More advanced idle processing schemes can be achieved
+ using processEvents().
We recommend that you connect clean-up code to the
\l{QCoreApplication::}{aboutToQuit()} signal, instead of putting it in
@@ -1367,7 +1474,7 @@ int QCoreApplication::exec()
if (!QCoreApplicationPrivate::checkInstance("exec"))
return -1;
- QThreadData *threadData = self->d_func()->threadData;
+ QThreadData *threadData = self->d_func()->threadData.loadAcquire();
if (threadData != QThreadData::current()) {
qWarning("%s::exec: Must be called from the main thread", self->metaObject()->className());
return -1;
@@ -1381,7 +1488,7 @@ int QCoreApplication::exec()
QEventLoop eventLoop;
self->d_func()->in_exec = true;
self->d_func()->aboutToQuitEmitted = false;
- int returnCode = eventLoop.exec();
+ int returnCode = eventLoop.exec(QEventLoop::ApplicationExec);
threadData->quitNow = false;
if (self)
@@ -1400,9 +1507,8 @@ void QCoreApplicationPrivate::execCleanup()
{
threadData.loadRelaxed()->quitNow = false;
in_exec = false;
- if (!aboutToQuitEmitted)
- emit q_func()->aboutToQuit(QCoreApplication::QPrivateSignal());
- aboutToQuitEmitted = true;
+
+ qCDebug(lcDeleteLater) << "Sending deferred delete events as part of exec cleanup";
QCoreApplication::sendPostedEvents(nullptr, QEvent::DeferredDelete);
}
@@ -1441,9 +1547,14 @@ void QCoreApplication::exit(int returnCode)
{
if (!self)
return;
- QThreadData *data = self->d_func()->threadData.loadRelaxed();
+ QCoreApplicationPrivate *d = self->d_func();
+ if (!d->aboutToQuitEmitted) {
+ emit self->aboutToQuit(QCoreApplication::QPrivateSignal());
+ d->aboutToQuitEmitted = true;
+ }
+ QThreadData *data = d->threadData.loadRelaxed();
data->quitNow = true;
- for (int i = 0; i < data->eventLoops.size(); ++i) {
+ for (qsizetype i = 0; i < data->eventLoops.size(); ++i) {
QEventLoop *eventLoop = data->eventLoops.at(i);
eventLoop->exit(returnCode);
}
@@ -1581,37 +1692,12 @@ 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
- QScopedPointer<QEvent> eventDeleter(event);
+ std::unique_ptr<QEvent> eventDeleter(event);
Q_TRACE(QCoreApplication_postEvent_event_posted, receiver, event, event->type());
data->postEventList.addEvent(QPostEvent(receiver, event, priority));
- eventDeleter.take();
+ Q_UNUSED(eventDeleter.release());
event->m_posted = true;
++receiver->d_func()->postedEvents;
data->canWait = false;
@@ -1632,36 +1718,27 @@ 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 (int i=0; i<postedEvents->size(); ++i) {
- const QPostEvent &e = postedEvents->at(i);
- 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) {
- for (int i = 0; i < postedEvents->size(); ++i) {
- const QPostEvent &cur = postedEvents->at(i);
+ if (event->type() == QEvent::Quit && receiverPostedEvents > 0) {
+ for (const QPostEvent &cur : std::as_const(*postedEvents)) {
if (cur.receiver != receiver
|| cur.event == nullptr
|| cur.event->type() != event->type())
@@ -1710,7 +1787,7 @@ void QCoreApplicationPrivate::sendPostedEvents(QObject *receiver, int event_type
event_type = 0;
}
- if (receiver && receiver->d_func()->threadData != data) {
+ if (receiver && receiver->d_func()->threadData.loadRelaxed() != data) {
qWarning("QCoreApplication::sendPostedEvents: Cannot send "
"posted events for objects in another thread");
return;
@@ -1734,8 +1811,8 @@ void QCoreApplicationPrivate::sendPostedEvents(QObject *receiver, int event_type
// okay. here is the tricky loop. be careful about optimizing
// this, it looks the way it does for good reasons.
- int startOffset = data->postEventList.startOffset;
- int &i = (!event_type && !receiver) ? data->postEventList.startOffset : startOffset;
+ qsizetype startOffset = data->postEventList.startOffset;
+ qsizetype &i = (!event_type && !receiver) ? data->postEventList.startOffset : startOffset;
data->postEventList.insertionOffset = data->postEventList.size();
// Exception-safe cleaning up without the need for a try/catch block
@@ -1794,14 +1871,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
@@ -1818,6 +1918,8 @@ void QCoreApplicationPrivate::sendPostedEvents(QObject *receiver, int event_type
data->postEventList.addEvent(pe_copy);
}
continue;
+ } else {
+ qCDebug(lcDeleteLater) << "Sending deferred delete to" << pe.receiver;
}
}
@@ -1884,10 +1986,10 @@ void QCoreApplication::removePostedEvents(QObject *receiver, int eventType)
//we will collect all the posted events for the QObject
//and we'll delete after the mutex was unlocked
QVarLengthArray<QEvent*> events;
- int n = data->postEventList.size();
- int j = 0;
+ qsizetype n = data->postEventList.size();
+ qsizetype j = 0;
- for (int i = 0; i < n; ++i) {
+ for (qsizetype i = 0; i < n; ++i) {
const QPostEvent &pe = data->postEventList.at(i);
if ((!receiver || pe.receiver == receiver)
@@ -1945,8 +2047,7 @@ void QCoreApplicationPrivate::removePostedEvent(QEvent * event)
#endif
}
- for (int i = 0; i < data->postEventList.size(); ++i) {
- const QPostEvent & pe = data->postEventList.at(i);
+ for (const QPostEvent &pe : std::as_const(data->postEventList)) {
if (pe.event == event) {
#ifndef QT_NO_DEBUG
qWarning("QCoreApplication::removePostedEvent: Event of type %d deleted while posted to %s %s",
@@ -2051,6 +2152,9 @@ void QCoreApplication::quit()
if (!self)
return;
+ if (!self->d_func()->in_exec)
+ return;
+
self->d_func()->quit();
}
@@ -2078,6 +2182,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()
*/
@@ -2101,12 +2211,15 @@ void QCoreApplicationPrivate::quit()
to all toplevel widgets, where a reimplementation of changeEvent can
re-translate the user interface by passing user-visible strings via the
tr() function to the respective property setters. User-interface classes
- generated by Qt Designer provide a \c retranslateUi() function that can be
+ generated by \QD provide a \c retranslateUi() function that can be
called.
The function returns \c true on success and false on failure.
- \sa removeTranslator(), translate(), QTranslator::load(), {Dynamic Translation}
+ \note QCoreApplication does \e not take ownership of \a translationFile.
+
+ \sa removeTranslator(), translate(), QTranslator::load(),
+ {Writing Source Code for Translation#Prepare for Dynamic Language Changes}{Prepare for Dynamic Language Changes}
*/
bool QCoreApplication::installTranslator(QTranslator *translationFile)
@@ -2169,26 +2282,26 @@ bool QCoreApplication::removeTranslator(QTranslator *translationFile)
static void replacePercentN(QString *result, int n)
{
if (n >= 0) {
- int percentPos = 0;
- int len = 0;
- while ((percentPos = result->indexOf(QLatin1Char('%'), percentPos + len)) != -1) {
+ qsizetype percentPos = 0;
+ qsizetype len = 0;
+ while ((percentPos = result->indexOf(u'%', percentPos + len)) != -1) {
len = 1;
- if (percentPos + len == result->length())
+ if (percentPos + len == result->size())
break;
QString fmt;
- if (result->at(percentPos + len) == QLatin1Char('L')) {
+ if (result->at(percentPos + len) == u'L') {
++len;
- if (percentPos + len == result->length())
+ if (percentPos + len == result->size())
break;
- fmt = QLatin1String("%L1");
+ fmt = "%L1"_L1;
} else {
- fmt = QLatin1String("%1");
+ fmt = "%1"_L1;
}
- if (result->at(percentPos + len) == QLatin1Char('n')) {
+ if (result->at(percentPos + len) == u'n') {
fmt = fmt.arg(n);
++len;
result->replace(percentPos, len, fmt);
- len = fmt.length();
+ len = fmt.size();
}
}
}
@@ -2224,7 +2337,8 @@ static void replacePercentN(QString *result, int n)
This function is not virtual. You can use alternative translation
techniques by subclassing \l QTranslator.
- \sa QObject::tr(), installTranslator(), removeTranslator(), translate()
+ \sa QObject::tr(), installTranslator(), removeTranslator(),
+ {Internationalization and Translations}
*/
QString QCoreApplication::translate(const char *context, const char *sourceText,
const char *disambiguation, int n)
@@ -2280,7 +2394,7 @@ QString QCoreApplication::translate(const char *context, const char *sourceText,
Q_UNUSED(disambiguation);
QString ret = QString::fromUtf8(sourceText);
if (n >= 0)
- ret.replace(QLatin1String("%n"), QString::number(n));
+ ret.replace("%n"_L1, QString::number(n));
return ret;
}
@@ -2382,10 +2496,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]);
}
}
@@ -2396,13 +2510,13 @@ QString QCoreApplication::applicationFilePath()
if (absPath.isEmpty() && !arguments().isEmpty()) {
QString argv0 = QFile::decodeName(arguments().at(0).toLocal8Bit());
- if (!argv0.isEmpty() && argv0.at(0) == QLatin1Char('/')) {
+ if (!argv0.isEmpty() && argv0.at(0) == u'/') {
/*
If argv0 starts with a slash, it is already an absolute
file path.
*/
absPath = argv0;
- } else if (argv0.contains(QLatin1Char('/'))) {
+ } else if (argv0.contains(u'/')) {
/*
If argv0 contains one or more slashes, it is a file path
relative to the current directory.
@@ -2529,7 +2643,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
@@ -2569,7 +2683,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
@@ -2605,11 +2719,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()
*/
@@ -2692,6 +2810,155 @@ QString QCoreApplication::applicationVersion()
return coreappdata() ? coreappdata()->applicationVersion : QString();
}
+#if QT_CONFIG(permissions) || defined(Q_QDOC)
+
+/*!
+ Checks the status of the given \a permission
+
+ If the result is Qt::PermissionStatus::Undetermined then permission should be
+ requested via requestPermission() to determine the user's intent.
+
+ \since 6.5
+ \sa requestPermission(), {Application Permissions}
+*/
+Qt::PermissionStatus QCoreApplication::checkPermission(const QPermission &permission)
+{
+ return QPermissions::Private::checkPermission(permission);
+}
+
+/*!
+ \fn template <typename Functor> void QCoreApplication::requestPermission(
+ const QPermission &permission, Functor &&functor)
+
+ Requests the given \a permission.
+
+ \include permissions.qdocinc requestPermission-functor
+
+ The \a functor can be a free-standing or static member function:
+
+ \code
+ qApp->requestPermission(QCameraPermission{}, &permissionUpdated);
+ \endcode
+
+ or a lambda:
+
+ \code
+ qApp->requestPermission(QCameraPermission{}, [](const QPermission &permission) {
+ });
+ \endcode
+
+ \include permissions.qdocinc requestPermission-postamble
+
+ \since 6.5
+ \sa checkPermission(), {Application Permissions}
+*/
+
+/*!
+ \fn template<typename Functor> void QCoreApplication::requestPermission(
+ const QPermission &permission, const QObject *context,
+ Functor functor)
+
+ Requests the given \a permission, in the context of \a context.
+
+ \include permissions.qdocinc requestPermission-functor
+
+ The \a functor can be a free-standing or static member function:
+
+ \code
+ qApp->requestPermission(QCameraPermission{}, context, &permissionUpdated);
+ \endcode
+
+ a lambda:
+
+ \code
+ qApp->requestPermission(QCameraPermission{}, context, [](const QPermission &permission) {
+ });
+ \endcode
+
+ or a slot in the \a context object:
+
+ \code
+ qApp->requestPermission(QCameraPermission{}, this, &CamerWidget::permissionUpdated);
+ \endcode
+
+ The \a functor will be called in the thread of the \a context object. If
+ \a context is destroyed before the request completes, the \a functor will
+ not be called.
+
+ \include permissions.qdocinc requestPermission-postamble
+
+ \since 6.5
+ \overload
+ \sa checkPermission(), {Application Permissions}
+*/
+
+/*!
+ \internal
+
+ Called by the various requestPermission overloads to perform the request.
+
+ 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 *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;
+ }
+
+ class PermissionReceiver : public QObject
+ {
+ public:
+ explicit PermissionReceiver(QtPrivate::SlotObjUniquePtr &&slotObject, const QObject *context)
+ : slotObject(std::move(slotObject)), context(context ? context : this)
+ {
+ Q_ASSERT(this->context);
+ moveToThread(this->context->thread());
+ }
+
+ void finalizePermissionRequest(const QPermission &permission)
+ {
+ Q_ASSERT(slotObject);
+ // only execute if context object is still alive
+ if (context) {
+ void *args[] = { nullptr, const_cast<QPermission *>(&permission) };
+ slotObject->call(const_cast<QObject *>(context.data()), args);
+ }
+ deleteLater();
+ }
+
+ private:
+ QtPrivate::SlotObjSharedPtr slotObject;
+ QPointer<const QObject> context;
+ };
+
+ PermissionReceiver *receiver = new PermissionReceiver(std::move(slotObj), context);
+
+ QPermissions::Private::requestPermission(requestedPermission, [=](Qt::PermissionStatus status) {
+ 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;
+ QMetaObject::invokeMethod(receiver,
+ &PermissionReceiver::finalizePermissionRequest,
+ Qt::QueuedConnection,
+ permission);
+ }
+ });
+}
+
+#endif // QT_CONFIG(permissions)
+
#if QT_CONFIG(library)
Q_GLOBAL_STATIC(QRecursiveMutex, libraryPathMutex)
@@ -2722,11 +2989,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}
*/
@@ -2761,9 +3023,6 @@ QStringList QCoreApplication::libraryPathsLocked()
}
};
setPathsFromEnv(qEnvironmentVariable("QT_PLUGIN_PATH"));
-#ifdef Q_OS_ANDROID
- setPathsFromEnv(qEnvironmentVariable("QT_BUNDLED_LIBS_PATH"));
-#endif
#ifdef Q_OS_DARWIN
// Check the main bundle's PlugIns directory as this is a standard location for Apple OSes.
// Note that the QLibraryInfo::PluginsPath below will coincidentally be the same as this value
@@ -2837,10 +3096,11 @@ void QCoreApplication::setLibraryPaths(const QStringList &paths)
it is searched for libraries first. If \a path is empty or already in the
path list, the path list is not changed.
- The default path list consists of a single entry, the installation
- directory for plugins. The default installation directory for plugins
- is \c INSTALL/plugins, where \c INSTALL is the directory where Qt was
- installed.
+ The default path list consists of one or two entries. The first is the
+ installation directory for plugins, which is \c INSTALL/plugins, where \c
+ INSTALL is the directory where Qt was installed. The second is the
+ application's own directory (\b not the current directory), but only after
+ the QCoreApplication object is instantiated.
The library paths are reset to the default when an instance of
QCoreApplication is destructed.
@@ -2858,14 +3118,14 @@ void QCoreApplication::addLibraryPath(const QString &path)
QMutexLocker locker(libraryPathMutex());
- QStringList *libpaths = coreappdata()->manual_libpaths.data();
+ QStringList *libpaths = coreappdata()->manual_libpaths.get();
if (libpaths) {
if (libpaths->contains(canonicalPath))
return;
} else {
// make sure that library paths are initialized
libraryPathsLocked();
- QStringList *app_libpaths = coreappdata()->app_libpaths.data();
+ QStringList *app_libpaths = coreappdata()->app_libpaths.get();
if (app_libpaths->contains(canonicalPath))
return;
@@ -2897,14 +3157,14 @@ void QCoreApplication::removeLibraryPath(const QString &path)
QMutexLocker locker(libraryPathMutex());
- QStringList *libpaths = coreappdata()->manual_libpaths.data();
+ QStringList *libpaths = coreappdata()->manual_libpaths.get();
if (libpaths) {
if (libpaths->removeAll(canonicalPath) == 0)
return;
} else {
// make sure that library paths is initialized
libraryPathsLocked();
- QStringList *app_libpaths = coreappdata()->app_libpaths.data();
+ QStringList *app_libpaths = coreappdata()->app_libpaths.get();
if (!app_libpaths->contains(canonicalPath))
return;
diff --git a/src/corelib/kernel/qcoreapplication.h b/src/corelib/kernel/qcoreapplication.h
index a3ef59f379..0078dc3295 100644
--- a/src/corelib/kernel/qcoreapplication.h
+++ b/src/corelib/kernel/qcoreapplication.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtCore module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 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 QCOREAPPLICATION_H
#define QCOREAPPLICATION_H
@@ -44,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
@@ -68,6 +33,11 @@ class QTranslator;
class QPostEventList;
class QAbstractEventDispatcher;
class QAbstractNativeEventFilter;
+class QEventLoopLocker;
+
+#if QT_CONFIG(permissions) || defined(Q_QDOC)
+class QPermission;
+#endif
#define qApp QCoreApplication::instance()
@@ -90,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
};
@@ -119,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);
@@ -143,6 +119,53 @@ public:
static QString applicationFilePath();
static qint64 applicationPid() Q_DECL_CONST_FUNCTION;
+#if QT_CONFIG(permissions) || defined(Q_QDOC)
+ Qt::PermissionStatus checkPermission(const QPermission &permission);
+
+# ifdef Q_QDOC
+ template <typename Functor>
+ void requestPermission(const QPermission &permission, const QObject *context, Functor functor);
+# 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,
+ 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)
+ {
+ requestPermission(permission,
+ QtPrivate::makeCallableObject<RequestPermissionPrototype>(std::forward<Functor>(func)),
+ receiver);
+ }
+# endif // Q_QDOC
+
+#ifndef QT_NO_CONTEXTLESS_CONNECT
+ #ifdef Q_QDOC
+ template <typename Functor>
+ #else
+ // requestPermission to a functor or function pointer (without context)
+ template <typename 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
+ void requestPermission(const QPermission &permission,
+ QtPrivate::QSlotObjectBase *slotObj, const QObject *context);
+public:
+
+#endif // QT_CONFIG(permission)
+
#if QT_CONFIG(library)
static void setLibraryPaths(const QStringList &);
static QStringList libraryPaths();
diff --git a/src/corelib/kernel/qcoreapplication_mac.cpp b/src/corelib/kernel/qcoreapplication_mac.cpp
index 6820d565c6..af047fe8cd 100644
--- a/src/corelib/kernel/qcoreapplication_mac.cpp
+++ b/src/corelib/kernel/qcoreapplication_mac.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtCore module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// 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 "qcoreapplication.h"
#include "private/qcoreapplication_p.h"
diff --git a/src/corelib/kernel/qcoreapplication_p.h b/src/corelib/kernel/qcoreapplication_p.h
index 1c3891927d..bfd65d2c9a 100644
--- a/src/corelib/kernel/qcoreapplication_p.h
+++ b/src/corelib/kernel/qcoreapplication_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtCore module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 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 QCOREAPPLICATION_P_H
#define QCOREAPPLICATION_P_H
@@ -55,11 +19,13 @@
#if QT_CONFIG(commandlineparser)
#include "QtCore/qcommandlineoption.h"
#endif
+#include "QtCore/qreadwritelock.h"
#include "QtCore/qtranslator.h"
#if QT_CONFIG(settings)
#include "QtCore/qsettings.h"
#endif
#ifndef QT_NO_QOBJECT
+#include <qloggingcategory.h>
#include "private/qobject_p.h"
#include "private/qlocking_p.h"
#endif
@@ -70,6 +36,10 @@
QT_BEGIN_NAMESPACE
+#ifndef QT_NO_QOBJECT
+Q_DECLARE_LOGGING_CATEGORY(lcDeleteLater)
+#endif
+
typedef QList<QTranslator*> QTranslatorList;
class QAbstractEventDispatcher;
@@ -91,7 +61,7 @@ public:
Gui
};
- QCoreApplicationPrivate(int &aargc, char **aargv, uint flags);
+ QCoreApplicationPrivate(int &aargc, char **aargv);
// If not inheriting from QObjectPrivate: force this class to be polymorphic
#ifdef QT_NO_QOBJECT
@@ -108,6 +78,7 @@ public:
static QString infoDictionaryStringProperty(const QString &propertyName);
#endif
+ void initConsole();
static void initLocale();
static bool checkInstance(const char *method);
@@ -137,6 +108,7 @@ public:
virtual void quit();
static QBasicAtomicPointer<QThread> theMainThread;
+ static QBasicAtomicPointer<void> theMainThreadId;
static QThread *mainThread();
static bool threadRequiresCoreApplication();
@@ -160,6 +132,7 @@ public:
#if defined(Q_OS_WIN)
int origArgc;
char **origArgv; // store unmodified arguments for QCoreApplication::arguments()
+ bool consoleAllocated = false;
#endif
void appendApplicationPathToLibraryPaths(void);
@@ -191,7 +164,6 @@ public:
static bool setuidAllowed;
static uint attribs;
static inline bool testAttribute(uint flag) { return attribs & (1 << flag); }
- static int app_compile_version;
void processCommandLineArguments();
QString qmljs_debug_arguments; // a string containing arguments for js/qml debugging.
diff --git a/src/corelib/kernel/qcoreapplication_platform.h b/src/corelib/kernel/qcoreapplication_platform.h
index 53376340fc..d5f266179e 100644
--- a/src/corelib/kernel/qcoreapplication_platform.h
+++ b/src/corelib/kernel/qcoreapplication_platform.h
@@ -1,55 +1,29 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtCore module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 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 QCOREAPPLICATION_PLATFORM_H
#define QCOREAPPLICATION_PLATFORM_H
+//
+// W A R N I N G
+// -------------
+//
+// This file is part of the native interface APIs. Usage of
+// this API may make your code source and binary incompatible
+// with future versions of Qt.
+//
+
#include <QtCore/qglobal.h>
#include <QtCore/qnativeinterface.h>
#include <QtCore/qcoreapplication.h>
-#if defined(Q_OS_ANDROID) || defined(Q_CLANG_QDOC)
+#if defined(Q_OS_ANDROID) || defined(Q_QDOC)
+#include <QtCore/qjnitypes.h>
#if QT_CONFIG(future) && !defined(QT_NO_QOBJECT)
#include <QtCore/qfuture.h>
#include <QtCore/qvariant.h>
#endif
-#endif // #if defined(Q_OS_ANDROID) || defined(Q_CLANG_QDOC)
+#endif // #if defined(Q_OS_ANDROID) || defined(Q_QDOC)
#if defined(Q_OS_ANDROID)
class _jobject;
@@ -58,13 +32,21 @@ typedef _jobject* jobject;
QT_BEGIN_NAMESPACE
+#if defined(Q_OS_ANDROID)
+Q_DECLARE_JNI_CLASS(Context, "android/content/Context")
+#endif
+
namespace QNativeInterface
{
-#if defined(Q_OS_ANDROID) || defined(Q_CLANG_QDOC)
+#if defined(Q_OS_ANDROID) || defined(Q_QDOC)
struct Q_CORE_EXPORT QAndroidApplication
{
QT_DECLARE_NATIVE_INTERFACE(QAndroidApplication, 1, QCoreApplication)
- static jobject context();
+#ifdef Q_QDOC
+ static QJniObject context();
+#else
+ static QtJniTypes::Context context();
+#endif
static bool isActivityContext();
static int sdkVersion();
static void hideSplashScreen(int duration = 0);
diff --git a/src/corelib/kernel/qcoreapplication_win.cpp b/src/corelib/kernel/qcoreapplication_win.cpp
index 628313fbac..3a69bec25b 100644
--- a/src/corelib/kernel/qcoreapplication_win.cpp
+++ b/src/corelib/kernel/qcoreapplication_win.cpp
@@ -1,42 +1,6 @@
-/****************************************************************************
-**
-** Copyright (C) 2013 Samuel Gaist <samuel.gaist@edeltech.ch>
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtCore module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2013 Samuel Gaist <samuel.gaist@edeltech.ch>
+// 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 "qcoreapplication.h"
#include "qcoreapplication_p.h"
@@ -49,12 +13,15 @@
#include <private/qlocking_p.h>
#endif
#include "qtextstream.h"
+#include "qvarlengtharray.h"
#include <ctype.h>
#include <qt_windows.h>
QT_BEGIN_NAMESPACE
-Q_CORE_EXPORT QString qAppFileName() // get application file name
+using namespace Qt::StringLiterals;
+
+QString qAppFileName() // get application file name
{
/*
GetModuleFileName() returns the length of the module name, when it has
@@ -171,8 +138,8 @@ static QString flagsValue(const QWinMessageMapping<IntType> *haystack,
for (auto p = haystack, end = haystack + haystackSize; p < end; ++p) {
if ((p->value & value) == p->value) {
if (!result.isEmpty())
- result += QLatin1String(" | ");
- result += QLatin1String(p->name);
+ result += " | "_L1;
+ result += QLatin1StringView(p->name);
}
}
return result;
@@ -663,8 +630,8 @@ QString decodeMSG(const MSG& msg)
// decoded message, since some of the common messages are quite long, and
// we don't want the decoded information to vary in output position
if (message.size() < 20)
- message.prepend(QString(20 - message.size(), QLatin1Char(' ')));
- message += QLatin1String(": ");
+ message.prepend(QString(20 - message.size(), u' '));
+ message += ": "_L1;
const QString hwndS = QString::asprintf("(%p)", reinterpret_cast<void *>(msg.hwnd));
const QString wParamS = QString::asprintf("(%p)", reinterpret_cast<void *>(wParam));
@@ -674,11 +641,11 @@ QString decodeMSG(const MSG& msg)
switch (msg.message) {
case WM_ACTIVATE:
if (const char *a = activateParameter(uint(wParam)))
- parameters += QLatin1String(a);
- parameters += QLatin1String(" Hwnd ") + hwndS;
+ parameters += QLatin1StringView(a);
+ parameters += " Hwnd "_L1 + hwndS;
break;
case WM_CAPTURECHANGED:
- parameters = QLatin1String("Hwnd gaining capture ") + hwndS;
+ parameters = "Hwnd gaining capture "_L1 + hwndS;
break;
case WM_CREATE:
{
@@ -703,7 +670,7 @@ QString decodeMSG(const MSG& msg)
}
break;
case WM_DESTROY:
- parameters = QLatin1String("Destroy hwnd ") + hwndS;
+ parameters = "Destroy hwnd "_L1 + hwndS;
break;
case 0x02E0u: { // WM_DPICHANGED
auto rect = reinterpret_cast<const RECT *>(lParam);
@@ -714,20 +681,20 @@ QString decodeMSG(const MSG& msg)
break;
case WM_IME_NOTIFY:
{
- parameters = QLatin1String("Command(");
+ parameters = "Command("_L1;
if (const char *c = imeCommand(uint(wParam)))
- parameters += QLatin1String(c);
- parameters += QLatin1String(" : ") + lParamS;
+ parameters += QLatin1StringView(c);
+ parameters += " : "_L1 + lParamS;
}
break;
case WM_IME_SETCONTEXT:
- parameters = QLatin1String("Input context(")
- + QLatin1String(wParam == TRUE ? "Active" : "Inactive")
- + QLatin1String(") Show flags(")
- + imeShowFlags(DWORD(lParam)) + QLatin1Char(')');
+ parameters = "Input context("_L1
+ + (wParam ? "Active"_L1 : "Inactive"_L1)
+ + ") Show flags("_L1
+ + imeShowFlags(DWORD(lParam)) + u')';
break;
case WM_KILLFOCUS:
- parameters = QLatin1String("Hwnd gaining keyboard focus ") + wParamS;
+ parameters = "Hwnd gaining keyboard focus "_L1 + wParamS;
break;
case WM_CHAR:
case WM_IME_CHAR:
@@ -750,7 +717,7 @@ QString decodeMSG(const MSG& msg)
parameters = QStringLiteral("Keyboard layout changed");
break;
case WM_NCACTIVATE:
- parameters = (msg.wParam? QLatin1String("Active Titlebar") : QLatin1String("Inactive Titlebar"));
+ parameters = (msg.wParam? "Active Titlebar"_L1 : "Inactive Titlebar"_L1);
break;
case WM_MOUSEACTIVATE:
{
@@ -777,37 +744,37 @@ QString decodeMSG(const MSG& msg)
case WM_MOUSEMOVE:
parameters = QString::asprintf("x,y(%4d,%4d) Virtual Keys(",
GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam))
- + virtualKeys(uint(wParam)) + QLatin1Char(')');
+ + virtualKeys(uint(wParam)) + u')';
break;
case WM_MOVE:
parameters = QString::asprintf("x,y(%4d,%4d)", LOWORD(lParam), HIWORD(lParam));
break;
case WM_ERASEBKGND:
case WM_PAINT:
- parameters = QLatin1String("hdc") + wParamS;
+ parameters = "hdc"_L1 + wParamS;
break;
case WM_QUERYNEWPALETTE:
break; // lParam & wParam are unused
case WM_SETCURSOR:
parameters = QString::asprintf("HitTestCode(0x%x) MouseMsg(", LOWORD(lParam));
if (const char *mouseMsg = findWMstr(HIWORD(lParam)))
- parameters += QLatin1String(mouseMsg);
- parameters += QLatin1Char(')');
+ parameters += QLatin1StringView(mouseMsg);
+ parameters += u')';
break;
case WM_SETFOCUS:
- parameters = QLatin1String("Lost Focus ") + wParamS;
+ parameters = "Lost Focus "_L1 + wParamS;
break;
case WM_SETTEXT:
- parameters = QLatin1String("Set Text (")
+ parameters = "Set Text ("_L1
+ QString::fromWCharArray(reinterpret_cast<const wchar_t *>(lParam))
- + QLatin1Char(')');
+ + u')';
break;
case WM_SIZE:
parameters = QString::asprintf("w,h(%4d,%4d) showmode(",
LOWORD(lParam), HIWORD(lParam));
if (const char *showMode = wmSizeParam(uint(wParam)))
- parameters += QLatin1String(showMode);
- parameters += QLatin1Char(')');
+ parameters += QLatin1StringView(showMode);
+ parameters += u')';
break;
case WM_WINDOWPOSCHANGED:
{
@@ -819,23 +786,23 @@ QString decodeMSG(const MSG& msg)
winPos->x, winPos->y, winPos->cx, winPos->cy,
winPosFlags(winPos->flags).toLatin1().constData());
if (const char *h = winPosInsertAfter(insertAfter))
- parameters += QLatin1String(h);
+ parameters += QLatin1StringView(h);
else
parameters += QString::number(insertAfter, 16);
- parameters += QLatin1Char(')');
+ parameters += u')';
}
break;
case WM_QUERYENDSESSION:
- parameters = QLatin1String("End session: ");
+ parameters = "End session: "_L1;
if (const char *logoffOption = sessionMgrLogOffOption(uint(wParam)))
- parameters += QLatin1String(logoffOption);
+ parameters += QLatin1StringView(logoffOption);
break;
default:
- parameters = QLatin1String("wParam") + wParamS + QLatin1String(" lParam") + lParamS;
+ parameters = "wParam"_L1 + wParamS + " lParam"_L1 + lParamS;
break;
}
- return message + QLatin1String("hwnd") + hwndS + QLatin1Char(' ') + parameters;
+ return message + "hwnd"_L1 + hwndS + u' ' + parameters;
}
QDebug operator<<(QDebug dbg, const MSG &msg)
diff --git a/src/corelib/kernel/qcorecmdlineargs_p.h b/src/corelib/kernel/qcorecmdlineargs_p.h
index 849d72901d..2b8d9763f6 100644
--- a/src/corelib/kernel/qcorecmdlineargs_p.h
+++ b/src/corelib/kernel/qcorecmdlineargs_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtCore module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// 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 QCORECMDLINEARGS_P_H
#define QCORECMDLINEARGS_P_H
diff --git a/src/corelib/kernel/qcoreevent.cpp b/src/corelib/kernel/qcoreevent.cpp
index 85b5d2457e..9c99530268 100644
--- a/src/corelib/kernel/qcoreevent.cpp
+++ b/src/corelib/kernel/qcoreevent.cpp
@@ -1,44 +1,9 @@
-/****************************************************************************
-**
-** Copyright (C) 2020 The Qt Company Ltd.
-** Copyright (C) 2016 Intel Corporation.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtCore module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// 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 "qcoreevent.h"
+#include "qcoreevent_p.h"
#include "qcoreapplication.h"
#include "qcoreapplication_p.h"
@@ -50,6 +15,9 @@
QT_BEGIN_NAMESPACE
+Q_TRACE_POINT(qtcore, QEvent_ctor, QEvent *event, QEvent::Type type);
+Q_TRACE_POINT(qtcore, QEvent_dtor, QEvent *event, QEvent::Type type);
+
/*!
\class QEvent
\inmodule QtCore
@@ -111,6 +79,8 @@ QT_BEGIN_NAMESPACE
\value ChildAdded An object gets a child (QChildEvent).
\value ChildPolished A widget child gets polished (QChildEvent).
\value ChildRemoved An object loses a child (QChildEvent).
+ \value [since 6.7] ChildWindowAdded A child window was added to the window.
+ \value [since 6.7] ChildWindowRemoved A child window was removed from the window.
\value Clipboard The clipboard contents have changed.
\value Close Widget was closed (QCloseEvent).
\value CloseSoftwareInputPanel A widget wants to close the software input panel (SIP).
@@ -118,6 +88,8 @@ QT_BEGIN_NAMESPACE
\value ContextMenu Context popup menu (QContextMenuEvent).
\value CursorChange The widget's cursor has changed.
\value DeferredDelete The object will be deleted after it has cleaned up (QDeferredDeleteEvent)
+ \value [since 6.6] DevicePixelRatioChange
+ The devicePixelRatio has changed for this widget's or window's underlying backing store.
\value DragEnter The cursor enters a widget during a drag and drop operation (QDragEnterEvent).
\value DragLeave The cursor leaves a widget during a drag and drop operation (QDragLeaveEvent).
\value DragMove A drag and drop operation is in progress (QDragMoveEvent).
@@ -190,8 +162,12 @@ QT_BEGIN_NAMESPACE
\value OrientationChange The screens orientation has changes (QScreenOrientationChangeEvent).
\value Paint Screen update necessary (QPaintEvent).
\value PaletteChange Palette of the widget changed.
- \value ParentAboutToChange The widget parent is about to change.
- \value ParentChange The widget parent has changed.
+ \value ParentAboutToChange The object parent is about to change.
+ Only sent to some object types, such as QWidget.
+ \value ParentChange The object parent has changed.
+ Only sent to some object types, such as QWidget.
+ \value [since 6.7] ParentWindowAboutToChange The parent window is about to change.
+ \value [since 6.7] ParentWindowChange The parent window has changed.
\value PlatformPanel A platform specific panel has been requested.
\value PlatformSurface A native platform surface has been created or is about to be destroyed (QPlatformSurfaceEvent).
\omitvalue Pointer
@@ -199,7 +175,7 @@ QT_BEGIN_NAMESPACE
\value PolishRequest The widget should be polished.
\value QueryWhatsThis The widget should accept the event if it has "What's This?" help (QHelpEvent).
\value Quit The application has exited.
- \value ReadOnlyChange Widget's read-only state has changed (since Qt 5.4).
+ \value [since 5.4] ReadOnlyChange Widget's read-only state has changed.
\value RequestSoftwareInputPanel A widget wants to open a software input panel (SIP).
\value Resize Widget's size changed (QResizeEvent).
\value ScrollPrepare The object needs to fill in its geometry information (QScrollPrepareEvent).
@@ -226,7 +202,7 @@ QT_BEGIN_NAMESPACE
\omitvalue OkRequest
\value TabletEnterProximity Wacom tablet enter proximity event (QTabletEvent), sent to QApplication.
\value TabletLeaveProximity Wacom tablet leave proximity event (QTabletEvent), sent to QApplication.
- \value TabletTrackingChange The Wacom tablet tracking state has changed (since Qt 5.9).
+ \value [since 5.9] TabletTrackingChange The Wacom tablet tracking state has changed.
\omitvalue ThemeChange
\value ThreadChange The object is moved to another thread. This is the last event sent to this object in the previous thread. See QObject::moveToThread().
\value Timer Regular timer events (QTimerEvent).
@@ -290,6 +266,7 @@ QT_BEGIN_NAMESPACE
\omitvalue NativeGesture
\omitvalue WindowChangeInternal
\omitvalue ScreenChangeInternal
+ \omitvalue WindowAboutToChangeInternal
*/
/*!
@@ -299,7 +276,7 @@ QEvent::QEvent(Type type)
: t(type), m_reserved(0),
m_inputEvent(false), m_pointerEvent(false), m_singlePointEvent(false)
{
- Q_TRACE(QEvent_ctor, this, t);
+ Q_TRACE(QEvent_ctor, this, type);
}
/*!
@@ -456,13 +433,16 @@ struct QBasicAtomicBitField {
QBasicAtomicInteger<uint> next;
QBasicAtomicInteger<uint> data[NumInts];
+ constexpr QBasicAtomicBitField() = default;
+
bool allocateSpecific(int which) noexcept
{
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'
@@ -496,7 +476,7 @@ struct QBasicAtomicBitField {
typedef QBasicAtomicBitField<QEvent::MaxUser - QEvent::User + 1> UserEventTypeRegistry;
-static UserEventTypeRegistry userEventTypeRegistry;
+Q_CONSTINIT static UserEventTypeRegistry userEventTypeRegistry {};
static inline int registerEventTypeZeroBased(int id) noexcept
{
@@ -539,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()
*/
@@ -556,12 +536,7 @@ QTimerEvent::QTimerEvent(int timerId)
: QEvent(Timer), id(timerId)
{}
-/*!
- \internal
-*/
-QTimerEvent::~QTimerEvent()
-{
-}
+Q_IMPL_EVENT_COMMON(QTimerEvent)
/*!
\fn int QTimerEvent::timerId() const
@@ -603,12 +578,7 @@ QChildEvent::QChildEvent(Type type, QObject *child)
: QEvent(type), c(child)
{}
-/*!
- \internal
-*/
-QChildEvent::~QChildEvent()
-{
-}
+Q_IMPL_EVENT_COMMON(QChildEvent)
/*!
\fn QObject *QChildEvent::child() const
@@ -660,12 +630,7 @@ QDynamicPropertyChangeEvent::QDynamicPropertyChangeEvent(const QByteArray &name)
{
}
-/*!
- \internal
-*/
-QDynamicPropertyChangeEvent::~QDynamicPropertyChangeEvent()
-{
-}
+Q_IMPL_EVENT_COMMON(QDynamicPropertyChangeEvent)
/*!
\fn QByteArray QDynamicPropertyChangeEvent::propertyName() const
@@ -677,26 +642,13 @@ QDynamicPropertyChangeEvent::~QDynamicPropertyChangeEvent()
*/
/*!
- Constructs a deferred delete event with an initial loopLevel() of zero.
-*/
-QDeferredDeleteEvent::QDeferredDeleteEvent()
- : QEvent(QEvent::DeferredDelete)
- , level(0)
-{ }
-
-/*!
- \internal
+ Constructs a deferred delete event with the given loop and scope level.
*/
-QDeferredDeleteEvent::~QDeferredDeleteEvent()
+QDeferredDeleteEvent::QDeferredDeleteEvent(int loopLevel, int scopeLevel)
+ : QEvent(QEvent::DeferredDelete), m_loopLevel(loopLevel), m_scopeLevel(scopeLevel)
{ }
-/*! \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()
-*/
+Q_IMPL_EVENT_COMMON(QDeferredDeleteEvent)
QT_END_NAMESPACE
diff --git a/src/corelib/kernel/qcoreevent.h b/src/corelib/kernel/qcoreevent.h
index 780b84f98d..a65dbee7da 100644
--- a/src/corelib/kernel/qcoreevent.h
+++ b/src/corelib/kernel/qcoreevent.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2020 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtCore module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2020 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_H
#define QCOREEVENT_H
@@ -53,6 +17,29 @@ protected: \
Class &operator=(const Class &other) = default; \
Class &operator=(Class &&) = delete
+#define Q_DECL_EVENT_COMMON(Class) \
+ protected: \
+ Class(const Class &); \
+ Class(Class &&) = delete; \
+ Class &operator=(const Class &other) = default; \
+ Class &operator=(Class &&) = delete; \
+ public: \
+ Class* clone() const override; \
+ ~Class() override; \
+ private:
+
+#define Q_IMPL_EVENT_COMMON(Class) \
+ Class::Class(const Class &) = default; \
+ Class::~Class() = default; \
+ Class* Class::clone() const \
+ { \
+ auto c = new Class(*this); \
+ [[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; \
+ }
+
class QEventPrivate;
class Q_CORE_EXPORT QEvent // event base class
{
@@ -90,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
@@ -285,7 +272,7 @@ public:
StyleAnimationUpdate = 213, // style animation target should be updated
ApplicationStateChange = 214,
- WindowChangeInternal = 215, // internal for QQuickWidget
+ WindowChangeInternal = 215, // internal for QQuickWidget and texture-based widgets
ScreenChangeInternal = 216,
PlatformSurface = 217, // Platform surface created or about to be destroyed
@@ -294,6 +281,16 @@ public:
TabletTrackingChange = 219, // tablet tracking state has changed
+ // GraphicsSceneLeave = 220,
+ WindowAboutToChangeInternal = 221, // internal for QQuickWidget and texture-based widgets
+
+ DevicePixelRatioChange = 222,
+
+ ChildWindowAdded = 223,
+ ChildWindowRemoved = 224,
+ ParentWindowAboutToChange = 225,
+ ParentWindowChange = 226,
+
// 512 reserved for Qt Jambi's MetaCall event
// 513 reserved for Qt Jambi's DeleteOnMainThread event
@@ -357,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
@@ -365,14 +364,11 @@ private:
class Q_CORE_EXPORT QTimerEvent : public QEvent
{
- Q_EVENT_DISABLE_COPY(QTimerEvent);
+ Q_DECL_EVENT_COMMON(QTimerEvent)
public:
explicit QTimerEvent(int timerId);
- ~QTimerEvent();
int timerId() const { return id; }
- QTimerEvent *clone() const override { return new QTimerEvent(*this); };
-
protected:
int id;
};
@@ -381,51 +377,31 @@ class QObject;
class Q_CORE_EXPORT QChildEvent : public QEvent
{
- Q_EVENT_DISABLE_COPY(QChildEvent);
+ Q_DECL_EVENT_COMMON(QChildEvent)
public:
QChildEvent(Type type, QObject *child);
- ~QChildEvent();
+
QObject *child() const { return c; }
bool added() const { return type() == ChildAdded; }
bool polished() const { return type() == ChildPolished; }
bool removed() const { return type() == ChildRemoved; }
- QChildEvent *clone() const override { return new QChildEvent(*this); };
-
protected:
QObject *c;
};
class Q_CORE_EXPORT QDynamicPropertyChangeEvent : public QEvent
{
- Q_EVENT_DISABLE_COPY(QDynamicPropertyChangeEvent);
+ Q_DECL_EVENT_COMMON(QDynamicPropertyChangeEvent)
public:
explicit QDynamicPropertyChangeEvent(const QByteArray &name);
- ~QDynamicPropertyChangeEvent();
inline QByteArray propertyName() const { return n; }
- QDynamicPropertyChangeEvent *clone() const override { return new QDynamicPropertyChangeEvent(*this); };
-
private:
QByteArray n;
};
-class Q_CORE_EXPORT QDeferredDeleteEvent : public QEvent
-{
- Q_EVENT_DISABLE_COPY(QDeferredDeleteEvent);
-public:
- explicit QDeferredDeleteEvent();
- ~QDeferredDeleteEvent();
- int loopLevel() const { return level; }
-
- QDeferredDeleteEvent *clone() const override { return new QDeferredDeleteEvent(*this); };
-
-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/qcoreglobaldata.cpp b/src/corelib/kernel/qcoreglobaldata.cpp
deleted file mode 100644
index 4df5a40e39..0000000000
--- a/src/corelib/kernel/qcoreglobaldata.cpp
+++ /dev/null
@@ -1,59 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtCore module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#include "qcoreglobaldata_p.h"
-
-QT_BEGIN_NAMESPACE
-
-Q_GLOBAL_STATIC(QCoreGlobalData, globalInstance)
-
-QCoreGlobalData::QCoreGlobalData()
-{
-}
-
-QCoreGlobalData::~QCoreGlobalData()
-{
-}
-
-QCoreGlobalData *QCoreGlobalData::instance()
-{
- return globalInstance();
-}
-
-QT_END_NAMESPACE
diff --git a/src/corelib/kernel/qcoreglobaldata_p.h b/src/corelib/kernel/qcoreglobaldata_p.h
deleted file mode 100644
index 4403c2ebdd..0000000000
--- a/src/corelib/kernel/qcoreglobaldata_p.h
+++ /dev/null
@@ -1,75 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtCore module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#ifndef QCOREGLOBALDATA_P_H
-#define QCOREGLOBALDATA_P_H
-
-//
-// W A R N I N G
-// -------------
-//
-// This file is not part of the Qt API. It exists purely as an
-// implementation detail. This header file may change from version to
-// version without notice, or even be removed.
-//
-// We mean it.
-//
-
-#include <QtCore/private/qglobal_p.h>
-#include "QtCore/qstringlist.h"
-#include "QtCore/qreadwritelock.h"
-#include "QtCore/qhash.h"
-#include "QtCore/qbytearray.h"
-#include "QtCore/qmutex.h"
-
-QT_BEGIN_NAMESPACE
-
-struct QCoreGlobalData
-{
- QCoreGlobalData();
- ~QCoreGlobalData();
-
- QHash<QString, QStringList> dirSearchPaths;
- QReadWriteLock dirSearchPathsLock;
-
- static QCoreGlobalData *instance();
-};
-
-QT_END_NAMESPACE
-#endif // QCOREGLOBALDATA_P_H
diff --git a/src/corelib/kernel/qdeadlinetimer.cpp b/src/corelib/kernel/qdeadlinetimer.cpp
index 20b2107d55..f99e68f990 100644
--- a/src/corelib/kernel/qdeadlinetimer.cpp
+++ b/src/corelib/kernel/qdeadlinetimer.cpp
@@ -1,332 +1,47 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 Intel Corporation.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtCore module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// 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 "qdeadlinetimer.h"
-#include "qdeadlinetimer_p.h"
#include "private/qnumeric_p.h"
QT_BEGIN_NAMESPACE
-namespace {
- class TimeReference
- {
- enum : unsigned {
- umega = 1000 * 1000,
- ugiga = umega * 1000
- };
-
- enum : qint64 {
- kilo = 1000,
- mega = kilo * 1000,
- giga = mega * 1000
- };
-
- public:
- enum RoundingStrategy {
- RoundDown,
- RoundUp,
- RoundDefault = RoundDown
- };
-
- static constexpr qint64 Min = std::numeric_limits<qint64>::min();
- static constexpr qint64 Max = std::numeric_limits<qint64>::max();
-
- inline TimeReference(qint64 = 0, unsigned = 0);
- inline void updateTimer(qint64 &, unsigned &);
-
- inline bool addNanoseconds(qint64);
- inline bool addMilliseconds(qint64);
- bool addSecsAndNSecs(qint64, qint64);
-
- inline bool subtract(const qint64, const unsigned);
-
- inline bool toMilliseconds(qint64 *, RoundingStrategy = RoundDefault) const;
- inline bool toNanoseconds(qint64 *) const;
-
- inline void saturate(bool toMax);
- static bool sign(qint64, qint64);
-
- private:
- bool adjust(const qint64, const unsigned, qint64 = 0);
-
- private:
- qint64 secs;
- unsigned nsecs;
- };
-}
+QT_IMPL_METATYPE_EXTERN(QDeadlineTimer)
-inline TimeReference::TimeReference(qint64 t1, unsigned t2)
- : secs(t1), nsecs(t2)
-{
-}
+using namespace std::chrono;
-inline void TimeReference::updateTimer(qint64 &t1, unsigned &t2)
-{
- t1 = secs;
- t2 = nsecs;
-}
-
-inline void TimeReference::saturate(bool toMax)
-{
- secs = toMax ? Max : Min;
-}
-
-/*!
- * \internal
- *
- * Determines the sign of a (seconds, nanoseconds) pair
- * for differentiating overflow from underflow. It doesn't
- * deal with equality as it shouldn't ever be called in that case.
- *
- * Returns true if the pair represents a positive time offset
- * false otherwise.
- */
-bool TimeReference::sign(qint64 secs, qint64 nsecs)
-{
- if (secs > 0) {
- if (nsecs > 0)
- return true;
- } else {
- if (nsecs < 0)
- return false;
- }
-
- // They are different in sign
- secs += nsecs / giga;
- if (secs > 0)
- return true;
- else if (secs < 0)
- return false;
-
- // We should never get over|underflow out of
- // the case: secs * giga == -nsecs
- // So the sign of nsecs is the deciding factor
- Q_ASSERT(nsecs % giga != 0);
- return nsecs > 0;
-}
-
-#if defined(Q_OS_UNIX) && !defined(Q_OS_DARWIN)
-inline bool TimeReference::addNanoseconds(qint64 arg)
-{
- return addSecsAndNSecs(arg / giga, arg % giga);
-}
-
-inline bool TimeReference::addMilliseconds(qint64 arg)
+namespace {
+struct TimeReference : std::numeric_limits<qint64>
{
- return addSecsAndNSecs(arg / kilo, (arg % kilo) * mega);
+ static constexpr qint64 Min = min();
+ static constexpr qint64 Max = max();
+};
}
-/*!
- * \internal
- *
- * Adds \a t1 addSecs seconds and \a addNSecs nanoseconds to the
- * time reference. The arguments are normalized to seconds (qint64)
- * and nanoseconds (unsigned) before the actual calculation is
- * delegated to adjust(). If the nanoseconds are negative the
- * owed second used for the normalization is passed on to adjust()
- * as third argument.
- *
- * Returns true if operation was successful, false on over|underflow
- */
-bool TimeReference::addSecsAndNSecs(qint64 addSecs, qint64 addNSecs)
+template <typename Duration1, typename... Durations>
+static qint64 add_saturate(qint64 t1, Duration1 dur, Durations... extra)
{
- // Normalize the arguments
- if (qAbs(addNSecs) >= giga) {
- if (add_overflow<qint64>(addSecs, addNSecs / giga, &addSecs))
- return false;
-
- addNSecs %= giga;
- }
+ qint64 v = dur.count();
+ qint64 saturated = std::numeric_limits<qint64>::max();
+ if (v < 0)
+ saturated = std::numeric_limits<qint64>::min();
- if (addNSecs < 0)
- return adjust(addSecs, ugiga - unsigned(-addNSecs), -1);
+ // convert to nanoseconds with saturation
+ using Ratio = std::ratio_divide<typename Duration1::period, nanoseconds::period>;
+ static_assert(Ratio::den == 1, "sub-multiples of nanosecond are not supported");
+ if (qMulOverflow<Ratio::num>(v, &v))
+ return saturated;
- return adjust(addSecs, unsigned(addNSecs));
-}
-
-/*!
- * \internal
- *
- * Adds \a t1 seconds and \a t2 nanoseconds to the internal members.
- * Takes into account the additional \a carrySeconds we may owe or need to carry over.
- *
- * Returns true if operation was successful, false on over|underflow
- */
-bool TimeReference::adjust(const qint64 t1, const unsigned t2, qint64 carrySeconds)
-{
- static_assert(QDeadlineTimerNanosecondsInT2);
- nsecs += t2;
- if (nsecs >= ugiga) {
- nsecs -= ugiga;
- carrySeconds++;
+ qint64 r;
+ if (qAddOverflow(t1, v, &r))
+ return saturated;
+ if constexpr (sizeof...(Durations)) {
+ // chain more additions
+ return add_saturate(r, extra...);
}
-
- // We don't worry about the order of addition, because the result returned by
- // callers of this function is unchanged regardless of us over|underflowing.
- // If we do, we do so by no more than a second, thus saturating the timer to
- // Forever has the same effect as if we did the arithmetic exactly and salvaged
- // the overflow.
- return !add_overflow<qint64>(secs, t1, &secs) && !add_overflow<qint64>(secs, carrySeconds, &secs);
-}
-
-/*!
- * \internal
- *
- * Subtracts \a t1 seconds and \a t2 nanoseconds from the time reference.
- * When normalizing the nanoseconds to a positive number the owed seconds is
- * passed as third argument to adjust() as the seconds may over|underflow
- * if we do the calculation directly. There is little sense to check the
- * seconds for over|underflow here in case we are going to need to carry
- * over a second _after_ we add the nanoseconds.
- *
- * Returns true if operation was successful, false on over|underflow
- */
-inline bool TimeReference::subtract(const qint64 t1, const unsigned t2)
-{
- Q_ASSERT(t2 < ugiga);
- return adjust(-t1, ugiga - t2, -1);
-}
-
-/*!
- * \internal
- *
- * Converts the time reference to milliseconds.
- *
- * Checks are done without making use of mul_overflow because it may
- * not be implemented on some 32bit platforms.
- *
- * Returns true if operation was successful, false on over|underflow
- */
-inline bool TimeReference::toMilliseconds(qint64 *result, RoundingStrategy rounding) const
-{
- static constexpr qint64 maxSeconds = Max / kilo;
- static constexpr qint64 minSeconds = Min / kilo;
- if (secs > maxSeconds || secs < minSeconds)
- return false;
-
- unsigned ns = rounding == RoundDown ? nsecs : nsecs + umega - 1;
-
- return !add_overflow<qint64>(secs * kilo, ns / umega, result);
-}
-
-/*!
- * \internal
- *
- * Converts the time reference to nanoseconds.
- *
- * Checks are done without making use of mul_overflow because it may
- * not be implemented on some 32bit platforms.
- *
- * Returns true if operation was successful, false on over|underflow
- */
-inline bool TimeReference::toNanoseconds(qint64 *result) const
-{
- static constexpr qint64 maxSeconds = Max / giga;
- static constexpr qint64 minSeconds = Min / giga;
- if (secs > maxSeconds || secs < minSeconds)
- return false;
-
- return !add_overflow<qint64>(secs * giga, nsecs, result);
-}
-#else
-inline bool TimeReference::addNanoseconds(qint64 arg)
-{
- return adjust(arg, 0);
-}
-
-inline bool TimeReference::addMilliseconds(qint64 arg)
-{
- static constexpr qint64 maxMilliseconds = Max / mega;
- if (qAbs(arg) > maxMilliseconds)
- return false;
-
- return addNanoseconds(arg * mega);
-}
-
-inline bool TimeReference::addSecsAndNSecs(qint64 addSecs, qint64 addNSecs)
-{
- static constexpr qint64 maxSeconds = Max / giga;
- static constexpr qint64 minSeconds = Min / giga;
- if (addSecs > maxSeconds || addSecs < minSeconds || add_overflow<qint64>(addSecs * giga, addNSecs, &addNSecs))
- return false;
-
- return addNanoseconds(addNSecs);
-}
-
-inline bool TimeReference::adjust(const qint64 t1, const unsigned t2, qint64 carrySeconds)
-{
- static_assert(!QDeadlineTimerNanosecondsInT2);
- Q_UNUSED(t2);
- Q_UNUSED(carrySeconds);
-
- return !add_overflow<qint64>(secs, t1, &secs);
-}
-
-inline bool TimeReference::subtract(const qint64 t1, const unsigned t2)
-{
- Q_UNUSED(t2);
-
- return addNanoseconds(-t1);
+ return r;
}
-inline bool TimeReference::toMilliseconds(qint64 *result, RoundingStrategy rounding) const
-{
- // Force QDeadlineTimer to treat the border cases as
- // over|underflow and saturate the results returned to the user.
- // We don't want to get valid milliseconds out of saturated timers.
- if (secs == Max || secs == Min)
- return false;
-
- *result = secs / mega;
- if (rounding == RoundUp && secs > *result * mega)
- (*result)++;
-
- return true;
-}
-
-inline bool TimeReference::toNanoseconds(qint64 *result) const
-{
- *result = secs;
- return true;
-}
-#endif
-
/*!
\class QDeadlineTimer
\inmodule QtCore
@@ -336,6 +51,8 @@ inline bool TimeReference::toNanoseconds(qint64 *result) const
\reentrant
\ingroup tools
+ \compares strong
+
The QDeadlineTimer class is usually used to calculate future deadlines and
verify whether the deadline has expired. QDeadlineTimer can also be used
for deadlines without expiration ("forever"). It forms a counterpart to
@@ -369,11 +86,12 @@ inline bool TimeReference::toNanoseconds(qint64 *result) const
\section1 Timer types
- Like QTimer, QDeadlineTimer can select among different levels of coarseness
- on the timers. You can select precise timing by passing Qt::PreciseTimer to
- the functions that set of change the timer, or you can select coarse timing
- by passing Qt::CoarseTimer. Qt::VeryCoarseTimer is currently interpreted
- the same way as Qt::CoarseTimer.
+ Like QTimer and QChronoTimer, QDeadlineTimer can select among
+ different levels of coarseness on the timers. You can select
+ precise timing by passing Qt::PreciseTimer to the functions that
+ set of change the timer, or you can select coarse timing by passing
+ Qt::CoarseTimer. Qt::VeryCoarseTimer is currently interpreted the same
+ way as Qt::CoarseTimer.
This feature is dependent on support from the operating system: if the OS
does not support a coarse timer functionality, then QDeadlineTimer will
@@ -405,7 +123,7 @@ inline bool TimeReference::toNanoseconds(qint64 *result) const
\snippet code/src_corelib_kernel_qdeadlinetimer.cpp 2
- \sa QTime, QTimer, QDeadlineTimer, Qt::TimerType
+ \sa QTime, QChronoTimer, QDeadlineTimer, Qt::TimerType
*/
/*!
@@ -416,10 +134,12 @@ inline bool TimeReference::toNanoseconds(qint64 *result) const
*/
/*!
+ \fn QDeadlineTimer::QDeadlineTimer()
\fn QDeadlineTimer::QDeadlineTimer(Qt::TimerType timerType)
Constructs an expired QDeadlineTimer object. For this object,
- remainingTime() will return 0.
+ remainingTime() will return 0. If \a timerType is not set, then the object
+ will use the \l{Qt::CoarseTimer}{coarse} \l{QDeadlineTimer#Timer types}{timer type}.
The timer type \a timerType may be ignored, since the timer is already
expired. Similarly, for optimization purposes, this function will not
@@ -449,7 +169,7 @@ inline bool TimeReference::toNanoseconds(qint64 *result) const
from the moment of the creation of this object, if msecs is positive. If \a
msecs is zero, this QDeadlineTimer will be marked as expired, causing
remainingTime() to return zero and deadline() to return an indeterminate
- time point in the past. If \a msecs is -1, the timer will be set to never
+ time point in the past. If \a msecs is negative, the timer will be set to never
expire, causing remainingTime() to return -1 and deadline() to return the
maximum value.
@@ -462,10 +182,12 @@ inline bool TimeReference::toNanoseconds(qint64 *result) const
functionality is required, use QDeadlineTimer::current() and add time to
it.
+ \note Prior to Qt 6.6, the only value that caused the timer to never expire
+ was -1.
+
\sa hasExpired(), isForever(), remainingTime(), setRemainingTime()
*/
QDeadlineTimer::QDeadlineTimer(qint64 msecs, Qt::TimerType type) noexcept
- : t2(0)
{
setRemainingTime(msecs, type);
}
@@ -529,51 +251,70 @@ QDeadlineTimer::QDeadlineTimer(qint64 msecs, Qt::TimerType type) noexcept
/*!
Sets the remaining time for this QDeadlineTimer object to \a msecs
milliseconds from now, if \a msecs has a positive value. If \a msecs is
- zero, this QDeadlineTimer object will be marked as expired, whereas a value
- of -1 will set it to never expire.
+ zero, this QDeadlineTimer object will be marked as expired, whereas a
+ negative value will set it to never expire.
+
+ For optimization purposes, if \a msecs is zero, this function may skip
+ obtaining the current time and may instead use a value known to be in the
+ past. If that happens, deadline() may return an unexpected value and this
+ object cannot be used in calculation of how long it is overdue. If that
+ functionality is required, use QDeadlineTimer::current() and add time to
+ it.
The timer type for this QDeadlineTimer object will be set to the specified \a timerType.
+ \note Prior to Qt 6.6, the only value that caused the timer to never expire
+ was -1.
+
\sa setPreciseRemainingTime(), hasExpired(), isForever(), remainingTime()
*/
void QDeadlineTimer::setRemainingTime(qint64 msecs, Qt::TimerType timerType) noexcept
{
- if (msecs == -1) {
+ if (msecs < 0) {
*this = QDeadlineTimer(Forever, timerType);
- return;
+ } else if (msecs == 0) {
+ *this = QDeadlineTimer(timerType);
+ t1 = std::numeric_limits<qint64>::min();
+ } else {
+ *this = current(timerType);
+ milliseconds ms(msecs);
+ t1 = add_saturate(t1, ms);
}
-
- *this = current(timerType);
-
- TimeReference ref(t1, t2);
- if (!ref.addMilliseconds(msecs))
- ref.saturate(msecs > 0);
- ref.updateTimer(t1, t2);
}
/*!
Sets the remaining time for this QDeadlineTimer object to \a secs seconds
plus \a nsecs nanoseconds from now, if \a secs has a positive value. If \a
- secs is -1, this QDeadlineTimer will be set it to never expire. If both
- parameters are zero, this QDeadlineTimer will be marked as expired.
+ secs is negative, this QDeadlineTimer will be set it to never expire (this
+ behavior does not apply to \a nsecs). If both parameters are zero, this
+ QDeadlineTimer will be marked as expired.
+
+ For optimization purposes, if both \a secs and \a nsecs are zero, this
+ function may skip obtaining the current time and may instead use a value
+ known to be in the past. If that happens, deadline() may return an
+ unexpected value and this object cannot be used in calculation of how long
+ it is overdue. If that functionality is required, use
+ QDeadlineTimer::current() and add time to it.
The timer type for this QDeadlineTimer object will be set to the specified
\a timerType.
+ \note Prior to Qt 6.6, the only condition that caused the timer to never
+ expire was when \a secs was -1.
+
\sa setRemainingTime(), hasExpired(), isForever(), remainingTime()
*/
void QDeadlineTimer::setPreciseRemainingTime(qint64 secs, qint64 nsecs, Qt::TimerType timerType) noexcept
{
- if (secs == -1) {
+ if (secs < 0) {
*this = QDeadlineTimer(Forever, timerType);
- return;
+ } else if (secs == 0 && nsecs == 0) {
+ *this = QDeadlineTimer(timerType);
+ t1 = std::numeric_limits<qint64>::min();
+ } else {
+ *this = current(timerType);
+ t1 = add_saturate(t1, seconds{secs}, nanoseconds{nsecs});
}
-
- *this = current(timerType);
- TimeReference ref(t1, t2);
- if (!ref.addSecsAndNSecs(secs, nsecs))
- ref.saturate(TimeReference::sign(secs, nsecs));
- ref.updateTimer(t1, t2);
}
/*!
@@ -623,6 +364,8 @@ bool QDeadlineTimer::hasExpired() const noexcept
{
if (isForever())
return false;
+ if (t1 == std::numeric_limits<qint64>::min())
+ return true;
return *this <= current(timerType());
}
@@ -671,19 +414,8 @@ qint64 QDeadlineTimer::remainingTime() const noexcept
if (isForever())
return -1;
- QDeadlineTimer now = current(timerType());
- TimeReference ref(t1, t2);
-
- qint64 msecs;
- if (!ref.subtract(now.t1, now.t2))
- return 0; // We can only underflow here
-
- // If we fail the conversion, t1 < now.t1 means we underflowed,
- // thus the deadline had long expired
- if (!ref.toMilliseconds(&msecs, TimeReference::RoundUp))
- return t1 < now.t1 ? 0 : -1;
-
- return msecs < 0 ? 0 : msecs;
+ nanoseconds nsecs(remainingTimeNSecs());
+ return ceil<milliseconds>(nsecs).count();
}
/*!
@@ -705,23 +437,19 @@ qint64 QDeadlineTimer::remainingTimeNSecs() const noexcept
/*!
\internal
Same as remainingTimeNSecs, but may return negative remaining times. Does
- not deal with Forever. In case of underflow the result is saturated to
- the minimum possible value, on overflow - the maximum possible value.
+ not deal with Forever. In case of underflow, which is only possible if the
+ timer has expired, an arbitrary negative value is returned.
*/
qint64 QDeadlineTimer::rawRemainingTimeNSecs() const noexcept
{
- QDeadlineTimer now = current(timerType());
- TimeReference ref(t1, t2);
+ if (t1 == std::numeric_limits<qint64>::min())
+ return t1; // we'd saturate to this anyway
- qint64 nsecs;
- if (!ref.subtract(now.t1, now.t2))
- return TimeReference::Min; // We can only underflow here
-
- // If we fail the conversion, t1 < now.t1 means we underflowed,
- // thus the deadline had long expired
- if (!ref.toNanoseconds(&nsecs))
- return t1 < now.t1 ? TimeReference::Min : TimeReference::Max;
- return nsecs;
+ QDeadlineTimer now = current(timerType());
+ qint64 r;
+ if (qSubOverflow(t1, now.t1, &r))
+ return -1; // any negative number is fine
+ return r;
}
/*!
@@ -748,12 +476,11 @@ qint64 QDeadlineTimer::deadline() const noexcept
{
if (isForever())
return TimeReference::Max;
+ if (t1 == TimeReference::Min)
+ return t1;
- qint64 result;
- if (!TimeReference(t1, t2).toMilliseconds(&result))
- return t1 < 0 ? TimeReference::Min : TimeReference::Max;
-
- return result;
+ nanoseconds ns(t1);
+ return duration_cast<milliseconds>(ns).count();
}
/*!
@@ -782,11 +509,7 @@ qint64 QDeadlineTimer::deadlineNSecs() const noexcept
if (isForever())
return TimeReference::Max;
- qint64 result;
- if (!TimeReference(t1, t2).toNanoseconds(&result))
- return t1 < 0 ? TimeReference::Min : TimeReference::Max;
-
- return result;
+ return t1;
}
/*!
@@ -810,11 +533,7 @@ void QDeadlineTimer::setDeadline(qint64 msecs, Qt::TimerType timerType) noexcept
}
type = timerType;
-
- TimeReference ref;
- if (!ref.addMilliseconds(msecs))
- ref.saturate(msecs > 0);
- ref.updateTimer(t1, t2);
+ t1 = add_saturate(0, milliseconds{msecs});
}
/*!
@@ -832,13 +551,7 @@ void QDeadlineTimer::setDeadline(qint64 msecs, Qt::TimerType timerType) noexcept
void QDeadlineTimer::setPreciseDeadline(qint64 secs, qint64 nsecs, Qt::TimerType timerType) noexcept
{
type = timerType;
-
- // We don't pass the seconds to the constructor, because we don't know
- // at this point if t1 holds the seconds or nanoseconds; it's platform specific.
- TimeReference ref;
- if (!ref.addSecsAndNSecs(secs, nsecs))
- ref.saturate(TimeReference::sign(secs, nsecs));
- ref.updateTimer(t1, t2);
+ t1 = add_saturate(0, seconds{secs}, nanoseconds{nsecs});
}
/*!
@@ -854,11 +567,7 @@ QDeadlineTimer QDeadlineTimer::addNSecs(QDeadlineTimer dt, qint64 nsecs) noexcep
if (dt.isForever())
return dt;
- TimeReference ref(dt.t1, dt.t2);
- if (!ref.addNanoseconds(nsecs))
- ref.saturate(nsecs > 0);
- ref.updateTimer(dt.t1, dt.t2);
-
+ dt.t1 = add_saturate(dt.t1, nanoseconds{nsecs});
return dt;
}
@@ -871,11 +580,22 @@ QDeadlineTimer QDeadlineTimer::addNSecs(QDeadlineTimer dt, qint64 nsecs) noexcep
The QDeadlineTimer object will be constructed with the specified \a timerType.
*/
+QDeadlineTimer QDeadlineTimer::current(Qt::TimerType timerType) noexcept
+{
+ // ensure we get nanoseconds; this will work so long as steady_clock's
+ // time_point isn't of finer resolution (picoseconds)
+ std::chrono::nanoseconds ns = std::chrono::steady_clock::now().time_since_epoch();
+
+ QDeadlineTimer result;
+ result.t1 = ns.count();
+ result.type = timerType;
+ return result;
+}
/*!
- \fn bool QDeadlineTimer::operator==(QDeadlineTimer d1, QDeadlineTimer d2)
+ \fn bool QDeadlineTimer::operator==(const QDeadlineTimer &lhs, const QDeadlineTimer &rhs)
- Returns true if the deadline on \a d1 and the deadline in \a d2 are the
+ Returns true if the deadline on \a lhs and the deadline in \a rhs are the
same, false otherwise. The timer type used to create the two deadlines is
ignored. This function is equivalent to:
@@ -886,9 +606,9 @@ QDeadlineTimer QDeadlineTimer::addNSecs(QDeadlineTimer dt, qint64 nsecs) noexcep
*/
/*!
- \fn bool QDeadlineTimer::operator!=(QDeadlineTimer d1, QDeadlineTimer d2)
+ \fn bool QDeadlineTimer::operator!=(const QDeadlineTimer &lhs, const QDeadlineTimer &rhs)
- Returns true if the deadline on \a d1 and the deadline in \a d2 are
+ Returns true if the deadline on \a lhs and the deadline in \a rhs are
different, false otherwise. The timer type used to create the two deadlines
is ignored. This function is equivalent to:
@@ -899,10 +619,10 @@ QDeadlineTimer QDeadlineTimer::addNSecs(QDeadlineTimer dt, qint64 nsecs) noexcep
*/
/*!
- \fn bool QDeadlineTimer::operator<(QDeadlineTimer d1, QDeadlineTimer d2)
+ \fn bool QDeadlineTimer::operator<(const QDeadlineTimer &lhs, const QDeadlineTimer &rhs)
- Returns true if the deadline on \a d1 is earlier than the deadline in \a
- d2, false otherwise. The timer type used to create the two deadlines is
+ Returns true if the deadline on \a lhs is earlier than the deadline in \a
+ rhs, false otherwise. The timer type used to create the two deadlines is
ignored. This function is equivalent to:
\snippet code/src_corelib_kernel_qdeadlinetimer.cpp 10
@@ -912,10 +632,10 @@ QDeadlineTimer QDeadlineTimer::addNSecs(QDeadlineTimer dt, qint64 nsecs) noexcep
*/
/*!
- \fn bool QDeadlineTimer::operator<=(QDeadlineTimer d1, QDeadlineTimer d2)
+ \fn bool QDeadlineTimer::operator<=(const QDeadlineTimer &lhs, const QDeadlineTimer &rhs)
- Returns true if the deadline on \a d1 is earlier than or the same as the
- deadline in \a d2, false otherwise. The timer type used to create the two
+ Returns true if the deadline on \a lhs is earlier than or the same as the
+ deadline in \a rhs, false otherwise. The timer type used to create the two
deadlines is ignored. This function is equivalent to:
\snippet code/src_corelib_kernel_qdeadlinetimer.cpp 11
@@ -925,10 +645,10 @@ QDeadlineTimer QDeadlineTimer::addNSecs(QDeadlineTimer dt, qint64 nsecs) noexcep
*/
/*!
- \fn bool QDeadlineTimer::operator>(QDeadlineTimer d1, QDeadlineTimer d2)
+ \fn bool QDeadlineTimer::operator>(const QDeadlineTimer &lhs, const QDeadlineTimer &rhs)
- Returns true if the deadline on \a d1 is later than the deadline in \a
- d2, false otherwise. The timer type used to create the two deadlines is
+ Returns true if the deadline on \a lhs is later than the deadline in \a
+ rhs, false otherwise. The timer type used to create the two deadlines is
ignored. This function is equivalent to:
\snippet code/src_corelib_kernel_qdeadlinetimer.cpp 12
@@ -938,10 +658,10 @@ QDeadlineTimer QDeadlineTimer::addNSecs(QDeadlineTimer dt, qint64 nsecs) noexcep
*/
/*!
- \fn bool QDeadlineTimer::operator>=(QDeadlineTimer d1, QDeadlineTimer d2)
+ \fn bool QDeadlineTimer::operator>=(const QDeadlineTimer &lhs, const QDeadlineTimer &rhs)
- Returns true if the deadline on \a d1 is later than or the same as the
- deadline in \a d2, false otherwise. The timer type used to create the two
+ Returns true if the deadline on \a lhs is later than or the same as the
+ deadline in \a rhs, false otherwise. The timer type used to create the two
deadlines is ignored. This function is equivalent to:
\snippet code/src_corelib_kernel_qdeadlinetimer.cpp 13
@@ -965,11 +685,7 @@ QDeadlineTimer operator+(QDeadlineTimer dt, qint64 msecs)
if (dt.isForever())
return dt;
- TimeReference ref(dt.t1, dt.t2);
- if (!ref.addMilliseconds(msecs))
- ref.saturate(msecs > 0);
- ref.updateTimer(dt.t1, dt.t2);
-
+ dt.t1 = add_saturate(dt.t1, milliseconds{msecs});
return dt;
}
@@ -1035,11 +751,4 @@ QDeadlineTimer operator+(QDeadlineTimer dt, qint64 msecs)
Returns the time remaining before the deadline.
*/
-/*!
- \fn QPair<qint64, unsigned> QDeadlineTimer::_q_data() const
- \internal
-*/
-
-// the rest of the functions are in qelapsedtimer_xxx.cpp
-
QT_END_NAMESPACE
diff --git a/src/corelib/kernel/qdeadlinetimer.h b/src/corelib/kernel/qdeadlinetimer.h
index 08e80e448c..515cdb5387 100644
--- a/src/corelib/kernel/qdeadlinetimer.h
+++ b/src/corelib/kernel/qdeadlinetimer.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 Intel Corporation.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtCore module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// 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 QDEADLINETIMER_H
#define QDEADLINETIMER_H
@@ -43,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()
@@ -52,25 +15,25 @@
#include <limits>
-#if __has_include(<chrono>)
-# include <chrono>
-#endif
+#include <chrono>
QT_BEGIN_NAMESPACE
class Q_CORE_EXPORT QDeadlineTimer
{
public:
- enum ForeverConstant { Forever };
+ enum class ForeverConstant { Forever };
+ static constexpr ForeverConstant Forever = ForeverConstant::Forever;
- constexpr QDeadlineTimer(Qt::TimerType type_ = Qt::CoarseTimer) noexcept
- : t1(0), t2(0), type(type_) {}
+ constexpr QDeadlineTimer() noexcept = default;
+ constexpr explicit QDeadlineTimer(Qt::TimerType type_) noexcept
+ : type(type_) {}
constexpr QDeadlineTimer(ForeverConstant, Qt::TimerType type_ = Qt::CoarseTimer) noexcept
- : t1(std::numeric_limits<qint64>::max()), t2(0), type(type_) {}
+ : t1((std::numeric_limits<qint64>::max)()), type(type_) {}
explicit QDeadlineTimer(qint64 msecs, Qt::TimerType type = Qt::CoarseTimer) noexcept;
void swap(QDeadlineTimer &other) noexcept
- { qSwap(t1, other.t1); qSwap(t2, other.t2); qSwap(type, other.type); }
+ { std::swap(t1, other.t1); std::swap(type, other.type); }
constexpr bool isForever() const noexcept
{ return t1 == (std::numeric_limits<qint64>::max)(); }
@@ -95,19 +58,6 @@ public:
static QDeadlineTimer addNSecs(QDeadlineTimer dt, qint64 nsecs) noexcept Q_DECL_PURE_FUNCTION;
static QDeadlineTimer current(Qt::TimerType timerType = Qt::CoarseTimer) noexcept;
- friend bool operator==(QDeadlineTimer d1, QDeadlineTimer d2) noexcept
- { return d1.t1 == d2.t1 && d1.t2 == d2.t2; }
- friend bool operator!=(QDeadlineTimer d1, QDeadlineTimer d2) noexcept
- { return !(d1 == d2); }
- friend bool operator<(QDeadlineTimer d1, QDeadlineTimer d2) noexcept
- { return d1.t1 < d2.t1 || (d1.t1 == d2.t1 && d1.t2 < d2.t2); }
- friend bool operator<=(QDeadlineTimer d1, QDeadlineTimer d2) noexcept
- { return d1 == d2 || d1 < d2; }
- friend bool operator>(QDeadlineTimer d1, QDeadlineTimer d2) noexcept
- { return d2 < d1; }
- friend bool operator>=(QDeadlineTimer d1, QDeadlineTimer d2) noexcept
- { return !(d1 < d2); }
-
friend Q_CORE_EXPORT QDeadlineTimer operator+(QDeadlineTimer dt, qint64 msecs);
friend QDeadlineTimer operator+(qint64 msecs, QDeadlineTimer dt)
{ return dt + msecs; }
@@ -120,26 +70,20 @@ public:
QDeadlineTimer &operator-=(qint64 msecs)
{ *this = *this + (-msecs); return *this; }
-#if __has_include(<chrono>) || defined(Q_CLANG_QDOC)
- template <class Clock, class Duration>
+ template <class Clock, class Duration = typename Clock::duration>
QDeadlineTimer(std::chrono::time_point<Clock, Duration> deadline_,
Qt::TimerType type_ = Qt::CoarseTimer) : t2(0)
{ setDeadline(deadline_, type_); }
- template <class Clock, class Duration>
+ template <class Clock, class Duration = typename Clock::duration>
QDeadlineTimer &operator=(std::chrono::time_point<Clock, Duration> deadline_)
{ setDeadline(deadline_); return *this; }
- template <class Clock, class Duration>
- void setDeadline(std::chrono::time_point<Clock, Duration> deadline_,
- Qt::TimerType type_ = Qt::CoarseTimer)
- { setRemainingTime(deadline_ == deadline_.max() ? Duration::max() : deadline_ - Clock::now(), type_); }
+ template <class Clock, class Duration = typename Clock::duration>
+ void setDeadline(std::chrono::time_point<Clock, Duration> tp,
+ Qt::TimerType type_ = Qt::CoarseTimer);
template <class Clock, class Duration = typename Clock::duration>
- std::chrono::time_point<Clock, Duration> deadline() const
- {
- auto val = std::chrono::nanoseconds(rawRemainingTimeNSecs()) + Clock::now();
- return std::chrono::time_point_cast<Duration>(val);
- }
+ std::chrono::time_point<Clock, Duration> deadline() const;
template <class Rep, class Period>
QDeadlineTimer(std::chrono::duration<Rep, Period> remaining, Qt::TimerType type_ = Qt::CoarseTimer)
@@ -153,10 +97,11 @@ public:
template <class Rep, class Period>
void setRemainingTime(std::chrono::duration<Rep, Period> remaining, Qt::TimerType type_ = Qt::CoarseTimer)
{
+ using namespace std::chrono;
if (remaining == remaining.max())
*this = QDeadlineTimer(Forever, type_);
else
- setPreciseRemainingTime(0, std::chrono::nanoseconds(remaining).count(), type_);
+ setPreciseRemainingTime(0, ceil<nanoseconds>(remaining).count(), type_);
}
std::chrono::nanoseconds remainingTimeAsDuration() const noexcept
@@ -178,52 +123,62 @@ public:
template <class Rep, class Period>
friend QDeadlineTimer operator+=(QDeadlineTimer &dt, std::chrono::duration<Rep, Period> value)
{ return dt = dt + value; }
-#endif
private:
- qint64 t1;
- unsigned t2;
- unsigned type;
+ friend bool comparesEqual(const QDeadlineTimer &lhs,
+ const QDeadlineTimer &rhs) noexcept
+ {
+ return lhs.t1 == rhs.t1;
+ }
+ friend Qt::strong_ordering compareThreeWay(const QDeadlineTimer &lhs,
+ const QDeadlineTimer &rhs) noexcept
+ {
+ return Qt::compareThreeWay(lhs.t1, rhs.t1);
+ }
+ Q_DECLARE_STRONGLY_ORDERED(QDeadlineTimer)
- qint64 rawRemainingTimeNSecs() const noexcept;
+ qint64 t1 = 0;
+#if QT_VERSION < QT_VERSION_CHECK(7, 0, 0)
+ unsigned t2 = 0;
+#endif
+ unsigned type = Qt::CoarseTimer;
-public:
- // This is not a public function, it's here only for Qt's internal convenience...
- QPair<qint64, unsigned> _q_data() const { return qMakePair(t1, t2); }
+ qint64 rawRemainingTimeNSecs() const noexcept;
};
-#if __has_include(<chrono>) && (defined(Q_OS_DARWIN) || defined(Q_OS_LINUX) || (defined(Q_CC_MSVC) && Q_CC_MSVC >= 1900))
-// We know for these OS/compilers that the std::chrono::steady_clock uses the same
-// reference time as QDeadlineTimer
-
-template <> inline std::chrono::steady_clock::time_point
-QDeadlineTimer::deadline<std::chrono::steady_clock, std::chrono::steady_clock::duration>() const
+template<class Clock, class Duration>
+std::chrono::time_point<Clock, Duration> QDeadlineTimer::deadline() const
{
- return std::chrono::steady_clock::time_point(std::chrono::nanoseconds(deadlineNSecs()));
+ using namespace std::chrono;
+ if constexpr (std::is_same_v<Clock, steady_clock>) {
+ auto val = duration_cast<Duration>(nanoseconds(deadlineNSecs()));
+ return time_point<Clock, Duration>(val);
+ } else {
+ auto val = nanoseconds(rawRemainingTimeNSecs()) + Clock::now();
+ return time_point_cast<Duration>(val);
+ }
}
-template <> inline void
-QDeadlineTimer::setDeadline<std::chrono::steady_clock, std::chrono::steady_clock::duration>(std::chrono::steady_clock::time_point tp, Qt::TimerType type_)
+template<class Clock, class Duration>
+void QDeadlineTimer::setDeadline(std::chrono::time_point<Clock, Duration> tp, Qt::TimerType type_)
{
using namespace std::chrono;
if (tp == tp.max()) {
*this = Forever;
type = type_;
- } else if (type_ != Qt::PreciseTimer) {
- // if we aren't using PreciseTimer, then we need to convert
- setPreciseRemainingTime(0, duration_cast<nanoseconds>(tp - steady_clock::now()).count(), type_);
- } else {
+ } else if constexpr (std::is_same_v<Clock, steady_clock>) {
setPreciseDeadline(0,
duration_cast<nanoseconds>(tp.time_since_epoch()).count(),
type_);
+ } else {
+ setPreciseRemainingTime(0, duration_cast<nanoseconds>(tp - Clock::now()).count(), type_);
}
}
-#endif
Q_DECLARE_SHARED(QDeadlineTimer)
QT_END_NAMESPACE
-Q_DECLARE_METATYPE(QDeadlineTimer)
+QT_DECL_METATYPE_EXTERN(QDeadlineTimer, Q_CORE_EXPORT)
#endif // QDEADLINETIMER_H
diff --git a/src/corelib/kernel/qdeadlinetimer_p.h b/src/corelib/kernel/qdeadlinetimer_p.h
deleted file mode 100644
index 46e12de6c1..0000000000
--- a/src/corelib/kernel/qdeadlinetimer_p.h
+++ /dev/null
@@ -1,70 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtCore module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#ifndef QDEADLINETIMER_P_H
-#define QDEADLINETIMER_P_H
-
-//
-// W A R N I N G
-// -------------
-//
-// This file is not part of the Qt API. It exists purely as an
-// implementation detail. This header file may change from version to
-// version without notice, or even be removed.
-//
-// We mean it.
-//
-
-#include <QtCore/qglobal.h>
-
-QT_BEGIN_NAMESPACE
-
-enum {
-#if defined(Q_OS_UNIX) && !defined(Q_OS_DARWIN)
- // t1 contains seconds and t2 contains nanoseconds
- QDeadlineTimerNanosecondsInT2 = 1
-#else
- // t1 contains nanoseconds, t2 is always zero
- QDeadlineTimerNanosecondsInT2 = 0
-#endif
-};
-
-QT_END_NAMESPACE
-
-#endif
diff --git a/src/corelib/kernel/qelapsedtimer.cpp b/src/corelib/kernel/qelapsedtimer.cpp
index af26a523c0..511b81a04e 100644
--- a/src/corelib/kernel/qelapsedtimer.cpp
+++ b/src/corelib/kernel/qelapsedtimer.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtCore module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qelapsedtimer.h"
@@ -111,19 +75,7 @@ QT_BEGIN_NAMESPACE
that the clock used is the same as QElapsedTimer (see
QElapsedTimer::clockType()).
- \section2 32-bit overflows
-
- Some of the clocks used by QElapsedTimer have a limited range and may
- overflow after hitting the upper limit (usually 32-bit). QElapsedTimer
- deals with this overflow issue and presents a consistent timing. However,
- when extracting the time since reference from QElapsedTimer, two
- different processes in the same machine may have different understanding
- of how much time has actually elapsed.
-
- The information on which clocks types may overflow and how to remedy that
- issue is documented along with the clock types.
-
- \sa QTime, QTimer, QDeadlineTimer
+ \sa QTime, QChronoTimer, QDeadlineTimer
*/
/*!
@@ -138,10 +90,13 @@ QT_BEGIN_NAMESPACE
used.
\value SystemTime The human-readable system time. This clock is not monotonic.
- \value MonotonicClock The system's monotonic clock, usually found in Unix systems. This clock is monotonic and does not overflow.
- \value TickCounter The system's tick counter, used on Windows systems. This clock may overflow.
- \value MachAbsoluteTime The Mach kernel's absolute time (\macos and iOS). This clock is monotonic and does not overflow.
- \value PerformanceCounter The high-resolution performance counter provided by Windows. This clock is monotonic and does not overflow.
+ \value MonotonicClock The system's monotonic clock, usually found in Unix systems.
+ This clock is monotonic.
+ \value TickCounter Not used anymore.
+ \value MachAbsoluteTime The Mach kernel's absolute time (\macos and iOS).
+ This clock is monotonic.
+ \value PerformanceCounter The performance counter provided by Windows.
+ This clock is monotonic.
\section2 SystemTime
@@ -159,26 +114,6 @@ QT_BEGIN_NAMESPACE
arbitrary point in the past. This clock type is used on Unix systems
which support POSIX monotonic clocks (\tt{_POSIX_MONOTONIC_CLOCK}).
- This clock does not overflow.
-
- \section2 TickCounter
-
- The tick counter clock type is based on the system's or the processor's
- tick counter, multiplied by the duration of a tick. This clock type is
- used on Windows platforms. If the high-precision performance
- counter is available on Windows, the \tt{PerformanceCounter} clock type
- is used instead.
-
- The TickCounter clock type is the only clock type that may overflow.
- Windows Vista and Windows Server 2008 support the extended 64-bit tick
- counter, which allows avoiding the overflow.
-
- On Windows systems, the clock overflows after 2^32 milliseconds, which
- corresponds to roughly 49.7 days. This means two processes' reckoning of
- the time since the reference may be different by multiples of 2^32
- milliseconds. When comparing such values, it's recommended that the high
- 32 bits of the millisecond count be masked off.
-
\section2 MachAbsoluteTime
This clock type is based on the absolute time presented by Mach kernels,
@@ -187,17 +122,14 @@ QT_BEGIN_NAMESPACE
a POSIX monotonic clock with values differing from the Mach absolute
time.
- This clock is monotonic and does not overflow.
+ This clock is monotonic.
\section2 PerformanceCounter
This clock uses the Windows functions \tt{QueryPerformanceCounter} and
- \tt{QueryPerformanceFrequency} to access the system's high-precision
- performance counter. Since this counter may not be available on all
- systems, QElapsedTimer will fall back to the \tt{TickCounter} clock
- automatically, if this clock cannot be used.
+ \tt{QueryPerformanceFrequency} to access the system's performance counter.
- This clock is monotonic and does not overflow.
+ This clock is monotonic.
\sa clockType(), isMonotonic()
*/
@@ -233,9 +165,229 @@ QT_BEGIN_NAMESPACE
function will return false.
*/
+/*!
+ \fn QElapsedTimer::clockType() noexcept
+
+ Returns the clock type that this QElapsedTimer implementation uses.
+
+ Since Qt 6.6, QElapsedTimer uses \c{std::chrono::steady_clock}, so the
+ clock type is always \l MonotonicClock.
+
+ \sa isMonotonic()
+*/
+
+QElapsedTimer::ClockType QElapsedTimer::clockType() noexcept
+{
+ // we use std::chrono::steady_clock
+ return MonotonicClock;
+}
+
+/*!
+ \fn QElapsedTimer::isMonotonic() noexcept
+
+ Returns \c true if this is a monotonic clock, false otherwise. See the
+ information on the different clock types to understand which ones are
+ monotonic.
+
+ Since Qt 6.6, QElapsedTimer uses \c{std::chrono::steady_clock}, so this
+ function now always returns true.
+
+ \sa clockType(), QElapsedTimer::ClockType
+*/
+bool QElapsedTimer::isMonotonic() noexcept
+{
+ // We trust std::chrono::steady_clock to be steady (monotonic); if the
+ // Standard Library is lying to us, users must complain to their vendor.
+ return true;
+}
+
+/*!
+ \typealias QElapsedTimer::Duration
+ Synonym for \c std::chrono::nanoseconds.
+*/
+
+/*!
+ \typealias QElapsedTimer::TimePoint
+ Synonym for \c {std::chrono::time_point<std::chrono::steady_clock, Duration>}.
+*/
+
+/*!
+ Starts this timer. Once started, a timer value can be checked with elapsed() or msecsSinceReference().
+
+ Normally, a timer is started just before a lengthy operation, such as:
+ \snippet qelapsedtimer/main.cpp 0
+
+ Also, starting a timer makes it valid again.
+
+ \sa restart(), invalidate(), elapsed()
+*/
+void QElapsedTimer::start() noexcept
+{
+ static_assert(sizeof(t1) == sizeof(Duration::rep));
+
+ // This assignment will work so long as TimePoint uses the same time
+ // duration or one of finer granularity than steady_clock::time_point. That
+ // means it will work until the first steady_clock using picoseconds.
+ TimePoint now = std::chrono::steady_clock::now();
+ t1 = now.time_since_epoch().count();
+ QT6_ONLY(t2 = 0);
+}
+
+/*!
+ Restarts the timer and returns the number of milliseconds elapsed since
+ the previous start.
+ This function is equivalent to obtaining the elapsed time with elapsed()
+ and then starting the timer again with start(), but it does so in one
+ single operation, avoiding the need to obtain the clock value twice.
+
+ Calling this function on a QElapsedTimer that is invalid
+ results in undefined behavior.
+
+ The following example illustrates how to use this function to calibrate a
+ parameter to a slow operation (for example, an iteration count) so that
+ this operation takes at least 250 milliseconds:
+
+ \snippet qelapsedtimer/main.cpp 3
+
+ \sa start(), invalidate(), elapsed(), isValid()
+*/
+qint64 QElapsedTimer::restart() noexcept
+{
+ QElapsedTimer old = *this;
+ start();
+ return old.msecsTo(*this);
+}
+
+/*!
+ \since 6.6
+
+ Returns a \c{std::chrono::nanoseconds} with the time since this QElapsedTimer was last
+ started.
+
+ Calling this function on a QElapsedTimer that is invalid
+ results in undefined behavior.
+
+ On platforms that do not provide nanosecond resolution, the value returned
+ will be the best estimate available.
+
+ \sa start(), restart(), hasExpired(), invalidate()
+*/
+auto QElapsedTimer::durationElapsed() const noexcept -> Duration
+{
+ TimePoint then{Duration(t1)};
+ return std::chrono::steady_clock::now() - then;
+}
+
+/*!
+ \since 4.8
+
+ Returns the number of nanoseconds since this QElapsedTimer was last
+ started.
+
+ Calling this function on a QElapsedTimer that is invalid
+ results in undefined behavior.
+
+ On platforms that do not provide nanosecond resolution, the value returned
+ will be the best estimate available.
+
+ \sa start(), restart(), hasExpired(), invalidate()
+*/
+qint64 QElapsedTimer::nsecsElapsed() const noexcept
+{
+ return durationElapsed().count();
+}
+
+/*!
+ Returns the number of milliseconds since this QElapsedTimer was last
+ started.
+
+ Calling this function on a QElapsedTimer that is invalid
+ results in undefined behavior.
+
+ \sa start(), restart(), hasExpired(), isValid(), invalidate()
+*/
+qint64 QElapsedTimer::elapsed() const noexcept
+{
+ using namespace std::chrono;
+ return duration_cast<milliseconds>(durationElapsed()).count();
+}
+
+/*!
+ Returns the number of milliseconds between last time this QElapsedTimer
+ object was started and its reference clock's start.
+
+ This number is usually arbitrary for all clocks except the
+ QElapsedTimer::SystemTime clock. For that clock type, this number is the
+ number of milliseconds since January 1st, 1970 at 0:00 UTC (that is, it
+ is the Unix time expressed in milliseconds).
+
+ On Linux, Windows and Apple platforms, this value is usually the time
+ since the system boot, though it usually does not include the time the
+ system has spent in sleep states.
+
+ \sa clockType(), elapsed()
+*/
+qint64 QElapsedTimer::msecsSinceReference() const noexcept
+{
+ using namespace std::chrono;
+ return duration_cast<milliseconds>(Duration(t1)).count();
+}
+
+/*!
+ \since 6.6
+
+ Returns the time difference between this QElapsedTimer and \a other as a
+ \c{std::chrono::nanoseconds}. If \a other was started before this object,
+ the returned value will be negative. If it was started later, the returned
+ value will be positive.
+
+ The return value is undefined if this object or \a other were invalidated.
+
+ \sa secsTo(), elapsed()
+*/
+auto QElapsedTimer::durationTo(const QElapsedTimer &other) const noexcept -> Duration
+{
+ Duration d1(t1);
+ Duration d2(other.t1);
+ return d2 - d1;
+}
+
+/*!
+ Returns the number of milliseconds between this QElapsedTimer and \a
+ other. If \a other was started before this object, the returned value
+ will be negative. If it was started later, the returned value will be
+ positive.
+
+ The return value is undefined if this object or \a other were invalidated.
+
+ \sa secsTo(), elapsed()
+*/
+qint64 QElapsedTimer::msecsTo(const QElapsedTimer &other) const noexcept
+{
+ using namespace std::chrono;
+ return duration_cast<milliseconds>(durationTo(other)).count();
+}
+
+/*!
+ Returns the number of seconds between this QElapsedTimer and \a other. If
+ \a other was started before this object, the returned value will be
+ negative. If it was started later, the returned value will be positive.
+
+ Calling this function on or with a QElapsedTimer that is invalid
+ results in undefined behavior.
+
+ \sa msecsTo(), elapsed()
+*/
+qint64 QElapsedTimer::secsTo(const QElapsedTimer &other) const noexcept
+{
+ using namespace std::chrono;
+ return duration_cast<seconds>(durationTo(other)).count();
+}
+
static const qint64 invalidData = Q_INT64_C(0x8000000000000000);
/*!
+ \fn QElapsedTimer::invalidate() noexcept
Marks this QElapsedTimer object as invalid.
An invalid object can be checked with isValid(). Calculations of timer
@@ -261,10 +413,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
*/
@@ -275,4 +429,9 @@ bool QElapsedTimer::hasExpired(qint64 timeout) const noexcept
return quint64(elapsed()) > quint64(timeout);
}
+bool operator<(const QElapsedTimer &lhs, const QElapsedTimer &rhs) noexcept
+{
+ return lhs.t1 < rhs.t1;
+}
+
QT_END_NAMESPACE
diff --git a/src/corelib/kernel/qelapsedtimer.h b/src/corelib/kernel/qelapsedtimer.h
index a7523add19..7d8b889f61 100644
--- a/src/corelib/kernel/qelapsedtimer.h
+++ b/src/corelib/kernel/qelapsedtimer.h
@@ -1,49 +1,14 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtCore module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// 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 QELAPSEDTIMER_H
#define QELAPSEDTIMER_H
#include <QtCore/qglobal.h>
-QT_BEGIN_NAMESPACE
+#include <chrono>
+QT_BEGIN_NAMESPACE
class Q_CORE_EXPORT QElapsedTimer
{
@@ -51,16 +16,17 @@ public:
enum ClockType {
SystemTime,
MonotonicClock,
- TickCounter,
+ TickCounter Q_DECL_ENUMERATOR_DEPRECATED_X(
+ "Not supported anymore. Use PerformanceCounter instead."),
MachAbsoluteTime,
PerformanceCounter
};
- constexpr QElapsedTimer()
- : t1(Q_INT64_C(0x8000000000000000)),
- t2(Q_INT64_C(0x8000000000000000))
- {
- }
+ // similar to std::chrono::*_clock
+ using Duration = std::chrono::nanoseconds;
+ using TimePoint = std::chrono::time_point<std::chrono::steady_clock, Duration>;
+
+ constexpr QElapsedTimer() = default;
static ClockType clockType() noexcept;
static bool isMonotonic() noexcept;
@@ -70,11 +36,13 @@ public:
void invalidate() noexcept;
bool isValid() const noexcept;
+ Duration durationElapsed() const noexcept;
qint64 nsecsElapsed() const noexcept;
qint64 elapsed() const noexcept;
bool hasExpired(qint64 timeout) const noexcept;
qint64 msecsSinceReference() const noexcept;
+ Duration durationTo(const QElapsedTimer &other) const noexcept;
qint64 msecsTo(const QElapsedTimer &other) const noexcept;
qint64 secsTo(const QElapsedTimer &other) const noexcept;
@@ -86,8 +54,8 @@ public:
friend bool Q_CORE_EXPORT operator<(const QElapsedTimer &lhs, const QElapsedTimer &rhs) noexcept;
private:
- qint64 t1;
- qint64 t2;
+ qint64 t1 = Q_INT64_C(0x8000000000000000);
+ qint64 t2 = Q_INT64_C(0x8000000000000000);
};
QT_END_NAMESPACE
diff --git a/src/corelib/kernel/qelapsedtimer_generic.cpp b/src/corelib/kernel/qelapsedtimer_generic.cpp
deleted file mode 100644
index 2f7b50ed84..0000000000
--- a/src/corelib/kernel/qelapsedtimer_generic.cpp
+++ /dev/null
@@ -1,205 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtCore module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#include "qelapsedtimer.h"
-#include "qdeadlinetimer.h"
-#include "qdatetime.h"
-
-QT_BEGIN_NAMESPACE
-
-/*!
- Returns the clock type that this QElapsedTimer implementation uses.
-
- \sa isMonotonic()
-*/
-QElapsedTimer::ClockType QElapsedTimer::clockType() noexcept
-{
- return SystemTime;
-}
-
-/*!
- Returns \c true if this is a monotonic clock, false otherwise. See the
- information on the different clock types to understand which ones are
- monotonic.
-
- \sa clockType(), QElapsedTimer::ClockType
-*/
-bool QElapsedTimer::isMonotonic() noexcept
-{
- return false;
-}
-
-/*!
- Starts this timer. Once started, a timer value can be checked with elapsed() or msecsSinceReference().
-
- Normally, a timer is started just before a lengthy operation, such as:
- \snippet qelapsedtimer/main.cpp 0
-
- Also, starting a timer makes it valid again.
-
- \sa restart(), invalidate(), elapsed()
-*/
-void QElapsedTimer::start() noexcept
-{
- restart();
-}
-
-/*!
- Restarts the timer and returns the number of milliseconds elapsed since
- the previous start.
- This function is equivalent to obtaining the elapsed time with elapsed()
- and then starting the timer again with start(), but it does so in one
- single operation, avoiding the need to obtain the clock value twice.
-
- Calling this function on a QElapsedTimer that is invalid
- results in undefined behavior.
-
- The following example illustrates how to use this function to calibrate a
- parameter to a slow operation (for example, an iteration count) so that
- this operation takes at least 250 milliseconds:
-
- \snippet qelapsedtimer/main.cpp 3
-
- \sa start(), invalidate(), elapsed(), isValid()
-*/
-qint64 QElapsedTimer::restart() noexcept
-{
- qint64 old = t1;
- t1 = QDateTime::currentMSecsSinceEpoch();
- t2 = 0;
- return t1 - old;
-}
-
-/*! \since 4.8
-
- Returns the number of nanoseconds since this QElapsedTimer was last
- started.
-
- Calling this function on a QElapsedTimer that is invalid
- results in undefined behavior.
-
- On platforms that do not provide nanosecond resolution, the value returned
- will be the best estimate available.
-
- \sa start(), restart(), hasExpired(), invalidate()
-*/
-qint64 QElapsedTimer::nsecsElapsed() const noexcept
-{
- return elapsed() * 1000000;
-}
-
-/*!
- Returns the number of milliseconds since this QElapsedTimer was last
- started.
-
- Calling this function on a QElapsedTimer that is invalid
- results in undefined behavior.
-
- \sa start(), restart(), hasExpired(), isValid(), invalidate()
-*/
-qint64 QElapsedTimer::elapsed() const noexcept
-{
- return QDateTime::currentMSecsSinceEpoch() - t1;
-}
-
-/*!
- Returns the number of milliseconds between last time this QElapsedTimer
- object was started and its reference clock's start.
-
- This number is usually arbitrary for all clocks except the
- QElapsedTimer::SystemTime clock. For that clock type, this number is the
- number of milliseconds since January 1st, 1970 at 0:00 UTC (that is, it
- is the Unix time expressed in milliseconds).
-
- On Linux, Windows and Apple platforms, this value is usually the time
- since the system boot, though it usually does not include the time the
- system has spent in sleep states.
-
- \sa clockType(), elapsed()
-*/
-qint64 QElapsedTimer::msecsSinceReference() const noexcept
-{
- return t1;
-}
-
-/*!
- Returns the number of milliseconds between this QElapsedTimer and \a
- other. If \a other was started before this object, the returned value
- will be negative. If it was started later, the returned value will be
- positive.
-
- The return value is undefined if this object or \a other were invalidated.
-
- \sa secsTo(), elapsed()
-*/
-qint64 QElapsedTimer::msecsTo(const QElapsedTimer &other) const noexcept
-{
- qint64 diff = other.t1 - t1;
- return diff;
-}
-
-/*!
- Returns the number of seconds between this QElapsedTimer and \a other. If
- \a other was started before this object, the returned value will be
- negative. If it was started later, the returned value will be positive.
-
- Calling this function on or with a QElapsedTimer that is invalid
- results in undefined behavior.
-
- \sa msecsTo(), elapsed()
-*/
-qint64 QElapsedTimer::secsTo(const QElapsedTimer &other) const noexcept
-{
- return msecsTo(other) / 1000;
-}
-
-bool operator<(const QElapsedTimer &lhs, const QElapsedTimer &rhs) noexcept
-{
- return lhs.t1 < rhs.t1;
-}
-
-QDeadlineTimer QDeadlineTimer::current(Qt::TimerType timerType) noexcept
-{
- QDeadlineTimer result;
- result.t1 = QDateTime::currentMSecsSinceEpoch() * 1000 * 1000;
- result.type = timerType;
- return result;
-}
-
-QT_END_NAMESPACE
diff --git a/src/corelib/kernel/qelapsedtimer_mac.cpp b/src/corelib/kernel/qelapsedtimer_mac.cpp
deleted file mode 100644
index 8fe03c726b..0000000000
--- a/src/corelib/kernel/qelapsedtimer_mac.cpp
+++ /dev/null
@@ -1,166 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtCore module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-// ask for the latest POSIX, just in case
-#define _POSIX_C_SOURCE 200809L
-
-#include "qelapsedtimer.h"
-#include "qdeadlinetimer.h"
-#include "qdeadlinetimer_p.h"
-#include <sys/time.h>
-#include <time.h>
-#include <unistd.h>
-
-#include <mach/mach_time.h>
-#include <private/qcore_unix_p.h>
-
-QT_BEGIN_NAMESPACE
-
-#ifdef __LP64__
-typedef __int128_t LargeInt;
-#else
-typedef qint64 LargeInt;
-#endif
-
-QElapsedTimer::ClockType QElapsedTimer::clockType() noexcept
-{
- return MachAbsoluteTime;
-}
-
-bool QElapsedTimer::isMonotonic() noexcept
-{
- return true;
-}
-
-static mach_timebase_info_data_t info = { 0, 0 };
-static qint64 absoluteToNSecs(qint64 cpuTime)
-{
- if (info.denom == 0)
- mach_timebase_info(&info);
-
- // don't do multiplication & division if those are equal
- // (mathematically it would be the same, but it's computationally expensive)
- if (info.numer == info.denom)
- return cpuTime;
- qint64 nsecs = LargeInt(cpuTime) * info.numer / info.denom;
- return nsecs;
-}
-
-static qint64 absoluteToMSecs(qint64 cpuTime)
-{
- return absoluteToNSecs(cpuTime) / 1000000;
-}
-
-timespec qt_gettime() noexcept
-{
- timespec tv;
-
- uint64_t cpu_time = mach_absolute_time();
- uint64_t nsecs = absoluteToNSecs(cpu_time);
- tv.tv_sec = nsecs / 1000000000ull;
- tv.tv_nsec = nsecs - (tv.tv_sec * 1000000000ull);
- return tv;
-}
-
-void qt_nanosleep(timespec amount)
-{
- // Mac doesn't have clock_nanosleep, but it does have nanosleep.
- // nanosleep is POSIX.1-1993
-
- int r;
- EINTR_LOOP(r, nanosleep(&amount, &amount));
-}
-
-void QElapsedTimer::start() noexcept
-{
- t1 = mach_absolute_time();
- t2 = 0;
-}
-
-qint64 QElapsedTimer::restart() noexcept
-{
- qint64 old = t1;
- t1 = mach_absolute_time();
- t2 = 0;
-
- return absoluteToMSecs(t1 - old);
-}
-
-qint64 QElapsedTimer::nsecsElapsed() const noexcept
-{
- uint64_t cpu_time = mach_absolute_time();
- return absoluteToNSecs(cpu_time - t1);
-}
-
-qint64 QElapsedTimer::elapsed() const noexcept
-{
- uint64_t cpu_time = mach_absolute_time();
- return absoluteToMSecs(cpu_time - t1);
-}
-
-qint64 QElapsedTimer::msecsSinceReference() const noexcept
-{
- return absoluteToMSecs(t1);
-}
-
-qint64 QElapsedTimer::msecsTo(const QElapsedTimer &other) const noexcept
-{
- return absoluteToMSecs(other.t1 - t1);
-}
-
-qint64 QElapsedTimer::secsTo(const QElapsedTimer &other) const noexcept
-{
- return msecsTo(other) / 1000;
-}
-
-bool operator<(const QElapsedTimer &v1, const QElapsedTimer &v2) noexcept
-{
- return v1.t1 < v2.t1;
-}
-
-QDeadlineTimer QDeadlineTimer::current(Qt::TimerType timerType) noexcept
-{
- static_assert(!QDeadlineTimerNanosecondsInT2);
- QDeadlineTimer result;
- result.type = timerType;
- result.t1 = absoluteToNSecs(mach_absolute_time());
- return result;
-}
-
-QT_END_NAMESPACE
diff --git a/src/corelib/kernel/qelapsedtimer_unix.cpp b/src/corelib/kernel/qelapsedtimer_unix.cpp
deleted file mode 100644
index 07a47d40a7..0000000000
--- a/src/corelib/kernel/qelapsedtimer_unix.cpp
+++ /dev/null
@@ -1,259 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Copyright (C) 2016 Intel Corporation.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtCore module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#include "qelapsedtimer.h"
-#include "qdeadlinetimer.h"
-#include "qdeadlinetimer_p.h"
-#if defined(Q_OS_VXWORKS)
-#include "qfunctions_vxworks.h"
-#else
-#include <sys/time.h>
-#include <time.h>
-#endif
-#include <unistd.h>
-
-#include <qatomic.h>
-#include "private/qcore_unix_p.h"
-
-#if defined(QT_NO_CLOCK_MONOTONIC) || defined(QT_BOOTSTRAPPED)
-// turn off the monotonic clock
-# ifdef _POSIX_MONOTONIC_CLOCK
-# undef _POSIX_MONOTONIC_CLOCK
-# endif
-# define _POSIX_MONOTONIC_CLOCK -1
-#endif
-
-QT_BEGIN_NAMESPACE
-
-/*
- * Design:
- *
- * POSIX offers a facility to select the system's monotonic clock when getting
- * the current timestamp. Whereas the functions are mandatory in POSIX.1-2008,
- * the presence of a monotonic clock is a POSIX Option (see the document
- * http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap02.html#tag_02_01_06 )
- *
- * The macro _POSIX_MONOTONIC_CLOCK can therefore assume the following values:
- * -1 monotonic clock is never supported on this system
- * 0 monotonic clock might be supported, runtime check is needed
- * >1 (such as 200809L) monotonic clock is always supported
- *
- * The unixCheckClockType() function will return the clock to use: either
- * CLOCK_MONOTONIC or CLOCK_REALTIME. In the case the POSIX option has a value
- * of zero, then this function stores a static that contains the clock to be
- * used.
- *
- * There's one extra case, which is when CLOCK_REALTIME isn't defined. When
- * that's the case, we'll emulate the clock_gettime function with gettimeofday.
- *
- * Conforming to:
- * POSIX.1b-1993 section "Clocks and Timers"
- * included in UNIX98 (Single Unix Specification v2)
- * included in POSIX.1-2001
- * see http://pubs.opengroup.org/onlinepubs/9699919799/functions/clock_getres.html
- */
-
-#if !defined(CLOCK_REALTIME)
-# define CLOCK_REALTIME 0
-static inline void qt_clock_gettime(int, struct timespec *ts)
-{
- // support clock_gettime with gettimeofday
- struct timeval tv;
- gettimeofday(&tv, 0);
- ts->tv_sec = tv.tv_sec;
- ts->tv_nsec = tv.tv_usec * 1000;
-}
-
-static inline int regularClock()
-{
- return 0;
-}
-#else
-static inline void qt_clock_gettime(clockid_t clock, struct timespec *ts)
-{
- clock_gettime(clock, ts);
-}
-
-static inline clock_t regularClockCheck()
-{
- struct timespec regular_clock_resolution;
- int r = -1;
-
-# ifdef CLOCK_MONOTONIC
- // try the monotonic clock
- r = clock_getres(CLOCK_MONOTONIC, &regular_clock_resolution);
-
-# ifdef Q_OS_LINUX
- // Despite glibc claiming that we should check at runtime, the Linux kernel
- // always supports the monotonic clock
- Q_ASSERT(r == 0);
- return CLOCK_MONOTONIC;
-# endif
-
- if (r == 0)
- return CLOCK_MONOTONIC;
-# endif
-
- // no monotonic, try the realtime clock
- r = clock_getres(CLOCK_REALTIME, &regular_clock_resolution);
- Q_ASSERT(r == 0);
- return CLOCK_REALTIME;
-}
-
-static inline clock_t regularClock()
-{
- static const clock_t clock = regularClockCheck();
- return clock;
-}
-#endif
-
-bool QElapsedTimer::isMonotonic() noexcept
-{
- return clockType() == MonotonicClock;
-}
-
-QElapsedTimer::ClockType QElapsedTimer::clockType() noexcept
-{
- return regularClock() == CLOCK_REALTIME ? SystemTime : MonotonicClock;
-}
-
-static inline void do_gettime(qint64 *sec, qint64 *frac)
-{
- timespec ts;
- qt_clock_gettime(regularClock(), &ts);
- *sec = ts.tv_sec;
- *frac = ts.tv_nsec;
-}
-
-// used in qcore_unix.cpp and qeventdispatcher_unix.cpp
-struct timespec qt_gettime() noexcept
-{
- qint64 sec, frac;
- do_gettime(&sec, &frac);
-
- timespec tv;
- tv.tv_sec = sec;
- tv.tv_nsec = frac;
-
- return tv;
-}
-
-void qt_nanosleep(timespec amount)
-{
- // We'd like to use clock_nanosleep.
- //
- // But clock_nanosleep is from POSIX.1-2001 and both are *not*
- // affected by clock changes when using relative sleeps, even for
- // CLOCK_REALTIME.
- //
- // nanosleep is POSIX.1-1993
-
- int r;
- EINTR_LOOP(r, nanosleep(&amount, &amount));
-}
-
-static qint64 elapsedAndRestart(qint64 sec, qint64 frac,
- qint64 *nowsec, qint64 *nowfrac)
-{
- do_gettime(nowsec, nowfrac);
- sec = *nowsec - sec;
- frac = *nowfrac - frac;
- return (sec * Q_INT64_C(1000000000) + frac) / Q_INT64_C(1000000);
-}
-
-void QElapsedTimer::start() noexcept
-{
- do_gettime(&t1, &t2);
-}
-
-qint64 QElapsedTimer::restart() noexcept
-{
- return elapsedAndRestart(t1, t2, &t1, &t2);
-}
-
-qint64 QElapsedTimer::nsecsElapsed() const noexcept
-{
- qint64 sec, frac;
- do_gettime(&sec, &frac);
- sec = sec - t1;
- frac = frac - t2;
- return sec * Q_INT64_C(1000000000) + frac;
-}
-
-qint64 QElapsedTimer::elapsed() const noexcept
-{
- return nsecsElapsed() / Q_INT64_C(1000000);
-}
-
-qint64 QElapsedTimer::msecsSinceReference() const noexcept
-{
- return t1 * Q_INT64_C(1000) + t2 / Q_INT64_C(1000000);
-}
-
-qint64 QElapsedTimer::msecsTo(const QElapsedTimer &other) const noexcept
-{
- qint64 secs = other.t1 - t1;
- qint64 fraction = other.t2 - t2;
- return (secs * Q_INT64_C(1000000000) + fraction) / Q_INT64_C(1000000);
-}
-
-qint64 QElapsedTimer::secsTo(const QElapsedTimer &other) const noexcept
-{
- return other.t1 - t1;
-}
-
-bool operator<(const QElapsedTimer &v1, const QElapsedTimer &v2) noexcept
-{
- return v1.t1 < v2.t1 || (v1.t1 == v2.t1 && v1.t2 < v2.t2);
-}
-
-QDeadlineTimer QDeadlineTimer::current(Qt::TimerType timerType) noexcept
-{
- static_assert(QDeadlineTimerNanosecondsInT2);
- QDeadlineTimer result;
- qint64 cursec, curnsec;
- do_gettime(&cursec, &curnsec);
- result.t1 = cursec;
- result.t2 = curnsec;
- result.type = timerType;
- return result;
-}
-
-QT_END_NAMESPACE
diff --git a/src/corelib/kernel/qelapsedtimer_win.cpp b/src/corelib/kernel/qelapsedtimer_win.cpp
deleted file mode 100644
index deeeb05921..0000000000
--- a/src/corelib/kernel/qelapsedtimer_win.cpp
+++ /dev/null
@@ -1,172 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtCore module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#include "qelapsedtimer.h"
-#include "qdeadlinetimer.h"
-#include "qdeadlinetimer_p.h"
-#include <qt_windows.h>
-
-QT_BEGIN_NAMESPACE
-
-// Result of QueryPerformanceFrequency, 0 indicates that the high resolution timer is unavailable
-static quint64 counterFrequency = 0;
-
-static void resolveCounterFrequency()
-{
- static bool done = false;
- if (done)
- return;
-
- // Retrieve the number of high-resolution performance counter ticks per second
- LARGE_INTEGER frequency;
- if (!QueryPerformanceFrequency(&frequency)) {
- qFatal("QueryPerformanceFrequency failed, even though Microsoft documentation promises it wouldn't.");
- counterFrequency = 0;
- } else {
- counterFrequency = frequency.QuadPart;
- }
-
- done = true;
-}
-
-static inline qint64 ticksToNanoseconds(qint64 ticks)
-{
- if (counterFrequency > 0) {
- // QueryPerformanceCounter uses an arbitrary frequency
- qint64 seconds = ticks / counterFrequency;
- qint64 nanoSeconds = (ticks - seconds * counterFrequency) * 1000000000 / counterFrequency;
- return seconds * 1000000000 + nanoSeconds;
- }
- // GetTickCount(64) returns milliseconds
- return ticks * 1000000;
-}
-
-
-static quint64 getTickCount()
-{
- resolveCounterFrequency();
-
- // This avoids a division by zero and disables the high performance counter if it's not available
- if (counterFrequency > 0) {
- LARGE_INTEGER counter;
-
- bool ok = QueryPerformanceCounter(&counter);
- Q_ASSERT_X(ok, "QElapsedTimer::start()",
- "QueryPerformanceCounter failed, although QueryPerformanceFrequency succeeded.");
- Q_UNUSED(ok);
- return counter.QuadPart;
- }
-
- return GetTickCount64();
-}
-
-quint64 qt_msectime()
-{
- return ticksToNanoseconds(getTickCount()) / 1000000;
-}
-
-QElapsedTimer::ClockType QElapsedTimer::clockType() noexcept
-{
- resolveCounterFrequency();
-
- return counterFrequency > 0 ? PerformanceCounter : TickCounter;
-}
-
-bool QElapsedTimer::isMonotonic() noexcept
-{
- return true;
-}
-
-void QElapsedTimer::start() noexcept
-{
- t1 = getTickCount();
- t2 = 0;
-}
-
-qint64 QElapsedTimer::restart() noexcept
-{
- qint64 oldt1 = t1;
- t1 = getTickCount();
- t2 = 0;
- return ticksToNanoseconds(t1 - oldt1) / 1000000;
-}
-
-qint64 QElapsedTimer::nsecsElapsed() const noexcept
-{
- qint64 elapsed = getTickCount() - t1;
- return ticksToNanoseconds(elapsed);
-}
-
-qint64 QElapsedTimer::elapsed() const noexcept
-{
- qint64 elapsed = getTickCount() - t1;
- return ticksToNanoseconds(elapsed) / 1000000;
-}
-
-qint64 QElapsedTimer::msecsSinceReference() const noexcept
-{
- return ticksToNanoseconds(t1) / 1000000;
-}
-
-qint64 QElapsedTimer::msecsTo(const QElapsedTimer &other) const noexcept
-{
- qint64 difference = other.t1 - t1;
- return ticksToNanoseconds(difference) / 1000000;
-}
-
-qint64 QElapsedTimer::secsTo(const QElapsedTimer &other) const noexcept
-{
- return msecsTo(other) / 1000;
-}
-
-bool operator<(const QElapsedTimer &v1, const QElapsedTimer &v2) noexcept
-{
- return (v1.t1 - v2.t1) < 0;
-}
-
-QDeadlineTimer QDeadlineTimer::current(Qt::TimerType timerType) noexcept
-{
- static_assert(!QDeadlineTimerNanosecondsInT2);
- QDeadlineTimer result;
- result.t1 = ticksToNanoseconds(getTickCount());
- result.type = timerType;
- return result;
-}
-
-QT_END_NAMESPACE
diff --git a/src/corelib/kernel/qeventdispatcher_cf.mm b/src/corelib/kernel/qeventdispatcher_cf.mm
index 6f6be9a73b..042b0651f4 100644
--- a/src/corelib/kernel/qeventdispatcher_cf.mm
+++ b/src/corelib/kernel/qeventdispatcher_cf.mm
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtCore module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// 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 "qeventdispatcher_cf_p.h"
@@ -205,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)
@@ -231,7 +195,7 @@ void QEventDispatcherCoreFoundation::startingUp()
QEventDispatcherCoreFoundation::~QEventDispatcherCoreFoundation()
{
invalidateTimer();
- qDeleteAll(m_timerInfoList);
+ m_timerInfoList.clearTimers();
m_cfSocketNotifier.removeSocketNotifiers();
}
@@ -542,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;
@@ -579,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()
@@ -602,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,
@@ -623,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
@@ -647,7 +617,7 @@ void QEventDispatcherCoreFoundation::invalidateTimer()
m_runLoopTimer = 0;
}
+QT_END_NAMESPACE
+
#include "qeventdispatcher_cf.moc"
#include "moc_qeventdispatcher_cf_p.cpp"
-
-QT_END_NAMESPACE
diff --git a/src/corelib/kernel/qeventdispatcher_cf_p.h b/src/corelib/kernel/qeventdispatcher_cf_p.h
index b79c82264c..3575a6fb39 100644
--- a/src/corelib/kernel/qeventdispatcher_cf_p.h
+++ b/src/corelib/kernel/qeventdispatcher_cf_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtCore module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// 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
/****************************************************************************
**
@@ -99,8 +63,8 @@ Q_FORWARD_DECLARE_OBJC_CLASS(QT_MANGLE_NAMESPACE(RunLoopModeTracker));
QT_BEGIN_NAMESPACE
namespace QtPrivate {
-Q_CORE_EXPORT Q_DECLARE_LOGGING_CATEGORY(lcEventDispatcher);
-Q_CORE_EXPORT Q_DECLARE_LOGGING_CATEGORY(lcEventDispatcherTimers)
+Q_DECLARE_EXPORTED_LOGGING_CATEGORY(lcEventDispatcher, Q_CORE_EXPORT)
+Q_DECLARE_EXPORTED_LOGGING_CATEGORY(lcEventDispatcherTimers, Q_CORE_EXPORT)
}
class QEventDispatcherCoreFoundation;
@@ -204,7 +168,7 @@ private:
CFRunLoopObserverRef m_observer;
};
-class Q_CORE_EXPORT QEventDispatcherCoreFoundation : public QAbstractEventDispatcher
+class Q_CORE_EXPORT QEventDispatcherCoreFoundation : public QAbstractEventDispatcherV2
{
Q_OBJECT
@@ -218,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;
@@ -240,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 3964c1ceac..1e906c4b27 100644
--- a/src/corelib/kernel/qeventdispatcher_glib.cpp
+++ b/src/corelib/kernel/qeventdispatcher_glib.cpp
@@ -1,55 +1,22 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtCore module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// 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 "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
@@ -77,12 +44,12 @@ static gboolean socketNotifierSourceCheck(GSource *source)
GSocketNotifierSource *src = reinterpret_cast<GSocketNotifierSource *>(source);
bool pending = false;
- for (int i = 0; !pending && i < src->pollfds.count(); ++i) {
+ for (int i = 0; !pending && i < src->pollfds.size(); ++i) {
GPollFDWithQSocketNotifier *p = src->pollfds.at(i);
if (p->pollfd.revents & G_IO_NVAL) {
// disable the invalid socket notifier
- static const char *t[] = { "Read", "Write", "Exception" };
+ const char * const t[] = { "Read", "Write", "Exception" };
qWarning("QSocketNotifier: Invalid socket %d and type '%s', disabling...",
p->pollfd.fd, t[int(p->socketNotifier->type())]);
// ### note, modifies src->pollfds!
@@ -101,7 +68,7 @@ static gboolean socketNotifierSourceDispatch(GSource *source, GSourceFunc, gpoin
QEvent event(QEvent::SockAct);
GSocketNotifierSource *src = reinterpret_cast<GSocketNotifierSource *>(source);
- for (src->activeNotifierPos = 0; src->activeNotifierPos < src->pollfds.count();
+ for (src->activeNotifierPos = 0; src->activeNotifierPos < src->pollfds.size();
++src->activeNotifierPos) {
GPollFDWithQSocketNotifier *p = src->pollfds.at(src->activeNotifierPos);
@@ -112,7 +79,7 @@ static gboolean socketNotifierSourceDispatch(GSource *source, GSourceFunc, gpoin
return true; // ??? don't remove, right?
}
-static GSourceFuncs socketNotifierSourceFuncs = {
+Q_CONSTINIT static GSourceFuncs socketNotifierSourceFuncs = {
socketNotifierSourcePrepare,
socketNotifierSourceCheck,
socketNotifierSourceDispatch,
@@ -131,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);
}
@@ -146,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)
@@ -186,7 +152,7 @@ static gboolean timerSourceDispatch(GSource *source, GSourceFunc, gpointer)
return true; // ??? don't remove, right again?
}
-static GSourceFuncs timerSourceFuncs = {
+Q_CONSTINIT static GSourceFuncs timerSourceFuncs = {
timerSourcePrepare,
timerSourceCheck,
timerSourceDispatch,
@@ -233,7 +199,7 @@ static gboolean idleTimerSourceDispatch(GSource *source, GSourceFunc, gpointer)
return true;
}
-static GSourceFuncs idleTimerSourceFuncs = {
+Q_CONSTINIT static GSourceFuncs idleTimerSourceFuncs = {
idleTimerSourcePrepare,
idleTimerSourceCheck,
idleTimerSourceDispatch,
@@ -281,7 +247,7 @@ static gboolean postEventSourceDispatch(GSource *s, GSourceFunc, gpointer)
return true; // i dunno, george...
}
-static GSourceFuncs postEventSourceFuncs = {
+Q_CONSTINIT static GSourceFuncs postEventSourceFuncs = {
postEventSourcePrepare,
postEventSourceCheck,
postEventSourceDispatch,
@@ -296,7 +262,7 @@ QEventDispatcherGlibPrivate::QEventDispatcherGlibPrivate(GMainContext *context)
{
#if GLIB_MAJOR_VERSION == 2 && GLIB_MINOR_VERSION < 32
if (qEnvironmentVariableIsEmpty("QT_NO_THREADED_GLIB")) {
- static QBasicMutex mutex;
+ Q_CONSTINIT static QBasicMutex mutex;
QMutexLocker locker(&mutex);
if (!g_thread_supported())
g_thread_init(NULL);
@@ -361,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()
@@ -374,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);
@@ -384,7 +350,7 @@ QEventDispatcherGlib::~QEventDispatcherGlib()
d->idleTimerSource = nullptr;
// destroy socket notifier source
- for (int i = 0; i < d->socketNotifierSource->pollfds.count(); ++i) {
+ for (int i = 0; i < d->socketNotifierSource->pollfds.size(); ++i) {
GPollFDWithQSocketNotifier *p = d->socketNotifierSource->pollfds[i];
g_source_remove_poll(&d->socketNotifierSource->source, &p->pollfd);
delete p;
@@ -441,7 +407,7 @@ bool QEventDispatcherGlib::processEvents(QEventLoop::ProcessEventsFlags flags)
void QEventDispatcherGlib::registerSocketNotifier(QSocketNotifier *notifier)
{
Q_ASSERT(notifier);
- int sockfd = notifier->socket();
+ int sockfd = int(notifier->socket());
int type = notifier->type();
#ifndef QT_NO_DEBUG
if (sockfd < 0) {
@@ -481,8 +447,7 @@ void QEventDispatcherGlib::unregisterSocketNotifier(QSocketNotifier *notifier)
{
Q_ASSERT(notifier);
#ifndef QT_NO_DEBUG
- int sockfd = notifier->socket();
- if (sockfd < 0) {
+ if (notifier->socket() < 0) {
qWarning("QSocketNotifier: Internal error");
return;
} else if (notifier->thread() != thread()
@@ -494,7 +459,7 @@ void QEventDispatcherGlib::unregisterSocketNotifier(QSocketNotifier *notifier)
Q_D(QEventDispatcherGlib);
- for (int i = 0; i < d->socketNotifierSource->pollfds.count(); ++i) {
+ for (int i = 0; i < d->socketNotifierSource->pollfds.size(); ++i) {
GPollFDWithQSocketNotifier *p = d->socketNotifierSource->pollfds.at(i);
if (p->socketNotifier == notifier) {
// found it
@@ -512,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()) {
@@ -528,10 +494,10 @@ void QEventDispatcherGlib::registerTimer(int timerId, qint64 interval, Qt::Timer
d->timerSource->timerList.registerTimer(timerId, interval, timerType, object);
}
-bool QEventDispatcherGlib::unregisterTimer(int timerId)
+bool QEventDispatcherGlib::unregisterTimer(Qt::TimerId timerId)
{
#ifndef QT_NO_DEBUG
- if (timerId < 1) {
+ if (qToUnderlying(timerId) < 1) {
qWarning("QEventDispatcherGlib::unregisterTimer: invalid argument");
return false;
} else if (thread() != QThread::currentThread()) {
@@ -560,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()
@@ -606,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 63bc4109a8..30783d3858 100644
--- a/src/corelib/kernel/qeventdispatcher_glib_p.h
+++ b/src/corelib/kernel/qeventdispatcher_glib_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtCore module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// 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 QEVENTDISPATCHER_GLIB_P_H
#define QEVENTDISPATCHER_GLIB_P_H
@@ -60,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)
@@ -75,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 c23fab2eef..21bd224415 100644
--- a/src/corelib/kernel/qeventdispatcher_unix.cpp
+++ b/src/corelib/kernel/qeventdispatcher_unix.cpp
@@ -1,50 +1,13 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Copyright (C) 2016 Intel Corporation.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtCore module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// Copyright (C) 2016 Intel Corporation.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qplatformdefs.h"
#include "qcoreapplication.h"
-#include "qpair.h"
+#include "qhash.h"
#include "qsocketnotifier.h"
#include "qthread.h"
-#include "qelapsedtimer.h"
#include "qeventdispatcher_unix_p.h"
#include <private/qthread_p.h>
@@ -55,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
@@ -91,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()
@@ -103,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)
@@ -140,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;
}
@@ -172,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);
@@ -201,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
@@ -231,7 +179,7 @@ QEventDispatcherUNIXPrivate::QEventDispatcherUNIXPrivate()
QEventDispatcherUNIXPrivate::~QEventDispatcherUNIXPrivate()
{
// cleanup timers
- qDeleteAll(timerList);
+ timerList.clearTimers();
}
void QEventDispatcherUNIXPrivate::setSocketNotifierPending(QSocketNotifier *notifier)
@@ -251,7 +199,7 @@ int QEventDispatcherUNIXPrivate::activateTimers()
void QEventDispatcherUNIXPrivate::markPendingSocketNotifiers()
{
- for (const pollfd &pfd : qAsConst(pollfds)) {
+ for (const pollfd &pfd : std::as_const(pollfds)) {
if (pfd.fd < 0 || pfd.revents == 0)
continue;
@@ -309,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()
@@ -322,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()) {
@@ -341,10 +289,10 @@ void QEventDispatcherUNIX::registerTimer(int timerId, qint64 interval, Qt::Timer
/*!
\internal
*/
-bool QEventDispatcherUNIX::unregisterTimer(int timerId)
+bool QEventDispatcherUNIX::unregisterTimer(Qt::TimerId timerId)
{
#ifndef QT_NO_DEBUG
- if (timerId < 1) {
+ if (qToUnderlying(timerId) < 1) {
qWarning("QEventDispatcherUNIX::unregisterTimer: invalid argument");
return false;
} else if (thread() != QThread::currentThread()) {
@@ -376,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);
@@ -481,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));
@@ -498,10 +454,11 @@ 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:
- perror("qt_safe_poll");
+ qErrnoWarning("qt_safe_poll");
+ if (QT_CONFIG(poll_exit_on_error))
+ abort();
break;
case 0:
break;
@@ -519,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 38115c1e2c..6596e998c9 100644
--- a/src/corelib/kernel/qeventdispatcher_unix_p.h
+++ b/src/corelib/kernel/qeventdispatcher_unix_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtCore module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// 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 QEVENTDISPATCHER_UNIX_P_H
#define QEVENTDISPATCHER_UNIX_P_H
@@ -56,6 +20,7 @@
#include "private/qabstracteventdispatcher_p.h"
#include "private/qcore_unix_p.h"
#include "QtCore/qvarlengtharray.h"
+#include "QtCore/qhash.h"
#include "private/qtimerinfo_unix_p.h"
QT_BEGIN_NAMESPACE
@@ -86,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)
@@ -110,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 5004d0079c..4aa435b64b 100644
--- a/src/corelib/kernel/qeventdispatcher_wasm.cpp
+++ b/src/corelib/kernel/qeventdispatcher_wasm.cpp
@@ -1,66 +1,33 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtCore module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 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 "qeventdispatcher_wasm_p.h"
+#include <QtCore/private/qabstracteventdispatcher_p.h> // for qGlobalPostedEventsCount()
#include <QtCore/qcoreapplication.h>
#include <QtCore/qthread.h>
+#include <QtCore/qsocketnotifier.h>
+#include <QtCore/private/qstdweb_p.h>
#include "emscripten.h"
#include <emscripten/html5.h>
#include <emscripten/threading.h>
+#include <emscripten/val.h>
+
+using namespace std::chrono;
+using namespace std::chrono_literals;
QT_BEGIN_NAMESPACE
// using namespace emscripten;
-extern int qGlobalPostedEventsCount(); // from qapplication.cpp
Q_LOGGING_CATEGORY(lcEventDispatcher, "qt.eventdispatcher");
Q_LOGGING_CATEGORY(lcEventDispatcherTimers, "qt.eventdispatcher.timers");
-#ifdef QT_HAVE_EMSCRIPTEN_ASYNCIFY
-
-// Enable/disable JavaScript-side debugging
-#if 0
- #define QT_ASYNCIFY_DEBUG(X) out(X)
+#if QT_CONFIG(thread)
+#define LOCK_GUARD(M) std::lock_guard<std::mutex> lock(M)
#else
- #define QT_ASYNCIFY_DEBUG(X)
+#define LOCK_GUARD(M)
#endif
// Emscripten asyncify currently supports one level of suspend -
@@ -69,30 +36,127 @@ Q_LOGGING_CATEGORY(lcEventDispatcherTimers, "qt.eventdispatcher.timers");
// track Qts own usage of asyncify.
static bool g_is_asyncify_suspended = false;
+#if defined(QT_STATIC)
+
+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, (), {
- QT_ASYNCIFY_DEBUG("qt_asyncify_suspend_js");
- let sleepFn = (wakeUp) = >
- {
- QT_ASYNCIFY_DEBUG("setting Module.qtAsyncifyWakeUp")
- Module.qtAsyncifyWakeUp = wakeUp; // ### not "Module" any more
+ if (Module.qtSuspendId === undefined)
+ Module.qtSuspendId = 0;
+ let sleepFn = (wakeUp) => {
+ Module.qtAsyncifyWakeUp = wakeUp;
};
+ ++Module.qtSuspendId;
return Asyncify.handleSleep(sleepFn);
});
EM_JS(void, qt_asyncify_resume_js, (), {
- QT_ASYNCIFY_DEBUG("qt_asyncify_resume_js");
let wakeUp = Module.qtAsyncifyWakeUp;
- if (wakeUp == = undefined) {
- QT_ASYNCIFY_DEBUG("qt_asyncify_resume_js no wakeup fn set - did not wake");
+ if (wakeUp == undefined)
return;
- }
Module.qtAsyncifyWakeUp = undefined;
+ const suspendId = Module.qtSuspendId;
// Delayed wakeup with zero-timer. Workaround/fix for
// https://github.com/emscripten-core/emscripten/issues/10515
- setTimeout(wakeUp);
- QT_ASYNCIFY_DEBUG("qt_asyncify_resume_js done");
+ setTimeout(() => {
+ // Another suspend occurred while the timeout was in queue.
+ if (Module.qtSuspendId !== suspendId)
+ return;
+ wakeUp();
+ });
});
+// clang-format on
+
+#else
+
+// EM_JS is not supported for side modules; disable asyncify
+
+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();
+}
+
+void qt_asyncify_resume_js()
+{
+ Q_UNREACHABLE();
+}
+
+#endif // defined(QT_STATIC)
// Suspends the main thread until qt_asyncify_resume() is called. Returns
// false immediately if Qt has already suspended the main thread (recursive
@@ -109,36 +173,27 @@ bool qt_asyncify_suspend()
// Wakes any currently suspended main thread. Returns true if the main
// thread was suspended, in which case it will now be asynchronously woken.
-bool qt_asyncify_resume()
+void qt_asyncify_resume()
{
if (!g_is_asyncify_suspended)
- return false;
+ return;
g_is_asyncify_suspended = false;
qt_asyncify_resume_js();
- return true;
-}
-
-// Yields control to the browser, so that it can process events. Must
-// be called on the main thread. Returns false immediately if Qt has
-// already suspended the main thread. Returns true after yielding.
-bool qt_asyncify_yield()
-{
- if (g_is_asyncify_suspended)
- return false;
- emscripten_sleep(0);
- return true;
}
-#endif // QT_HAVE_EMSCRIPTEN_ASYNCIFY
-QEventDispatcherWasm *QEventDispatcherWasm::g_mainThreadEventDispatcher = nullptr;
+Q_CONSTINIT QEventDispatcherWasm *QEventDispatcherWasm::g_mainThreadEventDispatcher = nullptr;
#if QT_CONFIG(thread)
-QVector<QEventDispatcherWasm *> QEventDispatcherWasm::g_secondaryThreadEventDispatchers;
-std::mutex QEventDispatcherWasm::g_secondaryThreadEventDispatchersMutex;
+Q_CONSTINIT QVector<QEventDispatcherWasm *> QEventDispatcherWasm::g_secondaryThreadEventDispatchers;
+Q_CONSTINIT std::mutex QEventDispatcherWasm::g_staticDataMutex;
+emscripten::ProxyingQueue QEventDispatcherWasm::g_proxyingQueue;
+pthread_t QEventDispatcherWasm::g_mainThread;
#endif
+// ### dynamic initialization:
+std::multimap<int, QSocketNotifier *> QEventDispatcherWasm::g_socketNotifiers;
+std::map<int, QEventDispatcherWasm::SocketReadyState> QEventDispatcherWasm::g_socketState;
QEventDispatcherWasm::QEventDispatcherWasm()
- : QAbstractEventDispatcher()
{
// QEventDispatcherWasm operates in two main modes:
// - On the main thread:
@@ -160,9 +215,18 @@ QEventDispatcherWasm::QEventDispatcherWasm()
// dispatchers so we set a global pointer to it.
Q_ASSERT(g_mainThreadEventDispatcher == nullptr);
g_mainThreadEventDispatcher = this;
+#if QT_CONFIG(thread)
+ g_mainThread = pthread_self();
+#endif
+
+ // Call the "onLoaded" JavaScript callback, unless startup tasks
+ // have been registered which should complete first. Run async
+ // to make sure event dispatcher construction (in particular any
+ // subclass construction) has completed first.
+ runAsync(callOnLoadedIfRequired);
} else {
#if QT_CONFIG(thread)
- std::lock_guard<std::mutex> lock(g_secondaryThreadEventDispatchersMutex);
+ std::lock_guard<std::mutex> lock(g_staticDataMutex);
g_secondaryThreadEventDispatchers.append(this);
#endif
}
@@ -176,14 +240,26 @@ QEventDispatcherWasm::~QEventDispatcherWasm()
#if QT_CONFIG(thread)
if (isSecondaryThreadEventDispatcher()) {
- std::lock_guard<std::mutex> lock(g_secondaryThreadEventDispatchersMutex);
+ std::lock_guard<std::mutex> lock(g_staticDataMutex);
g_secondaryThreadEventDispatchers.remove(g_secondaryThreadEventDispatchers.indexOf(this));
} else
#endif
{
if (m_timerId > 0)
emscripten_clear_timeout(m_timerId);
+ if (!g_socketNotifiers.empty()) {
+ qWarning("QEventDispatcherWasm: main thread event dispatcher deleted with active socket notifiers");
+ clearEmscriptenSocketCallbacks();
+ g_socketNotifiers.clear();
+ }
g_mainThreadEventDispatcher = nullptr;
+ if (!g_socketNotifiers.empty()) {
+ qWarning("QEventDispatcherWasm: main thread event dispatcher deleted with active socket notifiers");
+ clearEmscriptenSocketCallbacks();
+ g_socketNotifiers.clear();
+ }
+
+ g_socketState.clear();
}
}
@@ -197,61 +273,92 @@ bool QEventDispatcherWasm::isSecondaryThreadEventDispatcher()
return this != g_mainThreadEventDispatcher;
}
-bool QEventDispatcherWasm::processEvents(QEventLoop::ProcessEventsFlags flags)
+bool QEventDispatcherWasm::isValidEventDispatcherPointer(QEventDispatcherWasm *eventDispatcher)
{
- emit awake();
+ if (eventDispatcher == g_mainThreadEventDispatcher)
+ return true;
+#if QT_CONFIG(thread)
+ if (g_secondaryThreadEventDispatchers.contains(eventDispatcher))
+ return true;
+#endif
+ return false;
+}
- bool hasPendingEvents = qGlobalPostedEventsCount() > 0;
+bool QEventDispatcherWasm::processEvents(QEventLoop::ProcessEventsFlags flags)
+{
+ qCDebug(lcEventDispatcher) << "QEventDispatcherWasm::processEvents flags" << flags;
- qCDebug(lcEventDispatcher) << "QEventDispatcherWasm::processEvents flags" << flags
- << "pending events" << hasPendingEvents;
+ emit awake();
if (isMainThreadEventDispatcher()) {
if (flags & QEventLoop::DialogExec)
handleDialogExec();
- else if (flags & QEventLoop::EventLoopExec)
- handleEventLoopExec();
+ else if (flags & QEventLoop::ApplicationExec)
+ handleApplicationExec();
}
- if (!(flags & QEventLoop::ExcludeUserInputEvents))
- pollForNativeEvents();
+#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
- hasPendingEvents = qGlobalPostedEventsCount() > 0;
+ processPostedEvents();
- if (!hasPendingEvents && (flags & QEventLoop::WaitForMoreEvents))
- waitForForEvents();
+ // The processPostedEvents() call above may process an event which deletes the
+ // application object and the event dispatcher; stop event processing in that case.
+ if (!isValidEventDispatcherPointer(this))
+ return false;
if (m_interrupted) {
m_interrupted = false;
return false;
}
+ if (flags & QEventLoop::WaitForMoreEvents)
+ wait();
+
if (m_processTimers) {
m_processTimers = false;
processTimers();
}
- hasPendingEvents = qGlobalPostedEventsCount() > 0;
- QCoreApplication::sendPostedEvents();
- return hasPendingEvents;
+ return false;
}
void QEventDispatcherWasm::registerSocketNotifier(QSocketNotifier *notifier)
{
- Q_UNUSED(notifier);
- qWarning("QEventDispatcherWasm::registerSocketNotifier: socket notifiers are not supported");
+ LOCK_GUARD(g_staticDataMutex);
+
+ bool wasEmpty = g_socketNotifiers.empty();
+ g_socketNotifiers.insert({notifier->socket(), notifier});
+ if (wasEmpty)
+ runOnMainThread([] { setEmscriptenSocketCallbacks(); });
}
void QEventDispatcherWasm::unregisterSocketNotifier(QSocketNotifier *notifier)
{
- Q_UNUSED(notifier);
- qWarning("QEventDispatcherWasm::unregisterSocketNotifier: socket notifiers are not supported");
+ LOCK_GUARD(g_staticDataMutex);
+
+ auto notifiers = g_socketNotifiers.equal_range(notifier->socket());
+ for (auto it = notifiers.first; it != notifiers.second; ++it) {
+ if (it->second == notifier) {
+ g_socketNotifiers.erase(it);
+ break;
+ }
+ }
+
+ if (g_socketNotifiers.empty())
+ 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()) {
@@ -260,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()) {
@@ -279,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();
@@ -306,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()
@@ -332,43 +439,27 @@ void QEventDispatcherWasm::interrupt()
void QEventDispatcherWasm::wakeUp()
{
-#if QT_CONFIG(thread)
- if (isSecondaryThreadEventDispatcher()) {
- std::lock_guard<std::mutex> lock(m_mutex);
- m_wakeUpCalled = true;
- m_moreEvents.notify_one();
- return;
- }
-#endif
-
-#ifdef QT_HAVE_EMSCRIPTEN_ASYNCIFY
- // The main thread may be asyncify-blocked in processEvents(). If so resume it.
- if (qt_asyncify_resume()) // ### safe to call from secondary thread?
- return;
-#endif
-
- {
-#if QT_CONFIG(thread)
- // This function can be called from any thread (via wakeUp()),
- // so we need to lock access to m_pendingProcessEvents.
- std::lock_guard<std::mutex> lock(m_mutex);
-#endif
- if (m_pendingProcessEvents)
- return;
- m_pendingProcessEvents = true;
- }
-
-#if QT_CONFIG(thread)
- if (!emscripten_is_main_runtime_thread()) {
- runOnMainThread([this](){
- QEventDispatcherWasm::callProcessEvents(this);
+ // The event dispatcher thread may be blocked or suspended by
+ // wait(), or control may have been returned to the browser's
+ // event loop. Make sure the thread is unblocked or make it
+ // process events.
+ bool wasBlocked = wakeEventDispatcherThread();
+ // 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)
+ return;
+ m_pendingProcessEvents = true;
+ }
+ runOnMainThreadAsync([this](){
+ QEventDispatcherWasm::callProcessPostedEvents(this);
});
- } else
-#endif
- emscripten_async_call(&QEventDispatcherWasm::callProcessEvents, this, 0);
+ }
}
-void QEventDispatcherWasm::handleEventLoopExec()
+void QEventDispatcherWasm::handleApplicationExec()
{
// Start the main loop, and then stop it on the first callback. This
// is done for the "simulateInfiniteLoop" functionality where
@@ -378,75 +469,108 @@ void QEventDispatcherWasm::handleEventLoopExec()
// 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()
{
-#if !QT_HAVE_EMSCRIPTEN_ASYNCIFY
- qWarning() << "Warning: dialog exec() is not supported on Qt for WebAssembly in this"
- << "configuration. Please use show() instead, or enable experimental support"
- << "for asyncify.\n"
- << "When using exec() (without asyncify) the dialog will show, the user can interact"
- << "with it and the appropriate signals will be emitted on close. However, the"
- << "exec() call never returns, stack content at the time of the exec() call"
- << "is leaked, and the exec() call may interfere with input event processing";
- emscripten_sleep(1); // This call never returns
-#endif
- // For the asyncify case we do nothing here and wait for events in waitForForEvents()
+ if (!useAsyncify()) {
+ qWarning() << "Warning: exec() is not supported on Qt for WebAssembly in this configuration. Please build"
+ << "with asyncify support, or use an asynchronous API like QDialog::open()";
+ emscripten_sleep(1); // This call never returns
+ }
+ // For the asyncify case we do nothing here and wait for events in wait()
}
-void QEventDispatcherWasm::pollForNativeEvents()
+// Blocks/suspends the calling thread. This is possible in two cases:
+// - Caller is a secondary thread: block on m_moreEvents
+// - Caller is the main thread and asyncify is enabled: suspend using qt_asyncify_suspend()
+// Returns false if the wait timed out.
+bool QEventDispatcherWasm::wait(int timeout)
{
- // Secondary thread event dispatchers do not support native events
- if (isSecondaryThreadEventDispatcher())
- return;
+#if QT_CONFIG(thread)
+ using namespace std::chrono_literals;
+ Q_ASSERT(QThread::currentThread() == thread());
-#if HAVE_EMSCRIPTEN_ASYNCIFY
- // Asyncify allows us to yield to the browser and have it process native events -
- // but this will fail if we are recursing and are already in a yield.
- bool didYield = qt_asyncify_yield();
- if (!didYield)
- qWarning("QEventDispatcherWasm::processEvents() did not asyncify process native events");
+ if (isSecondaryThreadEventDispatcher()) {
+ std::unique_lock<std::mutex> lock(m_mutex);
+
+ // 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;
+ }
#endif
+ Q_ASSERT(emscripten_is_main_runtime_thread());
+ Q_ASSERT(isMainThreadEventDispatcher());
+ if (useAsyncify()) {
+ if (timeout > 0)
+ qWarning() << "QEventDispatcherWasm asyncify wait with timeout is not supported; timeout will be ignored"; // FIXME
+
+ 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 {
+ qWarning("QEventLoop::WaitForMoreEvents is not supported on the main thread without asyncify");
+ Q_UNUSED(timeout);
+ }
+ return false;
}
-// Waits for more events. This is possible in two cases:
-// - On a secondary thread
-// - On the main thread iff asyncify is used
-// Returns true if waiting was possible (at which point it
-// has already happened).
-bool QEventDispatcherWasm::waitForForEvents()
+// Wakes a blocked/suspended event dispatcher thread. Returns true if the
+// thread is unblocked or was resumed, false if the thread state could not
+// be determined.
+bool QEventDispatcherWasm::wakeEventDispatcherThread()
{
#if QT_CONFIG(thread)
if (isSecondaryThreadEventDispatcher()) {
- std::unique_lock<std::mutex> lock(m_mutex);
- m_moreEvents.wait(lock, [=] { return m_wakeUpCalled; });
- m_wakeUpCalled = false;
+ std::lock_guard<std::mutex> lock(m_mutex);
+ m_wakeUpCalled = true;
+ m_moreEvents.notify_one();
return true;
}
#endif
+ Q_ASSERT(isMainThreadEventDispatcher());
+ if (useJspi()) {
- Q_ASSERT(emscripten_is_main_runtime_thread());
-
-#if QT_HAVE_EMSCRIPTEN_ASYNCIFY
- // We can block on the main thread using asyncify:
- bool didSuspend = qt_asyncify_suspend();
- if (!didSuspend)
- qWarning("QEventDispatcherWasm: current thread is already suspended; could not asyncify wait for events");
- return didSuspend;
+#if QT_CONFIG(thread)
+ return qstdweb::runTaskOnMainThread<bool>(
+ []() { return qt_jspi_can_resume_js() && qt_jspi_resume_js(); }, &g_proxyingQueue);
#else
- qWarning("QEventLoop::WaitForMoreEvents is not supported on the main thread without asyncify");
- return false;
+ 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 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());
@@ -454,19 +578,24 @@ 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)
return;
{
-#if QT_CONFIG(thread)
- std::lock_guard<std::mutex> lock(g_mainThreadEventDispatcher->m_mutex);
-#endif
+ 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()
@@ -490,47 +619,48 @@ void QEventDispatcherWasm::updateNativeTimer()
// access to m_timerInfo), and then call native API to set the new
// wakeup time on the main thread.
- auto timespecToNanosec = [](timespec ts) -> uint64_t {
- return ts.tv_sec * 1000 + ts.tv_nsec / (1000 * 1000);
- };
- timespec toWait;
- m_timerInfo->timerWait(toWait);
- uint64_t currentTime = timespecToNanosec(m_timerInfo->currentTime);
- uint64_t toWaitDuration = timespecToNanosec(toWait);
- uint64_t newTargetTime = currentTime + toWaitDuration;
-
- auto maintainNativeTimer = [this, 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 (m_timerTargetTime != 0 && newTargetTime >= m_timerTargetTime)
+ if (!wait) {
+ if (m_timerId > 0) {
+ emscripten_clear_timeout(m_timerId);
+ m_timerId = 0;
+ m_timerTargetTime = 0ms;
+ }
+ return;
+ }
+
+ 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;
};
// Update the native timer for this thread/dispatcher. This must be
// done on the main thread where we have access to native API.
+ runOnMainThread([this, maintainNativeTimer]() {
+ Q_ASSERT(emscripten_is_main_runtime_thread());
-#if QT_CONFIG(thread)
- if (isSecondaryThreadEventDispatcher()) {
- runOnMainThread([this, maintainNativeTimer]() {
- Q_ASSERT(emscripten_is_main_runtime_thread());
-
- // "this" may have been deleted, or may be about to be deleted.
- // Check if the pointer we have is still a valid event dispatcher,
- // and keep the mutex locked while updating the native timer to
- // prevent it from being deleted.
- std::lock_guard<std::mutex> lock(g_secondaryThreadEventDispatchersMutex);
- if (g_secondaryThreadEventDispatchers.contains(this))
- maintainNativeTimer();
- });
- } else
-#endif
- maintainNativeTimer();
+ // "this" may have been deleted, or may be about to be deleted.
+ // Check if the pointer we have is still a valid event dispatcher,
+ // and keep the mutex locked while updating the native timer to
+ // prevent it from being deleted.
+ LOCK_GUARD(g_staticDataMutex);
+ if (isValidEventDispatcherPointer(this))
+ maintainNativeTimer();
+ });
}
// Static timer activation callback. Must be called on the main thread
@@ -540,48 +670,331 @@ void QEventDispatcherWasm::callProcessTimers(void *context)
{
Q_ASSERT(emscripten_is_main_runtime_thread());
- // Bail out if Qt has been shut down
- if (!g_mainThreadEventDispatcher)
- return;
-
// Note: "context" may be a stale pointer here,
// take care before casting and dereferencing!
// 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;
}
// Wake and process timers on the secondary thread if this a secondary thread dispatcher
#if QT_CONFIG(thread)
- std::lock_guard<std::mutex> lock(g_secondaryThreadEventDispatchersMutex);
+ 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();
}
#endif
}
-#if QT_CONFIG(thread)
+void QEventDispatcherWasm::setEmscriptenSocketCallbacks()
+{
+ qCDebug(lcEventDispatcher) << "setEmscriptenSocketCallbacks";
+
+ emscripten_set_socket_error_callback(nullptr, QEventDispatcherWasm::socketError);
+ emscripten_set_socket_open_callback(nullptr, QEventDispatcherWasm::socketOpen);
+ emscripten_set_socket_listen_callback(nullptr, QEventDispatcherWasm::socketListen);
+ emscripten_set_socket_connection_callback(nullptr, QEventDispatcherWasm::socketConnection);
+ emscripten_set_socket_message_callback(nullptr, QEventDispatcherWasm::socketMessage);
+ emscripten_set_socket_close_callback(nullptr, QEventDispatcherWasm::socketClose);
+}
+
+void QEventDispatcherWasm::clearEmscriptenSocketCallbacks()
+{
+ qCDebug(lcEventDispatcher) << "clearEmscriptenSocketCallbacks";
+
+ emscripten_set_socket_error_callback(nullptr, nullptr);
+ emscripten_set_socket_open_callback(nullptr, nullptr);
+ emscripten_set_socket_listen_callback(nullptr, nullptr);
+ emscripten_set_socket_connection_callback(nullptr, nullptr);
+ emscripten_set_socket_message_callback(nullptr, nullptr);
+ emscripten_set_socket_close_callback(nullptr, nullptr);
+}
+
+void QEventDispatcherWasm::socketError(int socket, int err, const char* msg, void *context)
+{
+ Q_UNUSED(err);
+ Q_UNUSED(msg);
+ Q_UNUSED(context);
+
+ // Emscripten makes socket callbacks while the main thread is busy-waiting for a mutex,
+ // which can cause deadlocks if the callback code also tries to lock the same mutex.
+ // This is most easily reproducible by adding print statements, where each print requires
+ // taking a mutex lock. Work around this by running the callback asynchronously, i.e. by using
+ // a native zero-timer, to make sure the main thread stack is completely unwond before calling
+ // the Qt handler.
+ // It is currently unclear if this problem is caused by code in Qt or in Emscripten, or
+ // if this completely fixes the problem.
+ runAsync([socket](){
+ auto notifiersRange = g_socketNotifiers.equal_range(socket);
+ std::vector<std::pair<int, QSocketNotifier *>> notifiers(notifiersRange.first, notifiersRange.second);
+ for (auto [_, notifier]: notifiers) {
+ QCoreApplication::postEvent(notifier, new QEvent(QEvent::SockAct));
+ }
+ setSocketState(socket, true, true);
+ });
+}
+
+void QEventDispatcherWasm::socketOpen(int socket, void *context)
+{
+ Q_UNUSED(context);
+
+ runAsync([socket](){
+ auto notifiersRange = g_socketNotifiers.equal_range(socket);
+ std::vector<std::pair<int, QSocketNotifier *>> notifiers(notifiersRange.first, notifiersRange.second);
+ for (auto [_, notifier]: notifiers) {
+ if (notifier->type() == QSocketNotifier::Write) {
+ QCoreApplication::postEvent(notifier, new QEvent(QEvent::SockAct));
+ }
+ }
+ setSocketState(socket, false, true);
+ });
+}
+
+void QEventDispatcherWasm::socketListen(int socket, void *context)
+{
+ Q_UNUSED(socket);
+ Q_UNUSED(context);
+}
+
+void QEventDispatcherWasm::socketConnection(int socket, void *context)
+{
+ Q_UNUSED(socket);
+ Q_UNUSED(context);
+}
+
+void QEventDispatcherWasm::socketMessage(int socket, void *context)
+{
+ Q_UNUSED(context);
+
+ runAsync([socket](){
+ auto notifiersRange = g_socketNotifiers.equal_range(socket);
+ std::vector<std::pair<int, QSocketNotifier *>> notifiers(notifiersRange.first, notifiersRange.second);
+ for (auto [_, notifier]: notifiers) {
+ if (notifier->type() == QSocketNotifier::Read) {
+ QCoreApplication::postEvent(notifier, new QEvent(QEvent::SockAct));
+ }
+ }
+ setSocketState(socket, true, false);
+ });
+}
+
+void QEventDispatcherWasm::socketClose(int socket, void *context)
+{
+ Q_UNUSED(context);
+
+ // Emscripten makes emscripten_set_socket_close_callback() calls to socket 0,
+ // which is not a valid socket. see https://github.com/emscripten-core/emscripten/issues/6596
+ if (socket == 0)
+ return;
+
+ runAsync([socket](){
+ auto notifiersRange = g_socketNotifiers.equal_range(socket);
+ std::vector<std::pair<int, QSocketNotifier *>> notifiers(notifiersRange.first, notifiersRange.second);
+ for (auto [_, notifier]: notifiers)
+ QCoreApplication::postEvent(notifier, new QEvent(QEvent::SockClose));
+
+ setSocketState(socket, true, true);
+ clearSocketState(socket);
+ });
+}
+
+void QEventDispatcherWasm::setSocketState(int socket, bool setReadyRead, bool setReadyWrite)
+{
+ LOCK_GUARD(g_staticDataMutex);
+ SocketReadyState &state = g_socketState[socket];
+
+ // Additively update socket ready state, e.g. if it
+ // was already ready read then it stays ready read.
+ state.readyRead |= setReadyRead;
+ state.readyWrite |= setReadyWrite;
+
+ // Wake any waiters for the given readiness. The waiter consumes
+ // the ready state, returning the socket to not-ready.
+ if (QEventDispatcherWasm *waiter = state.waiter)
+ if ((state.readyRead && state.waitForReadyRead) || (state.readyWrite && state.waitForReadyWrite))
+ waiter->wakeEventDispatcherThread();
+}
+
+void QEventDispatcherWasm::clearSocketState(int socket)
+{
+ LOCK_GUARD(g_staticDataMutex);
+ g_socketState.erase(socket);
+}
+
+void QEventDispatcherWasm::waitForSocketState(int timeout, int socket, bool checkRead, bool checkWrite,
+ bool *selectForRead, bool *selectForWrite, bool *socketDisconnect)
+{
+ // Loop until the socket becomes readyRead or readyWrite. Wait for
+ // socket activity if it currently is neither.
+ while (true) {
+ *selectForRead = false;
+ *selectForWrite = false;
+
+ {
+ LOCK_GUARD(g_staticDataMutex);
+
+ // Access or create socket state: we want to register that a thread is waitng
+ // even if we have not received any socket callbacks yet.
+ SocketReadyState &state = g_socketState[socket];
+ if (state.waiter) {
+ qWarning() << "QEventDispatcherWasm::waitForSocketState: a thread is already waiting";
+ break;
+ }
+
+ bool shouldWait = true;
+ if (checkRead && state.readyRead) {
+ shouldWait = false;
+ state.readyRead = false;
+ *selectForRead = true;
+ }
+ if (checkWrite && state.readyWrite) {
+ shouldWait = false;
+ state.readyWrite = false;
+ *selectForRead = true;
+ }
+ if (!shouldWait)
+ break;
+
+ state.waiter = this;
+ state.waitForReadyRead = checkRead;
+ state.waitForReadyWrite = checkWrite;
+ }
+
+ bool didTimeOut = !wait(timeout);
+ {
+ LOCK_GUARD(g_staticDataMutex);
+
+ // Missing socket state after a wakeup means that the socket has been closed.
+ auto it = g_socketState.find(socket);
+ if (it == g_socketState.end()) {
+ *socketDisconnect = true;
+ break;
+ }
+ it->second.waiter = nullptr;
+ it->second.waitForReadyRead = false;
+ it->second.waitForReadyWrite = false;
+ }
+
+ if (didTimeOut)
+ break;
+ }
+}
+
+void QEventDispatcherWasm::socketSelect(int timeout, int socket, bool waitForRead, bool waitForWrite,
+ bool *selectForRead, bool *selectForWrite, bool *socketDisconnect)
+{
+ QEventDispatcherWasm *eventDispatcher = static_cast<QEventDispatcherWasm *>(
+ QAbstractEventDispatcher::instance(QThread::currentThread()));
+
+ if (!eventDispatcher) {
+ qWarning("QEventDispatcherWasm::socketSelect called without eventdispatcher instance");
+ return;
+ }
+
+ eventDispatcher->waitForSocketState(timeout, socket, waitForRead, waitForWrite,
+ selectForRead, selectForWrite, socketDisconnect);
+}
+
+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) {
- std::function<void(void)> *fn = reinterpret_cast<std::function<void(void)> *>(context);
- (*fn)();
- delete fn;
+
+ auto async_fn = [](void *context){
+ std::function<void(void)> *fn = reinterpret_cast<std::function<void(void)> *>(context);
+ (*fn)();
+ delete fn;
+ };
+
+ emscripten_async_call(async_fn, context, 0);
}
}
-// Runs a function on the main thread
+// Runs a function right away
+void QEventDispatcherWasm::run(std::function<void(void)> fn)
+{
+ fn();
+}
+
void QEventDispatcherWasm::runOnMainThread(std::function<void(void)> fn)
{
- void *context = new std::function<void(void)>(fn);
- emscripten_async_run_in_main_runtime_thread_(EM_FUNC_SIG_VI, reinterpret_cast<void *>(trampoline), context);
+#if QT_CONFIG(thread)
+ qstdweb::runTaskOnMainThread<void>(fn, &g_proxyingQueue);
+#else
+ qstdweb::runTaskOnMainThread<void>(fn);
+#endif
+}
+
+// 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,
+// also if the calling thread is the main thread.
+void QEventDispatcherWasm::runOnMainThreadAsync(std::function<void(void)> fn)
+{
+ void *context = new std::function<void(void)>(fn);
+#if QT_CONFIG(thread)
+ if (!emscripten_is_main_runtime_thread()) {
+ g_proxyingQueue.proxyAsync(g_mainThread, [context]{
+ trampoline(context);
+ });
+ return;
+ }
#endif
+ trampoline(context);
+}
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 7eea28b052..7b257e02ad 100644
--- a/src/corelib/kernel/qeventdispatcher_wasm_p.h
+++ b/src/corelib/kernel/qeventdispatcher_wasm_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtCore module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 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 QEVENTDISPATCHER_WASM_P_H
#define QEVENTDISPATCHER_WASM_P_H
@@ -56,16 +20,19 @@
#include <QtCore/qloggingcategory.h>
#include <QtCore/qwaitcondition.h>
+#include <chrono>
#include <mutex>
#include <optional>
#include <tuple>
+#include <emscripten/proxying.h>
+
QT_BEGIN_NAMESPACE
Q_DECLARE_LOGGING_CATEGORY(lcEventDispatcher);
Q_DECLARE_LOGGING_CATEGORY(lcEventDispatcherTimers)
-class Q_CORE_EXPORT QEventDispatcherWasm : public QAbstractEventDispatcher
+class Q_CORE_EXPORT QEventDispatcherWasm : public QAbstractEventDispatcherV2
{
Q_OBJECT
public:
@@ -77,32 +44,60 @@ 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 bool processPostedEvents();
+
private:
bool isMainThreadEventDispatcher();
bool isSecondaryThreadEventDispatcher();
+ static bool isValidEventDispatcherPointer(QEventDispatcherWasm *eventDispatcher);
- void handleEventLoopExec();
+ void handleApplicationExec();
void handleDialogExec();
- void pollForNativeEvents();
- bool waitForForEvents();
- static void callProcessEvents(void *eventDispatcher);
+ bool wait(int timeout = -1);
+ bool wakeEventDispatcherThread();
+ static void callProcessPostedEvents(void *eventDispatcher);
void processTimers();
void updateNativeTimer();
static void callProcessTimers(void *eventDispatcher);
-#if QT_CONFIG(thread)
- void runOnMainThread(std::function<void(void)> fn);
-#endif
+ static void setEmscriptenSocketCallbacks();
+ static void clearEmscriptenSocketCallbacks();
+ static void socketError(int fd, int err, const char* msg, void *context);
+ static void socketOpen(int fd, void *context);
+ static void socketListen(int fd, void *context);
+ static void socketConnection(int fd, void *context);
+ static void socketMessage(int fd, void *context);
+ static void socketClose(int fd, void *context);
+
+ static void setSocketState(int socket, bool setReadyRead, bool setReadyWrite);
+ static void clearSocketState(int socket);
+ void waitForSocketState(int timeout, int socket, bool checkRead, bool checkWrite,
+ bool *selectForRead, bool *selectForWrite, bool *socketDisconnect);
+
+ static void run(std::function<void(void)> fn);
+ static void runAsync(std::function<void(void)> fn);
+ static void runOnMainThreadAsync(std::function<void(void)> fn);
static QEventDispatcherWasm *g_mainThreadEventDispatcher;
@@ -112,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;
@@ -120,8 +115,25 @@ private:
std::condition_variable m_moreEvents;
static QVector<QEventDispatcherWasm *> g_secondaryThreadEventDispatchers;
- static std::mutex g_secondaryThreadEventDispatchersMutex;
+ static std::mutex g_staticDataMutex;
+ static emscripten::ProxyingQueue g_proxyingQueue;
+ static pthread_t g_mainThread;
+
+ // Note on mutex usage: the global g_staticDataMutex protects the global (g_ prefixed) data,
+ // while the per eventdispatcher m_mutex protects the state accociated with blocking and waking
+ // that eventdispatcher thread. The locking order is g_staticDataMutex first, then m_mutex.
#endif
+
+ static std::multimap<int, QSocketNotifier *> g_socketNotifiers;
+
+ struct SocketReadyState {
+ QEventDispatcherWasm *waiter = nullptr;
+ bool waitForReadyRead = false;
+ bool waitForReadyWrite = false;
+ bool readyRead = false;
+ bool readyWrite = false;
+ };
+ static std::map<int, SocketReadyState> g_socketState;
};
#endif // QEVENTDISPATCHER_WASM_P_H
diff --git a/src/corelib/kernel/qeventdispatcher_win.cpp b/src/corelib/kernel/qeventdispatcher_win.cpp
index 97e3cbdb04..a7663b2481 100644
--- a/src/corelib/kernel/qeventdispatcher_win.cpp
+++ b/src/corelib/kernel/qeventdispatcher_win.cpp
@@ -1,49 +1,12 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Copyright (C) 2016 Intel Corporation.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtCore module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// Copyright (C) 2016 Intel Corporation.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qeventdispatcher_win_p.h"
#include "qcoreapplication.h"
#include <private/qsystemlibrary_p.h>
#include "qoperatingsystemversion.h"
-#include "qpair.h"
#include "qset.h"
#include "qsocketnotifier.h"
#include "qvarlengtharray.h"
@@ -54,8 +17,6 @@
QT_BEGIN_NAMESPACE
-extern uint qGlobalPostedEventsCount();
-
#ifndef TIME_KILL_SYNCHRONOUS
# define TIME_KILL_SYNCHRONOUS 0x0100
#endif
@@ -94,6 +55,13 @@ class QEventDispatcherWin32Private;
LRESULT QT_WIN_CALLBACK qt_internal_proc(HWND hwnd, UINT message, WPARAM wp, LPARAM lp);
+static quint64 qt_msectime()
+{
+ using namespace std::chrono;
+ auto t = duration_cast<milliseconds>(steady_clock::now().time_since_epoch());
+ return t.count();
+}
+
QEventDispatcherWin32Private::QEventDispatcherWin32Private()
: interrupt(false), internalHwnd(0),
sendPostedEventsTimerId(0), wakeUps(0),
@@ -137,11 +105,7 @@ LRESULT QT_WIN_CALLBACK qt_internal_proc(HWND hwnd, UINT message, WPARAM wp, LPA
if (dispatcher->filterNativeEvent(QByteArrayLiteral("windows_dispatcher_MSG"), &msg, &result))
return result;
-#ifdef GWLP_USERDATA
auto q = reinterpret_cast<QEventDispatcherWin32 *>(GetWindowLongPtr(hwnd, GWLP_USERDATA));
-#else
- auto q = reinterpret_cast<QEventDispatcherWin32 *>(GetWindowLong(hwnd, GWL_USERDATA));
-#endif
QEventDispatcherWin32Private *d = nullptr;
if (q != nullptr)
d = q->d_func();
@@ -171,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 {
@@ -249,6 +213,8 @@ LRESULT QT_WIN_CALLBACK qt_internal_proc(HWND hwnd, UINT message, WPARAM wp, LPA
void QEventDispatcherWin32Private::startPostedEventsTimer()
{
+ // we received WM_QT_SENDPOSTEDEVENTS, so allow posting it again
+ wakeUps.storeRelaxed(0);
if (sendPostedEventsTimerId == 0) {
// Start a timer to deliver posted events when the message queue is emptied.
sendPostedEventsTimerId = SetTimer(internalHwnd, SendPostedEventsTimerId,
@@ -325,24 +291,52 @@ static HWND qt_create_internal_window(const QEventDispatcherWin32 *eventDispatch
return 0;
}
-#ifdef GWLP_USERDATA
SetWindowLongPtr(wnd, GWLP_USERDATA, reinterpret_cast<LONG_PTR>(eventDispatcher));
-#else
- SetWindowLong(wnd, GWL_USERDATA, reinterpret_cast<LONG>(eventDispatcher));
-#endif
return wnd;
}
-static void calculateNextTimeout(WinTimerInfo *t, quint64 currentTime)
+static ULONG calculateNextTimeout(WinTimerInfo *t, quint64 currentTime)
{
uint interval = t->interval;
- if ((interval >= 20000u && t->timerType != Qt::PreciseTimer) || t->timerType == Qt::VeryCoarseTimer) {
- // round the interval, VeryCoarseTimers only have full second accuracy
- interval = ((interval + 500)) / 1000 * 1000;
+ ULONG tolerance = TIMERV_DEFAULT_COALESCING;
+ switch (t->timerType) {
+ case Qt::PreciseTimer:
+ // high precision timer is based on millisecond precision
+ // so no adjustment is necessary
+ break;
+
+ case Qt::CoarseTimer:
+ // this timer has up to 5% coarseness
+ // so our boundaries are 20 ms and 20 s
+ // below 20 ms, 5% inaccuracy is below 1 ms, so we convert to high precision
+ // above 20 s, 5% inaccuracy is above 1 s, so we convert to VeryCoarseTimer
+ if (interval >= 20000) {
+ t->timerType = Qt::VeryCoarseTimer;
+ } else if (interval <= 20) {
+ // no adjustment necessary
+ t->timerType = Qt::PreciseTimer;
+ break;
+ } else {
+ tolerance = interval / 20;
+ break;
+ }
+ Q_FALLTHROUGH();
+ case Qt::VeryCoarseTimer:
+ // the very coarse timer is based on full second precision,
+ // so we round to closest second (but never to zero)
+ tolerance = 1000;
+ if (interval < 1000)
+ interval = 1000;
+ else
+ interval = (interval + 500) / 1000 * 1000;
+ currentTime = currentTime / 1000 * 1000;
+ break;
}
+
t->interval = interval;
t->timeout = currentTime + interval;
+ return tolerance;
}
void QEventDispatcherWin32Private::registerTimer(WinTimerInfo *t)
@@ -352,13 +346,13 @@ void QEventDispatcherWin32Private::registerTimer(WinTimerInfo *t)
Q_Q(QEventDispatcherWin32);
bool ok = false;
- calculateNextTimeout(t, qt_msectime());
+ ULONG tolerance = calculateNextTimeout(t, qt_msectime());
uint interval = t->interval;
if (interval == 0u) {
// optimization for single-shot-zero-timer
QCoreApplication::postEvent(q, new QZeroTimerEvent(t->timerId));
ok = true;
- } else if (interval < 20u || t->timerType == Qt::PreciseTimer) {
+ } else if (tolerance == TIMERV_DEFAULT_COALESCING) {
// 3/2016: Although MSDN states timeSetEvent() is deprecated, the function
// is still deemed to be the most reliable precision timer.
t->fastTimerId = timeSetEvent(interval, 1, qt_fast_timer_proc, DWORD_PTR(t),
@@ -368,8 +362,10 @@ void QEventDispatcherWin32Private::registerTimer(WinTimerInfo *t)
if (!ok) {
// user normal timers for (Very)CoarseTimers, or if no more multimedia timers available
- ok = SetTimer(internalHwnd, t->timerId, interval, 0);
+ ok = SetCoalescableTimer(internalHwnd, t->timerId, interval, nullptr, tolerance);
}
+ if (!ok)
+ ok = SetTimer(internalHwnd, t->timerId, interval, nullptr);
if (!ok)
qErrnoWarning("QEventDispatcherWin32::registerTimer: Failed to create a timer");
@@ -412,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
@@ -561,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) {
@@ -585,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;
@@ -629,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;
@@ -646,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);
@@ -774,7 +770,7 @@ QEventDispatcherWin32::registeredTimers(QObject *object) const
Q_D(const QEventDispatcherWin32);
QList<TimerInfo> list;
- for (WinTimerInfo *t : qAsConst(d->timerDict)) {
+ for (WinTimerInfo *t : std::as_const(d->timerDict)) {
Q_ASSERT(t);
if (t->obj == object)
list << TimerInfo(t->timerId, t->interval, t->timerType);
@@ -842,7 +838,7 @@ void QEventDispatcherWin32::closingDown()
Q_ASSERT(d->active_fd.isEmpty());
// clean up any timers
- for (WinTimerInfo *t : qAsConst(d->timerDict))
+ for (WinTimerInfo *t : std::as_const(d->timerDict))
d->unregisterTimer(t);
d->timerDict.clear();
@@ -909,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 49a6d3ebbc..558490a85e 100644
--- a/src/corelib/kernel/qeventdispatcher_win_p.h
+++ b/src/corelib/kernel/qeventdispatcher_win_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtCore module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// 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 QEVENTDISPATCHER_WIN_P_H
#define QEVENTDISPATCHER_WIN_P_H
@@ -64,7 +28,6 @@ class QEventDispatcherWin32Private;
// forward declaration
LRESULT QT_WIN_CALLBACK qt_internal_proc(HWND hwnd, UINT message, WPARAM wp, LPARAM lp);
-quint64 qt_msectime();
class Q_CORE_EXPORT QEventDispatcherWin32 : public QAbstractEventDispatcher
{
@@ -108,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;
@@ -119,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;
@@ -172,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 4e4fa11b30..d318069ca0 100644
--- a/src/corelib/kernel/qeventloop.cpp
+++ b/src/corelib/kernel/qeventloop.cpp
@@ -1,48 +1,12 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtCore module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// 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 "qeventloop.h"
#include "qabstracteventdispatcher.h"
#include "qcoreapplication.h"
#include "qcoreapplication_p.h"
-#include "qelapsedtimer.h"
+#include "qdeadlinetimer.h"
#include "qobject_p.h"
#include "qeventloop_p.h"
@@ -88,6 +52,7 @@ QT_BEGIN_NAMESPACE
\omitvalue X11ExcludeTimers
\omitvalue EventLoopExec
\omitvalue DialogExec
+ \omitvalue ApplicationExec
\sa processEvents()
*/
@@ -151,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()
*/
@@ -186,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();
}
@@ -204,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);
@@ -221,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.
@@ -236,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;
}
}
@@ -328,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
@@ -407,8 +350,8 @@ private:
\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}
{
}
@@ -420,8 +363,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}
{
}
@@ -433,18 +376,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 9b83d692fa..ec79c07cd7 100644
--- a/src/corelib/kernel/qeventloop.h
+++ b/src/corelib/kernel/qeventloop.h
@@ -1,55 +1,22 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtCore module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// 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 QEVENTLOOP_H
#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);
@@ -62,12 +29,15 @@ public:
WaitForMoreEvents = 0x04,
X11ExcludeTimers = 0x08,
EventLoopExec = 0x20,
- DialogExec = 0x40
+ DialogExec = 0x40,
+ ApplicationExec = 0x80,
};
Q_DECLARE_FLAGS(ProcessEventsFlags, ProcessEventsFlag)
+ Q_FLAG(ProcessEventsFlags)
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;
@@ -85,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/qeventloop_p.h b/src/corelib/kernel/qeventloop_p.h
index 4ad6d92007..1fcb7a8b81 100644
--- a/src/corelib/kernel/qeventloop_p.h
+++ b/src/corelib/kernel/qeventloop_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtCore module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// 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 QEVENTLOOP_P_H
#define QEVENTLOOP_P_H
diff --git a/src/corelib/kernel/qfunctions_p.h b/src/corelib/kernel/qfunctions_p.h
index 8347dcca01..58afc207e6 100644
--- a/src/corelib/kernel/qfunctions_p.h
+++ b/src/corelib/kernel/qfunctions_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtCore module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// 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
//
// W A R N I N G
@@ -53,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 61704c1b54..0000000000
--- a/src/corelib/kernel/qfunctions_vxworks.cpp
+++ /dev/null
@@ -1,208 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtCore module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#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 86f243959f..0000000000
--- a/src/corelib/kernel/qfunctions_vxworks.h
+++ /dev/null
@@ -1,195 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtCore module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#ifndef 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
new file mode 100644
index 0000000000..048fdbc934
--- /dev/null
+++ b/src/corelib/kernel/qfunctions_win.cpp
@@ -0,0 +1,66 @@
+// 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
+
+#include "qfunctions_win_p.h"
+
+#include <QtCore/qdebug.h>
+
+#include <combaseapi.h>
+#include <objbase.h>
+
+#if __has_include(<appmodel.h>)
+# include <appmodel.h>
+# define HAS_APPMODEL
+#endif
+
+QT_BEGIN_NAMESPACE
+
+QComHelper::QComHelper(COINIT concurrencyModel)
+{
+ // Avoid overhead of initializing and using obsolete technology
+ concurrencyModel = COINIT(concurrencyModel | COINIT_DISABLE_OLE1DDE);
+
+ m_initResult = CoInitializeEx(nullptr, concurrencyModel);
+
+ if (FAILED(m_initResult))
+ qErrnoWarning(m_initResult, "Failed to initialize COM library");
+}
+
+QComHelper::~QComHelper()
+{
+ Q_ASSERT(m_threadId == GetCurrentThreadId());
+ if (SUCCEEDED(m_initResult))
+ CoUninitialize();
+}
+
+/*!
+ \internal
+ Checks if the application has a \e{package identity}
+
+ Having a \e{package identity} is required to use many modern
+ Windows APIs.
+
+ https://docs.microsoft.com/en-us/windows/apps/desktop/modernize/modernize-packaged-apps
+*/
+bool qt_win_hasPackageIdentity()
+{
+#if defined(HAS_APPMODEL)
+ static const bool hasPackageIdentity = []() {
+ UINT32 length = 0;
+ switch (const auto result = GetCurrentPackageFullName(&length, nullptr)) {
+ case ERROR_INSUFFICIENT_BUFFER:
+ return true;
+ case APPMODEL_ERROR_NO_PACKAGE:
+ return false;
+ default:
+ qWarning("Failed to resolve package identity (error code %ld)", result);
+ return false;
+ }
+ }();
+ return hasPackageIdentity;
+#else
+ return false;
+#endif
+}
+
+QT_END_NAMESPACE
diff --git a/src/corelib/kernel/qfunctions_win_p.h b/src/corelib/kernel/qfunctions_win_p.h
new file mode 100644
index 0000000000..ab5417f8a2
--- /dev/null
+++ b/src/corelib/kernel/qfunctions_win_p.h
@@ -0,0 +1,51 @@
+// 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 QFUNCTIONS_WIN_P_H
+#define QFUNCTIONS_WIN_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtCore/private/qglobal_p.h>
+
+#if defined(Q_OS_WIN) || defined(Q_QDOC)
+
+#if !defined(QT_BOOTSTRAPPED)
+#include <QtCore/private/qfunctions_winrt_p.h>
+#endif
+
+#include <QtCore/qt_windows.h>
+
+QT_BEGIN_NAMESPACE
+
+class Q_CORE_EXPORT QComHelper
+{
+ Q_DISABLE_COPY_MOVE(QComHelper)
+public:
+ QComHelper(COINIT concurrencyModel = COINIT_APARTMENTTHREADED);
+ ~QComHelper();
+
+ bool isValid() const { return SUCCEEDED(m_initResult); }
+ explicit operator bool() const { return isValid(); }
+
+private:
+ HRESULT m_initResult = E_FAIL;
+ DWORD m_threadId{ GetCurrentThreadId() };
+};
+
+Q_CORE_EXPORT bool qt_win_hasPackageIdentity();
+
+QT_END_NAMESPACE
+
+#endif // Q_OS_WIN
+
+#endif // QFUNCTIONS_WIN_P_H
diff --git a/src/corelib/kernel/qfunctions_winrt_p.h b/src/corelib/kernel/qfunctions_winrt_p.h
index 8db94c124b..c895733c93 100644
--- a/src/corelib/kernel/qfunctions_winrt_p.h
+++ b/src/corelib/kernel/qfunctions_winrt_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2020 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtCore module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2020 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_WINRT_P_H
#define QFUNCTIONS_WINRT_P_H
@@ -51,7 +15,7 @@
// We mean it.
//
-#include <QtCore/qglobal.h>
+#include <QtCore/private/qglobal_p.h>
#if defined(Q_OS_WIN) && defined(Q_CC_MSVC)
diff --git a/src/corelib/kernel/qiterable.cpp b/src/corelib/kernel/qiterable.cpp
index 7ee7be2696..a8c93fbc1c 100644
--- a/src/corelib/kernel/qiterable.cpp
+++ b/src/corelib/kernel/qiterable.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2020 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtCore module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2020 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 <QtCore/qiterable.h>
@@ -43,7 +7,7 @@ QT_BEGIN_NAMESPACE
/*!
\class QBaseIterator
-
+ \inmodule QtCore
QBaseIterator forms the common base class for all iterators operating on
subclasses of QIterable.
*/
@@ -187,7 +151,7 @@ QT_BEGIN_NAMESPACE
/*!
\fn template<class Container> QIterator<Container> &QIterator<Container>::operator++()
- The prefix ++ operator (\c{++it}) advances the iterator to the
+ The prefix \c{++} operator (\c{++it}) advances the iterator to the
next item in the container and returns an iterator to the new current
item.
@@ -200,7 +164,7 @@ QT_BEGIN_NAMESPACE
\fn template<class Container> QIterator<Container> QIterator<Container>::operator++(int)
\overload
- The postfix ++ operator (\c{it++}) advances the iterator to the
+ The postfix \c{++} operator (\c{it++}) advances the iterator to the
next item in the container and returns an iterator to the previously
current item.
*/
@@ -209,7 +173,7 @@ QT_BEGIN_NAMESPACE
/*!
\fn template<class Container> QIterator<Container> &QIterator<Container>::operator--()
- The prefix -- operator (\c{--it}) makes the preceding item
+ The prefix \c{--} operator (\c{--it}) makes the preceding item
current and returns an iterator to the new current item.
Calling this function on QSequentialIterable::begin() leads to undefined results.
@@ -225,7 +189,7 @@ QT_BEGIN_NAMESPACE
\overload
- The postfix -- operator (\c{it--}) makes the preceding item
+ The postfix \c{--} operator (\c{it--}) makes the preceding item
current and returns an iterator to the previously current item.
If the container in the QVariant does not support bi-directional iteration, calling this function
@@ -276,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.
@@ -323,7 +288,7 @@ QT_BEGIN_NAMESPACE
/*!
\fn template<class Container> QConstIterator<Container> &QConstIterator<Container>::operator++()
- The prefix ++ operator (\c{++it}) advances the iterator to the
+ The prefix \c{++} operator (\c{++it}) advances the iterator to the
next item in the container and returns an iterator to the new current
item.
@@ -337,7 +302,7 @@ QT_BEGIN_NAMESPACE
\overload
- The postfix ++ operator (\c{it++}) advances the iterator to the
+ The postfix \c{++} operator (\c{it++}) advances the iterator to the
next item in the container and returns an iterator to the previously
current item.
*/
@@ -345,7 +310,7 @@ QT_BEGIN_NAMESPACE
/*!
\fn template<class Container> QConstIterator<Container> &QConstIterator<Container>::operator--()
- The prefix -- operator (\c{--it}) makes the preceding item
+ The prefix \c{--} operator (\c{--it}) makes the preceding item
current and returns an iterator to the new current item.
Calling this function on QIterable<Container>::begin() leads to undefined results.
@@ -361,7 +326,7 @@ QT_BEGIN_NAMESPACE
\overload
- The postfix -- operator (\c{it--}) makes the preceding item
+ The postfix \c{--} operator (\c{it--}) makes the preceding item
current and returns an iterator to the previously current item.
If the container in the QVariant does not support bi-directional iteration, calling this function
@@ -413,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()
@@ -541,7 +508,7 @@ QT_BEGIN_NAMESPACE
/*!
\fn template<class Iterator, typename IteratorCategory> QTaggedIterator<Iterator, IteratorCategory> &QTaggedIterator<Iterator, IteratorCategory>::operator++()
- The prefix ++ operator (\c{++it}) advances the iterator to the
+ The prefix \c{++} operator (\c{++it}) advances the iterator to the
next item in the container and returns an iterator to the new current
item.
@@ -554,7 +521,7 @@ QT_BEGIN_NAMESPACE
\fn template<class Iterator, typename IteratorCategory> QTaggedIterator<Iterator, IteratorCategory> QTaggedIterator<Iterator, IteratorCategory>::operator++(int)
\overload
- The postfix ++ operator (\c{it++}) advances the iterator to the
+ The postfix \c{++} operator (\c{it++}) advances the iterator to the
next item in the container and returns an iterator to the previously
current item.
*/
@@ -563,7 +530,7 @@ QT_BEGIN_NAMESPACE
/*!
\fn template<class Iterator, typename IteratorCategory> QTaggedIterator<Iterator, IteratorCategory> &QTaggedIterator<Iterator, IteratorCategory>::operator--()
- The prefix -- operator (\c{--it}) makes the preceding item
+ The prefix \c{--} operator (\c{--it}) makes the preceding item
current and returns an iterator to the new current item.
Calling this function on QSequentialIterable::begin() leads to undefined results.
@@ -578,7 +545,7 @@ QT_BEGIN_NAMESPACE
\fn template<class Iterator, typename IteratorCategory> QTaggedIterator<Iterator, IteratorCategory> QTaggedIterator<Iterator, IteratorCategory>::operator--(int)
\overload
- The postfix -- operator (\c{it--}) makes the preceding item
+ The postfix \c{--} operator (\c{it--}) makes the preceding item
current and returns an iterator to the previously current item.
If the container in the QVariant does not support bi-directional iteration, calling this function
diff --git a/src/corelib/kernel/qiterable.h b/src/corelib/kernel/qiterable.h
index c77a158420..4adcdfd76f 100644
--- a/src/corelib/kernel/qiterable.h
+++ b/src/corelib/kernel/qiterable.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2020 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtCore module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2020 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 QITERABLE_H
#define QITERABLE_H
@@ -55,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);
@@ -72,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
{
@@ -107,37 +71,41 @@ 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();
+ }
}
}
bool operator==(const QTaggedIterator &o) const { return Iterator::operator==(o); }
bool operator!=(const QTaggedIterator &o) const { return Iterator::operator!=(o); }
QTaggedIterator &operator++() { Iterator::operator++(); return *this; }
- QTaggedIterator operator++(int x) { return QTaggedIterator(Iterator::operator++(x)); };
+ QTaggedIterator operator++(int x) { return QTaggedIterator(Iterator::operator++(x)); }
QTaggedIterator &operator--() { Iterator::operator--(); return *this; }
- QTaggedIterator operator--(int x) { return QTaggedIterator(Iterator::operator--(x)); };
+ QTaggedIterator operator--(int x) { return QTaggedIterator(Iterator::operator--(x)); }
QTaggedIterator &operator+=(qsizetype j) { Iterator::operator+=(j); return *this; }
QTaggedIterator &operator-=(qsizetype j) { Iterator::operator-=(j); return *this; }
QTaggedIterator operator+(qsizetype j) const { return QTaggedIterator(Iterator::operator+(j)); }
@@ -540,7 +508,7 @@ public:
Container metaContainer() const
{
return m_metaContainer;
- };
+ }
};
QT_END_NAMESPACE
diff --git a/src/corelib/kernel/qiterable_p.h b/src/corelib/kernel/qiterable_p.h
index d975a591fb..6a9c804745 100644
--- a/src/corelib/kernel/qiterable_p.h
+++ b/src/corelib/kernel/qiterable_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2020 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtCore module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2020 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 QITERABLE_P_H
#define QITERABLE_P_H
@@ -51,7 +15,7 @@
// We mean it.
//
-#include <QtCore/qglobal.h>
+#include <QtCore/private/qglobal_p.h>
#include <QtCore/qvariant.h>
QT_BEGIN_NAMESPACE
diff --git a/src/corelib/kernel/qjniarray.h b/src/corelib/kernel/qjniarray.h
new file mode 100644
index 0000000000..976b4e92e3
--- /dev/null
+++ b/src/corelib/kernel/qjniarray.h
@@ -0,0 +1,464 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QJNIARRAY_H
+#define QJNIARRAY_H
+
+#include <QtCore/qlist.h>
+
+#if defined(Q_QDOC) || defined(Q_OS_ANDROID)
+#include <QtCore/qbytearray.h>
+#include <QtCore/qjniobject.h>
+
+#include <iterator>
+#include <utility>
+#include <QtCore/q20type_traits.h>
+
+QT_BEGIN_NAMESPACE
+
+template <typename T> class QJniArray;
+template <typename T>
+struct 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 QJniArrayBase
+{
+ // for SFINAE'ing out the fromContainer named constructor
+ template <typename Container, typename = void> struct CanConvertHelper : std::false_type {};
+ template <typename Container>
+ struct CanConvertHelper<Container, std::void_t<decltype(std::data(std::declval<Container>())),
+ decltype(std::size(std::declval<Container>())),
+ typename Container::value_type
+ >
+ > : std::true_type {};
+
+public:
+ using size_type = jsize;
+ using difference_type = size_type;
+
+ operator QJniObject() const { return m_object; }
+
+ template <typename T = jobject>
+ T object() const { return m_object.object<T>(); }
+ bool isValid() const { return m_object.isValid(); }
+
+ size_type size() const
+ {
+ if (jarray array = m_object.object<jarray>())
+ return jniEnv()->GetArrayLength(array);
+ return 0;
+ }
+
+ template <typename Container>
+ static constexpr bool canConvert = CanConvertHelper<q20::remove_cvref_t<Container>>::value;
+ template <typename Container>
+ using IfCanConvert = std::enable_if_t<canConvert<Container>, bool>;
+ template <typename Container
+ , IfCanConvert<Container> = true
+ >
+ static auto fromContainer(Container &&container)
+ {
+ Q_ASSERT_X(size_t(std::size(container)) <= size_t((std::numeric_limits<size_type>::max)()),
+ "QJniArray::fromContainer", "Container is too large for a Java array");
+
+ using ElementType = typename std::remove_reference_t<Container>::value_type;
+ if constexpr (std::disjunction_v<std::is_same<ElementType, jobject>,
+ std::is_same<ElementType, QJniObject>,
+ std::is_same<ElementType, QString>,
+ std::is_base_of<QtJniTypes::JObjectBase, ElementType>
+ >) {
+ return makeObjectArray(std::forward<Container>(container));
+ } else if constexpr (std::is_same_v<ElementType, jfloat>) {
+ return makeArray<jfloat>(std::forward<Container>(container), &JNIEnv::NewFloatArray,
+ &JNIEnv::SetFloatArrayRegion);
+ } else if constexpr (std::is_same_v<ElementType, jdouble>) {
+ return makeArray<jdouble>(std::forward<Container>(container), &JNIEnv::NewDoubleArray,
+ &JNIEnv::SetDoubleArrayRegion);
+ } else if constexpr (std::disjunction_v<std::is_same<ElementType, jboolean>,
+ std::is_same<ElementType, bool>>) {
+ return makeArray<jboolean>(std::forward<Container>(container), &JNIEnv::NewBooleanArray,
+ &JNIEnv::SetBooleanArrayRegion);
+ } else if constexpr (std::disjunction_v<std::is_same<ElementType, jbyte>,
+ std::is_same<ElementType, char>>) {
+ return makeArray<jbyte>(std::forward<Container>(container), &JNIEnv::NewByteArray,
+ &JNIEnv::SetByteArrayRegion);
+ } else if constexpr (std::disjunction_v<std::is_same<ElementType, jchar>,
+ std::is_same<ElementType, QChar>>) {
+ return makeArray<jchar>(std::forward<Container>(container), &JNIEnv::NewCharArray,
+ &JNIEnv::SetCharArrayRegion);
+ } else if constexpr (std::is_same_v<ElementType, jshort>
+ || sizeof(ElementType) == sizeof(jshort)) {
+ return makeArray<jshort>(std::forward<Container>(container), &JNIEnv::NewShortArray,
+ &JNIEnv::SetShortArrayRegion);
+ } else if constexpr (std::is_same_v<ElementType, jint>
+ || sizeof(ElementType) == sizeof(jint)) {
+ return makeArray<jint>(std::forward<Container>(container), &JNIEnv::NewIntArray,
+ &JNIEnv::SetIntArrayRegion);
+ } else if constexpr (std::is_same_v<ElementType, jlong>
+ || sizeof(ElementType) == sizeof(jlong)) {
+ return makeArray<jlong>(std::forward<Container>(container), &JNIEnv::NewLongArray,
+ &JNIEnv::SetLongArrayRegion);
+ }
+ }
+
+protected:
+ QJniArrayBase() = default;
+ ~QJniArrayBase() = default;
+
+ explicit QJniArrayBase(jarray array)
+ : m_object(static_cast<jobject>(array))
+ {
+ }
+ explicit QJniArrayBase(const QJniObject &object)
+ : m_object(object)
+ {}
+ explicit QJniArrayBase(QJniObject &&object) noexcept
+ : m_object(std::move(object))
+ {}
+
+ JNIEnv *jniEnv() const noexcept { return QJniEnvironment::getJniEnv(); }
+
+ template <typename ElementType, typename List, typename NewFn, typename SetFn>
+ static auto makeArray(List &&list, NewFn &&newArray, SetFn &&setRegion);
+ template <typename List>
+ static auto makeObjectArray(List &&list);
+
+private:
+ QJniObject m_object;
+};
+
+template <typename T>
+class QJniArray : public QJniArrayBase
+{
+ friend struct QJniArrayIterator<T>;
+public:
+ using Type = T;
+
+ using value_type = T;
+ using reference = T;
+ using const_reference = const reference;
+
+ // read-only container, so no iterator typedef
+ using const_iterator = QJniArrayIterator<const T>;
+ using const_reverse_iterator = std::reverse_iterator<const_iterator>;
+
+ QJniArray() = default;
+ explicit QJniArray(jarray array) : QJniArrayBase(array) {}
+ explicit QJniArray(const QJniObject &object) : QJniArrayBase(object) {}
+ explicit QJniArray(QJniObject &&object) noexcept : QJniArrayBase(std::move(object)) {}
+
+ // base class destructor is protected, so need to provide all SMFs
+ QJniArray(const QJniArray &other) = default;
+ QJniArray(QJniArray &&other) noexcept = default;
+ QJniArray &operator=(const QJniArray &other) = default;
+ QJniArray &operator=(QJniArray &&other) noexcept = default;
+
+ template <typename Container
+ , IfCanConvert<Container> = true
+ >
+ explicit QJniArray(Container &&container)
+ : QJniArrayBase(QJniArrayBase::fromContainer(std::forward<Container>(container)))
+ {
+ }
+
+ template <typename E = T
+ , IfCanConvert<std::initializer_list<E>> = true
+ >
+ Q_IMPLICIT inline QJniArray(std::initializer_list<T> list)
+ : QJniArrayBase(QJniArrayBase::fromContainer(list))
+ {
+ }
+
+ template <typename Other, std::enable_if_t<std::is_convertible_v<Other, Type>, bool> = true>
+ QJniArray(QJniArray<Other> &&other)
+ : QJniArrayBase(std::forward<QJniArray<Other>>(other))
+ {
+ }
+ ~QJniArray() = default;
+
+ auto arrayObject() const
+ {
+ if constexpr (std::is_convertible_v<jobject, T>)
+ return object<jobjectArray>();
+ else if constexpr (std::is_same_v<T, jbyte>)
+ return object<jbyteArray>();
+ else if constexpr (std::is_same_v<T, jchar>)
+ return object<jcharArray>();
+ else if constexpr (std::is_same_v<T, jboolean>)
+ return object<jbooleanArray>();
+ else if constexpr (std::is_same_v<T, jshort>)
+ return object<jshortArray>();
+ else if constexpr (std::is_same_v<T, jint>)
+ return object<jintArray>();
+ else if constexpr (std::is_same_v<T, jlong>)
+ return object<jlongArray>();
+ else if constexpr (std::is_same_v<T, jfloat>)
+ return object<jfloatArray>();
+ else if constexpr (std::is_same_v<T, jdouble>)
+ return object<jdoubleArray>();
+ else
+ return object<jarray>();
+ }
+
+ const_iterator begin() const noexcept { return {0, this}; }
+ const_iterator constBegin() const noexcept { return begin(); }
+ const_iterator cbegin() const noexcept { return begin(); }
+ const_iterator end() const noexcept { return {size(), this}; }
+ const_iterator constEnd() const noexcept { return {end()}; }
+ const_iterator cend() const noexcept { return {end()}; }
+
+ const_reverse_iterator rbegin() const noexcept { return const_reverse_iterator(end()); }
+ const_reverse_iterator rend() const noexcept { return const_reverse_iterator(begin()); }
+ const_reverse_iterator crbegin() const noexcept { return const_reverse_iterator(end()); }
+ const_reverse_iterator crend() const noexcept { return const_reverse_iterator(begin()); }
+
+ const_reference operator[](size_type i) const { return at(i); }
+ const_reference at(size_type i) const
+ {
+ JNIEnv *env = jniEnv();
+ if constexpr (std::is_convertible_v<jobject, T>) {
+ jobject element = env->GetObjectArrayElement(object<jobjectArray>(), i);
+ if constexpr (std::is_base_of_v<QJniObject, T>)
+ return QJniObject::fromLocalRef(element);
+ else if constexpr (std::is_base_of_v<QtJniTypes::JObjectBase, T>)
+ return T::fromLocalRef(element);
+ else
+ return T{element};
+ } else if constexpr (std::is_base_of_v<std::remove_pointer_t<jobject>, std::remove_pointer_t<T>>) {
+ // jstring, jclass etc
+ return static_cast<T>(env->GetObjectArrayElement(object<jobjectArray>(), i));
+ } else {
+ T res = {};
+ if constexpr (std::is_same_v<T, jbyte>)
+ env->GetByteArrayRegion(object<jbyteArray>(), i, 1, &res);
+ else if constexpr (std::is_same_v<T, jchar>)
+ env->GetCharArrayRegion(object<jcharArray>(), i, 1, &res);
+ else if constexpr (std::is_same_v<T, jboolean>)
+ env->GetBooleanArrayRegion(object<jbooleanArray>(), i, 1, &res);
+ else if constexpr (std::is_same_v<T, jshort>)
+ env->GetShortArrayRegion(object<jshortArray>(), i, 1, &res);
+ else if constexpr (std::is_same_v<T, jint>)
+ env->GetIntArrayRegion(object<jbyteArray>(), i, 1, &res);
+ else if constexpr (std::is_same_v<T, jlong>)
+ env->GetLongArrayRegion(object<jlongArray>(), i, 1, &res);
+ else if constexpr (std::is_same_v<T, jfloat>)
+ env->GetFloatArrayRegion(object<jfloatArray>(), i, 1, &res);
+ else if constexpr (std::is_same_v<T, jdouble>)
+ env->GetDoubleArrayRegion(object<jdoubleArray>(), i, 1, &res);
+ return res;
+ }
+ }
+ auto toContainer() const
+ {
+ JNIEnv *env = jniEnv();
+ if constexpr (std::is_same_v<T, jobject>) {
+ QList<jobject> res;
+ res.reserve(size());
+ for (auto element : *this)
+ res.append(element);
+ return res;
+ } else if constexpr (std::is_same_v<T, jstring>) {
+ QStringList res;
+ res.reserve(size());
+ for (auto element : *this)
+ res.append(QJniObject(element).toString());
+ return res;
+ } else if constexpr (std::is_same_v<T, jbyte>) {
+ const qsizetype bytecount = size();
+ QByteArray res(bytecount, Qt::Initialization::Uninitialized);
+ env->GetByteArrayRegion(object<jbyteArray>(),
+ 0, bytecount, reinterpret_cast<jbyte *>(res.data()));
+ return res;
+ } else {
+ QList<T> res;
+ res.resize(size());
+ if constexpr (std::is_same_v<T, jchar>) {
+ env->GetCharArrayRegion(object<jcharArray>(),
+ 0, res.size(), res.data());
+ } else if constexpr (std::is_same_v<T, jboolean>) {
+ env->GetBooleanArrayRegion(object<jbooleanArray>(),
+ 0, res.size(), res.data());
+ } else if constexpr (std::is_same_v<T, jshort>) {
+ env->GetShortArrayRegion(object<jshortArray>(),
+ 0, res.size(), res.data());
+ } else if constexpr (std::is_same_v<T, jint>) {
+ env->GetIntArrayRegion(object<jintArray>(),
+ 0, res.size(), res.data());
+ } else if constexpr (std::is_same_v<T, jlong>) {
+ env->GetLongArrayRegion(object<jlongArray>(),
+ 0, res.size(), res.data());
+ } else if constexpr (std::is_same_v<T, jfloat>) {
+ env->GetFloatArrayRegion(object<jfloatArray>(),
+ 0, res.size(), res.data());
+ } else if constexpr (std::is_same_v<T, jdouble>) {
+ env->GetDoubleArrayRegion(object<jdoubleArray>(),
+ 0, res.size(), res.data());
+ } else {
+ res.clear();
+ }
+ return res;
+ }
+ }
+};
+
+template <typename ElementType, typename List, typename NewFn, typename SetFn>
+auto QJniArrayBase::makeArray(List &&list, NewFn &&newArray, SetFn &&setRegion)
+{
+ const size_type length = size_type(std::size(list));
+ JNIEnv *env = QJniEnvironment::getJniEnv();
+ auto localArray = (env->*newArray)(length);
+ if (QJniEnvironment::checkAndClearExceptions(env))
+ return QJniArray<ElementType>();
+
+ // can't use static_cast here because we have signed/unsigned mismatches
+ if (length) {
+ (env->*setRegion)(localArray, 0, length,
+ reinterpret_cast<const ElementType *>(std::data(std::as_const(list))));
+ }
+ return QJniArray<ElementType>(localArray);
+};
+
+template <typename List>
+auto QJniArrayBase::makeObjectArray(List &&list)
+{
+ using ElementType = typename q20::remove_cvref_t<List>::value_type;
+ using ResultType = QJniArray<decltype(std::declval<QJniObject::LocalFrame<>>().convertToJni(
+ std::declval<ElementType>()))
+ >;
+
+ if (std::size(list) == 0)
+ return ResultType();
+
+ JNIEnv *env = QJniEnvironment::getJniEnv();
+ const size_type length = size_type(std::size(list));
+
+ // this assumes that all objects in the list have the same class
+ jclass elementClass = nullptr;
+ if constexpr (std::disjunction_v<std::is_same<ElementType, QJniObject>,
+ std::is_base_of<QtJniTypes::JObjectBase, ElementType>>) {
+ elementClass = std::begin(list)->objectClass();
+ } else if constexpr (std::is_same_v<ElementType, QString>) {
+ elementClass = env->FindClass("java/lang/String");
+ } else {
+ elementClass = env->GetObjectClass(*std::begin(list));
+ }
+ auto localArray = env->NewObjectArray(length, elementClass, nullptr);
+ if (QJniEnvironment::checkAndClearExceptions(env))
+ return ResultType();
+
+ // explicitly manage the frame for local references in chunks of 100
+ QJniObject::LocalFrame frame(env);
+ constexpr jint frameCapacity = 100;
+ qsizetype i = 0;
+ for (const auto &element : std::as_const(list)) {
+ if (i % frameCapacity == 0) {
+ if (i)
+ env->PopLocalFrame(nullptr);
+ if (env->PushLocalFrame(frameCapacity) != 0)
+ return ResultType{};
+ }
+ jobject object = frame.convertToJni(element);
+ env->SetObjectArrayElement(localArray, i, object);
+ ++i;
+ }
+ if (i)
+ env->PopLocalFrame(nullptr);
+ return ResultType(localArray);
+}
+
+namespace QtJniTypes
+{
+template <typename T> struct IsJniArray: std::false_type {};
+template <typename T> struct IsJniArray<QJniArray<T>> : std::true_type {};
+template <typename T> struct Traits<QJniArray<T>> {
+ template <IfValidFieldType<T> = true>
+ static constexpr auto signature()
+ {
+ return CTString("[") + Traits<T>::signature();
+ }
+};
+template <typename T> struct Traits<QList<T>> {
+ template <IfValidFieldType<T> = true>
+ static constexpr auto signature()
+ {
+ return CTString("[") + Traits<T>::signature();
+ }
+};
+template <> struct Traits<QByteArray> {
+ static constexpr auto signature()
+ {
+ return CTString("[B");
+ }
+};
+}
+
+QT_END_NAMESPACE
+
+#endif
+
+#endif // QJNIARRAY_H
diff --git a/src/corelib/kernel/qjnienvironment.cpp b/src/corelib/kernel/qjnienvironment.cpp
index a72078d2cf..1e2826e76b 100644
--- a/src/corelib/kernel/qjnienvironment.cpp
+++ b/src/corelib/kernel/qjnienvironment.cpp
@@ -1,44 +1,7 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtCore module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 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 "qjnienvironment.h"
-#include "qjniobject.h"
#include "qjnihelpers_p.h"
#include <QtCore/QThread>
@@ -66,8 +29,6 @@ QT_BEGIN_NAMESPACE
It has not been tested for other platforms.
*/
-static const char qJniThreadName[] = "QtThread";
-
class QJniEnvironmentPrivate
{
public:
@@ -83,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;
}
/*!
@@ -148,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
@@ -158,8 +112,6 @@ JNIEnv *QJniEnvironment::operator->() const
}
/*!
- \fn JNIEnv &QJniEnvironment::operator*() const
-
Returns the JNI Environment's \c JNIEnv object.
*/
JNIEnv &QJniEnvironment::operator*() const
@@ -168,8 +120,6 @@ JNIEnv &QJniEnvironment::operator*() const
}
/*!
- \fn JNIEnv *QJniEnvironment::jniEnv() const
-
Returns the JNI Environment's \c JNIEnv pointer.
*/
JNIEnv *QJniEnvironment::jniEnv() const
@@ -232,6 +182,16 @@ jmethodID QJniEnvironment::findMethod(jclass clazz, const char *methodName, cons
}
/*!
+ \fn template<typename ...Args> jmethodId QJniEnvironment::findMethod(jclass clazz, const char *methodName)
+ \since 6.4
+
+ Searches for an instance method of a class \a clazz. The method is specified
+ by its \a methodName, the signature is deduced from the template parameters.
+
+ Returns the method ID or \c nullptr if the method is not found.
+*/
+
+/*!
Searches for a static method of a class \a clazz. The method is specified
by its \a methodName and \a signature.
@@ -265,9 +225,28 @@ jmethodID QJniEnvironment::findStaticMethod(jclass clazz, const char *methodName
return nullptr;
}
+/*!
+ \fn template<typename ...Args> jmethodId QJniEnvironment::findStaticMethod(jclass clazz, const char *methodName)
+ \since 6.4
+
+ Searches for an instance method of a class \a clazz. The method is specified
+ by its \a methodName, the signature is deduced from the template parameters.
+
+ Returns the method ID or \c nullptr if the method is not found.
+
+ \code
+ QJniEnvironment env;
+ jclass javaClass = env.findClass("org/qtproject/example/android/CustomClass");
+ jmethodID methodId = env.findStaticMethod<void, jstring>(javaClass, "staticJavaMethod");
+ QJniObject javaMessage = QJniObject::fromString("findStaticMethod example");
+ QJniObject::callStaticMethod<void>(javaClass,
+ methodId,
+ javaMessage.object<jstring>());
+ \endcode
+*/
/*!
- Searches for an member field of a class \a clazz. The field is specified
+ Searches for a member field of a class \a clazz. The field is specified
by its \a fieldName and \a signature.
Returns the field ID or \c nullptr if the field is not found.
@@ -289,6 +268,16 @@ jfieldID QJniEnvironment::findField(jclass clazz, const char *fieldName, const c
}
/*!
+ \fn template<typename T> jfieldID QJniEnvironment::findField(jclass clazz, const char *fieldName)
+ \since 6.4
+
+ Searches for a member field of a class \a clazz. The field is specified
+ by its \a fieldName. The signature of the field is deduced from the template parameter.
+
+ Returns the field ID or \c nullptr if the field is not found.
+*/
+
+/*!
Searches for a static field of a class \a clazz. The field is specified
by its \a fieldName and \a signature.
@@ -311,8 +300,16 @@ jfieldID QJniEnvironment::findStaticField(jclass clazz, const char *fieldName, c
}
/*!
- \fn JavaVM *QJniEnvironment::javaVM()
+ \fn template<typename T> jfieldID QJniEnvironment::findStaticField(jclass clazz, const char *fieldName)
+ \since 6.4
+
+ Searches for a static field of a class \a clazz. The field is specified
+ by its \a fieldName. The signature of the field is deduced from the template parameter.
+
+ Returns the field ID or \c nullptr if the field is not found.
+*/
+/*!
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.
@@ -347,12 +344,22 @@ JavaVM *QJniEnvironment::javaVM()
bool QJniEnvironment::registerNativeMethods(const char *className, const JNINativeMethod methods[],
int size)
{
- QJniObject classObject(className);
+ const jclass clazz = findClass(className);
- if (!classObject.isValid())
+ if (!clazz)
return false;
- return registerNativeMethods(classObject.objectClass(), methods, size);
+
+ 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
@@ -408,6 +415,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
@@ -430,20 +445,59 @@ bool QJniEnvironment::registerNativeMethods(jclass clazz, const JNINativeMethod
*/
bool QJniEnvironment::checkAndClearExceptions(QJniEnvironment::OutputMode outputMode)
{
- if (Q_UNLIKELY(d->jniEnv->ExceptionCheck())) {
- if (outputMode != OutputMode::Silent)
- d->jniEnv->ExceptionDescribe();
- d->jniEnv->ExceptionClear();
+ return checkAndClearExceptions(d->jniEnv, outputMode);
+}
- return true;
+namespace {
+ // Any pending exception need to be cleared before calling this
+ QString exceptionMessage(JNIEnv *env, jthrowable exception)
+ {
+ if (!exception)
+ return {};
+
+ auto logError = []() {
+ qWarning() << "QJniEnvironment: a null object returned or an exception occurred while "
+ "fetching a prior exception message";
+ };
+
+ auto checkAndClear = [env]() {
+ if (Q_UNLIKELY(env->ExceptionCheck())) {
+ env->ExceptionClear();
+ return true;
+ }
+ return false;
+ };
+
+ const jclass logClazz = env->FindClass("android/util/Log");
+ if (checkAndClear() || !logClazz) {
+ logError();
+ return {};
+ }
+
+ const jmethodID methodId = env->GetStaticMethodID(logClazz, "getStackTraceString",
+ "(Ljava/lang/Throwable;)Ljava/lang/String;");
+ if (checkAndClear() || !methodId) {
+ logError();
+ return {};
+ }
+
+ jvalue value;
+ value.l = static_cast<jobject>(exception);
+ const jobject messageObj = env->CallStaticObjectMethodA(logClazz, methodId, &value);
+ const jstring jmessage = static_cast<jstring>(messageObj);
+ if (checkAndClear())
+ return {};
+
+ char const *utfMessage = env->GetStringUTFChars(jmessage, 0);
+ const QString message = QString::fromUtf8(utfMessage);
+
+ env->ReleaseStringUTFChars(jmessage, utfMessage);
+
+ return message;
}
-
- return false;
-}
+} // end namespace
/*!
- \fn QJniEnvironment::checkAndClearExceptions(JNIEnv *env, OutputMode outputMode = OutputMode::Verbose)
-
Cleans any pending exceptions for \a env, either silently or reporting
stack backtrace, depending on the \a outputMode. This is useful when you
already have a \c JNIEnv pointer such as in a native function implementation.
@@ -458,9 +512,22 @@ bool QJniEnvironment::checkAndClearExceptions(QJniEnvironment::OutputMode output
bool QJniEnvironment::checkAndClearExceptions(JNIEnv *env, QJniEnvironment::OutputMode outputMode)
{
if (Q_UNLIKELY(env->ExceptionCheck())) {
- if (outputMode != OutputMode::Silent)
- env->ExceptionDescribe();
- env->ExceptionClear();
+ if (outputMode == OutputMode::Verbose) {
+ if (jthrowable exception = env->ExceptionOccurred()) {
+ env->ExceptionClear();
+ const QString message = exceptionMessage(env, exception);
+ // Print to QWARN since env->ExceptionDescribe() does the same
+ if (!message.isEmpty())
+ qWarning().noquote() << message;
+ env->DeleteLocalRef(exception);
+ } else {
+ // if the exception object is null for some reason just
+ env->ExceptionDescribe();
+ env->ExceptionClear();
+ }
+ } else {
+ env->ExceptionClear();
+ }
return true;
}
diff --git a/src/corelib/kernel/qjnienvironment.h b/src/corelib/kernel/qjnienvironment.h
index 16a6413d31..dda8dc0950 100644
--- a/src/corelib/kernel/qjnienvironment.h
+++ b/src/corelib/kernel/qjnienvironment.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtCore module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 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 QJNI_ENVIRONMENT_H
#define QJNI_ENVIRONMENT_H
@@ -44,6 +8,7 @@
#if defined(Q_QDOC) || defined(Q_OS_ANDROID)
#include <jni.h>
+#include <QtCore/qjnitypes_impl.h>
QT_BEGIN_NAMESPACE
@@ -59,14 +24,56 @@ public:
JNIEnv &operator*() const;
JNIEnv *jniEnv() const;
jclass findClass(const char *className);
+ template<typename Class>
+ 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) {
+ constexpr auto signature = QtJniTypes::methodSignature<Args...>();
+ return findMethod(clazz, methodName, signature.data());
+ }
jmethodID findStaticMethod(jclass clazz, const char *methodName, const char *signature);
+ template<typename ...Args>
+ jmethodID findStaticMethod(jclass clazz, const char *methodName) {
+ constexpr auto signature = QtJniTypes::methodSignature<Args...>();
+ return findStaticMethod(clazz, methodName, signature.data());
+ }
jfieldID findField(jclass clazz, const char *fieldName, const char *signature);
+ template<typename T>
+ jfieldID findField(jclass clazz, const char *fieldName) {
+ constexpr auto signature = QtJniTypes::fieldSignature<T>();
+ return findField(clazz, fieldName, signature.data());
+ }
jfieldID findStaticField(jclass clazz, const char *fieldName, const char *signature);
+ template<typename T>
+ jfieldID findStaticField(jclass clazz, const char *fieldName) {
+ constexpr auto signature = QtJniTypes::fieldSignature<T>();
+ return findStaticField(clazz, fieldName, signature.data());
+ }
static JavaVM *javaVM();
bool registerNativeMethods(const char *className, const JNINativeMethod methods[], int size);
bool registerNativeMethods(jclass clazz, const JNINativeMethod methods[], int size);
+ bool registerNativeMethods(const char *className, std::initializer_list<JNINativeMethod> methods)
+ {
+ return registerNativeMethods(className, std::data(methods), methods.size());
+ }
+
+ bool registerNativeMethods(jclass clazz, std::initializer_list<JNINativeMethod> methods)
+ {
+ 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.")
@@ -81,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 b2a5dc7441..d900b74d37 100644
--- a/src/corelib/kernel/qjnihelpers.cpp
+++ b/src/corelib/kernel/qjnihelpers.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtCore module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// 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
#include "qjnihelpers_p.h"
@@ -44,7 +8,9 @@
#include "qlist.h"
#include "qmutex.h"
#include "qsemaphore.h"
+#include "qreadwritelock.h"
#include <QtCore/private/qcoreapplication_p.h>
+#include <QtCore/private/qlocking_p.h>
#include <android/log.h>
#include <deque>
@@ -60,8 +26,6 @@ namespace QtAndroidPrivate {
ResumePauseListener::~ResumePauseListener() {}
void ResumePauseListener::handlePause() {}
void ResumePauseListener::handleResume() {}
- GenericMotionEventListener::~GenericMotionEventListener() {}
- KeyEventListener::~KeyEventListener() {}
}
static JavaVM *g_javaVM = nullptr;
@@ -69,43 +33,46 @@ static jobject g_jActivity = nullptr;
static jobject g_jService = nullptr;
static jobject g_jClassLoader = nullptr;
-Q_GLOBAL_STATIC_WITH_ARGS(QtAndroidPrivate::OnBindListener*, g_onBindListener, (nullptr));
-Q_GLOBAL_STATIC(QMutex, g_onBindListenerMutex);
+Q_CONSTINIT static QtAndroidPrivate::OnBindListener *g_onBindListener;
+Q_CONSTINIT static QBasicMutex g_onBindListenerMutex;
Q_GLOBAL_STATIC(QSemaphore, g_waitForServiceSetupSemaphore);
-Q_GLOBAL_STATIC(QAtomicInt, g_serviceSetupLockers);
+Q_CONSTINIT static QBasicAtomicInt g_serviceSetupLockers = Q_BASIC_ATOMIC_INITIALIZER(0);
-namespace {
- struct GenericMotionEventListeners {
- QMutex mutex;
- QList<QtAndroidPrivate::GenericMotionEventListener *> listeners;
- };
-}
-Q_GLOBAL_STATIC(GenericMotionEventListeners, g_genericMotionEventListeners)
+Q_GLOBAL_STATIC(QReadWriteLock, g_updateMutex);
-static jboolean dispatchGenericMotionEvent(JNIEnv *, jclass, jobject event)
+static jboolean updateNativeActivity(JNIEnv *env, jclass = nullptr)
{
- jboolean ret = JNI_FALSE;
- QMutexLocker locker(&g_genericMotionEventListeners()->mutex);
- for (auto *listener : qAsConst(g_genericMotionEventListeners()->listeners))
- ret |= listener->handleGenericMotionEvent(event);
- return ret;
-}
-namespace {
- struct KeyEventListeners {
- QMutex mutex;
- QList<QtAndroidPrivate::KeyEventListener *> listeners;
- };
-}
-Q_GLOBAL_STATIC(KeyEventListeners, g_keyEventListeners)
+ jclass jQtNative = env->FindClass("org/qtproject/qt/android/QtNative");
+ if (QJniEnvironment::checkAndClearExceptions(env))
+ return JNI_FALSE;
-static jboolean dispatchKeyEvent(JNIEnv *, jclass, jobject event)
-{
- jboolean ret = JNI_FALSE;
- QMutexLocker locker(&g_keyEventListeners()->mutex);
- for (auto *listener : qAsConst(g_keyEventListeners()->listeners))
- ret |= listener->handleKeyEvent(event);
- return ret;
+ jmethodID activityMethodID =
+ env->GetStaticMethodID(jQtNative, "activity", "()Landroid/app/Activity;");
+ if (QJniEnvironment::checkAndClearExceptions(env))
+ return JNI_FALSE;
+
+ jobject activity = env->CallStaticObjectMethod(jQtNative, activityMethodID);
+ if (QJniEnvironment::checkAndClearExceptions(env))
+ return JNI_FALSE;
+
+ QWriteLocker locker(g_updateMutex());
+
+ if (g_jActivity) {
+ env->DeleteGlobalRef(g_jActivity);
+ g_jActivity = nullptr;
+ }
+
+ if (activity) {
+ g_jActivity = env->NewGlobalRef(activity);
+ env->DeleteLocalRef(activity);
+ }
+
+ env->DeleteLocalRef(jQtNative);
+ if (QJniEnvironment::checkAndClearExceptions(env))
+ return JNI_FALSE;
+
+ return JNI_TRUE;
}
namespace {
@@ -269,8 +236,7 @@ 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) },
};
const bool regOk = (env->RegisterNatives(jQtNative, methods, sizeof(methods) / sizeof(methods[0])) == JNI_OK);
@@ -278,33 +244,51 @@ jint QtAndroidPrivate::initJNI(JavaVM *vm, JNIEnv *env)
if (!regOk && QJniEnvironment::checkAndClearExceptions(env))
return JNI_ERR;
- if (!registerPermissionNatives())
+ QJniEnvironment qJniEnv;
+ if (!registerPermissionNatives(qJniEnv))
+ return JNI_ERR;
+
+ if (!registerNativeInterfaceNatives(qJniEnv))
return JNI_ERR;
- if (!registerNativeInterfaceNatives())
+ if (!registerExtrasNatives(qJniEnv))
return JNI_ERR;
return JNI_OK;
}
-jobject QtAndroidPrivate::activity()
+Q_CORE_EXPORT jobject qt_androidActivity()
{
+ QReadLocker locker(g_updateMutex());
return g_jActivity;
}
-jobject QtAndroidPrivate::service()
+
+QtJniTypes::Activity QtAndroidPrivate::activity()
+{
+ QReadLocker locker(g_updateMutex());
+ return g_jActivity;
+}
+
+Q_CORE_EXPORT jobject qt_androidService()
{
return g_jService;
}
-jobject QtAndroidPrivate::context()
+QtJniTypes::Service QtAndroidPrivate::service()
{
+ return g_jService;
+}
+
+QtJniTypes::Context QtAndroidPrivate::context()
+{
+ QReadLocker locker(g_updateMutex());
if (g_jActivity)
return g_jActivity;
if (g_jService)
return g_jService;
- return 0;
+ return nullptr;
}
JavaVM *QtAndroidPrivate::javaVM()
@@ -325,30 +309,6 @@ jint QtAndroidPrivate::androidSdkVersion()
return sdkVersion;
}
-void QtAndroidPrivate::registerGenericMotionEventListener(QtAndroidPrivate::GenericMotionEventListener *listener)
-{
- QMutexLocker locker(&g_genericMotionEventListeners()->mutex);
- g_genericMotionEventListeners()->listeners.push_back(listener);
-}
-
-void QtAndroidPrivate::unregisterGenericMotionEventListener(QtAndroidPrivate::GenericMotionEventListener *listener)
-{
- QMutexLocker locker(&g_genericMotionEventListeners()->mutex);
- g_genericMotionEventListeners()->listeners.removeOne(listener);
-}
-
-void QtAndroidPrivate::registerKeyEventListener(QtAndroidPrivate::KeyEventListener *listener)
-{
- QMutexLocker locker(&g_keyEventListeners()->mutex);
- g_keyEventListeners()->listeners.push_back(listener);
-}
-
-void QtAndroidPrivate::unregisterKeyEventListener(QtAndroidPrivate::KeyEventListener *listener)
-{
- QMutexLocker locker(&g_keyEventListeners()->mutex);
- g_keyEventListeners()->listeners.removeOne(listener);
-}
-
void QtAndroidPrivate::waitForServiceSetup()
{
g_waitForServiceSetupSemaphore->acquire();
@@ -356,29 +316,41 @@ void QtAndroidPrivate::waitForServiceSetup()
int QtAndroidPrivate::acuqireServiceSetup(int flags)
{
- g_serviceSetupLockers->ref();
+ g_serviceSetupLockers.ref();
return flags;
}
void QtAndroidPrivate::setOnBindListener(QtAndroidPrivate::OnBindListener *listener)
{
- QMutexLocker lock(g_onBindListenerMutex());
- *g_onBindListener = listener;
- if (!g_serviceSetupLockers->deref())
+ const auto lock = qt_scoped_lock(g_onBindListenerMutex);
+ g_onBindListener = listener;
+ if (!g_serviceSetupLockers.deref())
g_waitForServiceSetupSemaphore->release();
}
jobject QtAndroidPrivate::callOnBindListener(jobject intent)
{
- QMutexLocker lock(g_onBindListenerMutex());
- if (*g_onBindListener)
- return (*g_onBindListener)->onBind(intent);
+ const auto lock = qt_scoped_lock(g_onBindListenerMutex);
+ if (g_onBindListener)
+ return g_onBindListener->onBind(intent);
return nullptr;
}
+Q_CONSTINIT static QBasicAtomicInt g_androidDeadlockProtector = Q_BASIC_ATOMIC_INITIALIZER(0);
+
+bool QtAndroidPrivate::acquireAndroidDeadlockProtector()
+{
+ return g_androidDeadlockProtector.testAndSetAcquire(0, 1);
+}
+
+void QtAndroidPrivate::releaseAndroidDeadlockProtector()
+{
+ g_androidDeadlockProtector.storeRelease(0);
+}
+
QT_END_NAMESPACE
-Q_CORE_EXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *reserved)
+JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *reserved)
{
Q_UNUSED(reserved);
diff --git a/src/corelib/kernel/qjnihelpers_p.h b/src/corelib/kernel/qjnihelpers_p.h
index 2860cef4c3..b5e05fcaf1 100644
--- a/src/corelib/kernel/qjnihelpers_p.h
+++ b/src/corelib/kernel/qjnihelpers_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtCore module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 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 QJNIHELPERS_H
#define QJNIHELPERS_H
@@ -54,9 +18,13 @@
#include <jni.h>
#include <functional>
#include <QtCore/private/qglobal_p.h>
+#include <QtCore/qcoreapplication_platform.h>
QT_BEGIN_NAMESPACE
+Q_DECLARE_JNI_CLASS(Activity, "android/app/Activity")
+Q_DECLARE_JNI_CLASS(Service, "android/app/Service")
+
namespace QtAndroidPrivate
{
class Q_CORE_EXPORT ActivityResultListener
@@ -81,20 +49,6 @@ namespace QtAndroidPrivate
virtual void handleResume();
};
- class Q_CORE_EXPORT GenericMotionEventListener
- {
- public:
- virtual ~GenericMotionEventListener();
- virtual bool handleGenericMotionEvent(jobject event) = 0;
- };
-
- class Q_CORE_EXPORT KeyEventListener
- {
- public:
- virtual ~KeyEventListener();
- virtual bool handleKeyEvent(jobject event) = 0;
- };
-
class Q_CORE_EXPORT OnBindListener
{
public:
@@ -102,17 +56,18 @@ namespace QtAndroidPrivate
virtual jobject onBind(jobject intent) = 0;
};
- Q_CORE_EXPORT jobject activity();
- Q_CORE_EXPORT jobject service();
- Q_CORE_EXPORT jobject context();
+ Q_CORE_EXPORT QtJniTypes::Activity activity();
+ Q_CORE_EXPORT QtJniTypes::Service service();
+ Q_CORE_EXPORT QtJniTypes::Context context();
Q_CORE_EXPORT JavaVM *javaVM();
Q_CORE_EXPORT jint initJNI(JavaVM *vm, JNIEnv *env);
Q_CORE_EXPORT jclass findClass(const char *className, JNIEnv *env);
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);
@@ -127,16 +82,13 @@ namespace QtAndroidPrivate
Q_CORE_EXPORT void registerResumePauseListener(ResumePauseListener *listener);
Q_CORE_EXPORT void unregisterResumePauseListener(ResumePauseListener *listener);
- Q_CORE_EXPORT void registerGenericMotionEventListener(GenericMotionEventListener *listener);
- Q_CORE_EXPORT void unregisterGenericMotionEventListener(GenericMotionEventListener *listener);
-
- Q_CORE_EXPORT void registerKeyEventListener(KeyEventListener *listener);
- Q_CORE_EXPORT void unregisterKeyEventListener(KeyEventListener *listener);
-
Q_CORE_EXPORT void waitForServiceSetup();
Q_CORE_EXPORT int acuqireServiceSetup(int flags);
Q_CORE_EXPORT void setOnBindListener(OnBindListener *listener);
Q_CORE_EXPORT jobject callOnBindListener(jobject intent);
+
+ Q_CORE_EXPORT bool acquireAndroidDeadlockProtector();
+ Q_CORE_EXPORT void releaseAndroidDeadlockProtector();
}
#define Q_JNI_FIND_AND_CHECK_CLASS(CLASS_NAME) \
diff --git a/src/corelib/kernel/qjniobject.cpp b/src/corelib/kernel/qjniobject.cpp
index dfb0dbd36d..8244a4390f 100644
--- a/src/corelib/kernel/qjniobject.cpp
+++ b/src/corelib/kernel/qjniobject.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtCore module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 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 "qjniobject.h"
@@ -44,9 +8,15 @@
#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;
+
/*!
\class QJniObject
\inmodule QtCore
@@ -54,7 +24,7 @@ QT_BEGIN_NAMESPACE
\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.
@@ -74,55 +44,77 @@ QT_BEGIN_NAMESPACE
\section1 Method Signatures
- For functions that take no arguments, QJniObject provides convenience functions that will use
- the correct signature based on the provided template type. For example:
+ 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:
\code
jint x = QJniObject::callMethod<jint>("getSize");
QJniObject::callMethod<void>("touch");
+ jint ret = jString1.callMethod<jint>("compareToIgnoreCase", jString2.object<jstring>());
\endcode
- In other cases you will need to supply the signature yourself, and it is important that the
- signature matches the function you want to call. 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.
+ 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.
- The example below demonstrates how to call two different static functions:
+ 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:
\code
// Java class
package org.qtproject.qt;
class TestClass
{
- static String fromNumber(int x) { ... }
- static String[] stringArray(String s1, String s2) { ... }
+ static TestClass create() { ... }
+ static String fromNumber(int x) { ... }
+ static String[] stringArray(String s1, String s2) { ... }
}
\endcode
- The signature for the first function is \c {"(I)Ljava/lang/String;"}:
+ 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
+ \c {"()Lorg/qtproject/qt/TestClass;}. The signatures for the second and third functions
+ are \c {"(I)Ljava/lang/String;"} and
+ \c {"(Ljava/lang/String;Ljava/lang/String;)[Ljava/lang/String;"}, respectively.
+
+ We can call the \c create() function like this:
+
+ \code
+ // C++ code
+ QJniObject testClass = QJniObject::callStaticObjectMethod("org/qtproject/qt/TestClass",
+ "create",
+ "()Lorg/qtproject/qt/TestClass;");
+ \endcode
+
+ For the second and third function we can rely on QJniObject's template methods to create
+ the implicit signature string, but we can also pass the signature string explicitly:
\code
// C++ code
QJniObject stringNumber = QJniObject::callStaticObjectMethod("org/qtproject/qt/TestClass",
- "fromNumber"
- "(I)Ljava/lang/String;",
- 10);
+ "fromNumber",
+ "(I)Ljava/lang/String;", 10);
\endcode
- and the signature for the second function is
- \c {"(Ljava/lang/String;Ljava/lang/String;)[Ljava/lang/String;"}:
+ For the implicit signature creation to work we need to specify the return type explicitly:
\code
// C++ code
QJniObject string1 = QJniObject::fromString("String1");
QJniObject string2 = QJniObject::fromString("String2");
- QJniObject stringArray = QJniObject::callStaticObjectMethod("org/qtproject/qt/TestClass",
+ QJniObject stringArray = QJniObject::callStaticObjectMethod<jstringArray>(
+ "org/qtproject/qt/TestClass",
"stringArray"
- "(Ljava/lang/String;Ljava/lang/String;)[Ljava/lang/String;"
string1.object<jstring>(),
string2.object<jstring>());
\endcode
+ 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
After calling Java functions that might throw exceptions, it is important
@@ -289,100 +281,151 @@ QT_BEGIN_NAMESPACE
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)
- {
- return QJniObject::loadClass(className, env, binEncoded);
- }
-
- static QByteArray toBinaryEncClassName(const QByteArray &className)
+ template <typename ...Args>
+ void construct(const char *signature = nullptr, Args &&...args)
{
- 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 QLatin1String keyBase()
-{
- return QLatin1String("%1%2:%3");
-}
-
-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 QLatin1String 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 (!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 (!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)
@@ -401,9 +444,12 @@ jmethodID QJniObject::getMethodID(JNIEnv *env,
return id;
}
-void QJniObject::callVoidMethodV(JNIEnv *env, jmethodID id, va_list args) const
+void QJniObject::callVoidMethodV(JNIEnv *env, jmethodID id, ...) const
{
+ va_list args;
+ va_start(args, id);
env->CallVoidMethodV(d->m_jobject, id, args);
+ va_end(args);
}
jmethodID QJniObject::getCachedMethodID(JNIEnv *env,
@@ -416,10 +462,8 @@ jmethodID QJniObject::getCachedMethodID(JNIEnv *env,
if (className.isEmpty())
return getMethodID(env, clazz, name, signature, isStatic);
- const QString key = keyBase().arg(QLatin1String(className),
- QLatin1String(name),
- QLatin1String(signature));
- QHash<QString, jmethodID>::const_iterator it;
+ const QByteArray key = cacheKey(className, name, signature);
+ QHash<QByteArray, jmethodID>::const_iterator it;
{
QReadLocker locker(cachedMethodIDLock);
@@ -447,7 +491,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)
@@ -476,10 +520,8 @@ jfieldID QJniObject::getCachedFieldID(JNIEnv *env,
if (className.isNull())
return getFieldID(env, clazz, name, signature, isStatic);
- const QString key = keyBase().arg(QLatin1String(className),
- QLatin1String(name),
- QLatin1String(signature));
- QHash<QString, jfieldID>::const_iterator it;
+ const QByteArray key = cacheKey(className, name, signature);
+ QHash<QByteArray, jfieldID>::const_iterator it;
{
QReadLocker locker(cachedFieldIDLock);
@@ -509,39 +551,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 QLatin1String 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()
@@ -566,21 +575,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();
}
/*!
@@ -599,43 +598,31 @@ 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);
}
-QJniObject::QJniObject(const char *className, const char *signature, const QVaListPrivate &args)
- : d(new QJniObjectPrivate())
-{
+/*!
+ \fn template<typename ...Args> QJniObject::QJniObject(const char *className, Args &&...args)
+ \since 6.4
+
+ Constructs a new JNI object by calling the constructor of \a className with
+ the arguments \a args. This constructor is only available if all \a args are
+ known \l {JNI Types}.
+
+ \code
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);
- }
- }
- }
-}
+ char* str = "Hello";
+ jstring myJStringArg = env->NewStringUTF(str);
+ QJniObject myNewJavaString("java/lang/String", myJStringArg);
+ \endcode
+*/
/*!
Constructs a new JNI object from \a clazz by calling the constructor with
@@ -650,26 +637,31 @@ 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);
}
}
/*!
+ \fn template<typename ...Args> QJniObject::QJniObject(jclass clazz, Args &&...args)
+ \since 6.4
+
+ Constructs a new JNI object from \a clazz by calling the constructor with
+ the arguments \a args. This constructor is only available if all \a args are
+ known \l {JNI Types}.
+
+ \code
+ QJniEnvironment env;
+ jclass myClazz = env.findClass("org/qtproject/qt/TestClass");
+ QJniObject(myClazz, 3);
+ \endcode
+*/
+
+/*!
Constructs a new JNI object by calling the default constructor of \a clazz.
\note The QJniObject will create a new reference to the class \a clazz
@@ -678,40 +670,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);
- }
- }
- }
- }
}
/*!
@@ -732,7 +692,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));
@@ -740,25 +700,19 @@ 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();
+ \fn template<typename Class, typename ...Args> static inline QJniObject QJniObject::construct(Args &&...args)
+ \since 6.4
- QJniEnvironment env;
- if (env.checkAndClearExceptions()) {
- env->DeleteLocalRef(object);
- return QJniObject();
- }
+ Constructs an instance of the Java class that is the equivalent of \c Class and
+ returns a QJniObject containing the JNI object. The arguments in \a args are
+ passed to the Java constructor.
- QJniObject res(object);
- env->DeleteLocalRef(object);
- return res;
-}
+ \code
+ QJniObject javaString = QJniObject::construct<jstring>();
+ \endcode
+
+ This function is only available if all \a args are known \l {JNI Types}.
+*/
/*!
\fn QJniObject::~QJniObject()
@@ -768,7 +722,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.
@@ -819,96 +803,51 @@ 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;
}
/*!
- \fn template <typename T> T QJniObject::callMethod(const char *methodName, const char *signature, ...) const
+ \fn template <typename Ret, typename ...Args> auto QJniObject::callMethod(const char *methodName, const char *signature, Args &&...args) const
+ \since 6.4
Calls the object's method \a methodName with \a signature specifying the types of any
- subsequent arguments.
+ subsequent arguments \a args, and returns the value (unless \c Ret is \c void). If \c Ret
+ is a jobject type, then the returned value will be a QJniObject.
\code
QJniObject myJavaStrin("org/qtproject/qt/TestClass");
jint index = myJavaString.callMethod<jint>("indexOf", "(I)I", 0x0051);
\endcode
-
*/
/*!
- \fn template <typename T> T QJniObject::callMethod(const char *methodName) const
+ \fn template <typename Ret, typename ...Args> auto QJniObject::callMethod(const char *methodName, Args &&...args) const
+ \since 6.4
- Calls the method \a methodName and returns the value.
+ Calls the method \a methodName with arguments \a args and returns the value
+ (unless \c Ret is \c void). If \c Ret is a jobject type, then the returned value
+ will be a QJniObject.
\code
QJniObject myJavaStrin("org/qtproject/qt/TestClass");
jint size = myJavaString.callMethod<jint>("length");
\endcode
+
+ The method signature is deduced at compile time from \c Ret and the types of \a args.
*/
/*!
- \fn template <typename T> T QJniObject::callStaticMethod(const char *className, const char *methodName, const char *signature, ...)
+ \fn template <typename Ret, typename ...Args> auto QJniObject::callStaticMethod(const char *className, const char *methodName, const char *signature, Args &&...args)
+ \since 6.4
Calls the static method \a methodName from class \a className with \a signature
- specifying the types of any subsequent arguments.
+ specifying the types of any subsequent arguments \a args. Returns the result of
+ the method (unless \c Ret is \c void). If \c Ret is a jobject type, then the
+ returned value will be a QJniObject.
\code
jint a = 2;
@@ -918,20 +857,27 @@ QJniObject QJniObject::callStaticObjectMethodV(jclass clazz,
*/
/*!
- \fn template <typename T> T QJniObject::callStaticMethod(const char *className, const char *methodName)
+ \fn template <typename Ret, typename ...Args> auto QJniObject::callStaticMethod(const char *className, const char *methodName, Args &&...args)
+ \since 6.4
- Calls the static method \a methodName on class \a className and returns the value.
+ Calls the static method \a methodName on class \a className with arguments \a args,
+ 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.
\code
jint value = QJniObject::callStaticMethod<jint>("MyClass", "staticMethod");
\endcode
+
+ The method signature is deduced at compile time from \c Ret and the types of \a args.
*/
/*!
- \fn template <typename T> T QJniObject::callStaticMethod(jclass clazz, const char *methodName, const char *signature, ...)
+ \fn template <typename Ret, typename ...Args> auto QJniObject::callStaticMethod(jclass clazz, const char *methodName, const char *signature, Args &&...args)
Calls the static method \a methodName from \a clazz with \a signature
- specifying the types of any subsequent arguments.
+ specifying the types of any subsequent arguments. Returns the result of
+ the method (unless \c Ret is \c void). If \c Ret is a jobject type, then the
+ returned value will be a QJniObject.
\code
QJniEnvironment env;
@@ -943,11 +889,15 @@ QJniObject QJniObject::callStaticObjectMethodV(jclass clazz,
*/
/*!
- \fn template <typename T> T QJniObject::callStaticMethod(jclass clazz, jmethodID methodId, ...)
+ \fn template <typename Ret, typename ...Args> auto QJniObject::callStaticMethod(jclass clazz, jmethodID methodId, Args &&...args)
+ \since 6.4
Calls the static method identified by \a methodId from the class \a clazz
- with any subsequent arguments. Useful when \a clazz and \a methodId are
- already cached from previous operations.
+ with any subsequent arguments, 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.
+
+ Useful when \a clazz and \a methodId are already cached from previous operations.
\code
QJniEnvironment env;
@@ -962,15 +912,32 @@ QJniObject QJniObject::callStaticObjectMethodV(jclass clazz,
*/
/*!
- \fn template <typename T> T QJniObject::callStaticMethod(jclass clazz, const char *methodName)
+ \fn template <typename Ret, typename ...Args> auto QJniObject::callStaticMethod(jclass clazz, const char *methodName, Args &&...args)
+ \since 6.4
- Calls the static method \a methodName on \a clazz and returns the value.
+ 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 is a jobject type, then the returned value will
+ be a QJniObject.
\code
QJniEnvironment env;
jclass javaMathClass = env.findClass("java/lang/Math");
jdouble randNr = QJniObject::callStaticMethod<jdouble>(javaMathClass, "random");
\endcode
+
+ The method signature is deduced at compile time from \c Ret and the types of \a args.
+*/
+
+/*!
+ \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.
*/
/*!
@@ -987,12 +954,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;
}
@@ -1016,16 +982,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;
}
@@ -1043,13 +1009,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;
}
@@ -1075,11 +1041,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;
}
@@ -1088,36 +1054,44 @@ QJniObject QJniObject::callStaticObjectMethod(jclass clazz, jmethodID methodId,
}
/*!
- \fn QJniObject QJniObject::callObjectMethod(const char *methodName) const
+ \fn template<typename Ret, typename ...Args> QJniObject QJniObject::callObjectMethod(const char *methodName, Args &&...args) const
+ \since 6.4
- Calls the Java objects method \a methodName and returns a new QJniObject for
- the returned Java object.
+ Calls the Java objects method \a methodName with arguments \a args and returns a
+ new QJniObject for the returned Java object.
\code
QJniObject myJavaString = QJniObject::fromString("Hello, Java");
QJniObject myJavaString2 = myJavaString1.callObjectMethod<jstring>("toString");
\endcode
+
+ The method signature is deduced at compile time from \c Ret and the types of \a args.
*/
/*!
- \fn QJniObject QJniObject::callStaticObjectMethod(const char *className, const char *methodName)
+ \fn template<typename Ret, typename ...Args> QJniObject QJniObject::callStaticObjectMethod(const char *className, const char *methodName, Args &&...args)
+ \since 6.4
- Calls the static method with \a methodName on the class \a className.
+ Calls the static method with \a methodName on the class \a className, passing
+ arguments \a args, and returns a new QJniObject for the returned Java object.
\code
QJniObject string = QJniObject::callStaticObjectMethod<jstring>("CustomClass", "getClassName");
\endcode
+
+ The method signature is deduced at compile time from \c Ret and the types of \a args.
*/
/*!
- \fn QJniObject QJniObject::callStaticObjectMethod(jclass clazz, const char *methodName)
-
- Calls the static method with \a methodName on \a clazz.
+ \fn template<typename Ret, typename ...Args> QJniObject QJniObject::callStaticObjectMethod(jclass clazz, const char *methodName, Args &&...args)
+ \since 6.4
+ Calls the static method with \a methodName on \a clazz, passing arguments \a args,
+ and returns a new QJniObject for the returned Java object.
*/
/*!
- \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.
*/
@@ -1138,7 +1112,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.
@@ -1149,18 +1123,26 @@ 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.
*/
/*!
+ \fn template <typename Klass, typename T> auto QJniObject::getStaticField(const char *fieldName)
+
+ Retrieves the value from the static field \a fieldName for the class \c Klass.
+
+ \c Klass needs to be a C++ type with a registered type mapping to a Java type.
+*/
+
+/*!
\fn template <typename T> void QJniObject::setStaticField(const char *className, const char *fieldName, T value)
Sets the static field \a fieldName of the class \a className to \a value.
@@ -1173,6 +1155,14 @@ QJniObject QJniObject::callStaticObjectMethod(jclass clazz, jmethodID methodId,
*/
/*!
+ \fn template <typename Klass, typename T> auto QJniObject::setStaticField(const char *fieldName, T value)
+
+ Sets the static field \a fieldName of the class \c Klass to \a value.
+
+ \c Klass needs to be a C++ type with a registered type mapping to a Java type.
+*/
+
+/*!
\fn QJniObject QJniObject::getStaticObjectField(const char *className, const char *fieldName, const char *signature)
Retrieves a JNI object from the field \a fieldName with \a signature from
@@ -1189,18 +1179,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);
}
/*!
@@ -1218,12 +1208,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);
}
/*!
@@ -1240,7 +1227,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.
@@ -1262,12 +1249,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());
}
/*!
@@ -1284,7 +1270,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.
@@ -1294,7 +1280,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.
@@ -1318,8 +1304,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;
}
/*!
@@ -1342,7 +1331,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;
}
/*!
@@ -1363,7 +1355,7 @@ bool QJniObject::isClassAvailable(const char *className)
if (!env.jniEnv())
return false;
- return loadClass(className, env.jniEnv());;
+ return loadClass(className, env.jniEnv());
}
/*!
@@ -1399,13 +1391,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
@@ -1415,15 +1411,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 80568b2970..589f6489f7 100644
--- a/src/corelib/kernel/qjniobject.h
+++ b/src/corelib/kernel/qjniobject.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtCore module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// 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 QJNIOBJECT_H
#define QJNIOBJECT_H
@@ -52,313 +16,391 @@ 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);
explicit QJniObject(const char *className, const char *signature, ...);
- explicit QJniObject(jclass clazz);
- explicit QJniObject(jclass clazz, const char *signature, ...);
- QJniObject(jobject globalRef);
- ~QJniObject();
-
- jobject object() const;
- template <typename T> T object() const
+ template<typename ...Args
+#ifndef Q_QDOC
+ , std::enable_if_t<!std::disjunction_v<QtJniTypes::IsStringType<std::decay_t<Args>>...>>* = nullptr
+#endif
+ >
+ explicit QJniObject(const char *className, Args &&...args)
+ : QJniObject(LocalFrame<Args...>{}, className, std::forward<Args>(args)...)
{
- assertJniObjectType<T>();
- return static_cast<T>(javaObject());
}
-
- jclass objectClass() const;
- QByteArray className() const;
-
- template <typename T>
- T callMethod(const char *methodName, const char *signature, ...) const
+private:
+ template<typename ...Args>
+ explicit QJniObject(LocalFrame<Args...> localFrame, const char *className, Args &&...args)
+ : QJniObject(className, QtJniTypes::constructorSignature<Args...>().data(),
+ localFrame.convertToJni(std::forward<Args>(args))...)
{
- assertJniPrimitiveType<T>();
- QJniEnvironment env;
- T res{};
- jmethodID id = getCachedMethodID(env.jniEnv(), methodName, signature);
- if (id) {
- va_list args;
- va_start(args, signature);
- callMethodForType<T>(env.jniEnv(), res, object(), id, args);
- va_end(args);
- if (env.checkAndClearExceptions())
- res = {};
- }
- return res;
}
+public:
+ explicit QJniObject(jclass clazz);
+ explicit QJniObject(jclass clazz, const char *signature, ...);
+ template<typename ...Args
+#ifndef Q_QDOC
+ , std::enable_if_t<!std::disjunction_v<QtJniTypes::IsStringType<std::decay_t<Args>>...>>* = nullptr
+#endif
+ >
+ explicit QJniObject(jclass clazz, Args &&...args)
+ : QJniObject(clazz, QtJniTypes::constructorSignature<Args...>().data(),
+ std::forward<Args>(args)...)
+ {}
+ QJniObject(jobject globalRef);
- template <>
- void callMethod<void>(const char *methodName, const char *signature, ...) const
- {
- QJniEnvironment env;
- jmethodID id = getCachedMethodID(env.jniEnv(), methodName, signature);
- if (id) {
- va_list args;
- va_start(args, signature);
- callVoidMethodV(env.jniEnv(), id, args);
- va_end(args);
- env.checkAndClearExceptions();
- }
- }
+ QJniObject(const QJniObject &other) noexcept = default;
+ QJniObject(QJniObject &&other) noexcept = default;
+ QJniObject &operator=(const QJniObject &other) noexcept = default;
+ QJniObject &operator=(QJniObject &&other) noexcept = default;
- template <typename T>
- T callMethod(const char *methodName) const
- {
- assertJniPrimitiveType<T>();
- constexpr const char *signature = getTypeSignature<T>();
- return callMethod<T>(methodName, QByteArray(signature).prepend("()").constData());
- }
+ ~QJniObject();
- template <>
- void callMethod<void>(const char *methodName) const
+ template<typename Class, typename ...Args>
+ static inline QJniObject construct(Args &&...args)
{
- callMethod<void>(methodName, "()V");
+ LocalFrame<Args...> frame;
+ return QJniObject(QtJniTypes::Traits<Class>::className().data(),
+ QtJniTypes::constructorSignature<Args...>().data(),
+ frame.convertToJni(std::forward<Args>(args))...);
}
- template <typename T>
- QJniObject callObjectMethod(const char *methodName) const
+ jobject object() const;
+ template <typename T> T object() const
{
- assertJniObjectType<T>();
- constexpr const char *signature = getTypeSignature<T>();
- return callObjectMethod(methodName, QByteArray(signature).prepend("()").constData());
+ QtJniTypes::assertObjectType<T>();
+ return static_cast<T>(javaObject());
}
- QJniObject callObjectMethod(const char *methodName, const char *signature, ...) const;
-
- template <typename T>
- static T callStaticMethod(const char *className, const char *methodName,
- const char *signature, ...)
- {
- assertJniPrimitiveType<T>();
- QJniEnvironment env;
- T res{};
- jclass clazz = QJniObject::loadClass(className, env.jniEnv());
- if (clazz) {
- jmethodID id = getCachedMethodID(env.jniEnv(), clazz,
- QJniObject::toBinaryEncClassName(className),
- methodName, signature, true);
- if (id) {
- va_list args;
- va_start(args, signature);
- callStaticMethodForType<T>(env.jniEnv(), res, clazz, id, args);
- va_end(args);
- if (env.checkAndClearExceptions())
- res = {};
- }
- }
- return res;
- }
+ jclass objectClass() const;
+ QByteArray className() const;
- template <>
- void callStaticMethod<void>(const char *className, const char *methodName,
- const char *signature, ...)
- {
- QJniEnvironment env;
- jclass clazz = QJniObject::loadClass(className, env.jniEnv());
- if (clazz) {
- jmethodID id = getCachedMethodID(env.jniEnv(), clazz,
- QJniObject::toBinaryEncClassName(className),
- methodName, signature, true);
+ 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 frame.template convertFromJni<Ret>(callObjectMethod(methodName, signature,
+ frame.convertToJni(std::forward<Args>(args))...));
+ } else {
+ jmethodID id = getCachedMethodID(frame.jniEnv(), methodName, signature);
if (id) {
- va_list args;
- va_start(args, signature);
- env->CallStaticVoidMethodV(clazz, id, args);
- va_end(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>(frame.jniEnv(), res, object(), id,
+ frame.convertToJni(std::forward<Args>(args))...);
+ if (frame.checkAndClearExceptions())
+ res = {};
+ return res;
+ }
}
+ if constexpr (!std::is_same_v<Ret, void>)
+ return Ret{};
}
}
- template <typename T>
- static T callStaticMethod(const char *className, const char *methodName)
+ template <typename Ret, typename ...Args
+#ifndef Q_QDOC
+ , QtJniTypes::IfValidSignatureTypes<Ret, Args...> = true
+#endif
+ >
+ auto callMethod(const char *methodName, Args &&...args) const
{
- assertJniPrimitiveType<T>();
- constexpr const char *signature = getTypeSignature<T>();
- return callStaticMethod<T>(className, methodName, QByteArray(signature).prepend("()").constData());
+ constexpr auto signature = QtJniTypes::methodSignature<Ret, Args...>();
+ return callMethod<Ret>(methodName, signature.data(), std::forward<Args>(args)...);
}
- template <>
- void callStaticMethod<void>(const char *className, const char *methodName)
+ template <typename Ret, typename ...Args
+#ifndef Q_QDOC
+ , QtJniTypes::IfValidSignatureTypes<Ret, Args...> = true
+#endif
+ >
+ QJniObject callObjectMethod(const char *methodName, Args &&...args) const
{
- callStaticMethod<void>(className, methodName, "()V");
+ QtJniTypes::assertObjectType<Ret>();
+ constexpr auto signature = QtJniTypes::methodSignature<Ret, Args...>();
+ LocalFrame<Args...> frame(jniEnv());
+ return frame.template convertFromJni<Ret>(callObjectMethod(methodName, signature,
+ frame.convertToJni(std::forward<Args>(args))...));
}
- template <typename T>
- static T callStaticMethod(jclass clazz, const char *methodName, const char *signature, ...)
- {
- assertJniPrimitiveType<T>();
- QJniEnvironment env;
- T res{};
- if (clazz) {
- jmethodID id = getMethodID(env.jniEnv(), clazz, methodName, signature, true);
- if (id) {
- va_list args;
- va_start(args, signature);
- callStaticMethodForType<T>(env.jniEnv(), res, clazz, id, args);
- va_end(args);
- if (env.checkAndClearExceptions())
- res = {};
- }
- }
- return res;
- }
+ QJniObject callObjectMethod(const char *methodName, const char *signature, ...) const;
- template <>
- void callStaticMethod<void>(jclass clazz, const char *methodName,
- const char *signature, ...)
+ template <typename Ret, typename ...Args>
+ static auto callStaticMethod(const char *className, const char *methodName, const char *signature, Args &&...args)
{
- QJniEnvironment env;
- if (clazz) {
- jmethodID id = getMethodID(env.jniEnv(), clazz, methodName, signature, true);
- if (id) {
- va_list args;
- va_start(args, signature);
- env->CallStaticVoidMethodV(clazz, id, args);
- va_end(args);
- env.checkAndClearExceptions();
- }
- }
+ JNIEnv *env = QJniEnvironment::getJniEnv();
+ jclass clazz = QJniObject::loadClass(className, env);
+ return callStaticMethod<Ret>(clazz, methodName, signature, std::forward<Args>(args)...);
}
- template <typename T>
- static T callStaticMethod(jclass clazz, jmethodID methodId, ...)
+ template <typename Ret, typename ...Args>
+ static auto callStaticMethod(jclass clazz, const char *methodName, const char *signature, Args &&...args)
{
- assertJniPrimitiveType<T>();
- QJniEnvironment env;
- T res{};
- if (clazz && methodId) {
- va_list args;
- va_start(args, methodId);
- callStaticMethodForType<T>(env.jniEnv(), res, clazz, methodId, args);
- va_end(args);
- if (env.checkAndClearExceptions())
- res = {};
- }
- return res;
+ JNIEnv *env = QJniEnvironment::getJniEnv();
+ jmethodID id = clazz ? getMethodID(env, clazz, methodName, signature, true)
+ : 0;
+ return callStaticMethod<Ret>(clazz, id, std::forward<Args>(args)...);
}
- template <>
- void callStaticMethod<void>(jclass clazz, jmethodID methodId, ...)
- {
- QJniEnvironment env;
- if (clazz && methodId) {
- va_list args;
- va_start(args, methodId);
- env->CallStaticVoidMethodV(clazz, methodId, args);
- va_end(args);
- env.checkAndClearExceptions();
+ 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 frame.template convertFromJni<Ret>(callStaticObjectMethod(clazz, methodId,
+ frame.convertToJni(std::forward<Args>(args))...));
+ } else {
+ if (clazz && methodId) {
+ 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>(frame.jniEnv(), res, clazz, methodId,
+ frame.convertToJni(std::forward<Args>(args))...);
+ if (frame.checkAndClearExceptions())
+ res = {};
+ return res;
+ }
+ }
+ if constexpr (!std::is_same_v<Ret, void>)
+ return Ret{};
}
}
- template <typename T> static T callStaticMethod(jclass clazz, const char *methodName)
+ 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)
{
- assertJniPrimitiveType<T>();
- constexpr const char *signature = getTypeSignature<T>();
- return callStaticMethod<T>(clazz, methodName, QByteArray(signature).prepend("()").constData());
+ 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 <>
- void callStaticMethod<void>(jclass clazz, const char *methodName)
+ template <typename Ret, typename ...Args
+#ifndef Q_QDOC
+ , QtJniTypes::IfValidSignatureTypes<Ret, Args...> = true
+#endif
+ >
+ static auto callStaticMethod(jclass clazz, const char *methodName, Args &&...args)
{
- callStaticMethod<void>(clazz, methodName, "()V");
+ constexpr auto signature = QtJniTypes::methodSignature<Ret, Args...>();
+ return callStaticMethod<Ret>(clazz, methodName, signature.data(), std::forward<Args>(args)...);
}
-
- template <typename T>
- static QJniObject callStaticObjectMethod(const char *className, const char *methodName)
+ template <typename Klass, typename Ret, typename ...Args
+#ifndef Q_QDOC
+ , QtJniTypes::IfValidSignatureTypes<Ret, Args...> = true
+#endif
+ >
+ static auto callStaticMethod(const char *methodName, Args &&...args)
{
- assertJniObjectType<T>();
- constexpr const char *signature = getTypeSignature<T>();
- return callStaticObjectMethod(className, methodName, QByteArray(signature).prepend("()").constData());
+ 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, ...);
- template <typename T>
- static QJniObject callStaticObjectMethod(jclass clazz, const char *methodName)
- {
- assertJniObjectType<T>();
- constexpr const char *signature = getTypeSignature<T>();
- return callStaticObjectMethod(clazz, methodName, QByteArray(signature).prepend("()").constData());
- }
-
static QJniObject callStaticObjectMethod(jclass clazz, const char *methodName,
const char *signature, ...);
static QJniObject callStaticObjectMethod(jclass clazz, jmethodID methodId, ...);
- template <typename T> T getField(const char *fieldName) const
+
+ 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)
{
- assertJniPrimitiveType<T>();
- QJniEnvironment env;
- T res{};
- constexpr const char *signature = getTypeSignature<T>();
- jfieldID id = getCachedFieldID(env.jniEnv(), fieldName, signature);
- if (id) {
- getFieldForType<T>(env.jniEnv(), res, object(), id);
- if (env.checkAndClearExceptions())
- res = {};
- }
- return res;
+ QtJniTypes::assertObjectType<Ret>();
+ constexpr auto signature = QtJniTypes::methodSignature<Ret, Args...>();
+ LocalFrame<Args...> frame;
+ return frame.template convertFromJni<Ret>(callStaticObjectMethod(className, methodName, signature.data(),
+ frame.convertToJni(std::forward<Args>(args))...));
}
- template <typename T>
- static T getStaticField(const char *className, const char *fieldName)
+ template <typename Ret, typename ...Args
+#ifndef Q_QDOC
+ , QtJniTypes::IfValidSignatureTypes<Ret, Args...> = true
+#endif
+ >
+ static QJniObject callStaticObjectMethod(jclass clazz, const char *methodName, Args &&...args)
{
- assertJniPrimitiveType<T>();
- QJniEnvironment env;
- jclass clazz = QJniObject::loadClass(className, env.jniEnv());
- if (!clazz)
- return 0;
+ QtJniTypes::assertObjectType<Ret>();
+ constexpr auto signature = QtJniTypes::methodSignature<Ret, Args...>();
+ LocalFrame<Args...> frame;
+ return frame.template convertFromJni<Ret>(callStaticObjectMethod(clazz, methodName, signature.data(),
+ frame.convertToJni(std::forward<Args>(args))...));
+ }
- constexpr const char *signature = getTypeSignature<T>();
- jfieldID id = getCachedFieldID(env.jniEnv(), clazz,
- QJniObject::toBinaryEncClassName(className),
- fieldName,
- signature, true);
- if (!id)
- return 0;
+ 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 frame.template convertFromJni<T>(getObjectField<T>(fieldName));
+ } else {
+ T res{};
+ constexpr auto signature = QtJniTypes::fieldSignature<T>();
+ jfieldID id = getCachedFieldID(frame.jniEnv(), fieldName, signature);
+ if (id) {
+ getFieldForType<T>(frame.jniEnv(), res, object(), id);
+ if (frame.checkAndClearExceptions())
+ res = {};
+ }
+ return res;
+ }
+ }
- T res{};
- getStaticFieldForType<T>(env.jniEnv(), res, clazz, id);
- if (env.checkAndClearExceptions())
- res = {};
- return res;
+ 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 frame.template convertFromJni<T>(getStaticObjectField<T>(className, fieldName));
+ } else {
+ jclass clazz = QJniObject::loadClass(className, frame.jniEnv());
+ if (!clazz)
+ return T{};
+ return getStaticField<T>(clazz, fieldName);
+ }
}
- template <typename T>
- static T getStaticField(jclass clazz, const char *fieldName)
- {
- assertJniPrimitiveType<T>();
- QJniEnvironment env;
- T res{};
- constexpr const char *signature = getTypeSignature<T>();
- jfieldID id = getFieldID(env.jniEnv(), clazz, fieldName, signature, true);
- if (id) {
- getStaticFieldForType<T>(env.jniEnv(), res, clazz, id);
- if (env.checkAndClearExceptions())
- res = {};
+ 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 frame.template convertFromJni<T>(getStaticObjectField<T>(clazz, fieldName));
+ } else {
+ T res{};
+ constexpr auto signature = QtJniTypes::fieldSignature<T>();
+ jfieldID id = getFieldID(frame.jniEnv(), clazz, fieldName, signature, true);
+ if (id) {
+ getStaticFieldForType<T>(frame.jniEnv(), res, clazz, id);
+ if (frame.checkAndClearExceptions())
+ res = {};
+ }
+ return res;
}
- return res;
}
- template <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::Traits<Klass>::className(), fieldName);
+ }
+
+ template <typename T
+#ifndef Q_QDOC
+ , std::enable_if_t<QtJniTypes::isObjectType<T>(), bool> = true
+#endif
+ >
QJniObject getObjectField(const char *fieldName) const
{
- assertJniObjectType<T>();
- constexpr const char *signature = getTypeSignature<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)
{
- assertJniObjectType<T>();
- constexpr const char *signature = getTypeSignature<T>();
+ constexpr auto signature = QtJniTypes::fieldSignature<T>();
return getStaticObjectField(className, fieldName, signature);
}
@@ -366,104 +408,128 @@ 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)
{
- assertJniObjectType<T>();
- constexpr const char *signature = getTypeSignature<T>();
+ constexpr auto signature = QtJniTypes::fieldSignature<T>();
return getStaticObjectField(clazz, fieldName, signature);
}
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)
{
- assertJniType<T>();
- QJniEnvironment env;
- constexpr const char *signature = getTypeSignature<T>();
- jfieldID id = getCachedFieldID(env.jniEnv(), fieldName, signature);
+ constexpr auto signature = QtJniTypes::fieldSignature<T>();
+ 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)
{
- assertJniType<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)
{
- assertJniType<T>();
- QJniEnvironment env;
- jclass clazz = QJniObject::loadClass(className, env.jniEnv());
+ LocalFrame<T> frame;
+ jclass clazz = QJniObject::loadClass(className, frame.jniEnv());
if (!clazz)
return;
- constexpr const char *signature = getTypeSignature<T>();
- jfieldID id = getCachedFieldID(env.jniEnv(), clazz, className, fieldName,
+ constexpr auto signature = QtJniTypes::fieldSignature<T>();
+ 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)
{
- assertJniType<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)
{
- assertJniType<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)
{
- assertJniType<T>();
- QJniEnvironment env;
- constexpr const char *signature = getTypeSignature<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
+#ifndef Q_QDOC
+ , QtJniTypes::IfValidFieldType<T> = true
+#endif
+ >
+ static void setStaticField(const char *fieldName, T value)
+ {
+ setStaticField(QtJniTypes::Traits<Klass>::className(), fieldName, value);
}
static QJniObject fromString(const QString &string);
@@ -475,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)
{
- assertJniType<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,
@@ -507,15 +579,7 @@ private:
static jmethodID getMethodID(JNIEnv *env, jclass clazz, const char *name,
const char *signature, bool isStatic = false);
- void callVoidMethodV(JNIEnv *env, jmethodID id, va_list args) const;
- QJniObject callObjectMethodV(const char *methodName, const char *signature,
- va_list args) const;
-
- static QJniObject callStaticObjectMethodV(const char *className, const char *methodName,
- const char *signature, va_list args);
-
- static QJniObject callStaticObjectMethodV(jclass clazz, const char *methodName,
- const char *signature, va_list args);
+ void callVoidMethodV(JNIEnv *env, jmethodID id, ...) const;
bool isSameObject(jobject obj) const;
bool isSameObject(const QJniObject &other) const;
@@ -525,262 +589,281 @@ private:
friend bool operator==(const QJniObject &, const QJniObject &);
friend bool operator!=(const QJniObject&, const QJniObject&);
- template<bool flag = false>
- static void staticAssertTypeMismatch()
+ template<typename T>
+ static constexpr void callMethodForType(JNIEnv *env, T &res, jobject obj, jmethodID id, ...)
{
- static_assert(flag, "The used type is not supported by this template call. "
- "Use a JNI based type instead.");
+ va_list args = {};
+ va_start(args, id);
+ QtJniTypes::Caller<T>::callMethodForType(env, res, obj, id, args);
+ va_end(args);
}
template<typename T>
- static constexpr bool isJniPrimitiveType()
- {
- if constexpr(!std::is_same<T, jboolean>::value
- && !std::is_same<T, jbyte>::value
- && !std::is_same<T, jchar>::value
- && !std::is_same<T, jshort>::value
- && !std::is_same<T, jint>::value
- && !std::is_same<T, jlong>::value
- && !std::is_same<T, jfloat>::value
- && !std::is_same<T, jdouble>::value) {
- return false;
- }
+ static constexpr void callStaticMethodForType(JNIEnv *env, T &res, jclass clazz,
+ jmethodID id, ...)
+ {
+ if (!clazz || !id)
+ return;
+ va_list args = {};
+ va_start(args, id);
+ QtJniTypes::Caller<T>::callStaticMethodForType(env, res, clazz, id, args);
+ va_end(args);
+ }
- return true;
+ 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);
+ va_end(args);
}
+
template<typename T>
- static constexpr void assertJniPrimitiveType()
+ static constexpr void getFieldForType(JNIEnv *env, T &res, jobject obj, jfieldID id)
{
- if constexpr(!isJniPrimitiveType<T>())
- staticAssertTypeMismatch();
+ QtJniTypes::Caller<T>::getFieldForType(env, res, obj, id);
}
template<typename T>
- static constexpr void assertJniObjectType()
+ static constexpr void getStaticFieldForType(JNIEnv *env, T &res, jclass clazz, jfieldID id)
{
- if constexpr(!std::is_convertible<T, jobject>::value)
- staticAssertTypeMismatch();
+ QtJniTypes::Caller<T>::getStaticFieldForType(env, res, clazz, id);
}
template<typename T>
- static constexpr void assertJniType()
+ static constexpr void setFieldForType(JNIEnv *env, jobject obj, jfieldID id, T value)
{
- if constexpr(!isJniPrimitiveType<T>() && !std::is_convertible<T, jobject>::value)
- staticAssertTypeMismatch();
+ 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 const char* getTypeSignature()
- {
- if constexpr(std::is_same<T, jobject>::value)
- return "Ljava/lang/Object;";
- else if constexpr(std::is_same<T, jclass>::value)
- return "Ljava/lang/Class;";
- else if constexpr(std::is_same<T, jstring>::value)
- return "Ljava/lang/String;";
- else if constexpr(std::is_same<T, jobjectArray>::value)
- return "[Ljava/lang/Object;";
- else if constexpr(std::is_same<T, jthrowable>::value)
- return "Ljava/lang/Throwable;";
- else if constexpr(std::is_same<T, jbooleanArray>::value)
- return "[Z";
- else if constexpr(std::is_same<T, jbyteArray>::value)
- return "[B";
- else if constexpr(std::is_same<T, jshortArray>::value)
- return "[S";
- else if constexpr(std::is_same<T, jintArray>::value)
- return "[I";
- else if constexpr(std::is_same<T, jlongArray>::value)
- return "[J";
- else if constexpr(std::is_same<T, jfloatArray>::value)
- return "[F";
- else if constexpr(std::is_same<T, jdoubleArray>::value)
- return "[D";
- else if constexpr(std::is_same<T, jcharArray>::value)
- return "[C";
- else if constexpr(std::is_same<T, jboolean>::value)
- return "Z";
- else if constexpr(std::is_same<T, jbyte>::value)
- return "B";
- else if constexpr(std::is_same<T, jchar>::value)
- return "C";
- else if constexpr(std::is_same<T, jshort>::value)
- return "S";
- else if constexpr(std::is_same<T, jint>::value)
- return "I";
- else if constexpr(std::is_same<T, jlong>::value)
- return "J";
- else if constexpr(std::is_same<T, jfloat>::value)
- return "F";
- else if constexpr(std::is_same<T, jdouble>::value)
- return "D";
- else
- 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);
+ }
}
- template<typename T>
- static constexpr void callMethodForType(JNIEnv *env, T &res, jobject obj,
- jmethodID id, va_list args)
- {
- 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
- staticAssertTypeMismatch();
+ friend QJniObjectPrivate;
+ QSharedPointer<QJniObjectPrivate> d;
+};
+
+inline bool operator==(const QJniObject &obj1, const QJniObject &obj2)
+{
+ return obj1.isSameObject(obj2);
+}
+
+inline bool operator!=(const QJniObject &obj1, const QJniObject &obj2)
+{
+ return !obj1.isSameObject(obj2);
+}
+
+namespace QtJniTypes {
+struct 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>();
}
- template<typename T>
- static constexpr void callStaticMethodForType(JNIEnv *env, T &res, jclass clazz,
- jmethodID id, va_list args)
- {
- 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
- staticAssertTypeMismatch();
+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 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);
}
- 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
- staticAssertTypeMismatch();
+ // 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));
}
- 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
- staticAssertTypeMismatch();
+ // 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>
- 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
- staticAssertTypeMismatch();
+ 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));
}
- 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
- staticAssertTypeMismatch();
+ QByteArray className() const {
+ return QtJniTypes::Traits<Class>::className().data();
}
- friend QJniObjectPrivate;
- QSharedPointer<QJniObjectPrivate> d;
+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);
};
+}
-inline bool operator==(const QJniObject &obj1, const QJniObject &obj2)
+// 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)
{
- return obj1.isSameObject(obj2);
+ using Type = q20::remove_cvref_t<T>;
+ if constexpr (std::is_same_v<Type, QString>) {
+ return newLocalRef<jstring>(QJniObject::fromString(value));
+ } else if constexpr (QtJniTypes::IsJniArray<Type>::value) {
+ return value.arrayObject();
+ } else if constexpr (QJniArrayBase::canConvert<T>) {
+ using QJniArrayType = decltype(QJniArrayBase::fromContainer(std::forward<T>(value)));
+ using ArrayType = decltype(std::declval<QJniArrayType>().arrayObject());
+ return newLocalRef<ArrayType>(QJniArrayBase::fromContainer(std::forward<T>(value)).template object<jobject>());
+ } else if constexpr (std::is_base_of_v<QJniObject, Type>
+ || std::is_base_of_v<QtJniTypes::JObjectBase, Type>) {
+ return value.object();
+ } else {
+ return std::forward<T>(value);
+ }
}
-inline bool operator!=(const QJniObject &obj1, const QJniObject &obj2)
+template <typename ...Args>
+template <typename T>
+auto QJniObject::LocalFrame<Args...>::convertFromJni(QJniObject &&object)
{
- return !obj1.isSameObject(obj2);
+ using Type = q20::remove_cvref_t<T>;
+ if constexpr (std::is_same_v<Type, QString>) {
+ return object.toString();
+ } else if constexpr (QtJniTypes::IsJniArray<Type>::value) {
+ return T(std::move(object));
+ } else if constexpr (QJniArrayBase::canConvert<Type>) {
+ // if we were to create a QJniArray from Type...
+ using QJniArrayType = decltype(QJniArrayBase::fromContainer(std::declval<Type>()));
+ // then that QJniArray would have elements of type
+ using ElementType = typename QJniArrayType::Type;
+ // construct a QJniArray from a jobject pointer of that type
+ return QJniArray<ElementType>(object.template object<jarray>()).toContainer();
+ } else if constexpr (std::is_array_v<Type>) {
+ using ElementType = std::remove_extent_t<Type>;
+ return QJniArray<ElementType>(std::move(object));
+ } else if constexpr (std::is_base_of_v<QJniObject, Type>
+ && !std::is_same_v<QJniObject, Type>) {
+ return T{std::move(object)};
+ } else if constexpr (std::is_base_of_v<QtJniTypes::JObjectBase, Type>) {
+ return T{std::move(object)};
+ } else {
+ return std::move(object);
+ }
}
+
QT_END_NAMESPACE
#endif
diff --git a/src/corelib/kernel/qjnitypes.h b/src/corelib/kernel/qjnitypes.h
new file mode 100644
index 0000000000..1eaae6312b
--- /dev/null
+++ b/src/corelib/kernel/qjnitypes.h
@@ -0,0 +1,218 @@
+// 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_H
+#define QJNITYPES_H
+
+#if defined(Q_QDOC) || defined(Q_OS_ANDROID)
+
+#include <QtCore/qjnitypes_impl.h>
+#include <QtCore/qjniobject.h>
+
+QT_BEGIN_NAMESPACE
+
+#define Q_DECLARE_JNI_TYPE_HELPER(Type) \
+namespace QtJniTypes { \
+struct Type : JObject<Type> \
+{ \
+ using JObject::JObject; \
+}; \
+} \
+
+
+#define Q_DECLARE_JNI_TYPE(Type, Signature) \
+Q_DECLARE_JNI_TYPE_HELPER(Type) \
+template<> \
+struct QtJniTypes::Traits<QtJniTypes::Type> { \
+ static constexpr auto signature() \
+ { \
+ static_assert((Signature[0] == 'L' \
+ || Signature[0] == '[') \
+ && Signature[sizeof(Signature) - 2] == ';', \
+ "Type signature needs to start with 'L' or" \
+ " '[' and end with ';'"); \
+ return QtJniTypes::CTString(Signature); \
+ } \
+}; \
+
+#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(";"); \
+ } \
+}; \
+
+// 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 static_cast<Arg>(t);
+ }
+};
+
+template <>
+struct JNITypeForArgImpl<QString>
+{
+ using Type = jstring;
+
+ static QString fromVarArg(Type t)
+ {
+ return QJniObject(t).toString();
+ }
+};
+
+template <typename T>
+struct JNITypeForArgImpl<QJniArray<T>>
+{
+ using Type = jobject;
+
+ static QJniArray<T> fromVarArg(Type t)
+ {
+ return QJniArray<T>(t);
+ }
+};
+
+template <typename T>
+struct JNITypeForArgImpl<QList<T>>
+{
+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;
+
+ static QList<T> fromVarArg(Type t)
+ {
+ return QJniArray<ElementType>(t).toContainer();
+ }
+};
+
+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 JNITypeForArgImpl<std::decay_t<Arg>>::fromVarArg(t);
+}
+
+// Turn a va_list into a tuple of typed arguments
+template <typename ...Args>
+static constexpr auto makeTupleFromArgsHelper(va_list args)
+{
+ return std::tuple(methodArgFromVarArg<Args>(va_arg(args, JNITypeForArg<Args>))...);
+}
+
+template <typename Ret, typename ...Args>
+static constexpr auto makeTupleFromArgs(Ret (*)(JNIEnv *, jobject, Args...), va_list args)
+{
+ return makeTupleFromArgsHelper<Args...>(args);
+}
+template <typename Ret, typename ...Args>
+static constexpr auto makeTupleFromArgs(Ret (*)(JNIEnv *, jclass, Args...), va_list args)
+{
+ return makeTupleFromArgsHelper<Args...>(args);
+}
+
+// Get the return type of a function point
+template <typename Ret, typename ...Args>
+auto nativeFunctionReturnType(Ret(*function)(Args...))
+{
+ return function(std::declval<Args>()...);
+}
+
+} // 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 *>(va_##Method) \
+}; \
+} \
+
+#define QT_DECLARE_JNI_NATIVE_METHOD_1(Method) \
+ QT_DECLARE_JNI_NATIVE_METHOD_2(Method, Method) \
+
+#define Q_JNI_NATIVE_METHOD(Method) QtJniMethods::Method##_method
+
+#define Q_DECLARE_JNI_NATIVE_METHOD_IN_CURRENT_SCOPE(...) \
+ 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 *>(va_##Method) \
+ };
+
+#define QT_DECLARE_JNI_NATIVE_METHOD_IN_CURRENT_SCOPE_1(Method) \
+ QT_DECLARE_JNI_NATIVE_METHOD_IN_CURRENT_SCOPE_2(Method, Method) \
+
+#define Q_JNI_NATIVE_SCOPED_METHOD(Method, Scope) Scope::Method##_method
+
+QT_END_NAMESPACE
+
+#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.cpp b/src/corelib/kernel/qmath.cpp
index 2778dc242d..8742a4bee5 100644
--- a/src/corelib/kernel/qmath.cpp
+++ b/src/corelib/kernel/qmath.cpp
@@ -1,42 +1,6 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Copyright (C) 2013 Laszlo Papp <lpapp@kde.org>
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtCore module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// Copyright (C) 2013 Laszlo Papp <lpapp@kde.org>
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include <QtCore/qmath.h>
diff --git a/src/corelib/kernel/qmath.h b/src/corelib/kernel/qmath.h
index 5e8d4fc276..72057ee16d 100644
--- a/src/corelib/kernel/qmath.h
+++ b/src/corelib/kernel/qmath.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtCore module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 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 QMATH_H
#define QMATH_H
@@ -149,7 +113,7 @@ class QHypotHelper
public:
QHypotHelper(T first) : scale(qAbs(first)), total(1) {}
T result() const
- { return qIsFinite(scale) ? scale > 0 ? scale * T(std::sqrt(total)) : T(0) : scale; }
+ { return qIsFinite(scale) ? scale > 0 ? scale * T(qSqrt(total)) : T(0) : scale; }
template<typename F, typename ...Fs>
auto add(F first, Fs... rest) const
@@ -169,7 +133,7 @@ public:
return QHypotHelper<R>(scale, total);
if (val > scale) {
const R ratio = scale / next;
- return QHypotHelper<R>(val, total * ratio * ratio + 1);
+ return QHypotHelper<R>(val, total * ratio * ratio + R(1));
}
const R ratio = next / scale;
return QHypotHelper<R>(scale, total + ratio * ratio);
@@ -370,6 +334,7 @@ constexpr inline quint64 qConstexprNextPowerOfTwo(qint64 v)
constexpr inline quint32 qNextPowerOfTwo(quint32 v)
{
+ Q_ASSERT(static_cast<qint32>(v) >= 0); // There is a next power of two
#if defined(__cpp_lib_int_pow2) && __cpp_lib_int_pow2 >= 202002L
return std::bit_ceil(v + 1);
#elif defined(QT_HAS_BUILTIN_CLZ)
@@ -383,6 +348,7 @@ constexpr inline quint32 qNextPowerOfTwo(quint32 v)
constexpr inline quint64 qNextPowerOfTwo(quint64 v)
{
+ Q_ASSERT(static_cast<qint64>(v) >= 0); // There is a next power of two
#if defined(__cpp_lib_int_pow2) && __cpp_lib_int_pow2 >= 202002L
return std::bit_ceil(v + 1);
#elif defined(QT_HAS_BUILTIN_CLZLL)
@@ -404,6 +370,16 @@ constexpr inline quint64 qNextPowerOfTwo(qint64 v)
return qNextPowerOfTwo(quint64(v));
}
+constexpr inline unsigned long qNextPowerOfTwo(unsigned long v)
+{
+ return qNextPowerOfTwo(QIntegerForSizeof<long>::Unsigned(v));
+}
+
+constexpr inline unsigned long qNextPowerOfTwo(long v)
+{
+ return qNextPowerOfTwo(QIntegerForSizeof<long>::Unsigned(v));
+}
+
QT_END_NAMESPACE
#endif // QMATH_H
diff --git a/src/corelib/kernel/qmath.qdoc b/src/corelib/kernel/qmath.qdoc
index f8d1f2c0f7..bc365f26fa 100644
--- a/src/corelib/kernel/qmath.qdoc
+++ b/src/corelib/kernel/qmath.qdoc
@@ -1,32 +1,9 @@
-/****************************************************************************
-**
-** Copyright (C) 2020 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the documentation of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:FDL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Free Documentation License Usage
-** Alternatively, this file may be used under the terms of the GNU Free
-** Documentation License version 1.3 as published by the Free Software
-** Foundation and appearing in the file included in the packaging of
-** this file. Please review the following information to ensure
-** the GNU Free Documentation License version 1.3 requirements
-** will be met: https://www.gnu.org/licenses/fdl-1.3.html.
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2020 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
/*!
\headerfile <QtMath>
+ \inmodule QtCore
\title Generic Math Functions
\ingroup funclists
@@ -155,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.
@@ -193,7 +170,7 @@
This is as for the two-argument and three-argument forms, supported by
std::hypot(), but with as many numeric parameters as you care to pass to
- it. Uses \a first and each of the \a rest as co-ordinates, performing a
+ it. Uses \a first and each of the \a rest as coordinates, performing a
calculation equivalent to squaring each, summing and returning the square
root, save that underflow and overflow are avoided as far as possible.
@@ -321,7 +298,7 @@
\relates <QtMath>
\since 5.4
- This function returns the nearest power of two greater than \a value. For 0 it returns 1, and for values larger than or equal to 2^31 it returns 0.
+ This function returns the nearest power of two greater than \a value. For 0 it returns 1, and for values larger than or equal to 2^31 the result is undefined.
*/
/*!
@@ -330,7 +307,7 @@
\since 5.4
\overload
- This function returns the nearest power of two greater than \a value. For negative values it returns 0.
+ This function returns the nearest power of two greater than \a value. For negative values the result is undefined.
*/
/*!
@@ -338,7 +315,7 @@
\relates <QtMath>
\since 5.4
- This function returns the nearest power of two greater than \a value. For 0 it returns 1, and for values larger than or equal to 2^63 it returns 0.
+ This function returns the nearest power of two greater than \a value. For 0 it returns 1, and for values larger than or equal to 2^63 the result is undefined.
*/
/*!
@@ -347,5 +324,5 @@
\since 5.4
\overload
- This function returns the nearest power of two greater than \a value. For negative values it returns 0.
+ This function returns the nearest power of two greater than \a value. For negative values the result is undefined.
*/
diff --git a/src/corelib/kernel/qmetacontainer.cpp b/src/corelib/kernel/qmetacontainer.cpp
index 6c83ee40b6..200724c9f4 100644
--- a/src/corelib/kernel/qmetacontainer.cpp
+++ b/src/corelib/kernel/qmetacontainer.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2020 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtCore module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2020 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 "qmetacontainer.h"
#include "qmetatype.h"
diff --git a/src/corelib/kernel/qmetacontainer.h b/src/corelib/kernel/qmetacontainer.h
index 3594a0136a..67c0ddcf36 100644
--- a/src/corelib/kernel/qmetacontainer.h
+++ b/src/corelib/kernel/qmetacontainer.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2020 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtCore module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2020 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 QMETACONTAINER_H
#define QMETACONTAINER_H
@@ -44,6 +8,8 @@
#include <QtCore/qflags.h>
#include <QtCore/qglobal.h>
+#include <iterator>
+
QT_BEGIN_NAMESPACE
class QMetaType;
@@ -1018,6 +984,8 @@ public:
return a.d() != b.d();
}
+ const QtMetaContainerPrivate::QMetaSequenceInterface *iface() const { return d(); }
+
private:
template<typename T>
struct MetaSequence
@@ -1212,6 +1180,8 @@ public:
return a.d() != b.d();
}
+ const QtMetaContainerPrivate::QMetaAssociationInterface *iface() const { return d(); }
+
private:
template<typename T>
struct MetaAssociation
diff --git a/src/corelib/kernel/qmetaobject.cpp b/src/corelib/kernel/qmetaobject.cpp
index 19c437a4ec..8d304bd890 100644
--- a/src/corelib/kernel/qmetaobject.cpp
+++ b/src/corelib/kernel/qmetaobject.cpp
@@ -1,70 +1,35 @@
-/****************************************************************************
-**
-** Copyright (C) 2020 The Qt Company Ltd.
-** Copyright (C) 2015 Olivier Goffart <ogoffart@woboq.com>
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtCore module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2020 The Qt Company Ltd.
+// Copyright (C) 2015 Olivier Goffart <ogoffart@woboq.com>
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qmetaobject.h"
+#include "qmetaobject_p.h"
#include "qmetatype.h"
+#include "qmetatype_p.h"
#include "qobject.h"
-#include "qmetaobject_p.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"
#include <ctype.h>
+#include <memory>
QT_BEGIN_NAMESPACE
+using namespace Qt::StringLiterals;
+
/*!
\class QMetaObject
\inmodule QtCore
@@ -129,12 +94,17 @@ QT_BEGIN_NAMESPACE
\internal
- \value InvokeSlot
- \value EmitSignal
+ \value InvokeMetaMethod
\value ReadProperty
\value WriteProperty
\value ResetProperty
\value CreateInstance
+ \value IndexOfMethod
+ \value RegisterPropertyMetaType
+ \value RegisterMethodArgumentMetaType
+ \value BindableProperty
+ \value CustomCall
+ \value ConstructInPlace
*/
/*!
@@ -157,13 +127,13 @@ static inline const char *rawStringData(const QMetaObject *mo, int index)
return reinterpret_cast<const char *>(mo->d.stringdata) + offset;
}
-static inline QLatin1String 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];
uint length = mo->d.stringdata[2*index + 1];
const char *string = reinterpret_cast<const char *>(mo->d.stringdata) + offset;
- return QLatin1String(string, length);
+ return {string, qsizetype(length)};
}
static inline QByteArray stringData(const QMetaObject *mo, int index)
@@ -172,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)
@@ -197,7 +157,21 @@ static inline int typeFromTypeInfo(const QMetaObject *mo, uint typeInfo)
return QMetaType::fromName(rawStringData(mo, typeInfo & TypeNameIndexMask)).id();
}
-class QMetaMethodPrivate : public QMetaMethod
+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
{
public:
static const QMetaMethodPrivate *get(const QMetaMethod *q)
@@ -213,18 +187,27 @@ public:
inline uint parameterTypeInfo(int index) const;
inline int parameterType(int index) const;
inline void getParameterTypes(int *types) const;
+ inline const QtPrivate::QMetaTypeInterface *returnMetaTypeInterface() const;
+ inline const QtPrivate::QMetaTypeInterface *const *parameterMetaTypeInterfaces() const;
inline QByteArray parameterTypeName(int index) const;
inline QList<QByteArray> parameterTypes() const;
inline QList<QByteArray> parameterNames() const;
inline QByteArray tag() const;
inline int ownMethodIndex() const;
+ inline int ownConstructorMethodIndex() const;
private:
+ void checkMethodMetaTypeConsistency(const QtPrivate::QMetaTypeInterface *iface, int index) const;
QMetaMethodPrivate();
};
+} // unnamed namespace
+enum { MaximumParamCount = 11 }; // up to 10 arguments + 1 return value
+
+#if QT_VERSION < QT_VERSION_CHECK(7, 0, 0)
/*!
\since 4.5
+ \obsolete [6.5] Please use the variadic overload of this function
Constructs a new instance of this class. You can pass up to ten arguments
(\a val0, \a val1, \a val2, \a val3, \a val4, \a val5, \a val6, \a val7,
@@ -247,54 +230,84 @@ QObject *QMetaObject::newInstance(QGenericArgument val0,
QGenericArgument val8,
QGenericArgument val9) const
{
- if (!inherits(&QObject::staticMetaObject))
- {
- qWarning("QMetaObject::newInstance: type %s does not inherit QObject", className());
- return nullptr;
- }
-
- QByteArray constructorName = className();
- {
- int idx = constructorName.lastIndexOf(':');
- if (idx != -1)
- constructorName.remove(0, idx+1); // remove qualified part
- }
- QVarLengthArray<char, 512> sig;
- sig.append(constructorName.constData(), constructorName.length());
- sig.append('(');
-
- enum { MaximumParamCount = 10 };
- const char *typeNames[] = {val0.name(), val1.name(), val2.name(), val3.name(), val4.name(),
- val5.name(), val6.name(), val7.name(), val8.name(), val9.name()};
+ const char *typeNames[] = {
+ nullptr,
+ val0.name(), val1.name(), val2.name(), val3.name(), val4.name(),
+ val5.name(), val6.name(), val7.name(), val8.name(), val9.name()
+ };
+ const void *parameters[] = {
+ nullptr,
+ val0.data(), val1.data(), val2.data(), val3.data(), val4.data(),
+ val5.data(), val6.data(), val7.data(), val8.data(), val9.data()
+ };
int paramCount;
- for (paramCount = 0; paramCount < MaximumParamCount; ++paramCount) {
+ for (paramCount = 1; paramCount < MaximumParamCount; ++paramCount) {
int len = int(qstrlen(typeNames[paramCount]));
if (len <= 0)
break;
- sig.append(typeNames[paramCount], len);
- sig.append(',');
}
- if (paramCount == 0)
- sig.append(')'); // no parameters
- else
- sig[sig.size() - 1] = ')';
- sig.append('\0');
- int idx = indexOfConstructor(sig.constData());
- if (idx < 0) {
- QByteArray norm = QMetaObject::normalizedSignature(sig.constData());
- idx = indexOfConstructor(norm.constData());
- }
- if (idx < 0)
+ return newInstanceImpl(this, paramCount, parameters, typeNames, nullptr);
+}
+#endif
+
+/*!
+ \fn template <typename... Args> QObject *QMetaObject::newInstance(Args &&... arguments) const
+ \since 6.5
+
+ Constructs a new instance of this class and returns the new object, or
+ \nullptr if no suitable constructor is available. The types of the
+ arguments \a arguments will be used to find a matching constructor, and then
+ forwarded to it the same way signal-slot connections do.
+
+ Note that only constructors that are declared with the Q_INVOKABLE
+ modifier are made available through the meta-object system.
+
+ \sa constructor()
+*/
+
+QObject *QMetaObject::newInstanceImpl(const QMetaObject *mobj, qsizetype paramCount,
+ const void **parameters, const char **typeNames,
+ const QtPrivate::QMetaTypeInterface **metaTypes)
+{
+ if (!mobj->inherits(&QObject::staticMetaObject)) {
+ qWarning("QMetaObject::newInstance: type %s does not inherit QObject", mobj->className());
return nullptr;
+ }
+QT_WARNING_PUSH
+#if Q_CC_GNU >= 1200
+QT_WARNING_DISABLE_GCC("-Wdangling-pointer")
+#endif
+
+ // set the return type
QObject *returnValue = nullptr;
- void *param[] = {&returnValue, val0.data(), val1.data(), val2.data(), val3.data(), val4.data(),
- val5.data(), val6.data(), val7.data(), val8.data(), val9.data()};
+ QMetaType returnValueMetaType = QMetaType::fromType<decltype(returnValue)>();
+ parameters[0] = &returnValue;
+ typeNames[0] = returnValueMetaType.name();
+ if (metaTypes)
+ metaTypes[0] = returnValueMetaType.iface();
+
+QT_WARNING_POP
+
+ // find the constructor
+ auto priv = QMetaObjectPrivate::get(mobj);
+ for (int i = 0; i < priv->constructorCount; ++i) {
+ QMetaMethod m = QMetaMethod::fromRelativeConstructorIndex(mobj, i);
+ if (m.parameterCount() != (paramCount - 1))
+ continue;
+
+ // attempt to call
+ QMetaMethodPrivate::InvokeFailReason r =
+ QMetaMethodPrivate::invokeImpl(m, nullptr, Qt::DirectConnection, paramCount,
+ parameters, typeNames, metaTypes);
+ if (r == QMetaMethodPrivate::InvokeFailReason::None)
+ return returnValue;
+ if (int(r) < 0)
+ return nullptr;
+ }
- if (static_metacall(CreateInstance, idx, param) >= 0)
- return nullptr;
return returnValue;
}
@@ -321,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);
}
/*!
@@ -333,7 +346,7 @@ static inline const char *objectClassName(const QMetaObject *m)
*/
const char *QMetaObject::className() const
{
- return objectClassName(this);
+ return objectClassName(this).constData();
}
/*!
@@ -388,8 +401,9 @@ 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
/*!
\since 6.2
@@ -405,11 +419,31 @@ QMetaType QMetaObject::metaType() const
return QMetaType::fromName(className());
} else {
/* in the metatype array, we store
- idx: 0 propertyCount - 1 propertyCount
- data:QMetaType(prop0), ..., QMetaType(propPropCount-1), QMetaType(class),...
- */
- auto iface = this->d.metaTypes[d->propertyCount];
- if (iface == QtPrivate::qMetaTypeInterfaceForType<void>())
+
+ | index | data |
+ |----------------------------------------------------------------------|
+ | 0 | QMetaType(property0) |
+ | ... | ... |
+ | propertyCount - 1 | QMetaType(propertyCount - 1) |
+ | propertyCount | QMetaType(enumerator0) |
+ | ... | ... |
+ | propertyCount + enumeratorCount - 1 | QMetaType(enumeratorCount - 1) |
+ | propertyCount + enumeratorCount | QMetaType(class) |
+
+ */
+#if QT_VERSION < QT_VERSION_CHECK(7, 0, 0)
+ // Before revision 12 we only stored metatypes for enums if they showed
+ // up as types of properties or method arguments or return values.
+ // From revision 12 on, we always store them in a predictable place.
+ const qsizetype offset = d->revision < 12
+ ? d->propertyCount
+ : d->propertyCount + d->enumeratorCount;
+#else
+ const qsizetype offset = d->propertyCount + d->enumeratorCount;
+#endif
+
+ auto iface = this->d.metaTypes[offset];
+ if (iface && QtMetaTypePrivate::isInterfaceFor<void>(iface))
return QMetaType(); // return invalid meta-type for namespaces
if (iface)
return QMetaType(iface);
@@ -417,7 +451,6 @@ QMetaType QMetaObject::metaType() const
return QMetaType::fromName(className()); // try lookup by name in that case
}
}
-#endif // QT_NO_TRANSLATION
/*!
Returns the method offset for this class; i.e. the index position
@@ -602,19 +635,25 @@ bool QMetaObjectPrivate::methodMatch(const QMetaObject *m, const QMetaMethod &me
const QArgumentType *types)
{
const QMetaMethod::Data &data = method.data;
- if (data.argc() != uint(argc))
+ auto priv = QMetaMethodPrivate::get(&method);
+ if (priv->parameterCount() != argc)
return false;
if (stringData(m, data.name()) != name)
return false;
+ const QtPrivate::QMetaTypeInterface * const *ifaces = priv->parameterMetaTypeInterfaces();
int paramsIndex = data.parameters() + 1;
for (int i = 0; i < argc; ++i) {
uint typeInfo = m->d.data[paramsIndex + i];
- if (types[i].type()) {
- if (types[i].type() != typeFromTypeInfo(m, typeInfo))
+ if (int id = types[i].type()) {
+ if (id == QMetaType(ifaces[i]).id())
+ continue;
+ if (id != typeFromTypeInfo(m, typeInfo))
return false;
} else {
+ if (types[i].name() == QMetaType(ifaces[i]).name())
+ continue;
if (types[i].name() != typeNameFromTypeInfo(m, typeInfo))
return false;
}
@@ -794,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
@@ -979,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 {
@@ -993,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);
@@ -1022,27 +1061,28 @@ static const QMetaObject *QMetaObject_findMetaObject(const QMetaObject *self, co
*/
int QMetaObject::indexOfEnumerator(const char *name) const
{
- const QMetaObject *m = this;
- while (m) {
- const QMetaObjectPrivate *d = priv(m->d.data);
- for (int i = 0; i < d->enumeratorCount; ++i) {
- const QMetaEnum e(m, i);
- const char *prop = rawStringData(m, e.data.name());
- if (name[0] == prop[0] && strcmp(name + 1, prop + 1) == 0) {
- i += m->enumeratorOffset();
- return i;
- }
- }
- m = m->d.superdata;
+ return QMetaObjectPrivate::indexOfEnumerator(this, name);
+}
+
+int QMetaObjectPrivate::indexOfEnumerator(const QMetaObject *m, QByteArrayView name)
+{
+ using W = QMetaObjectPrivate::Which;
+ for (auto which : { W::Name, W::Alias }) {
+ if (int index = indexOfEnumerator(m, name, which); index != -1)
+ return index;
}
- // Check alias names:
- m = this;
+ return -1;
+}
+
+int QMetaObjectPrivate::indexOfEnumerator(const QMetaObject *m, QByteArrayView name, Which which)
+{
while (m) {
const QMetaObjectPrivate *d = priv(m->d.data);
for (int i = 0; i < d->enumeratorCount; ++i) {
const QMetaEnum e(m, i);
- const char *prop = rawStringData(m, e.data.alias());
- if (name[0] == prop[0] && strcmp(name + 1, prop + 1) == 0) {
+ const quint32 id = which == Which::Name ? e.data.name() : e.data.alias();
+ QByteArrayView prop = stringDataView(m, id);
+ if (name == prop) {
i += m->enumeratorOffset();
return i;
}
@@ -1066,7 +1106,7 @@ int QMetaObject::indexOfProperty(const char *name) const
for (int i = 0; i < d->propertyCount; ++i) {
const QMetaProperty::Data data = QMetaProperty::getMetaPropertyData(m, i);
const char *prop = rawStringData(m, data.name());
- if (name[0] == prop[0] && strcmp(name + 1, prop + 1) == 0) {
+ if (strcmp(name, prop) == 0) {
i += m->propertyOffset();
return i;
}
@@ -1351,69 +1391,82 @@ QByteArray QMetaObject::normalizedSignature(const char *method)
return result;
}
-enum { MaximumParamCount = 11 }; // up to 10 arguments + 1 return value
-
-/*
- Returns the signatures of all methods whose name matches \a nonExistentMember,
- or an empty QByteArray if there are no matches.
-*/
-static inline QByteArray findMethodCandidates(const QMetaObject *metaObject, const char *nonExistentMember)
+Q_DECL_COLD_FUNCTION static inline bool
+printMethodNotFoundWarning(const QMetaObject *meta, QByteArrayView name, qsizetype paramCount,
+ const char *const *names,
+ const QtPrivate::QMetaTypeInterface * const *metaTypes)
{
+ // now find the candidates we couldn't use
QByteArray candidateMessage;
- // Prevent full string comparison in every iteration.
- const QByteArray memberByteArray = nonExistentMember;
- for (int i = 0; i < metaObject->methodCount(); ++i) {
- const QMetaMethod method = metaObject->method(i);
- if (method.name() == memberByteArray)
+ for (int i = 0; i < meta->methodCount(); ++i) {
+ const QMetaMethod method = meta->method(i);
+ if (method.name() == name)
candidateMessage += " " + method.methodSignature() + '\n';
}
if (!candidateMessage.isEmpty()) {
candidateMessage.prepend("\nCandidates are:\n");
candidateMessage.chop(1);
}
- return candidateMessage;
+
+ QVarLengthArray<char, 512> sig;
+ for (qsizetype i = 1; i < paramCount; ++i) {
+ if (names[i])
+ sig.append(names[i], qstrlen(names[i]));
+ else
+ sig.append(metaTypes[i]->name, qstrlen(metaTypes[i]->name));
+ sig.append(',');
+ }
+ if (paramCount != 1)
+ sig.resize(sig.size() - 1);
+
+ qWarning("QMetaObject::invokeMethod: No such method %s::%.*s(%.*s)%.*s",
+ meta->className(), int(name.size()), name.constData(),
+ int(sig.size()), sig.constData(),
+ int(candidateMessage.size()), candidateMessage.constData());
+ return false;
}
/*!
+ \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
\threadsafe
Invokes the \a member (a signal or a slot name) on the object \a
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.
- The invocation can be either synchronous or asynchronous,
- depending on \a type:
+ 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:
\list
- \li If \a type is Qt::DirectConnection, the member will be invoked immediately.
+ \li If \a type is Qt::DirectConnection, the member will be invoked immediately
+ in the current thread.
- \li If \a type is Qt::QueuedConnection,
- a QEvent will be sent and the member is invoked as soon as the application
- enters the main event loop.
+ \li If \a type is Qt::QueuedConnection, a QEvent will be sent and the
+ member is invoked as soon as the application enters the event loop in the
+ thread that the \a obj was created in or was moved to.
\li If \a type is Qt::BlockingQueuedConnection, the method will be invoked in
the same way as for Qt::QueuedConnection, except that the current thread
will block until the event is delivered. Using this connection type to
communicate between objects in the same thread will lead to deadlocks.
- \li If \a type is Qt::AutoConnection, the member is invoked
- synchronously if \a obj lives in the same thread as the
- caller; otherwise it will invoke the member asynchronously.
+ \li If \a type is Qt::AutoConnection, the member is invoked synchronously
+ if \a obj lives in the same thread as the caller; otherwise it will invoke
+ the member asynchronously. This is the behavior of the overloads that do
+ not have the \a type parameter.
\endlist
- The return value of the \a member function call is placed in \a
- ret. If the invocation is asynchronous, the return value cannot
- be evaluated. You can pass up to ten arguments (\a val0, \a val1,
- \a val2, \a val3, \a val4, \a val5, \a val6, \a val7, \a val8,
- and \a val9) to the \a member function.
-
- QGenericArgument and QGenericReturnArgument are internal
- helper classes. Because signals and slots can be dynamically
- invoked, you must enclose the arguments using the Q_ARG() and
- Q_RETURN_ARG() macros. Q_ARG() takes a type name and a
- const reference of that type; Q_RETURN_ARG() takes a type name
- and a non-const reference.
-
You only need to pass the name of the signal or slot to this function,
not the entire signature. For example, to asynchronously invoke
the \l{QThread::quit()}{quit()} slot on a
@@ -1421,8 +1474,62 @@ static inline QByteArray findMethodCandidates(const QMetaObject *metaObject, con
\snippet code/src_corelib_kernel_qmetaobject.cpp 2
+ With asynchronous method invocations, the parameters must be copyable
+ types, because Qt needs to copy the arguments to store them in an event
+ behind the scenes. Since Qt 6.5, this function automatically registers the
+ types being used; however, as a side-effect, it is not possible to make
+ calls using types that are only forward-declared. Additionally, it is not
+ possible to make asynchronous calls that use references to
+ non-const-qualified types as parameters either.
+
+ To synchronously invoke the \c compute(QString, int, double) slot on
+ some arbitrary object \c obj retrieve its return value:
+
+ \snippet code/src_corelib_kernel_qmetaobject.cpp invokemethod-no-macro
+
+ If the "compute" slot does not take exactly one \l QString, one \c int, and
+ one \c double in the specified order, the call will fail. Note how it was
+ necessary to be explicit about the type of the QString, as the character
+ literal is not exactly the right type to match. If the method instead took
+ a \l QStringView, a \l qsizetype, and a \c float, the call would need to be
+ written as:
+
+ \snippet code/src_corelib_kernel_qmetaobject.cpp invokemethod-no-macro-other-types
+
+ The same call can be executed using the Q_ARG() and Q_RETURN_ARG() macros,
+ as in:
+
+ \snippet code/src_corelib_kernel_qmetaobject.cpp 4
+
+ The macros are kept for compatibility with Qt 6.4 and earlier versions, and
+ can be freely mixed with parameters that do not use the macro. They may be
+ necessary in rare situations when calling a method that used a typedef to
+ forward-declared type as a parameter or the return type.
+
+ \sa Q_ARG(), Q_RETURN_ARG(), QMetaMethod::invoke()
+*/
+
+/*!
+ \threadsafe
+ \overload
+ \obsolete [6.5] Please use the variadic overload of this function
+
+ Invokes the \a member (a signal or a slot name) on the object \a
+ 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.
+
+ See the variadic invokeMethod() function for more information. This
+ function should behave the same way as that one, with the following
+ limitations:
+
+ \list
+ \li The number of parameters is limited to 10.
+ \li Parameter names may need to be an exact string match.
+ \li Meta types are not automatically registered.
+ \endlist
+
With asynchronous method invocations, the parameters must be of
- types that are known to Qt's meta-object system, because Qt needs
+ types that are already known to Qt's meta-object system, because Qt needs
to copy the arguments to store them in an event behind the
scenes. If you try to use a queued connection and get the error
message
@@ -1432,14 +1539,6 @@ static inline QByteArray findMethodCandidates(const QMetaObject *metaObject, con
call qRegisterMetaType() to register the data type before you
call invokeMethod().
- To synchronously invoke the \c compute(QString, int, double) slot on
- some arbitrary object \c obj retrieve its return value:
-
- \snippet code/src_corelib_kernel_qmetaobject.cpp 4
-
- If the "compute" slot does not take exactly one QString, one int
- and one double in the specified order, the call will fail.
-
\sa Q_ARG(), Q_RETURN_ARG(), qRegisterMetaType(), QMetaMethod::invoke()
*/
bool QMetaObject::invokeMethod(QObject *obj,
@@ -1460,58 +1559,70 @@ bool QMetaObject::invokeMethod(QObject *obj,
if (!obj)
return false;
- QVarLengthArray<char, 512> sig;
- int len = int(qstrlen(member));
- if (len <= 0)
- return false;
- sig.append(member, len);
- sig.append('(');
-
const char *typeNames[] = {ret.name(), val0.name(), val1.name(), val2.name(), val3.name(),
val4.name(), val5.name(), val6.name(), val7.name(), val8.name(),
val9.name()};
-
+ const void *parameters[] = {ret.data(), val0.data(), val1.data(), val2.data(), val3.data(),
+ val4.data(), val5.data(), val6.data(), val7.data(), val8.data(),
+ val9.data()};
int paramCount;
for (paramCount = 1; paramCount < MaximumParamCount; ++paramCount) {
- len = int(qstrlen(typeNames[paramCount]));
- if (len <= 0)
+ if (qstrlen(typeNames[paramCount]) <= 0)
break;
- sig.append(typeNames[paramCount], len);
- sig.append(',');
}
- if (paramCount == 1)
- sig.append(')'); // no parameters
- else
- sig[sig.size() - 1] = ')';
- sig.append('\0');
+ return invokeMethodImpl(obj, member, type, paramCount, parameters, typeNames, nullptr);
+}
- const QMetaObject *meta = obj->metaObject();
- int idx = meta->indexOfMethod(sig.constData());
- if (idx < 0) {
- QByteArray norm = QMetaObject::normalizedSignature(sig.constData());
- idx = meta->indexOfMethod(norm.constData());
- }
+bool QMetaObject::invokeMethodImpl(QObject *obj, const char *member, Qt::ConnectionType type,
+ qsizetype paramCount, const void * const *parameters,
+ const char * const *typeNames,
+ const QtPrivate::QMetaTypeInterface * const *metaTypes)
+{
+ if (!obj)
+ return false;
- if (idx < 0 || idx >= meta->methodCount()) {
- // This method doesn't belong to us; print out a nice warning with candidates.
- qWarning("QMetaObject::invokeMethod: No such method %s::%s%s",
- meta->className(), sig.constData(), findMethodCandidates(meta, member).constData());
+ Q_ASSERT(paramCount >= 1); // includes the return type
+ Q_ASSERT(parameters);
+ Q_ASSERT(typeNames);
+
+ // find the method
+ QByteArrayView name(member);
+ if (name.isEmpty())
return false;
+
+ const QMetaObject *meta = obj->metaObject();
+ for ( ; meta; meta = meta->superClass()) {
+ auto priv = QMetaObjectPrivate::get(meta);
+ for (int i = 0; i < priv->methodCount; ++i) {
+ QMetaMethod m = QMetaMethod::fromRelativeMethodIndex(meta, i);
+ if (m.parameterCount() != (paramCount - 1))
+ continue;
+ if (name != stringDataView(meta, m.data.name()))
+ continue;
+
+ // attempt to call
+ QMetaMethodPrivate::InvokeFailReason r =
+ QMetaMethodPrivate::invokeImpl(m, obj, type, paramCount, parameters,
+ typeNames, metaTypes);
+ if (int(r) <= 0)
+ return r == QMetaMethodPrivate::InvokeFailReason::None;
+ }
}
- QMetaMethod method = meta->method(idx);
- return method.invoke(obj, type, ret,
- val0, val1, val2, val3, val4, val5, val6, val7, val8, val9);
+
+ // This method doesn't belong to us; print out a nice warning with candidates.
+ 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();
@@ -1523,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) {
@@ -1533,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();
+
+ for (int i = 1; i < parameterCount; ++i) {
+ types[i] = QMetaType(metaTypes[i]);
+ args[i] = types[i].create(argv[i]);
+ }
- QCoreApplication::postEvent(object, new QMetaCallEvent(slot, nullptr, -1, 1));
+ 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 {
@@ -1564,6 +1682,7 @@ bool QMetaObject::invokeMethodImpl(QObject *object, QtPrivate::QSlotObjectBase *
QGenericArgument val8 = QGenericArgument(),
QGenericArgument val9 = QGenericArgument());
\threadsafe
+ \obsolete [6.5] Please use the variadic overload of this function.
\overload invokeMethod()
This overload always invokes the member using the connection type Qt::AutoConnection.
@@ -1583,6 +1702,7 @@ bool QMetaObject::invokeMethodImpl(QObject *object, QtPrivate::QSlotObjectBase *
QGenericArgument val9 = QGenericArgument())
\threadsafe
+ \obsolete [6.5] Please use the variadic overload of this function.
\overload invokeMethod()
This overload can be used if the return value of the member is of no interest.
@@ -1602,6 +1722,7 @@ bool QMetaObject::invokeMethodImpl(QObject *object, QtPrivate::QSlotObjectBase *
QGenericArgument val9 = QGenericArgument())
\threadsafe
+ \obsolete [6.5] Please use the variadic overload of this function.
\overload invokeMethod()
This overload invokes the member using the connection type Qt::AutoConnection and
@@ -1609,31 +1730,44 @@ bool QMetaObject::invokeMethodImpl(QObject *object, QtPrivate::QSlotObjectBase *
*/
/*!
- \fn template<typename Functor, typename FunctorReturnType> bool QMetaObject::invokeMethod(QObject *context, Functor function, Qt::ConnectionType type, FunctorReturnType *ret)
+ \fn template<typename Functor, typename FunctorReturnType> bool QMetaObject::invokeMethod(QObject *context, Functor &&function, Qt::ConnectionType type, FunctorReturnType *ret)
+ \fn template<typename Functor, typename FunctorReturnType> bool QMetaObject::invokeMethod(QObject *context, Functor &&function, FunctorReturnType *ret)
\since 5.10
-
\threadsafe
- \overload
Invokes the \a function in the event loop of \a context. \a function can be a functor
or a pointer to a member function. Returns \c true if the function could be invoked.
Returns \c false if there is no such function or the parameters did not match.
The return value of the function call is placed in \a ret.
+
+ If \a type is set, then the function is invoked using that connection type. Otherwise,
+ Qt::AutoConnection will be used.
*/
/*!
- \fn template<typename Functor, typename FunctorReturnType> bool QMetaObject::invokeMethod(QObject *context, Functor function, FunctorReturnType *ret)
-
- \since 5.10
+ \fn template<typename Functor, typename FunctorReturnType, typename... Args> bool QMetaObject::invokeMethod(QObject *context, Functor &&function, Qt::ConnectionType type, QTemplatedMetaMethodReturnArgument<FunctorReturnType> ret, Args &&...arguments)
+ \fn template<typename Functor, typename FunctorReturnType, typename... Args> bool QMetaObject::invokeMethod(QObject *context, Functor &&function, QTemplatedMetaMethodReturnArgument<FunctorReturnType> ret, Args &&...arguments)
+ \fn template<typename Functor, typename... Args> bool QMetaObject::invokeMethod(QObject *context, Functor &&function, Qt::ConnectionType type, Args &&...arguments)
+ \fn template<typename Functor, typename... Args> bool QMetaObject::invokeMethod(QObject *context, Functor &&function, Args &&...arguments)
+ \since 6.7
\threadsafe
- \overload
- Invokes the \a function in the event loop of \a context using the connection type Qt::AutoConnection.
- \a function can be a functor or a pointer to a member function. Returns \c true if the function could
- be invoked. Returns \c false if there is no such member or the parameters did not match.
- The return value of the function call is placed in \a ret.
+ Invokes the \a function with \a arguments in the event loop of \a context.
+ \a function can be a functor or a pointer to a member function. Returns
+ \c true if the function could be invoked. The return value of the
+ function call is placed in \a ret. The object used for the \a ret argument
+ should be obtained by passing your object to qReturnArg(). For example:
+
+ \badcode
+ MyClass *obj = ...;
+ int result = 0;
+ QMetaObject::invokeMethod(obj, &MyClass::myMethod, qReturnArg(result), parameter);
+ \endcode
+
+ If \a type is set, then the function is invoked using that connection type.
+ Otherwise, Qt::AutoConnection will be used.
*/
/*!
@@ -1805,6 +1939,35 @@ int QMetaMethodPrivate::parameterCount() const
return data.argc();
}
+inline void
+QMetaMethodPrivate::checkMethodMetaTypeConsistency(const QtPrivate::QMetaTypeInterface *iface,
+ int index) const
+{
+ uint typeInfo = parameterTypeInfo(index);
+ QMetaType mt(iface);
+ if (iface) {
+ if ((typeInfo & IsUnresolvedType) == 0)
+ Q_ASSERT(mt.id() == int(typeInfo & TypeNameIndexMask));
+ Q_ASSERT(mt.name());
+ } else {
+ // The iface can only be null for a parameter if that parameter is a
+ // const-ref to a forward-declared type. Since primitive types are
+ // never incomplete, we can assert it's not one of them.
+
+#define ASSERT_NOT_PRIMITIVE_TYPE(TYPE, METATYPEID, NAME) \
+ Q_ASSERT(typeInfo != QMetaType::TYPE);
+ QT_FOR_EACH_STATIC_PRIMITIVE_NON_VOID_TYPE(ASSERT_NOT_PRIMITIVE_TYPE)
+#undef ASSERT_NOT_PRIMITIVE_TYPE
+ Q_ASSERT(typeInfo != QMetaType::QObjectStar);
+
+ // Prior to Qt 6.4 we failed to record void and void*
+ if (priv(mobj->d.data)->revision >= 11) {
+ Q_ASSERT(typeInfo != QMetaType::Void);
+ Q_ASSERT(typeInfo != QMetaType::VoidStar);
+ }
+ }
+}
+
int QMetaMethodPrivate::parametersDataIndex() const
{
Q_ASSERT(priv(mobj->d.data)->revision >= 7);
@@ -1817,6 +1980,29 @@ uint QMetaMethodPrivate::parameterTypeInfo(int index) const
return mobj->d.data[parametersDataIndex() + index];
}
+const QtPrivate::QMetaTypeInterface *QMetaMethodPrivate::returnMetaTypeInterface() const
+{
+ Q_ASSERT(priv(mobj->d.data)->revision >= 7);
+ if (methodType() == QMetaMethod::Constructor)
+ return nullptr; // constructors don't have return types
+
+ const QtPrivate::QMetaTypeInterface *iface = mobj->d.metaTypes[data.metaTypeOffset()];
+ checkMethodMetaTypeConsistency(iface, -1);
+ return iface;
+}
+
+const QtPrivate::QMetaTypeInterface * const *QMetaMethodPrivate::parameterMetaTypeInterfaces() const
+{
+ Q_ASSERT(priv(mobj->d.data)->revision >= 7);
+ int offset = (methodType() == QMetaMethod::Constructor ? 0 : 1);
+ const auto ifaces = &mobj->d.metaTypes[data.metaTypeOffset() + offset];
+
+ for (int i = 0; i < parameterCount(); ++i)
+ checkMethodMetaTypeConsistency(ifaces[i], i);
+
+ return ifaces;
+}
+
int QMetaMethodPrivate::parameterType(int index) const
{
Q_ASSERT(priv(mobj->d.data)->revision >= 7);
@@ -1837,7 +2023,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
@@ -1847,8 +2033,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;
}
@@ -1872,10 +2060,17 @@ QByteArray QMetaMethodPrivate::tag() const
int QMetaMethodPrivate::ownMethodIndex() const
{
- // recompute the methodIndex by reversing the arithmetic in QMetaObject::property()
+ // recompute the methodIndex by reversing the arithmetic in QMetaObject::method()
return ( data.d - mobj->d.data - priv(mobj->d.data)->methodData)/Data::Size;
}
+int QMetaMethodPrivate::ownConstructorMethodIndex() const
+{
+ // recompute the methodIndex by reversing the arithmetic in QMetaObject::constructor()
+ Q_ASSERT(methodType() == Constructor);
+ return ( data.d - mobj->d.data - priv(mobj->d.data)->constructorData)/Data::Size;
+}
+
/*!
\since 5.0
@@ -2079,10 +2274,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
{
@@ -2196,7 +2390,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:
@@ -2224,36 +2418,43 @@ QMetaMethod QMetaMethod::fromSignalImpl(const QMetaObject *metaObject, void **si
}
/*!
+ \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 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.
- The invocation can be either synchronous or asynchronous, depending on the
- \a connectionType:
+ 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.
- \list
- \li If \a connectionType is Qt::DirectConnection, the member will be invoked immediately.
+ The overloads with a Qt::ConnectionType \a type parameter allow explicitly
+ selecting whether the invocation will be synchronous or not:
- \li If \a connectionType is Qt::QueuedConnection,
- a QEvent will be posted and the member is invoked as soon as the application
- enters the main event loop.
+ \list
+ \li If \a type is Qt::DirectConnection, the member will be invoked immediately
+ in the current thread.
- \li If \a connectionType is Qt::AutoConnection, the member is invoked
- synchronously if \a object lives in the same thread as the
- caller; otherwise it will invoke the member asynchronously.
- \endlist
+ \li If \a type is Qt::QueuedConnection, a QEvent will be sent and the
+ member is invoked as soon as the application enters the event loop in the
+ thread the \a obj was created in or was moved to.
- The return value of this method call is placed in \a
- returnValue. If the invocation is asynchronous, the return value cannot
- be evaluated. You can pass up to ten arguments (\a val0, \a val1,
- \a val2, \a val3, \a val4, \a val5, \a val6, \a val7, \a val8,
- and \a val9) to this method call.
+ \li If \a type is Qt::BlockingQueuedConnection, the method will be invoked in
+ the same way as for Qt::QueuedConnection, except that the current thread
+ will block until the event is delivered. Using this connection type to
+ communicate between objects in the same thread will lead to deadlocks.
- QGenericArgument and QGenericReturnArgument are internal
- helper classes. Because signals and slots can be dynamically
- invoked, you must enclose the arguments using the Q_ARG() and
- Q_RETURN_ARG() macros. Q_ARG() takes a type name and a
- const reference of that type; Q_RETURN_ARG() takes a type name
- and a non-const reference.
+ \li If \a type is Qt::AutoConnection, the member is invoked synchronously
+ if \a obj lives in the same thread as the caller; otherwise it will invoke
+ the member asynchronously. This is the behavior of the overloads that do
+ not have the \a type parameter.
+ \endlist
To asynchronously invoke the
\l{QPushButton::animateClick()}{animateClick()} slot on a
@@ -2261,6 +2462,56 @@ QMetaMethod QMetaMethod::fromSignalImpl(const QMetaObject *metaObject, void **si
\snippet code/src_corelib_kernel_qmetaobject.cpp 6
+ With asynchronous method invocations, the parameters must be copyable
+ types, because Qt needs to copy the arguments to store them in an event
+ behind the scenes. Since Qt 6.5, this function automatically registers the
+ types being used; however, as a side-effect, it is not possible to make
+ calls using types that are only forward-declared. Additionally, it is not
+ possible to make asynchronous calls that use references to
+ non-const-qualified types as parameters either.
+
+ To synchronously invoke the \c compute(QString, int, double) slot on
+ some arbitrary object \c obj retrieve its return value:
+
+ \snippet code/src_corelib_kernel_qmetaobject.cpp invoke-no-macro
+
+ If the "compute" slot does not take exactly one \l QString, one \c int, and
+ one \c double in the specified order, the call will fail. Note how it was
+ necessary to be explicit about the type of the QString, as the character
+ literal is not exactly the right type to match. If the method instead took
+ a \l QByteArray, a \l qint64, and a \c{long double}, the call would need to be
+ written as:
+
+ \snippet code/src_corelib_kernel_qmetaobject.cpp invoke-no-macro-other-types
+
+ The same call can be executed using the Q_ARG() and Q_RETURN_ARG() macros,
+ as in:
+
+ \snippet code/src_corelib_kernel_qmetaobject.cpp 8
+
+ \warning this method will not test the validity of the arguments: \a object
+ must be an instance of the class of the QMetaObject of which this QMetaMethod
+ has been constructed with.
+
+ \sa Q_ARG(), Q_RETURN_ARG(), qRegisterMetaType(), QMetaObject::invokeMethod()
+*/
+
+/*!
+ \obsolete [6.5] Please use the variadic overload of this function
+
+ 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.
+
+ See the variadic invokeMethod() function for more information. This
+ function should behave the same way as that one, with the following
+ limitations:
+
+ \list
+ \li The number of parameters is limited to 10.
+ \li Parameter names may need to be an exact string match.
+ \li Meta types are not automatically registered.
+ \endlist
+
With asynchronous method invocations, the parameters must be of
types that are known to Qt's meta-object system, because Qt needs
to copy the arguments to store them in an event behind the
@@ -2272,22 +2523,9 @@ QMetaMethod QMetaMethod::fromSignalImpl(const QMetaObject *metaObject, void **si
call qRegisterMetaType() to register the data type before you
call QMetaMethod::invoke().
- To synchronously invoke the \c compute(QString, int, double) slot on
- some arbitrary object \c obj retrieve its return value:
-
- \snippet code/src_corelib_kernel_qmetaobject.cpp 8
-
- QMetaObject::normalizedSignature() is used here to ensure that the format
- of the signature is what invoke() expects. E.g. extra whitespace is
- removed.
-
- If the "compute" slot does not take exactly one QString, one int
- and one double in the specified order, the call will fail.
-
- \warning this method will not test the validity of the arguments: \a object
- must be an instance of the class of the QMetaObject of which this QMetaMethod
- has been constructed with. The arguments must have the same type as the ones
- expected by the method, else, the behaviour is undefined.
+ \warning In addition to the limitations of the variadic invoke() overload,
+ the arguments must have the same type as the ones expected by the method,
+ else, the behavior is undefined.
\sa Q_ARG(), Q_RETURN_ARG(), qRegisterMetaType(), QMetaObject::invokeMethod()
*/
@@ -2308,23 +2546,6 @@ bool QMetaMethod::invoke(QObject *object,
if (!object || !mobj)
return false;
- Q_ASSERT(mobj->cast(object));
-
- // check return type
- if (returnValue.data()) {
- const char *retType = typeName();
- if (qstrcmp(returnValue.name(), retType) != 0) {
- // normalize the return value as well
- QByteArray normalized = QMetaObject::normalizedType(returnValue.name());
- if (qstrcmp(normalized.constData(), retType) != 0) {
- // String comparison failed, try compare the metatype.
- int t = returnType();
- if (t == QMetaType::UnknownType || t != QMetaType::fromName(normalized).id())
- return false;
- }
- }
- }
-
// check argument count (we don't allow invoking a method if given too few arguments)
const char *typeNames[] = {
returnValue.name(),
@@ -2339,27 +2560,184 @@ bool QMetaMethod::invoke(QObject *object,
val8.name(),
val9.name()
};
+ void *param[] = {
+ returnValue.data(),
+ val0.data(),
+ val1.data(),
+ val2.data(),
+ val3.data(),
+ val4.data(),
+ val5.data(),
+ val6.data(),
+ val7.data(),
+ val8.data(),
+ val9.data()
+ };
+
int paramCount;
for (paramCount = 1; paramCount < MaximumParamCount; ++paramCount) {
if (qstrlen(typeNames[paramCount]) <= 0)
break;
}
- if (paramCount <= QMetaMethodPrivate::get(this)->parameterCount())
+ return invokeImpl(*this, object, connectionType, paramCount, param, typeNames, nullptr);
+}
+
+bool QMetaMethod::invokeImpl(QMetaMethod self, void *target, Qt::ConnectionType connectionType,
+ qsizetype paramCount, const void *const *parameters,
+ const char *const *typeNames,
+ const QtPrivate::QMetaTypeInterface *const *metaTypes)
+{
+ if (!target || !self.mobj)
return false;
+ QMetaMethodPrivate::InvokeFailReason r =
+ QMetaMethodPrivate::invokeImpl(self, target, connectionType, paramCount, parameters,
+ typeNames, metaTypes);
+ if (Q_LIKELY(r == QMetaMethodPrivate::InvokeFailReason::None))
+ return true;
+
+ if (int(r) >= int(QMetaMethodPrivate::InvokeFailReason::FormalParameterMismatch)) {
+ int n = int(r) - int(QMetaMethodPrivate::InvokeFailReason::FormalParameterMismatch);
+ qWarning("QMetaMethod::invoke: cannot convert formal parameter %d from %s in call to %s::%s",
+ n, typeNames[n + 1] ? typeNames[n + 1] : metaTypes[n + 1]->name,
+ self.mobj->className(), self.methodSignature().constData());
+ }
+ if (r == QMetaMethodPrivate::InvokeFailReason::TooFewArguments) {
+ qWarning("QMetaMethod::invoke: too few arguments (%d) in call to %s::%s",
+ int(paramCount), self.mobj->className(), self.methodSignature().constData());
+ }
+ return false;
+}
- Qt::HANDLE currentThreadId = QThread::currentThreadId();
- QThread *objectThread = object->thread();
- bool receiverInSameThread = false;
- if (objectThread)
- receiverInSameThread = currentThreadId == QThreadData::get2(objectThread)->threadId.loadRelaxed();
+auto QMetaMethodInvoker::invokeImpl(QMetaMethod self, void *target,
+ Qt::ConnectionType connectionType,
+ qsizetype paramCount, const void *const *parameters,
+ const char *const *typeNames,
+ const QtPrivate::QMetaTypeInterface *const *metaTypes) -> InvokeFailReason
+{
+ auto object = static_cast<QObject *>(target);
+ auto priv = QMetaMethodPrivate::get(&self);
+ constexpr bool MetaTypesAreOptional = QT_VERSION < QT_VERSION_CHECK(7, 0, 0);
+ auto methodMetaTypes = priv->parameterMetaTypeInterfaces();
+ auto param = const_cast<void **>(parameters);
+
+ Q_ASSERT(priv->mobj);
+ Q_ASSERT(self.methodType() == Constructor || object);
+ Q_ASSERT(self.methodType() == Constructor || connectionType == Qt::ConnectionType(-1) ||
+ priv->mobj->cast(object));
+ Q_ASSERT(paramCount >= 1); // includes the return type
+ Q_ASSERT(parameters);
+ Q_ASSERT(typeNames);
+ Q_ASSERT(MetaTypesAreOptional || metaTypes);
+
+ if ((paramCount - 1) < qsizetype(priv->data.argc()))
+ return InvokeFailReason::TooFewArguments;
+
+ // 0 is the return type, 1 is the first formal parameter
+ auto checkTypesAreCompatible = [=](int idx) {
+ uint typeInfo = priv->parameterTypeInfo(idx - 1);
+ QByteArrayView userTypeName(typeNames[idx] ? typeNames[idx] : metaTypes[idx]->name);
+
+ if ((typeInfo & IsUnresolvedType) == 0) {
+ // this is a built-in type
+ if (MetaTypesAreOptional && !metaTypes)
+ return int(typeInfo) == QMetaType::fromName(userTypeName).id();
+ return int(typeInfo) == metaTypes[idx]->typeId;
+ }
- // check connection type
- if (connectionType == Qt::AutoConnection) {
- connectionType = receiverInSameThread
- ? Qt::DirectConnection
- : Qt::QueuedConnection;
+ QByteArrayView methodTypeName = stringDataView(priv->mobj, typeInfo & TypeNameIndexMask);
+ if ((MetaTypesAreOptional && !metaTypes) || !metaTypes[idx]) {
+ // compatibility call, compare strings
+ if (methodTypeName == userTypeName)
+ return true;
+
+ // maybe the user type needs normalization
+ QByteArray normalized = normalizeTypeInternal(userTypeName.begin(), userTypeName.end());
+ return methodTypeName == normalized;
+ }
+
+ QMetaType userType(metaTypes[idx]);
+ Q_ASSERT(userType.isValid());
+ if (QMetaType(methodMetaTypes[idx - 1]) == userType)
+ return true;
+
+ // if the parameter type was NOT only forward-declared, it MUST have
+ // matched
+ if (methodMetaTypes[idx - 1])
+ return false;
+
+ // resolve from the name moc stored for us
+ QMetaType resolved = QMetaType::fromName(methodTypeName);
+ return resolved == userType;
+ };
+
+ // force all types to be registered, just in case
+ for (qsizetype i = 0; metaTypes && i < paramCount; ++i)
+ QMetaType(metaTypes[i]).registerType();
+
+ // check formal parameters first (overload set)
+ for (qsizetype i = 1; i < paramCount; ++i) {
+ if (!checkTypesAreCompatible(i))
+ return InvokeFailReason(int(InvokeFailReason::FormalParameterMismatch) + i - 1);
}
+ // handle constructors first
+ if (self.methodType() == Constructor) {
+ if (object) {
+ qWarning("QMetaMethod::invokeMethod: cannot call constructor %s on object %p",
+ self.methodSignature().constData(), object);
+ return InvokeFailReason::ConstructorCallOnObject;
+ }
+
+ if (!parameters[0]) {
+ qWarning("QMetaMethod::invokeMethod: constructor call to %s must assign a return type",
+ self.methodSignature().constData());
+ return InvokeFailReason::ConstructorCallWithoutResult;
+ }
+
+ if (!MetaTypesAreOptional || metaTypes) {
+ if (metaTypes[0]->typeId != QMetaType::QObjectStar) {
+ qWarning("QMetaMethod::invokeMethod: cannot convert QObject* to %s on constructor call %s",
+ metaTypes[0]->name, self.methodSignature().constData());
+ return InvokeFailReason::ReturnTypeMismatch;
+ }
+ }
+
+ int idx = priv->ownConstructorMethodIndex();
+ if (priv->mobj->static_metacall(QMetaObject::CreateInstance, idx, param) >= 0)
+ return InvokeFailReason::ConstructorCallFailed;
+ return {};
+ }
+
+ // regular type - check return type
+ if (parameters[0]) {
+ if (!checkTypesAreCompatible(0)) {
+ const char *retType = typeNames[0] ? typeNames[0] : metaTypes[0]->name;
+ qWarning("QMetaMethod::invokeMethod: return type mismatch for method %s::%s:"
+ " cannot convert from %s to %s during invocation",
+ priv->mobj->className(), priv->methodSignature().constData(),
+ priv->rawReturnTypeName(), retType);
+ return InvokeFailReason::ReturnTypeMismatch;
+ }
+ }
+
+ Qt::HANDLE currentThreadId = nullptr;
+ QThread *objectThread = nullptr;
+ auto receiverInSameThread = [&]() {
+ if (!currentThreadId) {
+ currentThreadId = QThread::currentThreadId();
+ objectThread = object->thread();
+ }
+ if (objectThread)
+ return currentThreadId == QThreadData::get2(objectThread)->threadId.loadRelaxed();
+ return false;
+ };
+
+ // check connection type
+ if (connectionType == Qt::AutoConnection)
+ connectionType = receiverInSameThread() ? Qt::DirectConnection : Qt::QueuedConnection;
+ else if (connectionType == Qt::ConnectionType(-1))
+ connectionType = Qt::DirectConnection;
+
#if !QT_CONFIG(thread)
if (connectionType == Qt::BlockingQueuedConnection) {
connectionType = Qt::DirectConnection;
@@ -2367,69 +2745,53 @@ bool QMetaMethod::invoke(QObject *object,
#endif
// invoke!
- void *param[] = {
- returnValue.data(),
- val0.data(),
- val1.data(),
- val2.data(),
- val3.data(),
- val4.data(),
- val5.data(),
- val6.data(),
- val7.data(),
- val8.data(),
- val9.data()
- };
- int idx_relative = QMetaMethodPrivate::get(this)->ownMethodIndex();
- int idx_offset = mobj->methodOffset();
- Q_ASSERT(QMetaObjectPrivate::get(mobj)->revision >= 6);
- QObjectPrivate::StaticMetaCallFunction callFunction = mobj->d.static_metacall;
+ int idx_relative = priv->ownMethodIndex();
+ int idx_offset = priv->mobj->methodOffset();
+ QObjectPrivate::StaticMetaCallFunction callFunction = priv->mobj->d.static_metacall;
if (connectionType == Qt::DirectConnection) {
- if (callFunction) {
+ if (callFunction)
callFunction(object, QMetaObject::InvokeMetaMethod, idx_relative, param);
- return true;
- } else {
- return QMetaObject::metacall(object, QMetaObject::InvokeMetaMethod, idx_relative + idx_offset, param) < 0;
- }
+ else if (QMetaObject::metacall(object, QMetaObject::InvokeMetaMethod, idx_relative + idx_offset, param) >= 0)
+ return InvokeFailReason::CallViaVirtualFailed;
} else if (connectionType == Qt::QueuedConnection) {
- if (returnValue.data()) {
+ if (parameters[0]) {
qWarning("QMetaMethod::invoke: Unable to invoke methods with return values in "
"queued connections");
- return false;
+ return InvokeFailReason::CouldNotQueueParameter;
}
- QScopedPointer<QMetaCallEvent> event(new QMetaCallEvent(idx_offset, idx_relative, callFunction, nullptr, -1, paramCount));
+ auto event = std::make_unique<QMetaCallEvent>(idx_offset, idx_relative, callFunction, nullptr, -1, paramCount);
QMetaType *types = event->types();
void **args = event->args();
- int argIndex = 0;
+ // fill in the meta types first
for (int i = 1; i < paramCount; ++i) {
- types[i] = QMetaType::fromName(typeNames[i]);
- if (!types[i].isValid() && param[i]) {
- // Try to register the type and try again before reporting an error.
- void *argv[] = { &types[i], &argIndex };
- QMetaObject::metacall(object, QMetaObject::RegisterMethodArgumentMetaType,
- idx_relative + idx_offset, argv);
- if (!types[i].isValid()) {
- qWarning("QMetaMethod::invoke: Unable to handle unregistered datatype '%s'",
- typeNames[i]);
- return false;
- }
- }
- if (types[i].isValid()) {
- args[i] = QMetaType(types[i]).create(param[i]);
- ++argIndex;
+ types[i] = QMetaType(methodMetaTypes[i - 1]);
+ if (!types[i].iface() && (!MetaTypesAreOptional || metaTypes))
+ types[i] = QMetaType(metaTypes[i]);
+ if (!types[i].iface())
+ types[i] = priv->parameterMetaType(i - 1);
+ if (!types[i].iface() && typeNames[i])
+ types[i] = QMetaType::fromName(typeNames[i]);
+ if (!types[i].iface()) {
+ qWarning("QMetaMethod::invoke: Unable to handle unregistered datatype '%s'",
+ typeNames[i]);
+ return InvokeFailReason(int(InvokeFailReason::CouldNotQueueParameter) - i);
}
}
- QCoreApplication::postEvent(object, event.take());
+ // now create copies of our parameters using those meta types
+ for (int i = 1; i < paramCount; ++i)
+ args[i] = types[i].create(parameters[i]);
+
+ QCoreApplication::postEvent(object, event.release());
} else { // blocking queued connection
#if QT_CONFIG(thread)
- if (receiverInSameThread) {
- qWarning("QMetaMethod::invoke: Dead lock detected in "
- "BlockingQueuedConnection: Receiver is %s(%p)",
- mobj->className(), object);
+ if (receiverInSameThread()) {
+ qWarning("QMetaMethod::invoke: Dead lock detected in BlockingQueuedConnection: "
+ "Receiver is %s(%p)", priv->mobj->className(), object);
+ return InvokeFailReason::DeadLockDetected;
}
QSemaphore semaphore;
@@ -2438,7 +2800,7 @@ bool QMetaMethod::invoke(QObject *object,
semaphore.acquire();
#endif // QT_CONFIG(thread)
}
- return true;
+ return {};
}
/*! \fn bool QMetaMethod::invoke(QObject *object,
@@ -2453,6 +2815,7 @@ bool QMetaMethod::invoke(QObject *object,
QGenericArgument val7 = QGenericArgument(),
QGenericArgument val8 = QGenericArgument(),
QGenericArgument val9 = QGenericArgument()) const
+ \obsolete [6.5] Please use the variadic overload of this function
\overload invoke()
This overload always invokes this method using the connection type Qt::AutoConnection.
@@ -2470,7 +2833,7 @@ bool QMetaMethod::invoke(QObject *object,
QGenericArgument val7 = QGenericArgument(),
QGenericArgument val8 = QGenericArgument(),
QGenericArgument val9 = QGenericArgument()) const
-
+ \obsolete [6.5] Please use the variadic overload of this function
\overload invoke()
This overload can be used if the return value of the member is of no interest.
@@ -2488,7 +2851,7 @@ bool QMetaMethod::invoke(QObject *object,
QGenericArgument val7 = QGenericArgument(),
QGenericArgument val8 = QGenericArgument(),
QGenericArgument val9 = QGenericArgument()) const
-
+ \obsolete [6.5] Please use the variadic overload of this function
\overload invoke()
This overload invokes this method using the
@@ -2496,7 +2859,9 @@ bool QMetaMethod::invoke(QObject *object,
*/
/*!
- \since 5.5
+ \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
Invokes this method on a Q_GADGET. Returns \c true if the member could be invoked.
Returns \c false if there is no such member or the parameters did not match.
@@ -2505,15 +2870,39 @@ bool QMetaMethod::invoke(QObject *object,
The invocation is always synchronous.
- The return value of this method call is placed in \a
- returnValue. You can pass up to ten arguments (\a val0, \a val1,
- \a val2, \a val3, \a val4, \a val5, \a val6, \a val7, \a val8,
- and \a val9) to this method call.
+ 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
- has been constructed with. The arguments must have the same type as the ones
- expected by the method, else, the behavior is undefined.
+ has been constructed with.
+
+ \sa Q_ARG(), Q_RETURN_ARG(), qRegisterMetaType(), QMetaObject::invokeMethod()
+*/
+
+/*!
+ \since 5.5
+ \obsolete [6.5] Please use the variadic overload of this function
+
+ Invokes this method on a Q_GADGET. Returns \c true if the member could be invoked.
+ Returns \c false if there is no such member or the parameters did not match.
+
+ See the variadic invokeMethod() function for more information. This
+ function should behave the same way as that one, with the following
+ limitations:
+
+ \list
+ \li The number of parameters is limited to 10.
+ \li Parameter names may need to be an exact string match.
+ \li Meta types are not automatically registered.
+ \endlist
+
+ \warning In addition to the limitations of the variadic invoke() overload,
+ the arguments must have the same type as the ones expected by the method,
+ else, the behavior is undefined.
\sa Q_ARG(), Q_RETURN_ARG(), qRegisterMetaType(), QMetaObject::invokeMethod()
*/
@@ -2607,6 +2996,7 @@ bool QMetaMethod::invokeOnGadget(void *gadget,
QGenericArgument val9 = QGenericArgument()) const
\overload
+ \obsolete [6.5] Please use the variadic overload of this function
\since 5.5
This overload invokes this method for a \a gadget and ignores return values.
@@ -2676,7 +3066,7 @@ const char *QMetaEnum::name() const
Returns the enum name of the flag (without the scope).
For example, the Qt::AlignmentFlag flag has \c
- AlignmentFlag as the enum name, but \c Alignment as as the type name.
+ AlignmentFlag as the enum name, but \c Alignment as the type name.
Non flag enums has the same type and enum names.
Enum names have the same scope as the type name.
@@ -2692,6 +3082,32 @@ const char *QMetaEnum::enumName() const
}
/*!
+ Returns the meta type of the enum.
+
+ If the QMetaObject that this enum is part of was generated with Qt 6.5 or
+ earlier, this will be an invalid meta type.
+
+ \note This is the meta type of the enum itself, not of its underlying
+ integral type. You can retrieve the meta type of the underlying type of the
+ enum using \l{QMetaType::underlyingType()}.
+
+ \since 6.6
+*/
+QMetaType QMetaEnum::metaType() const
+{
+ if (!mobj)
+ return {};
+
+ const QMetaObjectPrivate *p = priv(mobj->d.data);
+#if QT_VERSION < QT_VERSION_CHECK(7, 0, 0)
+ if (p->revision < 12)
+ QMetaType();
+#endif
+
+ return QMetaType(mobj->d.metaTypes[data.index(mobj) + p->propertyCount]);
+}
+
+/*!
Returns the number of keys.
\sa key()
@@ -2771,7 +3187,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;
}
/*!
@@ -2791,19 +3234,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];
@@ -2830,17 +3265,49 @@ const char *QMetaEnum::valueToKey(int value) const
return nullptr;
}
-static auto parse_scope(QLatin1String qualifiedKey) noexcept
+static bool parseEnumFlags(QByteArrayView v, QVarLengthArray<QByteArrayView, 10> &list)
{
- struct R {
- std::optional<QLatin1String> scope;
- QLatin1String key;
- };
- const auto scopePos = qualifiedKey.lastIndexOf(QLatin1String("::"));
- 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;
}
/*!
@@ -2860,19 +3327,22 @@ int QMetaEnum::keysToValue(const char *keys, bool *ok) const
if (!mobj || !keys)
return -1;
- auto lookup = [&] (QLatin1String 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 QLatin1String &untrimmed : qTokenize(QLatin1String{keys}, QLatin1Char{'|'})) {
+ 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;
@@ -2917,7 +3387,7 @@ QByteArray QMetaEnum::valueToKeys(int value) const
QByteArray keys;
if (!mobj)
return keys;
- QVarLengthArray<QLatin1String, 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) {
@@ -2940,8 +3410,13 @@ QMetaEnum::QMetaEnum(const QMetaObject *mobj, int index)
Q_ASSERT(index >= 0 && index < priv(mobj->d.data)->enumeratorCount);
}
+int QMetaEnum::Data::index(const QMetaObject *mobj) const
+{
+ return (d - mobj->d.data - priv(mobj->d.data)->enumeratorData) / Size;
+}
+
/*!
- \fn QMetaEnum QMetaEnum::fromType()
+ \fn template<typename T> QMetaEnum QMetaEnum::fromType()
\since 5.5
Returns the QMetaEnum corresponding to the type in the template parameter.
@@ -3027,7 +3502,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
@@ -3160,19 +3635,20 @@ bool QMetaProperty::isAlias() const
return (data.flags() & Alias);
}
+#if QT_DEPRECATED_SINCE(6, 4)
/*!
\internal
+ Historically:
Executes metacall with QMetaObject::RegisterPropertyMetaType flag.
Returns id of registered type or QMetaType::UnknownType if a type
could not be registered for any reason.
+ Obsolete since Qt 6
*/
int QMetaProperty::registerPropertyType() const
{
- int registerResult = -1;
- void *argv[] = { &registerResult };
- mobj->static_metacall(QMetaObject::RegisterPropertyMetaType, data.index(mobj), argv);
- return registerResult == -1 ? QMetaType::UnknownType : registerResult;
+ return typeId();
}
+#endif
QMetaProperty::QMetaProperty(const QMetaObject *mobj, int index)
: mobj(mobj),
@@ -3180,37 +3656,30 @@ QMetaProperty::QMetaProperty(const QMetaObject *mobj, int index)
{
Q_ASSERT(index >= 0 && index < priv(mobj->d.data)->propertyCount);
- if (data.flags() & EnumOrFlag) {
- const char *type = rawTypeNameFromTypeInfo(mobj, data.type());
- menum = mobj->enumerator(mobj->indexOfEnumerator(type));
- if (!menum.isValid()) {
- 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;
- }
+ if (!(data.flags() & EnumOrFlag))
+ return;
+ QByteArrayView enum_name = typeNameFromTypeInfo(mobj, data.type());
+ menum = mobj->enumerator(QMetaObjectPrivate::indexOfEnumerator(mobj, enum_name));
+ if (menum.isValid())
+ return;
- const QMetaObject *scope = nullptr;
- if (qstrcmp(scope_name, "Qt") == 0)
- scope = &Qt::staticMetaObject;
- else
- scope = QMetaObject_findMetaObject(mobj, scope_name);
- if (scope)
- menum = scope->enumerator(scope->indexOfEnumerator(enum_name));
- if (scope_buffer)
- free(scope_buffer);
- }
+ 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 (scope_name == "Qt")
+ scope = &Qt::staticMetaObject;
+ else
+ scope = QMetaObject_findMetaObject(mobj, QByteArrayView(scope_name));
+
+ if (scope)
+ menum = scope->enumerator(QMetaObjectPrivate::indexOfEnumerator(scope, enum_name));
}
/*!
@@ -3278,31 +3747,43 @@ QVariant QMetaProperty::read(const QObject *object) const
Writes \a value as the property's value to the given \a object. Returns
true if the write succeeded; otherwise returns \c false.
- If \a value is not of the same type type as the property, a conversion
+ If \a value is not of the same type as the property, a conversion
is attempted. An empty QVariant() is equivalent to a call to reset()
if this property is resettable, or setting a default-constructed object
otherwise.
+ \note This function internally makes a copy of \a value. Prefer to use the
+ rvalue overload when possible.
+
\sa read(), reset(), isWritable()
*/
bool QMetaProperty::write(QObject *object, const QVariant &value) const
{
if (!object || !isWritable())
return false;
+ return write(object, QVariant(value));
+}
- QVariant v = value;
+/*!
+ \overload
+ \since 6.6
+*/
+bool QMetaProperty::write(QObject *object, QVariant &&v) const
+{
+ if (!object || !isWritable())
+ return false;
QMetaType t(mobj->d.metaTypes[data.index(mobj)]);
if (t != QMetaType::fromType<QVariant>() && t != v.metaType()) {
if (isEnumType() && !t.metaObject() && v.metaType().id() == QMetaType::QString) {
// Assigning a string to a property of type Q_ENUMS (instead of Q_ENUM)
bool ok;
if (isFlagType())
- v = QVariant(menum.keysToValue(value.toByteArray(), &ok));
+ v = QVariant(menum.keysToValue(v.toByteArray(), &ok));
else
- v = QVariant(menum.keyToValue(value.toByteArray(), &ok));
+ v = QVariant(menum.keyToValue(v.toByteArray(), &ok));
if (!ok)
return false;
- } else if (!value.isValid()) {
+ } else if (!v.isValid()) {
if (isResettable())
return reset(object);
v = QVariant(t, nullptr);
@@ -3397,6 +3878,16 @@ bool QMetaProperty::writeOnGadget(void *gadget, const QVariant &value) const
}
/*!
+ \overload
+ \since 6.6
+*/
+bool QMetaProperty::writeOnGadget(void *gadget, QVariant &&value) const
+{
+ Q_ASSERT(priv(mobj->d.data)->flags & PropertyAccessInStaticMetaCall && mobj->d.static_metacall);
+ return write(reinterpret_cast<QObject*>(gadget), std::move(value));
+}
+
+/*!
\since 5.5
Resets the property for the given \a gadget with a reset method.
@@ -3480,20 +3971,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
@@ -3654,8 +4148,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
*/
@@ -3709,23 +4206,24 @@ const char *QMetaClassInfo::value() const
*/
/*!
- \macro QGenericArgument Q_ARG(Type, const Type &value)
+ \macro QMetaMethodArgument Q_ARG(Type, const Type &value)
\relates QMetaObject
This macro takes a \a Type and a \a value of that type and
- returns a \l QGenericArgument object that can be passed to
- QMetaObject::invokeMethod().
+ returns a QMetaMethodArgument, which can be passed to the template
+ QMetaObject::invokeMethod() with the \c {Args &&...} arguments.
\sa Q_RETURN_ARG()
*/
/*!
- \macro QGenericReturnArgument Q_RETURN_ARG(Type, Type &value)
+ \macro QMetaMethodReturnArgument Q_RETURN_ARG(Type, Type &value)
\relates QMetaObject
This macro takes a \a Type and a non-const reference to a \a
- value of that type and returns a QGenericReturnArgument object
- that can be passed to QMetaObject::invokeMethod().
+ value of that type and returns a QMetaMethodReturnArgument, which can be
+ passed to the template QMetaObject::invokeMethod() with the \c {Args &&...}
+ arguments.
\sa Q_ARG()
*/
diff --git a/src/corelib/kernel/qmetaobject.h b/src/corelib/kernel/qmetaobject.h
index 6fe259868f..4e52e854d9 100644
--- a/src/corelib/kernel/qmetaobject.h
+++ b/src/corelib/kernel/qmetaobject.h
@@ -1,42 +1,6 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Copyright (C) 2014 Olivier Goffart <ogoffart@woboq.com>
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtCore module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// Copyright (C) 2014 Olivier Goffart <ogoffart@woboq.com>
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QMETAOBJECT_H
#define QMETAOBJECT_H
@@ -81,6 +45,7 @@ public:
inline const QMetaObject *enclosingMetaObject() const { return mobj; }
+#if QT_VERSION <= QT_VERSION_CHECK(7, 0, 0)
bool invoke(QObject *object,
Qt::ConnectionType connectionType,
QGenericReturnArgument returnValue,
@@ -112,7 +77,7 @@ public:
}
inline bool invoke(QObject *object,
Qt::ConnectionType connectionType,
- QGenericArgument val0 = QGenericArgument(nullptr),
+ QGenericArgument val0,
QGenericArgument val1 = QGenericArgument(),
QGenericArgument val2 = QGenericArgument(),
QGenericArgument val3 = QGenericArgument(),
@@ -127,7 +92,7 @@ public:
val0, val1, val2, val3, val4, val5, val6, val7, val8, val9);
}
inline bool invoke(QObject *object,
- QGenericArgument val0 = QGenericArgument(nullptr),
+ QGenericArgument val0,
QGenericArgument val1 = QGenericArgument(),
QGenericArgument val2 = QGenericArgument(),
QGenericArgument val3 = QGenericArgument(),
@@ -154,7 +119,7 @@ public:
QGenericArgument val8 = QGenericArgument(),
QGenericArgument val9 = QGenericArgument()) const;
inline bool invokeOnGadget(void *gadget,
- QGenericArgument val0 = QGenericArgument(nullptr),
+ QGenericArgument val0,
QGenericArgument val1 = QGenericArgument(),
QGenericArgument val2 = QGenericArgument(),
QGenericArgument val3 = QGenericArgument(),
@@ -168,6 +133,78 @@ public:
return invokeOnGadget(gadget, QGenericReturnArgument(),
val0, val1, val2, val3, val4, val5, val6, val7, val8, val9);
}
+#endif
+
+ template <typename ReturnArg, typename... Args>
+#ifdef Q_QDOC
+ bool
+#else
+ QtPrivate::Invoke::IfNotOldStyleArgs<bool, Args...>
+#endif
+ invoke(QObject *obj, Qt::ConnectionType c, QTemplatedMetaMethodReturnArgument<ReturnArg> r,
+ Args &&... arguments) const
+ {
+ auto h = QtPrivate::invokeMethodHelper(r, std::forward<Args>(arguments)...);
+ return invokeImpl(*this, obj, c, h.parameterCount(), h.parameters.data(),
+ h.typeNames.data(), h.metaTypes.data());
+ }
+
+ template <typename... Args>
+#ifdef Q_QDOC
+ bool
+#else
+ QtPrivate::Invoke::IfNotOldStyleArgs<bool, Args...>
+#endif
+ invoke(QObject *obj, Qt::ConnectionType c, Args &&... arguments) const
+ {
+ return invoke(obj, c, QTemplatedMetaMethodReturnArgument<void>{}, std::forward<Args>(arguments)...);
+ }
+
+ template <typename ReturnArg, typename... Args>
+#ifdef Q_QDOC
+ bool
+#else
+ QtPrivate::Invoke::IfNotOldStyleArgs<bool, Args...>
+#endif
+ invoke(QObject *obj, QTemplatedMetaMethodReturnArgument<ReturnArg> r, Args &&... arguments) const
+ {
+ return invoke(obj, Qt::AutoConnection, r, std::forward<Args>(arguments)...);
+ }
+
+ template <typename... Args>
+#ifdef Q_QDOC
+ bool
+#else
+ QtPrivate::Invoke::IfNotOldStyleArgs<bool, Args...>
+#endif
+ invoke(QObject *obj, Args &&... arguments) const
+ {
+ return invoke(obj, Qt::AutoConnection, std::forward<Args>(arguments)...);
+ }
+
+ template <typename ReturnArg, typename... Args>
+#ifdef Q_QDOC
+ bool
+#else
+ QtPrivate::Invoke::IfNotOldStyleArgs<bool, Args...>
+#endif
+ 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(),
+ h.parameters.data(), h.typeNames.data(), h.metaTypes.data());
+ }
+
+ template <typename... Args>
+#ifdef Q_QDOC
+ bool
+#else
+ QtPrivate::Invoke::IfNotOldStyleArgs<bool, Args...>
+#endif
+ invokeOnGadget(void *gadget, Args &&... arguments) const
+ {
+ return invokeOnGadget(gadget, QTemplatedMetaMethodReturnArgument<void>{}, std::forward<Args>(arguments)...);
+ }
inline bool isValid() const { return mobj != nullptr; }
@@ -182,10 +219,14 @@ public:
}
private:
+ static bool invokeImpl(QMetaMethod self, void *target, Qt::ConnectionType, qsizetype paramCount,
+ const void *const *parameters, const char *const *typeNames,
+ const QtPrivate::QMetaTypeInterface *const *metaTypes);
static QMetaMethod fromSignalImpl(const QMetaObject *, void **);
static QMetaMethod fromRelativeMethodIndex(const QMetaObject *mobj, int index);
static QMetaMethod fromRelativeConstructorIndex(const QMetaObject *mobj, int index);
+protected:
struct Data {
enum { Size = 6 };
@@ -199,13 +240,14 @@ private:
const uint *d;
};
+private:
constexpr QMetaMethod(const QMetaObject *metaObject, const Data &data_)
: mobj(metaObject), data(data_)
{}
+protected:
const QMetaObject *mobj;
Data data;
- friend class QMetaMethodPrivate;
friend struct QMetaObject;
friend struct QMetaObjectPrivate;
friend class QObject;
@@ -223,6 +265,8 @@ public:
const char *name() const;
const char *enumName() const;
+ QMetaType metaType() const;
+
bool isFlag() const;
bool isScoped() const;
@@ -260,6 +304,7 @@ private:
quint32 flags() const { return d[2]; }
qint32 keyCount() const { return static_cast<qint32>(d[3]); }
quint32 data() const { return d[4]; }
+ int index(const QMetaObject *mobj) const;
const uint *d;
};
@@ -318,12 +363,14 @@ public:
QVariant read(const QObject *obj) const;
bool write(QObject *obj, const QVariant &value) const;
+ bool write(QObject *obj, QVariant &&value) const;
bool reset(QObject *obj) const;
QUntypedBindable bindable(QObject *object) const;
QVariant readOnGadget(const void *gadget) const;
bool writeOnGadget(void *gadget, const QVariant &value) const;
+ bool writeOnGadget(void *gadget, QVariant &&value) const;
bool resetOnGadget(void *gadget) const;
bool hasStdCppSet() const;
@@ -332,7 +379,10 @@ public:
inline const QMetaObject *enclosingMetaObject() const { return mobj; }
private:
+#if QT_DEPRECATED_SINCE(6, 4)
+ QT_DEPRECATED_VERSION_X_6_4("obsolete, simply returns typeId()")
int registerPropertyType() const;
+#endif
struct Data {
enum { Size = 5 };
diff --git a/src/corelib/kernel/qmetaobject_moc_p.h b/src/corelib/kernel/qmetaobject_moc_p.h
index 9770d9c576..ad8c6f92d6 100644
--- a/src/corelib/kernel/qmetaobject_moc_p.h
+++ b/src/corelib/kernel/qmetaobject_moc_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtCore module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2019 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
#if !defined(QMETAOBJECT_P_H) && !defined(UTILS_H)
# error "Include qmetaobject_p.h (or moc's utils.h) before including this file."
@@ -53,6 +17,7 @@
//
#include <QtCore/qbytearray.h>
+#include <QtCore/private/qglobal_p.h>
QT_BEGIN_NAMESPACE
diff --git a/src/corelib/kernel/qmetaobject_p.h b/src/corelib/kernel/qmetaobject_p.h
index 35aa6ea6b9..d2c36fceb4 100644
--- a/src/corelib/kernel/qmetaobject_p.h
+++ b/src/corelib/kernel/qmetaobject_p.h
@@ -1,42 +1,6 @@
-/****************************************************************************
-**
-** Copyright (C) 2020 The Qt Company Ltd.
-** Copyright (C) 2014 Olivier Goffart <ogoffart@woboq.com>
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtCore module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2020 The Qt Company Ltd.
+// Copyright (C) 2014 Olivier Goffart <ogoffart@woboq.com>
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QMETAOBJECT_P_H
#define QMETAOBJECT_P_H
@@ -59,11 +23,14 @@
#ifndef QT_NO_QOBJECT
#include <private/qobject_p.h> // For QObjectPrivate::Connection
#endif
+#include <private/qtools_p.h>
#include <QtCore/qvarlengtharray.h>
QT_BEGIN_NAMESPACE
// ### TODO - QTBUG-87869: wrap in a proper Q_NAMESPACE and use scoped enums, to avoid name clashes
+using namespace QtMiscUtils;
+
enum PropertyFlags {
Invalid = 0x00000000,
Readable = 0x00000001,
@@ -167,7 +134,38 @@ Q_DECLARE_TYPEINFO(QArgumentType, Q_RELOCATABLE_TYPE);
typedef QVarLengthArray<QArgumentType, 10> QArgumentTypeArray;
-class QMetaMethodPrivate;
+namespace { class QMetaMethodPrivate; }
+class QMetaMethodInvoker : public QMetaMethod
+{
+ QMetaMethodInvoker() = delete;
+
+public:
+ enum class InvokeFailReason : int {
+ // negative values mean a match was found but the invocation failed
+ // (and a warning has been printed)
+ ReturnTypeMismatch = -1,
+ DeadLockDetected = -2,
+ CallViaVirtualFailed = -3, // no warning
+ ConstructorCallOnObject = -4,
+ ConstructorCallWithoutResult = -5,
+ ConstructorCallFailed = -6, // no warning
+
+ CouldNotQueueParameter = -0x1000,
+
+ // zero is success
+ None = 0,
+
+ // positive values mean the parameters did not match
+ TooFewArguments,
+ FormalParameterMismatch = 0x1000,
+ };
+
+ // shadows the public function
+ static InvokeFailReason Q_CORE_EXPORT
+ invokeImpl(QMetaMethod self, void *target, Qt::ConnectionType, qsizetype paramCount,
+ const void *const *parameters, const char *const *typeNames,
+ const QtPrivate::QMetaTypeInterface *const *metaTypes);
+};
struct QMetaObjectPrivate
{
@@ -176,7 +174,9 @@ struct QMetaObjectPrivate
// revision 9 is Qt 6.0: It adds the metatype of properties and methods
// revision 10 is Qt 6.2: The metatype of the metaobject is stored in the metatypes array
// and metamethods store a flag stating whether they are const
- enum { OutputRevision = 10 }; // Used by moc, qmetaobjectbuilder and qdbus
+ // revision 11 is Qt 6.5: The metatype for void is stored in the metatypes array
+ // revision 12 is Qt 6.6: It adds the metatype for enums
+ enum { OutputRevision = 12 }; // Used by moc, qmetaobjectbuilder and qdbus
enum { IntsPerMethod = QMetaMethod::Data::Size };
enum { IntsPerEnum = QMetaEnum::Data::Size };
enum { IntsPerProperty = QMetaProperty::Data::Size };
@@ -212,6 +212,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)
{
@@ -269,11 +274,7 @@ enum { MetaObjectPrivateFieldCount = sizeof(QMetaObjectPrivate) / sizeof(int) };
// mirrored in moc's utils.h
static inline bool is_ident_char(char s)
{
- return ((s >= 'a' && s <= 'z')
- || (s >= 'A' && s <= 'Z')
- || (s >= '0' && s <= '9')
- || s == '_'
- );
+ return isAsciiLetterOrNumber(s) || s == '_';
}
static inline bool is_space(char s)
diff --git a/src/corelib/kernel/qmetaobjectbuilder.cpp b/src/corelib/kernel/qmetaobjectbuilder.cpp
index 1a95d7265f..c2b44a4f00 100644
--- a/src/corelib/kernel/qmetaobjectbuilder.cpp
+++ b/src/corelib/kernel/qmetaobjectbuilder.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtCore module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// 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 "qmetaobjectbuilder_p.h"
@@ -89,7 +53,7 @@ Q_CORE_EXPORT bool isBuiltinType(const QByteArray &type)
} // namespace QtPrivate
// copied from qmetaobject.cpp
-[[maybe_unused]] static inline const QMetaObjectPrivate *priv(const uint* data)
+[[maybe_unused]] static inline const QMetaObjectPrivate *qmobPriv(const uint* data)
{ return reinterpret_cast<const QMetaObjectPrivate*>(data); }
class QMetaMethodBuilderPrivate
@@ -195,6 +159,7 @@ public:
QByteArray name;
QByteArray enumName;
+ QMetaType metaType;
bool isFlag;
bool isScoped;
QList<QByteArray> keys;
@@ -558,7 +523,7 @@ QMetaMethodBuilder QMetaObjectBuilder::addConstructor(const QMetaMethod &prototy
QMetaPropertyBuilder QMetaObjectBuilder::addProperty(const QByteArray &name, const QByteArray &type,
int notifierId)
{
- return addProperty(name, type, QMetaType::fromName(name), notifierId);
+ return addProperty(name, type, QMetaType::fromName(type), notifierId);
}
/*!
@@ -633,6 +598,7 @@ QMetaEnumBuilder QMetaObjectBuilder::addEnumerator(const QMetaEnum &prototype)
{
QMetaEnumBuilder en = addEnumerator(prototype.name());
en.setEnumName(prototype.enumName());
+ en.setMetaType(prototype.metaType());
en.setIsFlag(prototype.isFlag());
en.setIsScoped(prototype.isScoped());
int count = prototype.keyCount();
@@ -740,7 +706,7 @@ void QMetaObjectBuilder::addMetaObject(const QMetaObject *prototype,
}
if ((members & RelatedMetaObjects) != 0) {
- Q_ASSERT(priv(prototype->d.data)->revision >= 2);
+ Q_ASSERT(qmobPriv(prototype->d.data)->revision >= 2);
const auto *objects = prototype->d.relatedMetaObjects;
if (objects) {
while (*objects != nullptr) {
@@ -751,7 +717,7 @@ void QMetaObjectBuilder::addMetaObject(const QMetaObject *prototype,
}
if ((members & StaticMetacall) != 0) {
- Q_ASSERT(priv(prototype->d.data)->revision >= 6);
+ Q_ASSERT(qmobPriv(prototype->d.data)->revision >= 6);
if (prototype->d.static_metacall)
setStaticMetacallFunction(prototype->d.static_metacall);
}
@@ -1062,6 +1028,9 @@ int QMetaObjectBuilder::indexOfClassInfo(const QByteArray &name)
}
// Align on a specific type boundary.
+#ifdef ALIGN
+# undef ALIGN
+#endif
#define ALIGN(size,type) \
(size) = ((size) + sizeof(type) - 1) & ~(sizeof(type) - 1)
@@ -1193,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 =
@@ -1203,7 +1172,7 @@ static int buildMetaObject(QMetaObjectBuilderPrivate *d, char *buf,
- int(d->methods.size()) // return "parameters" don't have names
- int(d->constructors.size()); // "this" parameters don't have names
if constexpr (mode == Construct) {
- static_assert(QMetaObjectPrivate::OutputRevision == 10, "QMetaObjectBuilder should generate the same version as moc");
+ static_assert(QMetaObjectPrivate::OutputRevision == 12, "QMetaObjectBuilder should generate the same version as moc");
pmeta->revision = QMetaObjectPrivate::OutputRevision;
pmeta->flags = d->flags.toInt();
pmeta->className = 0; // Class name is always the first string.
@@ -1439,41 +1408,39 @@ static int buildMetaObject(QMetaObjectBuilderPrivate *d, char *buf,
size += sizeof(SuperData) * (d->relatedMetaObjects.size() + 1);
}
- if (d->properties.size() > 0 || d->methods.size() > 0 || d->constructors.size() > 0) {
- ALIGN(size, QtPrivate::QMetaTypeInterface *);
- auto types = reinterpret_cast<QtPrivate::QMetaTypeInterface **>(buf + size);
- if constexpr (mode == Construct) {
- meta->d.metaTypes = types;
- for (const auto &prop : d->properties) {
- QMetaType mt = prop.metaType;
+ ALIGN(size, QtPrivate::QMetaTypeInterface *);
+ auto types = reinterpret_cast<QtPrivate::QMetaTypeInterface **>(buf + size);
+ if constexpr (mode == Construct) {
+ meta->d.metaTypes = types;
+ for (const auto &prop : d->properties) {
+ QMetaType mt = prop.metaType;
+ *types = reinterpret_cast<QtPrivate::QMetaTypeInterface *&>(mt);
+ types++;
+ }
+ // add metatype interface for this metaobject - must be null
+ // as we can't know our metatype
+ *types = nullptr;
+ types++;
+ for (const auto &method: d->methods) {
+ QMetaType mt(QMetaType::fromName(method.returnType).id());
+ *types = reinterpret_cast<QtPrivate::QMetaTypeInterface *&>(mt);
+ types++;
+ for (const auto &parameterType: method.parameterTypes()) {
+ QMetaType mt = QMetaType::fromName(parameterType);
*types = reinterpret_cast<QtPrivate::QMetaTypeInterface *&>(mt);
types++;
}
- // add metatype interface for this metaobject - must be null
- // as we can't know our metatype
- *types = nullptr;
- types++;
- for (const auto &method: d->methods) {
- QMetaType mt(QMetaType::fromName(method.returnType).id());
+ }
+ for (const auto &constructor : d->constructors) {
+ for (const auto &parameterType : constructor.parameterTypes()) {
+ QMetaType mt = QMetaType::fromName(parameterType);
*types = reinterpret_cast<QtPrivate::QMetaTypeInterface *&>(mt);
types++;
- for (const auto &parameterType: method.parameterTypes()) {
- QMetaType mt = QMetaType::fromName(parameterType);
- *types = reinterpret_cast<QtPrivate::QMetaTypeInterface *&>(mt);
- types++;
- }
- }
- for (const auto &constructor : d->constructors) {
- for (const auto &parameterType : constructor.parameterTypes()) {
- QMetaType mt = QMetaType::fromName(parameterType);
- *types = reinterpret_cast<QtPrivate::QMetaTypeInterface *&>(mt);
- types++;
- }
}
}
- // parameterMetaTypesIndex is equal to the total number of metatypes
- size += sizeof(QMetaType) * parameterMetaTypesIndex;
}
+ // parameterMetaTypesIndex is equal to the total number of metatypes
+ size += sizeof(QMetaType) * parameterMetaTypesIndex;
// Align the final size and return it.
ALIGN(size, void *);
@@ -2341,6 +2308,31 @@ void QMetaEnumBuilder::setEnumName(const QByteArray &alias)
}
/*!
+ Returns the meta type of the enumerator.
+
+ \since 6.6
+*/
+QMetaType QMetaEnumBuilder::metaType() const
+{
+ if (QMetaEnumBuilderPrivate *d = d_func())
+ return d->metaType;
+ return QMetaType();
+}
+
+/*!
+ Sets this enumerator to have the given \c metaType.
+
+ \since 6.6
+ \sa metaType()
+*/
+void QMetaEnumBuilder::setMetaType(QMetaType metaType)
+{
+ QMetaEnumBuilderPrivate *d = d_func();
+ if (d)
+ d->metaType = metaType;
+}
+
+/*!
Returns \c true if this enumerator is used as a flag; otherwise returns
false.
diff --git a/src/corelib/kernel/qmetaobjectbuilder_p.h b/src/corelib/kernel/qmetaobjectbuilder_p.h
index 925fed694b..b52487986c 100644
--- a/src/corelib/kernel/qmetaobjectbuilder_p.h
+++ b/src/corelib/kernel/qmetaobjectbuilder_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtCore module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// 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 QMETAOBJECTBUILDER_P_H
#define QMETAOBJECTBUILDER_P_H
@@ -294,6 +258,9 @@ public:
QByteArray enumName() const;
void setEnumName(const QByteArray &alias);
+ QMetaType metaType() const;
+ void setMetaType(QMetaType metaType);
+
bool isFlag() const;
void setIsFlag(bool value);
diff --git a/src/corelib/kernel/qmetatype.cpp b/src/corelib/kernel/qmetatype.cpp
index 3758d3c941..387c0f49ab 100644
--- a/src/corelib/kernel/qmetatype.cpp
+++ b/src/corelib/kernel/qmetatype.cpp
@@ -1,50 +1,16 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtCore module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#define QT_QMETATYPE_BC_COMPAT 1
+// Copyright (C) 2021 The Qt Company Ltd.
+// Copyright (C) 2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
+// Copyright (C) 2021 Intel Corporation.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
#include "qmetatype.h"
-#undef QT_QMETATYPE_BC_COMPAT
#include "qmetatype_p.h"
#include "qobjectdefs.h"
#include "qdatetime.h"
#include "qbytearray.h"
#include "qreadwritelock.h"
+#include "qhash.h"
+#include "qmap.h"
#include "qstring.h"
#include "qstringlist.h"
#include "qlist.h"
@@ -54,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"
@@ -76,6 +42,7 @@
# include "qmetaobject.h"
# include "qsequentialiterable.h"
# include "qassociativeiterable.h"
+# include "qobject.h"
#endif
#if QT_CONFIG(itemmodel)
@@ -89,7 +56,6 @@
# include "qline.h"
#endif
-#include <bitset>
#include <new>
#include <cstring>
@@ -97,37 +63,64 @@ QT_BEGIN_NAMESPACE
#define NS(x) QT_PREPEND_NAMESPACE(x)
+QT_IMPL_METATYPE_EXTERN_TAGGED(QtMetaTypePrivate::QPairVariantInterfaceImpl, QPairVariantInterfaceImpl)
+
+using QtMetaTypePrivate::isInterfaceFor;
namespace {
-struct DefinedTypesFilter {
- template<typename T>
- struct Acceptor {
- static const bool IsAccepted = QtMetaTypePrivate::TypeDefinition<T>::IsAvailable && QModulesPrivate::QTypeModuleInfo<T>::IsCore;
- };
+struct QMetaTypeDeleter
+{
+ const QtPrivate::QMetaTypeInterface *iface;
+ void operator()(void *data)
+ {
+ if (iface->alignment > __STDCPP_DEFAULT_NEW_ALIGNMENT__) {
+ operator delete(data, std::align_val_t(iface->alignment));
+ } else {
+ operator delete(data);
+ }
+ }
};
struct QMetaTypeCustomRegistry
{
+
+#if QT_VERSION < QT_VERSION_CHECK(7, 0, 0) && !defined(QT_BOOTSTRAPPED)
+ QMetaTypeCustomRegistry()
+ {
+ /* qfloat16 was neither a builtin, nor unconditionally registered
+ in QtCore in Qt <= 6.2.
+ Inserting it as an alias ensures that a QMetaType::id call
+ will get the correct built-in type-id (the interface pointers
+ might still not match, but we already deal with that case.
+ */
+ aliases.insert("qfloat16", QtPrivate::qMetaTypeInterfaceForType<qfloat16>());
+ }
+#endif
+
QReadWriteLock lock;
QList<const QtPrivate::QMetaTypeInterface *> registry;
QHash<QByteArray, const QtPrivate::QMetaTypeInterface *> aliases;
// index of first empty (unregistered) type in registry, if any.
int firstEmpty = 0;
- int registerCustomType(const QtPrivate::QMetaTypeInterface *ti)
+ int registerCustomType(const QtPrivate::QMetaTypeInterface *cti)
{
+ // we got here because cti->typeId is 0, so this is a custom meta type
+ // (not read-only)
+ auto ti = const_cast<QtPrivate::QMetaTypeInterface *>(cti);
{
QWriteLocker l(&lock);
- if (ti->typeId)
- return ti->typeId;
+ if (int id = ti->typeId.loadRelaxed())
+ return id;
QByteArray name =
#ifndef QT_NO_QOBJECT
QMetaObject::normalizedType
#endif
(ti->name);
if (auto ti2 = aliases.value(name)) {
- ti->typeId.storeRelaxed(ti2->typeId.loadRelaxed());
- return ti2->typeId;
+ const auto id = ti2->typeId.loadRelaxed();
+ ti->typeId.storeRelaxed(id);
+ return id;
}
aliases[name] = ti;
int size = registry.size();
@@ -140,11 +133,11 @@ struct QMetaTypeCustomRegistry
registry.append(ti);
firstEmpty = registry.size();
}
- ti->typeId = firstEmpty + QMetaType::User;
+ ti->typeId.storeRelaxed(firstEmpty + QMetaType::User);
}
if (ti->legacyRegisterOp)
ti->legacyRegisterOp();
- return ti->typeId;
+ return ti->typeId.loadRelaxed();
};
void unregisterDynamicType(int id)
@@ -157,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;
@@ -181,6 +168,44 @@ Q_GLOBAL_STATIC(QMetaTypeCustomRegistry, customTypeRegistry)
} // namespace
+// used by QVariant::save(): returns the name used in the Q_DECLARE_METATYPE
+// macro (one of them, indetermine which one)
+const char *QtMetaTypePrivate::typedefNameForType(const QtPrivate::QMetaTypeInterface *type_d)
+{
+ const char *name = nullptr;
+ if (!customTypeRegistry.exists())
+ return name;
+ QMetaTypeCustomRegistry *r = &*customTypeRegistry;
+
+ QByteArrayView officialName(type_d->name);
+ QReadLocker l(&r->lock);
+ auto it = r->aliases.constBegin();
+ auto end = r->aliases.constEnd();
+ for ( ; it != end; ++it) {
+ if (it.value() != type_d)
+ continue;
+ if (it.key() == officialName)
+ continue; // skip the official name
+ name = it.key().constData();
+ ++it;
+ break;
+ }
+
+#ifndef QT_NO_DEBUG
+ QByteArrayList otherNames;
+ for ( ; it != end; ++it) {
+ if (it.value() == type_d && it.key() != officialName)
+ otherNames << it.key();
+ }
+ l.unlock();
+ if (!otherNames.isEmpty())
+ qWarning("QMetaType: type %s has more than one typedef alias: %s, %s",
+ type_d->name, name, otherNames.join(", ").constData());
+#endif
+
+ return name;
+}
+
/*!
\macro Q_DECLARE_OPAQUE_POINTER(PointerType)
\relates QMetaType
@@ -237,7 +262,7 @@ Q_GLOBAL_STATIC(QMetaTypeCustomRegistry, customTypeRegistry)
\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
@@ -335,8 +360,12 @@ Q_GLOBAL_STATIC(QMetaTypeCustomRegistry, customTypeRegistry)
\value SChar \c{signed char}
\value UChar \c{unsigned char}
\value Float \c float
+ \value Float16 qfloat16
+ \omitvalue Float128
+ \omitvalue BFloat16
+ \omitvalue Int128
+ \omitvalue UInt128
\value QObjectStar QObject *
- \value QVariant QVariant
\value QCursor QCursor
\value QDate QDate
@@ -354,6 +383,7 @@ Q_GLOBAL_STATIC(QMetaTypeCustomRegistry, customTypeRegistry)
\value QStringList QStringList
\value QVariantMap QVariantMap
\value QVariantHash QVariantHash
+ \value QVariantPair QVariantPair
\value QIcon QIcon
\value QPen QPen
\value QLineF QLineF
@@ -394,13 +424,13 @@ Q_GLOBAL_STATIC(QMetaTypeCustomRegistry, customTypeRegistry)
\value QPersistentModelIndex QPersistentModelIndex (introduced in Qt 5.5)
\value QUuid QUuid
\value QByteArrayList QByteArrayList
+ \value QVariant QVariant
\value User Base value for user types
\value UnknownType This is an invalid type id. It is returned from QMetaType for types that are not registered
- \omitvalue LastCoreType
- \omitvalue LastGuiType
- Additional types can be registered using Q_DECLARE_METATYPE().
+ Additional types can be registered using qRegisterMetaType() or by calling
+ registerType().
\sa type(), typeName()
*/
@@ -410,21 +440,31 @@ Q_GLOBAL_STATIC(QMetaTypeCustomRegistry, customTypeRegistry)
The enum describes attributes of a type supported by QMetaType.
- \value NeedsConstruction This type has non-trivial constructors. If the flag is not set instances can be safely initialized with memset to 0.
- \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 NeedsConstruction This type has a default constructor. If the flag is not set, instances can be safely initialized with memset to 0.
+ \value NeedsCopyConstruction (since 6.5) This type has a non-trivial copy constructor. If the flag is not set, instances can be copied with memcpy.
+ \value NeedsMoveConstruction (since 6.5) This type has a non-trivial move constructor. If the flag is not set, instances can be moved with memcpy.
+ \value NeedsDestruction This type has a non-trivial destructor. If the flag is not set, calls to the destructor are not necessary before discarding objects.
\value RelocatableType An instance of a type having this attribute can be safely moved to a different memory location using memcpy.
\omitvalue MovableType
\omitvalue SharedPointerToQObject
\value IsEnumeration This type is an enumeration.
\value IsUnsignedEnumeration If the type is an Enumeration, its underlying type is unsigned.
- \value PointerToQObject This type is a pointer to a derived of QObject.
+ \value PointerToQObject This type is a pointer to a class derived from QObject.
\value IsPointer This type is a pointer to another type.
\omitvalue WeakPointerToQObject
\omitvalue TrackingPointerToQObject
- \omitvalue IsGadget \omit This type is a Q_GADGET and it's corresponding QMetaObject can be accessed with QMetaType::metaObject Since 5.5. \endomit
+ \omitvalue IsGadget \omit (since Qt 5.5) This type is a Q_GADGET and its corresponding QMetaObject can be accessed with QMetaType::metaObject. \endomit
\omitvalue PointerToGadget
\omitvalue IsQmlList
- \value IsConst Indicates that values of this types are immutable; for instance because they are pointers to const objects.
+ \value IsConst Indicates that values of this type are immutable; for instance, because they are pointers to const objects.
+
+ \note Before Qt 6.5, both the NeedsConstruction and NeedsDestruction flags
+ were incorrectly set if the either copy construtor or destructor were
+ non-trivial (that is, if the type was not trivial).
+
+ Note that the Needs flags may be set but the meta type may not have a
+ publicly-accessible constructor of the relevant type or a
+ publicly-accessible destructor.
*/
/*!
@@ -438,17 +478,19 @@ Q_GLOBAL_STATIC(QMetaTypeCustomRegistry, customTypeRegistry)
The class is used as a helper to marshall types in QVariant and
in queued signals and slots connections. It associates a type
name to a type so that it can be created and destructed
- dynamically at run-time. Declare new types with Q_DECLARE_METATYPE()
- to make them available to QVariant and other template-based functions.
- Call qRegisterMetaType() to make types available to non-template based
- functions, such as the queued signal and slot connections.
+ dynamically at run-time.
- Any class or struct that has a public default
- constructor, a public copy constructor, and a public destructor
- can be registered.
+ Type names can be registered with QMetaType by using either
+ qRegisterMetaType() or registerType(). Registration is not required for
+ most operations; it's only required for operations that attempt to resolve
+ a type name in string form back to a QMetaType object or the type's ID.
+ Those include some old-style signal-slot connections using
+ QObject::connect(), reading user-types from \l QDataStream to \l QVariant,
+ or binding to other languages and IPC mechanisms, like QML, D-Bus,
+ JavaScript, etc.
- The following code allocates and destructs an instance of
- \c{MyClass}:
+ The following code allocates and destructs an instance of \c{MyClass} by
+ its name, which requires that \c{MyClass} have been previously registered:
\snippet code/src_corelib_kernel_qmetatype.cpp 3
@@ -466,6 +508,8 @@ Q_GLOBAL_STATIC(QMetaTypeCustomRegistry, customTypeRegistry)
Returns \c true if this QMetaType object contains valid
information about a type, false otherwise.
+
+ \sa isRegistered()
*/
bool QMetaType::isValid() const
{
@@ -476,43 +520,43 @@ bool QMetaType::isValid() const
\fn bool QMetaType::isRegistered() const
\since 5.0
- Returns \c true if this QMetaType object contains valid
- information about a type, false otherwise.
+ Returns \c true if this QMetaType object has been registered with the Qt
+ global metatype registry. Registration allows the type to be found by its
+ name (using QMetaType::fromName()) or by its ID (using the constructor).
+
+ \sa qRegisterMetaType(), isValid()
*/
bool QMetaType::isRegistered() const
{
- return d_ptr;
+ return d_ptr && d_ptr->typeId.loadRelaxed();
}
/*!
\fn int QMetaType::id() const
\since 5.13
- Returns id type hold by this QMetatype instance.
+ Returns id type held by this QMetatype instance.
*/
-// keep in sync with version in header
-// ### Qt 7::remove BC helper
-int QMetaType::id() const
-{
- if (d_ptr) {
- if (int id = d_ptr->typeId.loadRelaxed())
- return id;
- return idHelper();
- }
- return 0;
-}
+/*!
+ \fn void QMetaType::registerType() const
+ \since 6.5
+
+ Registers this QMetaType with the type registry so it can be found by name,
+ using QMetaType::fromName().
+ \sa qRegisterMetaType()
+ */
/*!
\internal
- The slowpath of id(). Precondition: d_ptr != nullptr
-*/
-int QMetaType::idHelper() const
+ Out-of-line path for registerType() and slow path id().
+ */
+int QMetaType::registerHelper(const QtPrivate::QMetaTypeInterface *iface)
{
- Q_ASSERT(d_ptr);
+ Q_ASSERT(iface);
auto reg = customTypeRegistry();
if (reg) {
- return reg->registerCustomType(d_ptr);
+ return reg->registerCustomType(iface);
}
return 0;
}
@@ -550,28 +594,34 @@ int QMetaType::idHelper() const
\fn constexpr TypeFlags QMetaType::flags() const
\since 5.0
- Returns flags of the type for which this QMetaType instance was constructed.
+ Returns flags of the type for which this QMetaType instance was
+ constructed. To inspect specific type traits, prefer using one of the "is-"
+ functions rather than the flags directly.
- \sa QMetaType::TypeFlags, QMetaType::flags()
+ \sa QMetaType::TypeFlags, QMetaType::flags(), isDefaultConstructible(),
+ isCopyConstructible(), isMoveConstructible(), isDestructible(),
+ isEqualityComparable(), isOrdered()
*/
/*!
\fn constexpr const QMetaObject *QMetaType::metaObject() const
\since 5.5
- return a QMetaObject relative to this type.
+ Returns a QMetaObject relative to this type.
If the type is a pointer type to a subclass of QObject, flags() contains
- QMetaType::PointerToQObject and this function returns the corresponding QMetaObject. This can
- be used to in combinaison with QMetaObject::construct to create QObject of this type.
+ QMetaType::PointerToQObject and this function returns the corresponding QMetaObject.
+ This can be used in combination with QMetaObject::newInstance() to create QObjects of this type.
- If the type is a Q_GADGET, flags() contains QMetaType::IsGadget, and this function returns its
- QMetaObject. This can be used to retrieve QMetaMethod and QMetaProperty and use them on a
- pointer of this type. (given by QVariant::data for example)
+ If the type is a Q_GADGET, flags() contains QMetaType::IsGadget.
+ If the type is a pointer to a Q_GADGET, flags() contains QMetaType::PointerToGadget.
+ In both cases, this function returns its QMetaObject.
+ This can be used to retrieve QMetaMethod and QMetaProperty and use them on a
+ pointer of this type for example, as given by QVariant::data().
- If the type is an enumeration, flags() contains QMetaType::IsEnumeration, and this function
- returns the QMetaObject of the enclosing object if the enum was registered as a Q_ENUM or
- \nullptr otherwise
+ If the type is an enumeration, flags() contains QMetaType::IsEnumeration.
+ In this case, this function returns the QMetaObject of the enclosing
+ object if the enum was registered as a Q_ENUM or \nullptr otherwise.
\sa QMetaType::flags()
*/
@@ -588,16 +638,17 @@ int QMetaType::idHelper() const
*/
void *QMetaType::create(const void *copy) const
{
- if (d_ptr && (copy ? !!d_ptr->copyCtr : !!d_ptr->defaultCtr)) {
- void *where =
-#ifdef __STDCPP_DEFAULT_NEW_ALIGNMENT__
- d_ptr->alignment > __STDCPP_DEFAULT_NEW_ALIGNMENT__ ?
- operator new(d_ptr->size, std::align_val_t(d_ptr->alignment)) :
-#endif
- operator new(d_ptr->size);
- return construct(where, copy);
- }
- return nullptr;
+ if (copy ? !isCopyConstructible() : !isDefaultConstructible())
+ return nullptr;
+
+ std::unique_ptr<void, QMetaTypeDeleter> where(nullptr, {d_ptr});
+ if (d_ptr->alignment > __STDCPP_DEFAULT_NEW_ALIGNMENT__)
+ where.reset(operator new(d_ptr->size, std::align_val_t(d_ptr->alignment)));
+ else
+ where.reset(operator new(d_ptr->size));
+
+ QtMetaTypePrivate::construct(d_ptr, where.get(), copy);
+ return where.release();
}
/*!
@@ -611,14 +662,9 @@ void *QMetaType::create(const void *copy) const
*/
void QMetaType::destroy(void *data) const
{
- if (d_ptr) {
- if (d_ptr->dtor)
- d_ptr->dtor(d_ptr, data);
- if (d_ptr->alignment > __STDCPP_DEFAULT_NEW_ALIGNMENT__) {
- operator delete(data, std::align_val_t(d_ptr->alignment));
- } else {
- operator delete(data);
- }
+ if (data && isDestructible()) {
+ QtMetaTypePrivate::destruct(d_ptr, data);
+ QMetaTypeDeleter{d_ptr}(data);
}
}
@@ -652,16 +698,11 @@ void *QMetaType::construct(void *where, const void *copy) const
{
if (!where)
return nullptr;
- if (d_ptr) {
- if (copy && d_ptr->copyCtr) {
- d_ptr->copyCtr(d_ptr, where, copy);
- return where;
- } else if (!copy && d_ptr->defaultCtr) {
- d_ptr->defaultCtr(d_ptr, where);
- return where;
- }
- }
- return nullptr;
+ if (copy ? !isCopyConstructible() : !isDefaultConstructible())
+ return nullptr;
+
+ QtMetaTypePrivate::construct(d_ptr, where, copy);
+ return where;
}
/*!
@@ -677,12 +718,8 @@ void *QMetaType::construct(void *where, const void *copy) const
*/
void QMetaType::destruct(void *data) const
{
- if (!data)
- return;
- if (d_ptr && d_ptr->dtor) {
- d_ptr->dtor(d_ptr, data);
- return;
- }
+ if (data && isDestructible())
+ QtMetaTypePrivate::destruct(d_ptr, data);
}
static QPartialOrdering threeWayCompare(const void *ptr1, const void *ptr2)
@@ -725,7 +762,7 @@ QPartialOrdering QMetaType::compare(const void *lhs, const void *rhs) const
{
if (!lhs || !rhs)
return QPartialOrdering::Unordered;
- if (d_ptr->flags & QMetaType::IsPointer)
+ if (d_ptr && d_ptr->flags & QMetaType::IsPointer)
return threeWayCompare(*reinterpret_cast<const void * const *>(lhs),
*reinterpret_cast<const void * const *>(rhs));
if (d_ptr && d_ptr->lessThan) {
@@ -774,6 +811,68 @@ bool QMetaType::equals(const void *lhs, const void *rhs) const
}
/*!
+ \fn bool QMetaType::isDefaultConstructible() const noexcept
+ \since 6.5
+
+ Returns true if this type can be default-constructed. If it can be, then
+ construct() and create() can be used with a \c{copy} parameter that is
+ null.
+
+ \sa flags(), isCopyConstructible(), isMoveConstructible(), isDestructible()
+ */
+
+/*!
+ \fn bool QMetaType::isCopyConstructible() const noexcept
+ \since 6.5
+
+ Returns true if this type can be copy-constructed. If it can be, then
+ construct() and create() can be used with a \c{copy} parameter that is
+ not null.
+
+ \sa flags(), isDefaultConstructible(), isMoveConstructible(), isDestructible()
+ */
+
+/*!
+ \fn bool QMetaType::isMoveConstructible() const noexcept
+ \since 6.5
+
+ Returns true if this type can be move-constructed. QMetaType currently does
+ not have an API to make use of this trait.
+
+ \sa flags(), isDefaultConstructible(), isCopyConstructible(), isDestructible()
+ */
+
+/*!
+ \fn bool QMetaType::isDestructible() const noexcept
+ \since 6.5
+
+ Returns true if this type can be destroyed. If it can be, then destroy()
+ and destruct() can be called.
+
+ \sa flags(), isDefaultConstructible(), isCopyConstructible(), isMoveConstructible()
+ */
+
+bool QMetaType::isDefaultConstructible(const QtPrivate::QMetaTypeInterface *iface) noexcept
+{
+ return !isInterfaceFor<void>(iface) && QtMetaTypePrivate::isDefaultConstructible(iface);
+}
+
+bool QMetaType::isCopyConstructible(const QtPrivate::QMetaTypeInterface *iface) noexcept
+{
+ return !isInterfaceFor<void>(iface) && QtMetaTypePrivate::isCopyConstructible(iface);
+}
+
+bool QMetaType::isMoveConstructible(const QtPrivate::QMetaTypeInterface *iface) noexcept
+{
+ return !isInterfaceFor<void>(iface) && QtMetaTypePrivate::isMoveConstructible(iface);
+}
+
+bool QMetaType::isDestructible(const QtPrivate::QMetaTypeInterface *iface) noexcept
+{
+ return !isInterfaceFor<void>(iface) && QtMetaTypePrivate::isDestructible(iface);
+}
+
+/*!
Returns \c true if a less than or equality operator for the type described by
this metatype was visible to the metatype declaration, otherwise \c false.
@@ -801,11 +900,22 @@ bool QMetaType::isOrdered() const
*/
void QMetaType::unregisterMetaType(QMetaType type)
{
- if (type.d_ptr && type.d_ptr->typeId.loadRelaxed() >= QMetaType::User) {
- if (auto reg = customTypeRegistry())
- reg->unregisterDynamicType(type.d_ptr->typeId.loadRelaxed());
- type.d_ptr->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);
}
/*!
@@ -831,6 +941,12 @@ void QMetaType::unregisterMetaType(QMetaType type)
than the QMetaType \a b, 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 },
@@ -846,10 +962,11 @@ 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>, QLatin1String, const char *>>
+ std::conditional_t<std::is_same_v<T, QString>, QLatin1StringView, const char *>>
static inline bool convertToBool(const T &source)
{
T str = source.toLower();
@@ -893,6 +1010,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;)
@@ -1047,6 +1166,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);
@@ -1084,6 +1206,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;);
@@ -1091,7 +1221,7 @@ static const struct : QMetaTypeModuleHelper
#endif
QMETATYPE_CONVERTER(QString, QByteArray, result = QString::fromUtf8(source); return true;);
QMETATYPE_CONVERTER(QString, QStringList,
- return (source.count() == 1) ? (result = source.at(0), true) : false;
+ return (source.size() == 1) ? (result = source.at(0), true) : false;
);
#ifndef QT_BOOTSTRAPPED
QMETATYPE_CONVERTER(QString, QUrl, result = source.toString(); return true;);
@@ -1146,34 +1276,36 @@ static const struct : QMetaTypeModuleHelper
QMETATYPE_CONVERTER_ASSIGN(QRectF, QRect);
QMETATYPE_CONVERTER(QPoint, QPointF, result = source.toPoint(); return true;);
QMETATYPE_CONVERTER_ASSIGN(QPointF, QPoint);
- #endif
+#endif
+ QMETATYPE_CONVERTER(QStringList, QString, result = QStringList() << source; return true;);
+
+#ifndef QT_NO_VARIANT
QMETATYPE_CONVERTER(QByteArrayList, QVariantList,
result.reserve(source.size());
- for (auto v: source)
+ for (const auto &v: source)
result.append(v.toByteArray());
return true;
);
QMETATYPE_CONVERTER(QVariantList, QByteArrayList,
result.reserve(source.size());
- for (auto v: source)
+ for (const auto &v: source)
result.append(QVariant(v));
return true;
);
QMETATYPE_CONVERTER(QStringList, QVariantList,
result.reserve(source.size());
- for (auto v: source)
+ for (const auto &v: source)
result.append(v.toString());
return true;
);
QMETATYPE_CONVERTER(QVariantList, QStringList,
result.reserve(source.size());
- for (auto v: source)
+ for (const auto &v: source)
result.append(QVariant(v));
return true;
);
- QMETATYPE_CONVERTER(QStringList, QString, result = QStringList() << source; return true;);
QMETATYPE_CONVERTER(QVariantHash, QVariantMap,
for (auto it = source.begin(); it != source.end(); ++it)
@@ -1185,7 +1317,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,
@@ -1524,16 +1656,15 @@ static const struct : QMetaTypeModuleHelper
}
return false;
}
-} metatypeHelper;
+} metatypeHelper = {};
-static const QMetaTypeModuleHelper *qMetaTypeCoreHelper = &metatypeHelper;
-Q_CORE_EXPORT const QMetaTypeModuleHelper *qMetaTypeGuiHelper = nullptr;
-Q_CORE_EXPORT const QMetaTypeModuleHelper *qMetaTypeWidgetsHelper = nullptr;
+Q_CONSTINIT Q_CORE_EXPORT const QMetaTypeModuleHelper *qMetaTypeGuiHelper = nullptr;
+Q_CONSTINIT Q_CORE_EXPORT const QMetaTypeModuleHelper *qMetaTypeWidgetsHelper = nullptr;
static const QMetaTypeModuleHelper *qModuleHelperForType(int type)
{
if (type <= QMetaType::LastCoreType)
- return qMetaTypeCoreHelper;
+ return &metatypeHelper;
if (type >= QMetaType::FirstGuiType && type <= QMetaType::LastGuiType)
return qMetaTypeGuiHelper;
else if (type >= QMetaType::FirstWidgetsType && type <= QMetaType::LastWidgetsType)
@@ -1586,17 +1717,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.
@@ -1605,7 +1736,7 @@ Q_GLOBAL_STATIC(QMetaTypeMutableViewRegistry, customTypesMutableViewRegistry)
*/
/*!
- \fn template<typename From, typename To> static bool registerConverter(To(From::*function)() const)
+ \fn template<typename From, typename To> static bool QMetaType::registerConverter(To(From::*function)() const)
\since 5.2
\overload
Registers a method \a function like To From::function() const as converter from type From
@@ -1615,26 +1746,27 @@ Q_GLOBAL_STATIC(QMetaTypeMutableViewRegistry, customTypesMutableViewRegistry)
*/
/*!
- \fn template<typename From, typename To> static bool registerConverter(To(From::*function)(bool*) const)
+ \fn template<typename From, typename To> static bool QMetaType::registerConverter(To(From::*function)(bool*) const)
\since 5.2
\overload
Registers a method \a function like To From::function(bool *ok) const as converter from type From
to type To in the meta type system. Returns \c true if the registration succeeded, otherwise false.
- The \a ok pointer can be used by the function to indicate whether the conversion succeeded.
+ The \c ok pointer can be used by the function to indicate whether the conversion succeeded.
\snippet qmetatype/registerConverters.cpp memberOk
*/
/*!
- \fn template<typename From, typename To, typename UnaryFunction> static bool registerConverter(UnaryFunction function)
+ \fn template<typename From, typename To, typename UnaryFunction> static bool QMetaType::registerConverter(UnaryFunction function)
\since 5.2
\overload
Registers a unary function object \a function as converter from type From
to type To in the meta type system. Returns \c true if the registration succeeded, otherwise false.
- \a function must take an instance of type \a From and return an instance of \a To. It can be a function
- pointer, a lambda or a functor object.
+ \a function must take an instance of type \c From and return an instance of \c To. It can be a function
+ pointer, a lambda or a functor object. Since Qt 6.5, the \a function can also return an instance of
+ \c std::optional<To> to be able to indicate failed conversions.
\snippet qmetatype/registerConverters.cpp unaryfunc
*/
@@ -1647,7 +1779,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;
@@ -1656,7 +1788,7 @@ bool QMetaType::registerConverterFunction(const ConverterFunction &f, QMetaType
}
/*!
- \fn template<typename From, typename To> static bool registerMutableView(To(From::*function)())
+ \fn template<typename From, typename To> static bool QMetaType::registerMutableView(To(From::*function)())
\since 6.0
\overload
Registers a method \a function like \c {To From::function()} as mutable view of type \c {To} on
@@ -1665,7 +1797,7 @@ bool QMetaType::registerConverterFunction(const ConverterFunction &f, QMetaType
*/
/*!
- \fn template<typename From, typename To, typename UnaryFunction> static bool registerMutableView(UnaryFunction function)
+ \fn template<typename From, typename To, typename UnaryFunction> static bool QMetaType::registerMutableView(UnaryFunction function)
\since 6.0
\overload
Registers a unary function object \a function as mutable view of type To on type From
@@ -1680,7 +1812,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;
@@ -1713,6 +1845,17 @@ void QMetaType::unregisterConverterFunction(QMetaType from, QMetaType to)
#ifndef QT_NO_DEBUG_STREAM
/*!
+ \fn QDebug QMetaType::operator<<(QDebug d, QMetaType m)
+ \since 6.5
+ Writes the QMetaType \a m to the stream \a d, and returns the stream.
+*/
+QDebug operator<<(QDebug d, QMetaType m)
+{
+ const QDebugStateSaver saver(d);
+ return d.nospace() << "QMetaType(" << m.name() << ")";
+}
+
+/*!
Streams the object at \a rhs to the debug stream \a dbg. Returns \c true
on success, otherwise false.
\since 5.2
@@ -1737,7 +1880,7 @@ bool QMetaType::debugStream(QDebug& dbg, const void *rhs)
*/
/*!
- \fn bool QMetaType::hasRegisteredDebugStreamOperator()
+ \fn template<typename T> bool QMetaType::hasRegisteredDebugStreamOperator()
\deprecated
\since 5.2
@@ -1774,10 +1917,16 @@ static QMetaEnum metaEnumFromType(QMetaType t)
{
if (t.flags() & QMetaType::IsEnumeration) {
if (const QMetaObject *metaObject = t.metaObject()) {
- const QByteArray enumName = t.name();
- const char *lastColon = std::strrchr(enumName, ':');
- return metaObject->enumerator(metaObject->indexOfEnumerator(
- lastColon ? lastColon + 1 : enumName.constData()));
+ QByteArrayView qflagsNamePrefix = "QFlags<";
+ QByteArray enumName = t.name();
+ if (enumName.endsWith('>') && enumName.startsWith(qflagsNamePrefix)) {
+ // extract the template argument
+ enumName.chop(1);
+ enumName = enumName.sliced(qflagsNamePrefix.size());
+ }
+ if (qsizetype lastColon = enumName.lastIndexOf(':'); lastColon != -1)
+ enumName = enumName.sliced(lastColon + 1);
+ return metaObject->enumerator(metaObject->indexOfEnumerator(enumName));
}
}
return QMetaEnum();
@@ -1836,18 +1985,27 @@ static bool convertFromEnum(QMetaType fromType, const void *from, QMetaType toTy
if (toType.id() != QMetaType::QString && toType.id() != QMetaType::QByteArray)
return QMetaType::convert(QMetaType::fromType<qlonglong>(), &ll, toType, to);
}
- Q_ASSERT(toType.id() == QMetaType::QString || toType.id() == QMetaType::QByteArray);
#ifndef QT_NO_QOBJECT
QMetaEnum en = metaEnumFromType(fromType);
if (en.isValid()) {
- const char *key = en.valueToKey(ll);
- if (toType.id() == QMetaType::QString)
- *static_cast<QString *>(to) = QString::fromUtf8(key);
- else
- *static_cast<QByteArray *>(to) = key;
+ if (en.isFlag()) {
+ const QByteArray keys = en.valueToKeys(static_cast<int>(ll));
+ if (toType.id() == QMetaType::QString)
+ *static_cast<QString *>(to) = QString::fromUtf8(keys);
+ else
+ *static_cast<QByteArray *>(to) = keys;
+ } else {
+ const char *key = en.valueToKey(static_cast<int>(ll));
+ if (toType.id() == QMetaType::QString)
+ *static_cast<QString *>(to) = QString::fromUtf8(key);
+ else
+ *static_cast<QByteArray *>(to) = key;
+ }
return true;
}
#endif
+ if (toType.id() == QMetaType::QString || toType.id() == QMetaType::QByteArray)
+ return QMetaType::convert(QMetaType::fromType<qlonglong>(), &ll, toType, to);
return false;
}
@@ -1859,12 +2017,12 @@ static bool convertToEnum(QMetaType fromType, const void *from, QMetaType toType
#ifndef QT_NO_QOBJECT
if (fromTypeId == QMetaType::QString || fromTypeId == QMetaType::QByteArray) {
QMetaEnum en = metaEnumFromType(toType);
- if (!en.isValid())
- return false;
- QByteArray keys = (fromTypeId == QMetaType::QString)
- ? static_cast<const QString *>(from)->toUtf8()
- : *static_cast<const QByteArray *>(from);
- value = en.keysToValue(keys.constData(), &ok);
+ if (en.isValid()) {
+ QByteArray keys = (fromTypeId == QMetaType::QString)
+ ? static_cast<const QString *>(from)->toUtf8()
+ : *static_cast<const QByteArray *>(from);
+ value = en.keysToValue(keys.constData(), &ok);
+ }
}
#endif
if (!ok) {
@@ -1893,8 +2051,7 @@ static bool convertToEnum(QMetaType fromType, const void *from, QMetaType toType
*static_cast<qint64 *>(to) = value;
return true;
default:
- Q_UNREACHABLE();
- return false;
+ Q_UNREACHABLE_RETURN(false);
}
}
@@ -1942,13 +2099,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;
@@ -1974,7 +2130,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;
@@ -2100,6 +2255,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) {
@@ -2156,7 +2314,7 @@ static bool viewAsAssociativeIterable(QMetaType fromType, void *from, void *to)
return false;
}
-static bool convertQObject(QMetaType fromType, const void *from, QMetaType toType, void *to)
+static bool convertMetaObject(QMetaType fromType, const void *from, QMetaType toType, void *to)
{
// handle QObject conversion
if ((fromType.flags() & QMetaType::PointerToQObject) && (toType.flags() & QMetaType::PointerToQObject)) {
@@ -2169,13 +2327,20 @@ static bool convertQObject(QMetaType fromType, const void *from, QMetaType toTyp
// if fromObject is null, use static fromType to check if conversion works
*static_cast<void **>(to) = nullptr;
return fromType.metaObject()->inherits(toType.metaObject());
- } else {
- return false;
+ }
+ } 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)) {
+ toType.destruct(to);
+ toType.construct(to, from);
+ return true;
}
}
return false;
}
-#endif
+#endif // !QT_BOOTSTRAPPED
/*!
\fn bool QMetaType::convert(const void *from, int fromTypeId, void *to, int toTypeId)
@@ -2216,8 +2381,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);
@@ -2233,10 +2397,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;
@@ -2246,6 +2411,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);
@@ -2253,7 +2419,7 @@ bool QMetaType::convert(QMetaType fromType, const void *from, QMetaType toType,
if (toTypeId == qMetaTypeId<QAssociativeIterable>())
return convertToAssociativeIterable(fromType, from, to);
- return convertQObject(fromType, from, toType, to);
+ return convertMetaObject(fromType, from, toType, to);
#else
return false;
#endif
@@ -2272,8 +2438,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);
@@ -2284,7 +2449,7 @@ bool QMetaType::view(QMetaType fromType, void *from, QMetaType toType, void *to)
if (toTypeId == qMetaTypeId<QAssociativeIterable>())
return viewAsAssociativeIterable(fromType, from, to);
- return convertQObject(fromType, from, toType, to);
+ return convertMetaObject(fromType, from, toType, to);
#else
return false;
#endif
@@ -2315,8 +2480,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;
@@ -2421,7 +2585,7 @@ bool QMetaType::canConvert(QMetaType fromType, QMetaType toType)
return true;
}
const ConverterFunction * const f =
- customTypesConversionRegistry()->function(qMakePair(fromTypeId, toTypeId));
+ customTypesConversionRegistry()->function(std::make_pair(fromTypeId, toTypeId));
if (f)
return true;
@@ -2431,7 +2595,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;
@@ -2441,11 +2606,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)
@@ -2477,7 +2642,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
@@ -2490,11 +2655,11 @@ 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()});
}
/*!
- \fn bool QMetaType::hasRegisteredMutableViewFunction()
+ \fn template<typename From, typename To> bool QMetaType::hasRegisteredMutableViewFunction()
Returns \c true, if the meta type system has a registered mutable view on type From of type To.
\since 6.0
\overload
@@ -2507,7 +2672,7 @@ 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()});
}
/*!
@@ -2552,12 +2717,13 @@ static inline int qMetaTypeStaticType(const char *typeName, int length)
*/
static int qMetaTypeCustomType_unlocked(const char *typeName, int length)
{
- if (auto reg = customTypeRegistry()) {
+ if (customTypeRegistry.exists()) {
+ auto reg = &*customTypeRegistry;
#if QT_CONFIG(thread)
Q_ASSERT(!reg->lock.tryLockForWrite());
#endif
- if (auto ti = reg->aliases.value(QByteArray(typeName, length), nullptr)) {
- return ti->typeId;
+ if (auto ti = reg->aliases.value(QByteArray::fromRawData(typeName, length), nullptr)) {
+ return ti->typeId.loadRelaxed();
}
}
return QMetaType::UnknownType;
@@ -2584,6 +2750,20 @@ void QMetaType::registerNormalizedTypedef(const NS(QByteArray) & normalizedTypeN
}
}
+
+static const QtPrivate::QMetaTypeInterface *interfaceForTypeNoWarning(int typeId)
+{
+ const QtPrivate::QMetaTypeInterface *iface = nullptr;
+ if (typeId >= QMetaType::User) {
+ if (customTypeRegistry.exists())
+ iface = customTypeRegistry->getCustomType(typeId);
+ } else {
+ if (auto moduleHelper = qModuleHelperForType(typeId))
+ iface = moduleHelper->interfaceForType(typeId);
+ }
+ return iface;
+}
+
/*!
Returns \c true if the datatype with ID \a type is registered;
otherwise returns \c false.
@@ -2592,7 +2772,7 @@ void QMetaType::registerNormalizedTypedef(const NS(QByteArray) & normalizedTypeN
*/
bool QMetaType::isRegistered(int type)
{
- return QMetaType(type).isRegistered();
+ return interfaceForTypeNoWarning(type) != nullptr;
}
template <bool tryNormalizedType>
@@ -2630,7 +2810,7 @@ static inline int qMetaTypeTypeImpl(const char *typeName, int length)
*/
/*!
- \a internal
+ \internal
Similar to QMetaType::type(); the only difference is that this function
doesn't attempt to normalize the type name (i.e., the lookup will fail
@@ -2660,9 +2840,6 @@ Q_CORE_EXPORT int qMetaTypeTypeInternal(const char *typeName)
Returns \c true if the object is saved successfully; otherwise
returns \c false.
- The type must have been registered with Q_DECLARE_METATYPE()
- beforehand.
-
Normally, you should not need to call this function directly.
Instead, use QVariant's \c operator<<(), which relies on save()
to stream custom types.
@@ -2701,9 +2878,6 @@ bool QMetaType::save(QDataStream &stream, const void *data) const
Returns \c true if the object is loaded successfully; otherwise
returns \c false.
- The type must have been registered with Q_DECLARE_METATYPE()
- beforehand.
-
Normally, you should not need to call this function directly.
Instead, use QVariant's \c operator>>(), which relies on load()
to stream custom types.
@@ -2749,6 +2923,59 @@ bool QMetaType::hasRegisteredDataStreamOperators() const
}
/*!
+ \since 6.6
+
+ If this metatype represents an enumeration, this method returns a
+ metatype of a numeric class of the same signedness and size as the
+ enums underlying type.
+ If it represents a QFlags type, it returns QMetaType::Int.
+ In all other cases an invalid QMetaType is returned.
+ */
+QMetaType QMetaType::underlyingType() const
+{
+ if (!d_ptr || !(flags() & IsEnumeration))
+ return {};
+ /* QFlags has enumeration set so that's handled here (qint32
+ case), as QFlags uses int as the underlying type
+ Note that we do some approximation here, as we cannot
+ differentiate between different underlying types of the
+ same size and signedness (consider char <-> (un)signed char,
+ int <-> long <-> long long).
+
+ ### TODO PENDING: QTBUG-111926 - QFlags supporting >32 bit int
+ */
+ if (flags() & IsUnsignedEnumeration) {
+ switch (sizeOf()) {
+ case 1:
+ return QMetaType::fromType<quint8>();
+ case 2:
+ return QMetaType::fromType<quint16>();
+ case 4:
+ return QMetaType::fromType<quint32>();
+ case 8:
+ return QMetaType::fromType<quint64>();
+ default:
+ break;
+ }
+ } else {
+ switch (sizeOf()) {
+ case 1:
+ return QMetaType::fromType<qint8>();
+ case 2:
+ return QMetaType::fromType<qint16>();
+ case 4:
+ return QMetaType::fromType<qint32>();
+ case 8:
+ return QMetaType::fromType<qint64>();
+ default:
+ break;
+ }
+ }
+ // int128 can be handled above once we have qint128
+ return QMetaType();
+}
+
+/*!
\fn bool QMetaType::load(QDataStream &stream, int type, void *data)
\overload
\deprecated
@@ -2860,8 +3087,9 @@ QMetaType QMetaType::fromName(QByteArrayView typeName)
*/
/*!
- \fn int qRegisterMetaType(const char *typeName)
+ \fn template <typename T> int qRegisterMetaType(const char *typeName)
\relates QMetaType
+ \obsolete
\threadsafe
Registers the type name \a typeName for the type \c{T}. Returns
@@ -2893,13 +3121,12 @@ QMetaType QMetaType::fromName(QByteArrayView typeName)
*/
/*!
- \fn int qRegisterMetaType()
+ \fn template <typename T> int qRegisterMetaType()
\relates QMetaType
\threadsafe
\since 4.2
- Call this function to register the type \c T. \c T must be declared with
- Q_DECLARE_METATYPE(). Returns the meta type Id.
+ Call this function to register the type \c T. Returns the meta type Id.
Example:
@@ -2910,24 +3137,47 @@ QMetaType QMetaType::fromName(QByteArrayView typeName)
pointed to type is fully defined. Use Q_DECLARE_OPAQUE_POINTER() to be able
to register pointers to forward declared types.
- After a type has been registered, you can create and destroy
- objects of that type dynamically at run-time.
+ To use the type \c T in QMetaType, QVariant, or with the
+ QObject::property() API, registration is not necessary.
- To use the type \c T in QVariant, using Q_DECLARE_METATYPE() is
- sufficient. To use the type \c T in queued signal and slot connections,
- \c{qRegisterMetaType<T>()} must be called before the first connection
- is established.
+ To use the type \c T in queued signal and slot connections,
+ \c{qRegisterMetaType<T>()} must be called before the first connection is
+ established. That is typically done in the constructor of the class that
+ uses \c T, or in the \c{main()} function.
- Also, to use type \c T with the QObject::property() API,
- \c{qRegisterMetaType<T>()} must be called before it is used, typically
- in the constructor of the class that uses \c T, or in the \c{main()}
- function.
+ After a type has been registered, it can be found by its name using
+ QMetaType::fromName().
\sa Q_DECLARE_METATYPE()
*/
/*!
- \fn int qMetaTypeId()
+ \fn int qRegisterMetaType(QMetaType meta)
+ \relates QMetaType
+ \threadsafe
+ \since 6.5
+
+ Registers the meta type \a meta and returns its type Id.
+
+ This function requires that \c{T} is a fully defined type at the point
+ where the function is called. For pointer types, it also requires that the
+ pointed to type is fully defined. Use Q_DECLARE_OPAQUE_POINTER() to be able
+ to register pointers to forward declared types.
+
+ To use the type \c T in QMetaType, QVariant, or with the
+ QObject::property() API, registration is not necessary.
+
+ To use the type \c T in queued signal and slot connections,
+ \c{qRegisterMetaType<T>()} must be called before the first connection is
+ established. That is typically done in the constructor of the class that
+ uses \c T, or in the \c{main()} function.
+
+ After a type has been registered, it can be found by its name using
+ QMetaType::fromName().
+ */
+
+/*!
+ \fn template <typename T> int qMetaTypeId()
\relates QMetaType
\threadsafe
\since 4.1
@@ -2950,15 +3200,7 @@ QMetaType QMetaType::fromName(QByteArrayView typeName)
static const QtPrivate::QMetaTypeInterface *interfaceForType(int typeId)
{
- const QtPrivate::QMetaTypeInterface *iface = nullptr;
- if (typeId >= QMetaType::User) {
- if (auto reg = customTypeRegistry())
- iface = reg->getCustomType(typeId);
- } else {
- if (auto moduleHelper = qModuleHelperForType(typeId))
- iface = moduleHelper->interfaceForType(typeId);
- }
-
+ const QtPrivate::QMetaTypeInterface *iface = interfaceForTypeNoWarning(typeId);
if (!iface && typeId != QMetaType::UnknownType)
qWarning("Trying to construct an instance of an invalid type, type id: %i", typeId);
@@ -2966,6 +3208,13 @@ static const QtPrivate::QMetaTypeInterface *interfaceForType(int typeId)
}
/*!
+ \fn QMetaType::QMetaType()
+ \since 6.0
+
+ Constructs a default, invalid, QMetaType object.
+*/
+
+/*!
\fn QMetaType::QMetaType(int typeId)
\since 5.0
@@ -2973,25 +3222,28 @@ static const QtPrivate::QMetaTypeInterface *interfaceForType(int typeId)
*/
QMetaType::QMetaType(int typeId) : QMetaType(interfaceForType(typeId)) {}
-namespace QtPrivate {
-#ifndef QT_BOOTSTRAPPED
-#if defined(Q_CC_MSVC) && defined(QT_BUILD_CORE_LIB)
-#define QT_METATYPE_TEMPLATE_EXPORT Q_CORE_EXPORT
-#else
-#define QT_METATYPE_TEMPLATE_EXPORT
-#endif
+/*! \fn size_t qHash(QMetaType type, size_t seed = 0)
+ \relates QMetaType
+ \since 6.4
+
+ Returns the hash value for the \a type, using \a seed to seed the calculation.
+*/
+
+namespace QtPrivate {
+#if !defined(QT_BOOTSTRAPPED) && !defined(Q_CC_MSVC) && !defined(Q_OS_INTEGRITY)
// Explicit instantiation definition
-#define QT_METATYPE_DECLARE_TEMPLATE_ITER(TypeName, Id, Name) \
- template class QT_METATYPE_TEMPLATE_EXPORT QMetaTypeForType<Name>;
-QT_FOR_EACH_STATIC_PRIMITIVE_TYPE(QT_METATYPE_DECLARE_TEMPLATE_ITER)
+#define QT_METATYPE_DECLARE_TEMPLATE_ITER(TypeName, Id, Name) \
+ template class QMetaTypeForType<Name>; \
+ template struct QMetaTypeInterfaceWrapper<Name>;
+QT_FOR_EACH_STATIC_PRIMITIVE_NON_VOID_TYPE(QT_METATYPE_DECLARE_TEMPLATE_ITER)
QT_FOR_EACH_STATIC_PRIMITIVE_POINTER(QT_METATYPE_DECLARE_TEMPLATE_ITER)
QT_FOR_EACH_STATIC_CORE_CLASS(QT_METATYPE_DECLARE_TEMPLATE_ITER)
QT_FOR_EACH_STATIC_CORE_POINTER(QT_METATYPE_DECLARE_TEMPLATE_ITER)
QT_FOR_EACH_STATIC_CORE_TEMPLATE(QT_METATYPE_DECLARE_TEMPLATE_ITER)
+
#undef QT_METATYPE_DECLARE_TEMPLATE_ITER
-#undef QT_METATYPE_TEMPLATE_EXPORT
#endif
}
diff --git a/src/corelib/kernel/qmetatype.h b/src/corelib/kernel/qmetatype.h
index 346ae3667a..e3ef1474da 100644
--- a/src/corelib/kernel/qmetatype.h
+++ b/src/corelib/kernel/qmetatype.h
@@ -1,43 +1,7 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Copyright (C) 2018 Intel Corporation.
-** Copyright (C) 2014 Olivier Goffart <ogoffart@woboq.com>
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtCore module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// Copyright (C) 2018 Intel Corporation.
+// Copyright (C) 2014 Olivier Goffart <ogoffart@woboq.com>
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QMETATYPE_H
#define QMETATYPE_H
@@ -46,14 +10,14 @@
#include <QtCore/qatomic.h>
#include <QtCore/qbytearray.h>
#include <QtCore/qcompare.h>
-#include <QtCore/qvarlengtharray.h>
-#include <QtCore/qrefcount.h>
-#include <QtCore/qscopeguard.h>
#include <QtCore/qdatastream.h>
+#include <QtCore/qfloat16.h>
+#include <QtCore/qhashfunctions.h>
#include <QtCore/qiterable.h>
#ifndef QT_NO_QOBJECT
#include <QtCore/qobjectdefs.h>
#endif
+#include <QtCore/qscopeguard.h>
#include <array>
#include <new>
@@ -61,6 +25,8 @@
#include <list>
#include <map>
#include <functional>
+#include <optional>
+#include <QtCore/q20type_traits.h>
#ifdef Bool
#error qmetatype.h must be included before any header file that defines Bool
@@ -78,8 +44,7 @@ template <typename T>
inline constexpr int qMetaTypeId();
// F is a tuple: (QMetaType::TypeName, QMetaType::TypeNameID, RealType)
-#define QT_FOR_EACH_STATIC_PRIMITIVE_TYPE(F)\
- F(Void, 43, void) \
+#define QT_FOR_EACH_STATIC_PRIMITIVE_NON_VOID_TYPE(F)\
F(Bool, 1, bool) \
F(Int, 2, int) \
F(UInt, 3, uint) \
@@ -99,7 +64,11 @@ inline constexpr int qMetaTypeId();
F(Nullptr, 51, std::nullptr_t) \
F(QCborSimpleType, 52, QCborSimpleType) \
-#define QT_FOR_EACH_STATIC_PRIMITIVE_POINTER(F)\
+#define QT_FOR_EACH_STATIC_PRIMITIVE_TYPE(F) \
+ QT_FOR_EACH_STATIC_PRIMITIVE_NON_VOID_TYPE(F) \
+ F(Void, 43, void) \
+
+#define QT_FOR_EACH_STATIC_PRIMITIVE_POINTER(F) \
F(VoidStar, 31, void*) \
#if QT_CONFIG(easingcurve)
@@ -123,6 +92,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) \
@@ -144,7 +119,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) \
@@ -153,18 +128,26 @@ inline constexpr int qMetaTypeId();
F(QCborValue, 53, QCborValue) \
F(QCborArray, 54, QCborArray) \
F(QCborMap, 55, QCborMap) \
+ F(Float16, 63, qfloat16) \
QT_FOR_EACH_STATIC_ITEMMODEL_CLASS(F)
#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)\
@@ -218,12 +201,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)\
@@ -259,14 +250,35 @@ struct QMetaObject;
namespace QtPrivate
{
+class QMetaTypeInterface;
+
+// MSVC is the only supported compiler that includes the type of a variable in
+// its mangled form, so it's not binary-compatible to drop the const in
+// QMetaTypeInterfaceWrapper::metaType for it, which means we must keep the
+// mutable field until Qt 7.
+#if QT_VERSION >= QT_VERSION_CHECK(7, 0, 0) || defined(QT_BOOTSTRAPPED) || !defined(Q_CC_MSVC)
+# define QMTI_MUTABLE
+using NonConstMetaTypeInterface = QMetaTypeInterface;
+#else
+# define QMTI_MUTABLE mutable
+using NonConstMetaTypeInterface = const QMetaTypeInterface;
+#endif
+
class QMetaTypeInterface
{
public:
- ushort revision; // 0 in Qt 6.0. Can increase if new field are added
+
+ /* Revision: Can increase if new field are added, or if semantics changes
+ 0: Initial Revision
+ 1: the meaning of the NeedsDestruction flag changed
+ */
+ static inline constexpr ushort CurrentRevision = 1;
+
+ ushort revision;
ushort alignment;
uint size;
uint flags;
- mutable QBasicAtomicInt typeId;
+ QMTI_MUTABLE QBasicAtomicInt typeId;
using MetaObjectFn = const QMetaObject *(*)(const QMetaTypeInterface *);
MetaObjectFn metaObjectFn;
@@ -295,6 +307,7 @@ public:
using LegacyRegisterOp = void (*)();
LegacyRegisterOp legacyRegisterOp;
};
+#undef QMTI_MUTABLE
/*!
This template is used for implicit conversion from type From to type To.
@@ -327,14 +340,14 @@ To convertImplicit(const From& from)
class Q_CORE_EXPORT QMetaType {
public:
-#ifndef Q_CLANG_QDOC
+#ifndef Q_QDOC
// The code that actually gets compiled.
enum Type {
// these are merged with QVariant
QT_FOR_EACH_STATIC_TYPE(QT_DEFINE_METATYPE_ID)
FirstCoreType = Bool,
- LastCoreType = QVariantPair,
+ LastCoreType = Float16,
FirstGuiType = QFont,
LastGuiType = QColorSpace,
FirstWidgetsType = QSizePolicy,
@@ -363,9 +376,10 @@ public:
QByteArrayList = 49, QObjectStar = 39, SChar = 40,
Void = 43,
Nullptr = 51,
- QVariantMap = 8, QVariantList = 9, QVariantHash = 28,
+ QVariantMap = 8, QVariantList = 9, QVariantHash = 28, QVariantPair = 58,
QCborSimpleType = 52, QCborValue = 53, QCborArray = 54, QCborMap = 55,
Char16 = 56, Char32 = 57,
+ Int128 = 59, UInt128 = 60, Float128 = 61, BFloat16 = 62, Float16 = 63,
// Gui types
QFont = 0x1000, QPixmap = 0x1001, QBrush = 0x1002, QColor = 0x1003, QPalette = 0x1004,
@@ -376,8 +390,8 @@ public:
// Widget types
QSizePolicy = 0x2000,
- LastCoreType = Char32,
- LastGuiType = QColorSpace,
+
+ // Start-point for client-code types:
User = 65536
};
#endif
@@ -400,6 +414,9 @@ public:
IsPointer = 0x800,
IsQmlList =0x1000, // used in the QML engine to recognize QQmlListProperty<T> and list<T>
IsConst = 0x2000,
+ // since 6.5:
+ NeedsCopyConstruction = 0x4000,
+ NeedsMoveConstruction = 0x8000,
};
Q_DECLARE_FLAGS(TypeFlags, TypeFlag)
@@ -417,7 +434,7 @@ public:
{ return QMetaType(type).name(); }
QT_DEPRECATED_VERSION_6_0
static int sizeOf(int type)
- { return QMetaType(type).sizeOf(); }
+ { return int(QMetaType(type).sizeOf()); }
QT_DEPRECATED_VERSION_6_0
static TypeFlags typeFlags(int type)
{ return QMetaType(type).flags(); }
@@ -445,19 +462,20 @@ public:
bool isValid() const;
bool isRegistered() const;
-#if defined(QT_QMETATYPE_BC_COMPAT) || defined(Q_QDOC)
+ void registerType() const
+ {
+ // "register" is a reserved keyword
+ registerHelper();
+ }
+#if QT_CORE_REMOVED_SINCE(6, 1) || defined(Q_QDOC)
int id() const;
#else
// ### Qt 7: Remove traces of out of line version
// unused int parameter is used to avoid ODR violation
int id(int = 0) const
{
- if (d_ptr) {
- if (int id = d_ptr->typeId.loadRelaxed())
- return id;
- return idHelper();
- }
- return 0;
+ // keep in sync with the version in removed_api.cpp
+ return registerHelper();
}
#endif
constexpr qsizetype sizeOf() const;
@@ -473,6 +491,10 @@ public:
QPartialOrdering compare(const void *lhs, const void *rhs) const;
bool equals(const void *lhs, const void *rhs) const;
+ bool isDefaultConstructible() const noexcept { return d_ptr && isDefaultConstructible(d_ptr); }
+ bool isCopyConstructible() const noexcept { return d_ptr && isCopyConstructible(d_ptr); }
+ bool isMoveConstructible() const noexcept { return d_ptr && isMoveConstructible(d_ptr); }
+ bool isDestructible() const noexcept { return d_ptr && isDestructible(d_ptr); }
bool isEqualityComparable() const;
bool isOrdered() const;
@@ -491,6 +513,8 @@ public:
#endif
#endif
+ QMetaType underlyingType() const;
+
template<typename T>
constexpr static QMetaType fromType();
static QMetaType fromName(QByteArrayView name);
@@ -508,9 +532,10 @@ public:
}
friend bool operator!=(QMetaType a, QMetaType b) { return !(a == b); }
-public:
-
#ifndef QT_NO_DEBUG_STREAM
+private:
+ friend Q_CORE_EXPORT QDebug operator<<(QDebug d, QMetaType m);
+public:
bool debugStream(QDebug& dbg, const void *rhs);
bool hasRegisteredDebugStreamOperator() const;
@@ -610,7 +635,14 @@ public:
auto converter = [function = std::move(function)](const void *from, void *to) -> bool {
const From *f = static_cast<const From *>(from);
To *t = static_cast<To *>(to);
- *t = function(*f);
+ auto &&r = function(*f);
+ if constexpr (std::is_same_v<q20::remove_cvref_t<decltype(r)>, std::optional<To>>) {
+ if (!r)
+ return false;
+ *t = *std::forward<decltype(r)>(r);
+ } else {
+ *t = std::forward<decltype(r)>(r);
+ }
return true;
};
return registerConverterImpl<From, To>(std::move(converter), fromType, toType);
@@ -719,7 +751,7 @@ public:
static bool hasRegisteredMutableViewFunction(QMetaType fromType, QMetaType toType);
-#ifndef Q_CLANG_QDOC
+#ifndef Q_QDOC
template<typename, bool> friend struct QtPrivate::SequentialValueTypeIsMetaType;
template<typename, bool> friend struct QtPrivate::AssociativeValueTypeIsMetaType;
template<typename, bool> friend struct QtPrivate::IsMetaTypePair;
@@ -732,10 +764,35 @@ public:
static void unregisterMutableViewFunction(QMetaType from, QMetaType to);
static void unregisterMetaType(QMetaType type);
+
+#if QT_VERSION < QT_VERSION_CHECK(7, 0, 0)
const QtPrivate::QMetaTypeInterface *iface() { return d_ptr; }
+#endif
+ const QtPrivate::QMetaTypeInterface *iface() const { return d_ptr; }
private:
+ static bool isDefaultConstructible(const QtPrivate::QMetaTypeInterface *) noexcept Q_DECL_PURE_FUNCTION;
+ static bool isCopyConstructible(const QtPrivate::QMetaTypeInterface *) noexcept Q_DECL_PURE_FUNCTION;
+ static bool isMoveConstructible(const QtPrivate::QMetaTypeInterface *) noexcept Q_DECL_PURE_FUNCTION;
+ static bool isDestructible(const QtPrivate::QMetaTypeInterface *) noexcept Q_DECL_PURE_FUNCTION;
+
+#if QT_CORE_REMOVED_SINCE(6, 5)
int idHelper() const;
+#endif
+ static int registerHelper(const QtPrivate::QMetaTypeInterface *iface);
+ int registerHelper() const
+ {
+ // keep in sync with the QMetaType::id() version in removed_api.cpp
+ if (d_ptr) {
+ if (int id = d_ptr->typeId.loadRelaxed())
+ return id;
+ return registerHelper(d_ptr);
+ }
+ return 0;
+ }
+
+ friend int qRegisterMetaType(QMetaType meta);
+
friend class QVariant;
const QtPrivate::QMetaTypeInterface *d_ptr = nullptr;
};
@@ -816,6 +873,34 @@ QT_FOR_EACH_AUTOMATIC_TEMPLATE_SMART_POINTER(QT_FORWARD_DECLARE_SHARED_POINTER_T
namespace QtPrivate
{
+ namespace detail {
+ template<typename T, typename ODR_VIOLATION_PREVENTER>
+ struct is_complete_helper
+ {
+ template<typename U>
+ static auto check(U *) -> std::integral_constant<bool, sizeof(U) != 0>;
+ static auto check(...) -> std::false_type;
+ using type = decltype(check(static_cast<T *>(nullptr)));
+ };
+ } // namespace detail
+
+ template <typename T, typename ODR_VIOLATION_PREVENTER>
+ struct is_complete : detail::is_complete_helper<std::remove_reference_t<T>, ODR_VIOLATION_PREVENTER>::type {};
+
+ template <typename T> struct MetatypeDecay { using type = T; };
+ template <typename T> struct MetatypeDecay<const T> { using type = T; };
+ template <typename T> struct MetatypeDecay<const T &> { using type = T; };
+
+ template <typename T> struct IsPointerDeclaredOpaque :
+ std::disjunction<std::is_member_pointer<T>,
+ std::is_function<std::remove_pointer_t<T>>>
+ {};
+ template <> struct IsPointerDeclaredOpaque<void *> : std::true_type {};
+ template <> struct IsPointerDeclaredOpaque<const void *> : std::true_type {};
+
+ // Note: this does not check that T = U* isn't pointing to a
+ // forward-declared type. You may want to combine with
+ // checkTypeIsSuitableForMetaType().
template<typename T>
struct IsPointerToTypeDerivedFromQObject
{
@@ -850,7 +935,6 @@ namespace QtPrivate
static yes_type checkType(const QObject* );
#endif
static no_type checkType(...);
- static_assert(sizeof(T), "Type argument of Q_PROPERTY or Q_DECLARE_METATYPE(T*) must be fully defined");
enum { Value = sizeof(checkType(static_cast<T*>(nullptr))) == sizeof(yes_type) };
};
@@ -869,6 +953,9 @@ namespace QtPrivate
};
};
+ template <typename T>
+ using IsRealGadget = std::bool_constant<IsGadgetHelper<T>::IsRealGadget>;
+
template<typename T, typename Enable = void>
struct IsPointerToGadgetHelper { enum { IsRealGadget = false, IsGadgetOrDerivedFrom = false }; };
@@ -907,13 +994,6 @@ namespace QtPrivate
static constexpr MetaObjectFn metaObjectFunction = nullptr;
};
#ifndef QT_NO_QOBJECT
- template<>
- struct MetaObjectForType<void>
- {
- static constexpr const QMetaObject *value() { return nullptr; }
- using MetaObjectFn = const QMetaObject *(*)(const QMetaTypeInterface *);
- static constexpr MetaObjectFn metaObjectFunction = nullptr;
- };
template<typename T>
struct MetaObjectForType<T*, typename std::enable_if<IsPointerToTypeDerivedFromQObject<T*>::Value>::type>
{
@@ -921,7 +1001,12 @@ namespace QtPrivate
static constexpr const QMetaObject *metaObjectFunction(const QMetaTypeInterface *) { return &T::staticMetaObject; }
};
template<typename T>
- struct MetaObjectForType<T, typename std::enable_if<IsGadgetHelper<T>::IsGadgetOrDerivedFrom>::type>
+ struct MetaObjectForType<T, std::enable_if_t<
+ std::disjunction_v<
+ std::bool_constant<IsGadgetHelper<T>::IsGadgetOrDerivedFrom>,
+ std::is_base_of<QObject, T>
+ >
+ >>
{
static constexpr const QMetaObject *value() { return &T::staticMetaObject; }
static constexpr const QMetaObject *metaObjectFunction(const QMetaTypeInterface *) { return &T::staticMetaObject; }
@@ -1113,12 +1198,28 @@ namespace QtPrivate
};
#endif
+ template <typename X> static constexpr bool checkTypeIsSuitableForMetaType()
+ {
+ using T = typename MetatypeDecay<X>::type;
+ static_assert(is_complete<T, void>::value || std::is_void_v<T>,
+ "Meta Types must be fully defined");
+ static_assert(!std::is_reference_v<T>,
+ "Meta Types cannot be non-const references or rvalue references.");
+ if constexpr (std::is_pointer_v<T> && !IsPointerDeclaredOpaque<T>::value) {
+ using Pointed = std::remove_pointer_t<T>;
+ static_assert(is_complete<Pointed, void>::value,
+ "Pointer Meta Types must either point to fully-defined types "
+ "or be declared with Q_DECLARE_OPAQUE_POINTER(T *)");
+ }
+ return true;
+ }
+
Q_CORE_EXPORT bool isBuiltinType(const QByteArray &type);
} // namespace QtPrivate
template <typename T, int =
QtPrivate::IsPointerToTypeDerivedFromQObject<T>::Value ? QMetaType::PointerToQObject :
- QtPrivate::IsGadgetHelper<T>::IsRealGadget ? QMetaType::IsGadget :
+ QtPrivate::IsRealGadget<T>::value ? QMetaType::IsGadget :
QtPrivate::IsPointerToGadgetHelper<T>::IsRealGadget ? QMetaType::PointerToGadget :
QtPrivate::IsQEnumHelper<T>::Value ? QMetaType::IsEnumeration : 0>
struct QMetaTypeIdQObject
@@ -1145,7 +1246,12 @@ template <typename T>
struct QMetaTypeId2<const T&> : QMetaTypeId2<T> {};
template <typename T>
-struct QMetaTypeId2<T&> { enum {Defined = false }; };
+struct QMetaTypeId2<T&>
+{
+ using NameAsArrayType = void;
+ enum { Defined = false, IsBuiltIn = false };
+ static inline constexpr int qt_metatype_id() { return 0; }
+};
namespace QtPrivate {
template <typename T, bool Defined = QMetaTypeId2<T>::Defined>
@@ -1174,8 +1280,10 @@ namespace QtPrivate {
struct QMetaTypeTypeFlags
{
enum { Flags = (QTypeInfo<T>::isRelocatable ? QMetaType::RelocatableType : 0)
- | (QTypeInfo<T>::isComplex ? QMetaType::NeedsConstruction : 0)
- | (QTypeInfo<T>::isComplex ? QMetaType::NeedsDestruction : 0)
+ | ((!std::is_default_constructible_v<T> || !QTypeInfo<T>::isValueInitializationBitwiseZero) ? QMetaType::NeedsConstruction : 0)
+ | (!std::is_trivially_destructible_v<T> ? QMetaType::NeedsDestruction : 0)
+ | (!std::is_trivially_copy_constructible_v<T> ? QMetaType::NeedsCopyConstruction : 0)
+ | (!std::is_trivially_move_constructible_v<T> ? QMetaType::NeedsMoveConstruction : 0)
| (IsPointerToTypeDerivedFromQObject<T>::Value ? QMetaType::PointerToQObject : 0)
| (IsSharedPointerToTypeDerivedFromQObject<T>::Value ? QMetaType::SharedPointerToQObject : 0)
| (IsWeakPointerToTypeDerivedFromQObject<T>::Value ? QMetaType::WeakPointerToQObject : 0)
@@ -1183,7 +1291,7 @@ namespace QtPrivate {
| (IsEnumOrFlags<T>::value ? QMetaType::IsEnumeration : 0)
| (IsGadgetHelper<T>::IsGadgetOrDerivedFrom ? QMetaType::IsGadget : 0)
| (IsPointerToGadgetHelper<T>::IsGadgetOrDerivedFrom ? QMetaType::PointerToGadget : 0)
- | (QTypeInfo<T>::isPointer ? QMetaType::IsPointer : 0)
+ | (std::is_pointer_v<T> ? QMetaType::IsPointer : 0)
| (IsUnsignedEnum<T> ? QMetaType::IsUnsignedEnumeration : 0)
| (IsQmlListType<T> ? QMetaType::IsQmlList : 0)
| (std::is_const_v<std::remove_pointer_t<T>> ? QMetaType::IsConst : 0)
@@ -1221,7 +1329,7 @@ namespace QtPrivate {
}
template <typename T>
-int qRegisterNormalizedMetaType(const QT_PREPEND_NAMESPACE(QByteArray) &normalizedTypeName)
+int qRegisterNormalizedMetaTypeImplementation(const QT_PREPEND_NAMESPACE(QByteArray) &normalizedTypeName)
{
#ifndef QT_NO_QOBJECT
Q_ASSERT_X(normalizedTypeName == QMetaObject::normalizedType(normalizedTypeName.constData()),
@@ -1249,6 +1357,39 @@ int qRegisterNormalizedMetaType(const QT_PREPEND_NAMESPACE(QByteArray) &normaliz
return id;
}
+// This primary template calls the -Implementation, like all other specialisations should.
+// But the split allows to
+// - in a header:
+// - define a specialization of this template calling an out-of-line function
+// (QT_DECL_METATYPE_EXTERN{,_TAGGED})
+// - in the .cpp file:
+// - define the out-of-line wrapper to call the -Implementation
+// (QT_IMPL_METATYPE_EXTERN{,_TAGGED})
+// The _TAGGED variants let you choose a tag (must be a C identifier) to disambiguate
+// the out-of-line function; the non-_TAGGED variants use the passed class name as tag.
+template <typename T>
+int qRegisterNormalizedMetaType(const QT_PREPEND_NAMESPACE(QByteArray) &normalizedTypeName)
+{
+ return qRegisterNormalizedMetaTypeImplementation<T>(normalizedTypeName);
+}
+
+#define QT_DECL_METATYPE_EXTERN_TAGGED(TYPE, TAG, EXPORT) \
+ QT_BEGIN_NAMESPACE \
+ EXPORT int qRegisterNormalizedMetaType_ ## TAG (const QByteArray &); \
+ template <> inline int qRegisterNormalizedMetaType< TYPE >(const QByteArray &name) \
+ { return qRegisterNormalizedMetaType_ ## TAG (name); } \
+ QT_END_NAMESPACE \
+ Q_DECLARE_METATYPE(TYPE) \
+ /* end */
+#define QT_IMPL_METATYPE_EXTERN_TAGGED(TYPE, TAG) \
+ int qRegisterNormalizedMetaType_ ## TAG (const QByteArray &name) \
+ { return qRegisterNormalizedMetaTypeImplementation< TYPE >(name); } \
+ /* end */
+#define QT_DECL_METATYPE_EXTERN(TYPE, EXPORT) \
+ QT_DECL_METATYPE_EXTERN_TAGGED(TYPE, TYPE, EXPORT)
+#define QT_IMPL_METATYPE_EXTERN(TYPE) \
+ QT_IMPL_METATYPE_EXTERN_TAGGED(TYPE, TYPE)
+
template <typename T>
int qRegisterMetaType(const char *typeName)
{
@@ -1264,6 +1405,9 @@ template <typename T>
inline constexpr int qMetaTypeId()
{
if constexpr (bool(QMetaTypeId2<T>::IsBuiltIn)) {
+ // this has the same result as the below code, but avoids asking the
+ // compiler to load a global variable whose value we know at compile
+ // time
return QMetaTypeId2<T>::MetaType;
} else {
return QMetaType::fromType<T>().id();
@@ -1277,6 +1421,11 @@ inline constexpr int qRegisterMetaType()
return id;
}
+inline int qRegisterMetaType(QMetaType meta)
+{
+ return meta.registerHelper();
+}
+
#ifndef QT_NO_QOBJECT
template <typename T>
struct QMetaTypeIdQObject<T*, QMetaType::PointerToQObject>
@@ -1287,12 +1436,12 @@ struct QMetaTypeIdQObject<T*, QMetaType::PointerToQObject>
static int qt_metatype_id()
{
- static QBasicAtomicInt metatype_id = Q_BASIC_ATOMIC_INITIALIZER(0);
+ Q_CONSTINIT static QBasicAtomicInt metatype_id = Q_BASIC_ATOMIC_INITIALIZER(0);
if (const int id = metatype_id.loadAcquire())
return id;
const char *const cName = T::staticMetaObject.className();
QByteArray typeName;
- typeName.reserve(int(strlen(cName)) + 1);
+ typeName.reserve(strlen(cName) + 1);
typeName.append(cName).append('*');
const int newId = qRegisterNormalizedMetaType<T *>(typeName);
metatype_id.storeRelease(newId);
@@ -1309,7 +1458,7 @@ struct QMetaTypeIdQObject<T, QMetaType::IsGadget>
static int qt_metatype_id()
{
- static QBasicAtomicInt metatype_id = Q_BASIC_ATOMIC_INITIALIZER(0);
+ Q_CONSTINIT static QBasicAtomicInt metatype_id = Q_BASIC_ATOMIC_INITIALIZER(0);
if (const int id = metatype_id.loadAcquire())
return id;
const char *const cName = T::staticMetaObject.className();
@@ -1328,12 +1477,12 @@ struct QMetaTypeIdQObject<T*, QMetaType::PointerToGadget>
static int qt_metatype_id()
{
- static QBasicAtomicInt metatype_id = Q_BASIC_ATOMIC_INITIALIZER(0);
+ Q_CONSTINIT static QBasicAtomicInt metatype_id = Q_BASIC_ATOMIC_INITIALIZER(0);
if (const int id = metatype_id.loadAcquire())
return id;
const char *const cName = T::staticMetaObject.className();
QByteArray typeName;
- typeName.reserve(int(strlen(cName)) + 1);
+ typeName.reserve(strlen(cName) + 1);
typeName.append(cName).append('*');
const int newId = qRegisterNormalizedMetaType<T *>(typeName);
metatype_id.storeRelease(newId);
@@ -1350,13 +1499,13 @@ struct QMetaTypeIdQObject<T, QMetaType::IsEnumeration>
static int qt_metatype_id()
{
- static QBasicAtomicInt metatype_id = Q_BASIC_ATOMIC_INITIALIZER(0);
+ Q_CONSTINIT static QBasicAtomicInt metatype_id = Q_BASIC_ATOMIC_INITIALIZER(0);
if (const int id = metatype_id.loadAcquire())
return id;
const char *eName = qt_getEnumName(T());
const char *cName = qt_getEnumMetaObject(T())->className();
QByteArray typeName;
- typeName.reserve(int(strlen(cName) + 2 + strlen(eName)));
+ typeName.reserve(strlen(cName) + 2 + strlen(eName));
typeName.append(cName).append("::").append(eName);
const int newId = qRegisterNormalizedMetaType<T>(typeName);
metatype_id.storeRelease(newId);
@@ -1367,11 +1516,8 @@ struct QMetaTypeIdQObject<T, QMetaType::IsEnumeration>
#define Q_DECLARE_OPAQUE_POINTER(POINTER) \
QT_BEGIN_NAMESPACE namespace QtPrivate { \
- template <> \
- struct IsPointerToTypeDerivedFromQObject<POINTER > \
- { \
- enum { Value = false }; \
- }; \
+ template <> struct IsPointerDeclaredOpaque<POINTER> \
+ : std::true_type {}; \
} QT_END_NAMESPACE \
/**/
@@ -1383,9 +1529,10 @@ struct QMetaTypeIdQObject<T, QMetaType::IsEnumeration>
struct QMetaTypeId< TYPE > \
{ \
enum { Defined = 1 }; \
+ static_assert(QtPrivate::checkTypeIsSuitableForMetaType<TYPE>()); \
static int qt_metatype_id() \
{ \
- static QBasicAtomicInt metatype_id = Q_BASIC_ATOMIC_INITIALIZER(0); \
+ Q_CONSTINIT static QBasicAtomicInt metatype_id = Q_BASIC_ATOMIC_INITIALIZER(0); \
if (const int id = metatype_id.loadAcquire()) \
return id; \
constexpr auto arr = QtPrivate::typenameHelper<TYPE>(); \
@@ -1433,14 +1580,14 @@ struct QMetaTypeId< SINGLE_ARG_TEMPLATE<T> > \
}; \
static int qt_metatype_id() \
{ \
- static QBasicAtomicInt metatype_id = Q_BASIC_ATOMIC_INITIALIZER(0); \
+ Q_CONSTINIT static QBasicAtomicInt metatype_id = Q_BASIC_ATOMIC_INITIALIZER(0); \
if (const int id = metatype_id.loadRelaxed()) \
return id; \
const char *tName = QMetaType::fromType<T>().name(); \
Q_ASSERT(tName); \
- const int tNameLen = int(qstrlen(tName)); \
+ const size_t tNameLen = qstrlen(tName); \
QByteArray typeName; \
- typeName.reserve(int(sizeof(#SINGLE_ARG_TEMPLATE)) + 1 + tNameLen + 1 + 1); \
+ typeName.reserve(sizeof(#SINGLE_ARG_TEMPLATE) + 1 + tNameLen + 1 + 1); \
typeName.append(#SINGLE_ARG_TEMPLATE, int(sizeof(#SINGLE_ARG_TEMPLATE)) - 1) \
.append('<').append(tName, tNameLen); \
typeName.append('>'); \
@@ -1461,17 +1608,17 @@ struct QMetaTypeId< DOUBLE_ARG_TEMPLATE<T, U> > \
}; \
static int qt_metatype_id() \
{ \
- static QBasicAtomicInt metatype_id = Q_BASIC_ATOMIC_INITIALIZER(0); \
+ Q_CONSTINIT static QBasicAtomicInt metatype_id = Q_BASIC_ATOMIC_INITIALIZER(0); \
if (const int id = metatype_id.loadAcquire()) \
return id; \
const char *tName = QMetaType::fromType<T>().name(); \
const char *uName = QMetaType::fromType<U>().name(); \
Q_ASSERT(tName); \
Q_ASSERT(uName); \
- const int tNameLen = int(qstrlen(tName)); \
- const int uNameLen = int(qstrlen(uName)); \
+ const size_t tNameLen = qstrlen(tName); \
+ const size_t uNameLen = qstrlen(uName); \
QByteArray typeName; \
- typeName.reserve(int(sizeof(#DOUBLE_ARG_TEMPLATE)) + 1 + tNameLen + 1 + uNameLen + 1 + 1); \
+ typeName.reserve(sizeof(#DOUBLE_ARG_TEMPLATE) + 1 + tNameLen + 1 + uNameLen + 1 + 1); \
typeName.append(#DOUBLE_ARG_TEMPLATE, int(sizeof(#DOUBLE_ARG_TEMPLATE)) - 1) \
.append('<').append(tName, tNameLen).append(',').append(uName, uNameLen); \
typeName.append('>'); \
@@ -1509,12 +1656,12 @@ struct SharedPointerMetaTypeIdHelper<SMART_POINTER<T>, true> \
}; \
static int qt_metatype_id() \
{ \
- static QBasicAtomicInt metatype_id = Q_BASIC_ATOMIC_INITIALIZER(0); \
+ Q_CONSTINIT static QBasicAtomicInt metatype_id = Q_BASIC_ATOMIC_INITIALIZER(0); \
if (const int id = metatype_id.loadAcquire()) \
return id; \
const char * const cName = T::staticMetaObject.className(); \
QByteArray typeName; \
- typeName.reserve(int(sizeof(#SMART_POINTER) + 1 + strlen(cName) + 1)); \
+ typeName.reserve(sizeof(#SMART_POINTER) + 1 + strlen(cName) + 1); \
typeName.append(#SMART_POINTER, int(sizeof(#SMART_POINTER)) - 1) \
.append('<').append(cName).append('>'); \
const int newId = qRegisterNormalizedMetaType< SMART_POINTER<T> >(typeName); \
@@ -1524,7 +1671,7 @@ struct SharedPointerMetaTypeIdHelper<SMART_POINTER<T>, true> \
}; \
template<typename T> \
struct MetaTypeSmartPointerHelper<SMART_POINTER<T> , \
- typename std::enable_if<IsPointerToTypeDerivedFromQObject<T*>::Value>::type> \
+ typename std::enable_if<IsPointerToTypeDerivedFromQObject<T*>::Value && !std::is_const_v<T>>::type> \
{ \
static bool registerConverter() \
{ \
@@ -1728,7 +1875,7 @@ private:
#ifdef Q_CC_MSVC
/// On MSVC, keywords like class or struct are not separated with spaces in constexpr
/// context
- if (msvcKw)
+ if (msvcKw && !is_ident_char(*b))
return true;
#endif
Q_UNUSED(msvcKw);
@@ -2109,6 +2256,7 @@ struct is_std_pair<std::pair<T1_, T2_>> : std::true_type {
using T2 = T2_;
};
+namespace TypeNameHelper {
template<typename T>
constexpr auto typenameHelper()
{
@@ -2149,23 +2297,25 @@ constexpr auto typenameHelper()
#ifdef QT_NAMESPACE
QT_STRINGIFY(QT_NAMESPACE) "::"
#endif
-#ifdef Q_CC_MSVC
- "auto __cdecl QtPrivate::typenameHelper<"
+#if defined(Q_CC_MSVC) && defined(Q_CC_CLANG)
+ "auto __cdecl QtPrivate::TypeNameHelper::typenameHelper(void) [T = "
+#elif defined(Q_CC_MSVC)
+ "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;
-#ifdef Q_CC_MSVC
+#if defined(Q_CC_MSVC) && !defined(Q_CC_CLANG)
constexpr int suffix = sizeof(">(void)");
#else
constexpr int suffix = sizeof("]");
#endif
-#if (defined(Q_CC_GNU) && !defined(Q_CC_INTEL) && !defined(Q_CC_CLANG) && Q_CC_GNU < 804)
+#if defined(Q_CC_GNU_ONLY) && Q_CC_GNU_ONLY < 804
auto func = Q_FUNC_INFO;
const char *begin = func + prefix;
const char *end = func + sizeof(Q_FUNC_INFO) - suffix;
@@ -2182,6 +2332,8 @@ constexpr auto typenameHelper()
return result;
}
}
+} // namespace TypeNameHelper
+using TypeNameHelper::typenameHelper;
template<typename T, typename = void>
struct BuiltinMetaType : std::integral_constant<int, 0>
@@ -2238,28 +2390,45 @@ struct QDebugStreamOperatorForType <T, false>
template<typename T, bool = QTypeTraits::has_stream_operator_v<QDataStream, T>>
struct QDataStreamOperatorForType
{
+ static constexpr QMetaTypeInterface::DataStreamOutFn dataStreamOut = nullptr;
+ static constexpr QMetaTypeInterface::DataStreamInFn dataStreamIn = nullptr;
+};
+
+#ifndef QT_NO_DATASTREAM
+template<typename T>
+struct QDataStreamOperatorForType <T, true>
+{
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
-template<typename T>
-struct QDataStreamOperatorForType <T, false>
-{
- static constexpr QMetaTypeInterface::DataStreamOutFn dataStreamOut = nullptr;
- static constexpr QMetaTypeInterface::DataStreamInFn dataStreamIn = nullptr;
-};
+// Performance optimization:
+//
+// Don't add all these symbols to the dynamic symbol tables on ELF systems and
+// on Darwin. Each library is going to have a copy anyway and QMetaType already
+// copes with some of these being "hidden" (see QMetaType::idHelper()). We may
+// as well let the linker know it can always use the local copy.
+//
+// This is currently not enabled for GCC due to
+// https://gcc.gnu.org/bugzilla/show_bug.cgi?id=106023
+
+#if !defined(Q_OS_WIN) && defined(Q_CC_CLANG)
+# pragma GCC visibility push(hidden)
+#endif
template<typename S>
class QMetaTypeForType
{
public:
static constexpr decltype(typenameHelper<S>()) name = typenameHelper<S>();
+ static constexpr unsigned Flags = QMetaTypeTypeFlags<S>::Flags;
static constexpr QMetaTypeInterface::DefaultCtrFn getDefaultCtr()
{
- if constexpr (std::is_default_constructible_v<S>) {
+ if constexpr (std::is_default_constructible_v<S> && !QTypeInfo<S>::isValueInitializationBitwiseZero) {
return [](const QMetaTypeInterface *, void *addr) { new (addr) S(); };
} else {
return nullptr;
@@ -2268,7 +2437,7 @@ public:
static constexpr QMetaTypeInterface::CopyCtrFn getCopyCtr()
{
- if constexpr (std::is_copy_constructible_v<S>) {
+ if constexpr (std::is_copy_constructible_v<S> && !std::is_trivially_copy_constructible_v<S>) {
return [](const QMetaTypeInterface *, void *addr, const void *other) {
new (addr) S(*reinterpret_cast<const S *>(other));
};
@@ -2279,7 +2448,7 @@ public:
static constexpr QMetaTypeInterface::MoveCtrFn getMoveCtr()
{
- if constexpr (std::is_move_constructible_v<S>) {
+ if constexpr (std::is_move_constructible_v<S> && !std::is_trivially_move_constructible_v<S>) {
return [](const QMetaTypeInterface *, void *addr, void *other) {
new (addr) S(std::move(*reinterpret_cast<S *>(other)));
};
@@ -2320,11 +2489,17 @@ public:
template<typename T>
struct QMetaTypeInterfaceWrapper
{
- static inline constexpr const QMetaTypeInterface metaType = {
- /*.revision=*/ 0,
+ // if the type ID for T is known at compile-time, then we can declare
+ // the QMetaTypeInterface object const; otherwise, we declare it as
+ // non-const and the .typeId is updated by QMetaType::idHelper().
+ static constexpr bool IsConstMetaTypeInterface = !!BuiltinMetaType<T>::value;
+ using InterfaceType = std::conditional_t<IsConstMetaTypeInterface, const QMetaTypeInterface, NonConstMetaTypeInterface>;
+
+ static inline InterfaceType metaType = {
+ /*.revision=*/ QMetaTypeInterface::CurrentRevision,
/*.alignment=*/ alignof(T),
/*.size=*/ sizeof(T),
- /*.flags=*/ QMetaTypeTypeFlags<T>::Flags,
+ /*.flags=*/ QMetaTypeForType<T>::Flags,
/*.typeId=*/ BuiltinMetaType<T>::value,
/*.metaObjectFn=*/ MetaObjectForType<T>::metaObjectFunction,
/*.name=*/ QMetaTypeForType<T>::getName(),
@@ -2341,6 +2516,9 @@ struct QMetaTypeInterfaceWrapper
};
};
+#if !defined(Q_OS_WIN) && defined(Q_CC_CLANG)
+# pragma GCC visibility pop
+#endif
template<>
class QMetaTypeInterfaceWrapper<void>
@@ -2367,52 +2545,37 @@ public:
/*.legacyRegisterOp=*/ nullptr
};
};
-#undef QT_METATYPE_CONSTEXPRLAMDA
-#ifndef QT_BOOTSTRAPPED
+/*
+ MSVC instantiates extern templates
+(https://developercommunity.visualstudio.com/t/c11-extern-templates-doesnt-work-for-class-templat/157868)
+
+ The INTEGRITY compiler apparently does too.
-#if !defined(Q_CC_MSVC) || !defined(QT_BUILD_CORE_LIB)
-#define QT_METATYPE_TEMPLATE_EXPORT Q_CORE_EXPORT
+ On Windows (with other compilers or whenever MSVC is fixed), we can't declare
+ QMetaTypeInterfaceWrapper with __declspec(dllimport) because taking its
+ address is not a core constant expression.
+ */
+#if !defined(QT_BOOTSTRAPPED) && !defined(Q_CC_MSVC) && !defined(Q_OS_INTEGRITY)
+
+#ifdef QT_NO_DATA_RELOCATION
+# define QT_METATYPE_DECLARE_EXTERN_TEMPLATE_ITER(TypeName, Id, Name) \
+ extern template class Q_CORE_EXPORT QMetaTypeForType<Name>;
#else
-#define QT_METATYPE_TEMPLATE_EXPORT
+# define QT_METATYPE_DECLARE_EXTERN_TEMPLATE_ITER(TypeName, Id, Name) \
+ extern template class Q_CORE_EXPORT QMetaTypeForType<Name>; \
+ extern template struct Q_CORE_EXPORT QMetaTypeInterfaceWrapper<Name>;
#endif
-#define QT_METATYPE_DECLARE_EXTERN_TEMPLATE_ITER(TypeName, Id, Name) \
- extern template class QT_METATYPE_TEMPLATE_EXPORT QMetaTypeForType<Name>;
-QT_WARNING_PUSH
-QT_WARNING_DISABLE_GCC("-Wattributes") // false positive because of QMetaTypeForType<void>
-QT_FOR_EACH_STATIC_PRIMITIVE_TYPE(QT_METATYPE_DECLARE_EXTERN_TEMPLATE_ITER)
-QT_WARNING_POP
+QT_FOR_EACH_STATIC_PRIMITIVE_NON_VOID_TYPE(QT_METATYPE_DECLARE_EXTERN_TEMPLATE_ITER)
QT_FOR_EACH_STATIC_PRIMITIVE_POINTER(QT_METATYPE_DECLARE_EXTERN_TEMPLATE_ITER)
QT_FOR_EACH_STATIC_CORE_CLASS(QT_METATYPE_DECLARE_EXTERN_TEMPLATE_ITER)
QT_FOR_EACH_STATIC_CORE_POINTER(QT_METATYPE_DECLARE_EXTERN_TEMPLATE_ITER)
QT_FOR_EACH_STATIC_CORE_TEMPLATE(QT_METATYPE_DECLARE_EXTERN_TEMPLATE_ITER)
#undef QT_METATYPE_DECLARE_EXTERN_TEMPLATE_ITER
-#undef QT_METATYPE_TEMPLATE_EXPORT
#endif
template<typename T>
-constexpr const QMetaTypeInterface *qMetaTypeInterfaceForType()
-{
- using Ty = std::remove_cv_t<std::remove_reference_t<T>>;
- return &QMetaTypeInterfaceWrapper<Ty>::metaType;
-}
-
-namespace detail {
-template<typename T, typename ODR_VIOLATION_PREVENTER>
-struct is_complete_helper
-{
- template<typename U>
- static auto check(U *) -> std::integral_constant<bool, sizeof(U) != 0>;
- static auto check(...) -> std::false_type;
- using type = decltype(check(static_cast<T *>(nullptr)));
-};
-} // namespace detail
-
-template <typename T, typename ODR_VIOLATION_PREVENTER>
-struct is_complete : detail::is_complete_helper<T, ODR_VIOLATION_PREVENTER>::type {};
-
-template<typename T>
struct qRemovePointerLike
{
using type = std::remove_pointer_t<T>;
@@ -2437,16 +2600,34 @@ struct TypeAndForceComplete
using ForceComplete = ForceComplete_;
};
+template<typename T>
+constexpr const QMetaTypeInterface *qMetaTypeInterfaceForType()
+{
+ // don't check the type is suitable here
+ using Ty = typename MetatypeDecay<T>::type;
+ return &QMetaTypeInterfaceWrapper<Ty>::metaType;
+}
+
template<typename Unique, typename TypeCompletePair>
constexpr const QMetaTypeInterface *qTryMetaTypeInterfaceForType()
{
using T = typename TypeCompletePair::type;
using ForceComplete = typename TypeCompletePair::ForceComplete;
- using Ty = std::remove_cv_t<std::remove_reference_t<T>>;
+ using Ty = typename MetatypeDecay<T>::type;
using Tz = qRemovePointerLike_t<Ty>;
- if constexpr (!is_complete<Tz, Unique>::value && !ForceComplete::value) {
+
+ if constexpr (std::is_void_v<Tz>) {
+ // early out to avoid expanding the rest of the templates
+ return &QMetaTypeInterfaceWrapper<Ty>::metaType;
+ } else if constexpr (ForceComplete::value) {
+ checkTypeIsSuitableForMetaType<Ty>();
+ return &QMetaTypeInterfaceWrapper<Ty>::metaType;
+ } else if constexpr (std::is_reference_v<Tz>) {
+ return nullptr;
+ } else if constexpr (!is_complete<Tz, Unique>::value) {
return nullptr;
} else {
+ // don't check the type is suitable here
return &QMetaTypeInterfaceWrapper<Ty>::metaType;
}
}
@@ -2456,6 +2637,7 @@ constexpr const QMetaTypeInterface *qTryMetaTypeInterfaceForType()
template<typename T>
constexpr QMetaType QMetaType::fromType()
{
+ QtPrivate::checkTypeIsSuitableForMetaType<T>();
return QMetaType(QtPrivate::qMetaTypeInterfaceForType<T>());
}
@@ -2481,7 +2663,14 @@ constexpr const QMetaObject *QMetaType::metaObject() const
template<typename... T>
constexpr const QtPrivate::QMetaTypeInterface *const qt_metaTypeArray[] = {
- QtPrivate::qMetaTypeInterfaceForType<T>()...
+ /*
+ Unique in qTryMetaTypeInterfaceForType does not have to be unique here
+ as we require _all_ types here to be actually complete.
+ We just want to have the additional type processing that exist in
+ QtPrivate::qTryMetaTypeInterfaceForType as opposed to the normal
+ QtPrivate::qMetaTypeInterfaceForType used in QMetaType::fromType
+ */
+ QtPrivate::qTryMetaTypeInterfaceForType<void, QtPrivate::TypeAndForceComplete<T, std::true_type>>()...
};
constexpr const char *QMetaType::name() const
@@ -2494,8 +2683,16 @@ constexpr const QtPrivate::QMetaTypeInterface *const qt_incomplete_metaTypeArray
QtPrivate::qTryMetaTypeInterfaceForType<Unique, T>()...
};
+inline size_t qHash(QMetaType type, size_t seed = 0)
+{
+ // We cannot use d_ptr here since the same type in different DLLs
+ // might result in different pointers!
+ return qHash(type.id(), seed);
+}
+
QT_END_NAMESPACE
-Q_DECLARE_METATYPE(QtMetaTypePrivate::QPairVariantInterfaceImpl)
+QT_DECL_METATYPE_EXTERN_TAGGED(QtMetaTypePrivate::QPairVariantInterfaceImpl,
+ QPairVariantInterfaceImpl, Q_CORE_EXPORT)
#endif // QMETATYPE_H
diff --git a/src/corelib/kernel/qmetatype_p.h b/src/corelib/kernel/qmetatype_p.h
index 16270bc374..7e0457771f 100644
--- a/src/corelib/kernel/qmetatype_p.h
+++ b/src/corelib/kernel/qmetatype_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtCore module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// 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 QMETATYPE_P_H
#define QMETATYPE_P_H
@@ -56,78 +20,6 @@
QT_BEGIN_NAMESPACE
-namespace QModulesPrivate {
-enum Names { Core, Gui, Widgets, Unknown, ModulesCount /* ModulesCount has to be at the end */ };
-
-static inline int moduleForType(const uint typeId)
-{
- if (typeId <= QMetaType::LastCoreType)
- return Core;
- if (typeId >= QMetaType::FirstGuiType && typeId <= QMetaType::LastGuiType)
- return Gui;
- if (typeId >= QMetaType::FirstWidgetsType && typeId <= QMetaType::LastWidgetsType)
- return Widgets;
- return Unknown;
-}
-
-template <typename T>
-class QTypeModuleInfo
-{
-public:
- enum Module : bool {
- IsCore = false,
- IsWidget = false,
- IsGui = false,
- IsUnknown = true
- };
-};
-
-#define QT_ASSIGN_TYPE_TO_MODULE(TYPE, MODULE) \
-template<> \
-class QTypeModuleInfo<TYPE > \
-{ \
-public: \
- enum Module : bool { \
- IsCore = (((MODULE) == (QModulesPrivate::Core))), \
- IsWidget = (((MODULE) == (QModulesPrivate::Widgets))), \
- IsGui = (((MODULE) == (QModulesPrivate::Gui))), \
- IsUnknown = !(IsCore || IsWidget || IsGui) \
- }; \
- static inline int module() { return MODULE; } \
- static_assert((IsUnknown && !(IsCore || IsWidget || IsGui)) \
- || (IsCore && !(IsUnknown || IsWidget || IsGui)) \
- || (IsWidget && !(IsUnknown || IsCore || IsGui)) \
- || (IsGui && !(IsUnknown || IsCore || IsWidget))); \
-};
-
-
-#define QT_DECLARE_CORE_MODULE_TYPES_ITER(TypeName, TypeId, Name) \
- QT_ASSIGN_TYPE_TO_MODULE(Name, QModulesPrivate::Core)
-#define QT_DECLARE_GUI_MODULE_TYPES_ITER(TypeName, TypeId, Name) \
- QT_ASSIGN_TYPE_TO_MODULE(Name, QModulesPrivate::Gui)
-#define QT_DECLARE_WIDGETS_MODULE_TYPES_ITER(TypeName, TypeId, Name) \
- QT_ASSIGN_TYPE_TO_MODULE(Name, QModulesPrivate::Widgets)
-
-QT_WARNING_PUSH
-#if defined(Q_CC_CLANG) && Q_CC_CLANG >= 900
-QT_WARNING_DISABLE_CLANG("-Wconstant-logical-operand")
-#endif
-
-QT_FOR_EACH_STATIC_PRIMITIVE_TYPE(QT_DECLARE_CORE_MODULE_TYPES_ITER)
-QT_FOR_EACH_STATIC_PRIMITIVE_POINTER(QT_DECLARE_CORE_MODULE_TYPES_ITER)
-QT_FOR_EACH_STATIC_CORE_CLASS(QT_DECLARE_CORE_MODULE_TYPES_ITER)
-QT_FOR_EACH_STATIC_CORE_POINTER(QT_DECLARE_CORE_MODULE_TYPES_ITER)
-QT_FOR_EACH_STATIC_CORE_TEMPLATE(QT_DECLARE_CORE_MODULE_TYPES_ITER)
-QT_FOR_EACH_STATIC_GUI_CLASS(QT_DECLARE_GUI_MODULE_TYPES_ITER)
-QT_FOR_EACH_STATIC_WIDGETS_CLASS(QT_DECLARE_WIDGETS_MODULE_TYPES_ITER)
-
-QT_WARNING_POP
-} // namespace QModulesPrivate
-
-#undef QT_DECLARE_CORE_MODULE_TYPES_ITER
-#undef QT_DECLARE_GUI_MODULE_TYPES_ITER
-#undef QT_DECLARE_WIDGETS_MODULE_TYPES_ITER
-
#define QMETATYPE_CONVERTER(To, From, assign_and_return) \
case makePair(QMetaType::To, QMetaType::From): \
if (onlyCheck) \
@@ -146,16 +38,21 @@ QT_WARNING_POP
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;
@@ -220,6 +117,94 @@ template<> struct TypeDefinition<QQuaternion> { static const bool IsAvailable =
template<> struct TypeDefinition<QIcon> { static const bool IsAvailable = false; };
#endif
+template <typename T> inline bool isInterfaceFor(const QtPrivate::QMetaTypeInterface *iface)
+{
+ // typeId for built-in types are fixed and require no registration
+ static_assert(QMetaTypeId2<T>::IsBuiltIn, "This function only works for built-in types");
+ static constexpr int typeId = QtPrivate::BuiltinMetaType<T>::value;
+ return iface->typeId.loadRelaxed() == typeId;
+}
+
+template <typename FPointer>
+inline bool checkMetaTypeFlagOrPointer(const QtPrivate::QMetaTypeInterface *iface, FPointer ptr, QMetaType::TypeFlag Flag)
+{
+ // helper to the isXxxConstructible & isDestructible functions below: a
+ // meta type has the trait if the trait is trivial or we have the pointer
+ // to perform the operation
+ Q_ASSERT(!isInterfaceFor<void>(iface));
+ Q_ASSERT(iface->size);
+ return ptr != nullptr || (iface->flags & Flag) == 0;
+}
+
+inline bool isDefaultConstructible(const QtPrivate::QMetaTypeInterface *iface) noexcept
+{
+ return checkMetaTypeFlagOrPointer(iface, iface->defaultCtr, QMetaType::NeedsConstruction);
+}
+
+inline bool isCopyConstructible(const QtPrivate::QMetaTypeInterface *iface) noexcept
+{
+ return checkMetaTypeFlagOrPointer(iface, iface->copyCtr, QMetaType::NeedsCopyConstruction);
+}
+
+inline bool isMoveConstructible(const QtPrivate::QMetaTypeInterface *iface) noexcept
+{
+ return checkMetaTypeFlagOrPointer(iface, iface->moveCtr, QMetaType::NeedsMoveConstruction);
+}
+
+inline bool isDestructible(const QtPrivate::QMetaTypeInterface *iface) noexcept
+{
+ /* For metatypes of revision 1, the NeedsDestruction was set even for trivially
+ destructible types, but their dtor pointer would be null.
+ For that reason, we need the additional check here.
+ */
+ return iface->revision < 1 ||
+ checkMetaTypeFlagOrPointer(iface, iface->dtor, QMetaType::NeedsDestruction);
+}
+
+inline void defaultConstruct(const QtPrivate::QMetaTypeInterface *iface, void *where)
+{
+ Q_ASSERT(isDefaultConstructible(iface));
+ if (iface->defaultCtr)
+ iface->defaultCtr(iface, where);
+ else
+ memset(where, 0, iface->size);
+}
+
+inline void copyConstruct(const QtPrivate::QMetaTypeInterface *iface, void *where, const void *copy)
+{
+ Q_ASSERT(isCopyConstructible(iface));
+ if (iface->copyCtr)
+ iface->copyCtr(iface, where, copy);
+ else
+ 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)
+ copyConstruct(iface, where, copy);
+ else
+ defaultConstruct(iface, where);
+}
+
+inline void destruct(const QtPrivate::QMetaTypeInterface *iface, void *where)
+{
+ Q_ASSERT(isDestructible(iface));
+ if (iface->dtor)
+ iface->dtor(iface, where);
+}
+
+const char *typedefNameForType(const QtPrivate::QMetaTypeInterface *type_d);
+
template<typename T>
static const QT_PREPEND_NAMESPACE(QtPrivate::QMetaTypeInterface) *getInterfaceFromType()
{
diff --git a/src/corelib/kernel/qmimedata.cpp b/src/corelib/kernel/qmimedata.cpp
index 6a0a4d6ed1..2c0a89dbd7 100644
--- a/src/corelib/kernel/qmimedata.cpp
+++ b/src/corelib/kernel/qmimedata.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtCore module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// 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 "qmimedata.h"
@@ -46,12 +10,14 @@
QT_BEGIN_NAMESPACE
-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"); }
+using namespace Qt::StringLiterals;
+
+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
{
@@ -110,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);
@@ -118,7 +106,7 @@ QVariant QMimeDataPrivate::retrieveTypedData(const QString &format, QMetaType ty
QVariant data = q->retrieveData(format, type);
// Text data requested: fallback to URL data if available
- if (format == QLatin1String("text/plain") && !data.isValid()) {
+ if (format == "text/plain"_L1 && !data.isValid()) {
data = retrieveTypedData(textUriListLiteral(), QMetaType(QMetaType::QVariantList));
if (data.metaType().id() == QMetaType::QUrl) {
data = QVariant(data.toUrl().toDisplayString());
@@ -126,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() + QLatin1Char('\n');
+ for (const auto &element : list) {
+ if (element.metaType().id() == QMetaType::QUrl) {
+ text += element.toUrl().toDisplayString();
+ text += u'\n';
++numUrls;
}
}
@@ -158,11 +147,12 @@ QVariant QMimeDataPrivate::retrieveTypedData(const QString &format, QMetaType ty
switch (typeId) {
case QMetaType::QString: {
const QByteArray ba = data.toByteArray();
- if (format == QLatin1String("text/html")) {
- auto encoding = QStringConverter::encodingForHtml(ba);
- if (encoding) {
- QStringDecoder toUtf16(*encoding);
- return QString(toUtf16(ba));
+ if (ba.isNull())
+ return QVariant();
+ if (format == "text/html"_L1) {
+ QStringDecoder decoder = QStringDecoder::decoderForHtml(ba);
+ if (decoder.isValid()) {
+ return QString(decoder(ba));
}
// fall back to utf8
}
@@ -170,32 +160,22 @@ QVariant QMimeDataPrivate::retrieveTypedData(const QString &format, QMetaType ty
}
case QMetaType::QColor: {
QVariant newData = data;
-QT_WARNING_PUSH QT_WARNING_DISABLE_DEPRECATED
- newData.convert(QMetaType::QColor);
-QT_WARNING_POP
+ newData.convert(QMetaType(QMetaType::QColor));
return newData;
}
case QMetaType::QVariantList: {
- if (format != QLatin1String("text/uri-list"))
+ if (format != "text/uri-list"_L1)
break;
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;
@@ -215,10 +195,10 @@ QT_WARNING_POP
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";
}
}
@@ -315,9 +295,9 @@ QT_WARNING_POP
\snippet code/src_corelib_kernel_qmimedata.cpp 8
On Windows, the MIME format does not always map directly to the
- clipboard formats. Qt provides QWinMime to map clipboard
+ clipboard formats. Qt provides QWindowsMimeConverter to map clipboard
formats to open-standard MIME formats. Similarly, the
- QMacPasteboardMime maps MIME to Mac flavors.
+ QUtiMimeConverter maps MIME to Uniform Type Identifiers on macOS and iOS.
\sa QClipboard, QDragEnterEvent, QDragMoveEvent, QDropEvent, QDrag,
{Drag and Drop}
@@ -353,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;
@@ -376,13 +356,7 @@ QList<QUrl> QMimeData::urls() const
void QMimeData::setUrls(const QList<QUrl> &urls)
{
Q_D(QMimeData);
- QList<QVariant> list;
- const int numUrls = urls.size();
- list.reserve(numUrls);
- for (int i = 0; i < numUrls; ++i)
- list.append(urls.at(i));
-
- d->setData(textUriListLiteral(), list);
+ d->setData(textUriListLiteral(), QList<QVariant>(urls.cbegin(), urls.cend()));
}
/*!
@@ -594,18 +568,11 @@ void QMimeData::setData(const QString &mimeType, const QByteArray &data)
{
Q_D(QMimeData);
- if (mimeType == QLatin1String("text/uri-list")) {
- QByteArray ba = data;
+ if (mimeType == "text/uri-list"_L1) {
+ 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/qmimedata.h b/src/corelib/kernel/qmimedata.h
index cf26c08073..0969a7315c 100644
--- a/src/corelib/kernel/qmimedata.h
+++ b/src/corelib/kernel/qmimedata.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtCore module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// 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 QMIMEDATA_H
#define QMIMEDATA_H
diff --git a/src/corelib/kernel/qobject.cpp b/src/corelib/kernel/qobject.cpp
index ec4da51ad9..708b10a75e 100644
--- a/src/corelib/kernel/qobject.cpp
+++ b/src/corelib/kernel/qobject.cpp
@@ -1,52 +1,18 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Copyright (C) 2016 Intel Corporation.
-** Copyright (C) 2013 Olivier Goffart <ogoffart@woboq.com>
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtCore module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// Copyright (C) 2016 Intel Corporation.
+// Copyright (C) 2013 Olivier Goffart <ogoffart@woboq.com>
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qobject.h"
#include "qobject_p.h"
+#include "qobject_p_p.h"
#include "qmetaobject_p.h"
#include "qabstracteventdispatcher.h"
#include "qabstracteventdispatcher_p.h"
#include "qcoreapplication.h"
#include "qcoreapplication_p.h"
+#include "qcoreevent_p.h"
#include "qloggingcategory.h"
#include "qvariant.h"
#include "qmetaobject.h"
@@ -56,14 +22,12 @@
#include <qthread.h>
#include <private/qthread_p.h>
#include <qdebug.h>
-#include <qpair.h>
#include <qvarlengtharray.h>
#include <qscopeguard.h>
#include <qset.h>
#if QT_CONFIG(thread)
#include <qsemaphore.h>
#endif
-#include <qsharedpointer.h>
#include <private/qorderedmutexlocker_p.h>
#include <private/qhooks_p.h>
@@ -71,12 +35,24 @@
#include <new>
#include <mutex>
+#include <memory>
#include <ctype.h>
#include <limits.h>
QT_BEGIN_NAMESPACE
+Q_TRACE_POINT(qtcore, QObject_ctor, QObject *object);
+Q_TRACE_POINT(qtcore, QObject_dtor, QObject *object);
+Q_TRACE_POINT(qtcore, QMetaObject_activate_entry, QObject *sender, int signalIndex);
+Q_TRACE_POINT(qtcore, QMetaObject_activate_exit);
+Q_TRACE_POINT(qtcore, QMetaObject_activate_slot_entry, QObject *receiver, int slotIndex);
+Q_TRACE_POINT(qtcore, QMetaObject_activate_slot_exit);
+Q_TRACE_POINT(qtcore, QMetaObject_activate_slot_functor_entry, void *slotObject);
+Q_TRACE_POINT(qtcore, QMetaObject_activate_slot_functor_exit);
+Q_TRACE_POINT(qtcore, QMetaObject_activate_declarative_signal_entry, QObject *sender, int signalIndex);
+Q_TRACE_POINT(qtcore, QMetaObject_activate_declarative_signal_exit);
+
static int DIRECT_CONNECTION_ONLY = 0;
Q_LOGGING_CATEGORY(lcConnectSlotsByName, "qt.core.qmetaobject.connectslotsbyname")
@@ -108,6 +84,8 @@ static int *queuedConnectionTypes(const QMetaMethod &method)
typeIds[i] = QMetaType::VoidStar;
else
typeIds[i] = metaType.id();
+ if (!typeIds[i] && method.parameterTypeName(i).endsWith('*'))
+ typeIds[i] = QMetaType::VoidStar;
if (!typeIds[i]) {
const QByteArray typeName = method.parameterTypeName(i);
qCWarning(lcConnect,
@@ -125,7 +103,7 @@ static int *queuedConnectionTypes(const QMetaMethod &method)
static int *queuedConnectionTypes(const QArgumentType *argumentTypes, int argc)
{
- QScopedArrayPointer<int> types(new int[argc + 1]);
+ auto types = std::make_unique<int[]>(argc + 1);
for (int i = 0; i < argc; ++i) {
const QArgumentType &type = argumentTypes[i];
if (type.type())
@@ -145,10 +123,10 @@ static int *queuedConnectionTypes(const QArgumentType *argumentTypes, int argc)
}
types[argc] = 0;
- return types.take();
+ return types.release();
}
-static QBasicMutex _q_ObjectMutexPool[131];
+Q_CONSTINIT static QBasicMutex _q_ObjectMutexPool[131];
/**
* \internal
@@ -197,6 +175,10 @@ QObjectPrivate::QObjectPrivate(int version)
metaObject = nullptr;
isWindow = false;
deleteLaterCalled = false;
+ isQuickItem = false;
+ willBeWidget = false;
+ wasWidget = false;
+ receiveParentEvents = false; // If object wants ParentAboutToChange and ParentChange
}
QObjectPrivate::~QObjectPrivate()
@@ -209,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");
}
@@ -245,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()) {
@@ -286,17 +247,17 @@ QObjectList QObjectPrivate::receiverList(const char *signal) const
return returnValue;
}
-// Used by QAccessibleWidget
-QObjectList QObjectPrivate::senderList() const
+/*!
+ \internal
+ The signalSlotLock() of the sender must be locked while calling this function
+*/
+inline void QObjectPrivate::ensureConnectionData()
{
- 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;
+ if (connections.loadRelaxed())
+ return;
+ ConnectionData *cd = new ConnectionData;
+ cd->ref.ref();
+ connections.storeRelease(cd);
}
/*!
@@ -309,7 +270,7 @@ QObjectList QObjectPrivate::senderList() const
Will also add the connection in the sender's list of the receiver.
*/
-void QObjectPrivate::addConnection(int signal, Connection *c)
+inline void QObjectPrivate::addConnection(int signal, Connection *c)
{
Q_ASSERT(c->sender == q_ptr);
ensureConnectionData();
@@ -379,16 +340,16 @@ void QObjectPrivate::ConnectionData::removeConnection(QObjectPrivate::Connection
c->prevConnectionList->nextConnectionList.storeRelaxed(n);
c->prevConnectionList = nullptr;
- Q_ASSERT(c != orphaned.loadRelaxed());
+ Q_ASSERT(c != static_cast<Connection *>(orphaned.load(std::memory_order_relaxed)));
// add c to orphanedConnections
- Connection *o = nullptr;
+ TaggedSignalVector o = nullptr;
/* No ABA issue here: When adding a node, we only care about the list head, it doesn't
* matter if the tail changes.
*/
+ o = orphaned.load(std::memory_order_acquire);
do {
- o = orphaned.loadRelaxed();
c->nextInOrphanList = o;
- } while (!orphaned.testAndSetRelease(o, c));
+ } while (!orphaned.compare_exchange_strong(o, TaggedSignalVector(c), std::memory_order_release));
#ifndef QT_NO_DEBUG
found = false;
@@ -406,7 +367,7 @@ void QObjectPrivate::ConnectionData::removeConnection(QObjectPrivate::Connection
void QObjectPrivate::ConnectionData::cleanOrphanedConnectionsImpl(QObject *sender, LockPolicy lockPolicy)
{
QBasicMutex *senderMutex = signalSlotLock(sender);
- ConnectionOrSignalVector *c = nullptr;
+ TaggedSignalVector c = nullptr;
{
std::unique_lock<QBasicMutex> lock(*senderMutex, std::defer_lock_t{});
if (lockPolicy == NeedToLock)
@@ -417,7 +378,7 @@ void QObjectPrivate::ConnectionData::cleanOrphanedConnectionsImpl(QObject *sende
// Since ref == 1, no activate() is in process since we locked the mutex. That implies,
// that nothing can reference the orphaned connection objects anymore and they can
// be safely deleted
- c = orphaned.fetchAndStoreRelaxed(nullptr);
+ c = orphaned.exchange(nullptr, std::memory_order_relaxed);
}
if (c) {
// Deleting c might run arbitrary user code, so we must not hold the lock
@@ -431,11 +392,11 @@ void QObjectPrivate::ConnectionData::cleanOrphanedConnectionsImpl(QObject *sende
}
}
-void QObjectPrivate::ConnectionData::deleteOrphaned(QObjectPrivate::ConnectionOrSignalVector *o)
+inline void QObjectPrivate::ConnectionData::deleteOrphaned(TaggedSignalVector o)
{
while (o) {
- QObjectPrivate::ConnectionOrSignalVector *next = nullptr;
- if (SignalVector *v = ConnectionOrSignalVector::asSignalVector(o)) {
+ TaggedSignalVector next = nullptr;
+ if (SignalVector *v = static_cast<SignalVector *>(o)) {
next = v->nextInOrphanList;
free(v);
} else {
@@ -461,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();
@@ -484,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();
@@ -501,6 +462,13 @@ bool QObjectPrivate::maybeSignalConnected(uint signalIndex) const
return false;
}
+void QObjectPrivate::reinitBindingStorageAfterThreadMove()
+{
+ bindingStorage.reinitAfterThreadMove();
+ for (int i = 0; i < children.size(); ++i)
+ children[i]->d_func()->reinitBindingStorageAfterThreadMove();
+}
+
/*!
\internal
*/
@@ -554,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_)
@@ -564,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.
*/
@@ -588,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_)
@@ -598,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()
{
@@ -610,8 +609,6 @@ QMetaCallEvent::~QMetaCallEvent()
if (reinterpret_cast<void *>(d.args_) != reinterpret_cast<void *>(prealloc_))
free(d.args_);
}
- if (d.slotObj_)
- d.slotObj_->destroyIfLastRef();
}
/*!
@@ -629,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().
@@ -716,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.
@@ -756,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
@@ -839,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.
@@ -931,7 +955,7 @@ QObject::QObject(QObjectPrivate &dd, QObject *parent)
QT_TRY {
if (!check_parent_thread(parent, parent ? parent->d_func()->threadData.loadRelaxed() : nullptr, threadData))
parent = nullptr;
- if (d->isWidget) {
+ if (d->willBeWidget) {
if (parent) {
d->parent = parent;
d->parent->d_func()->children.append(this);
@@ -969,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
@@ -985,6 +1009,16 @@ QObject::~QObject()
d->wasDeleted = true;
d->blockSig = 0; // unblock signals so we always emit destroyed()
+ if (!d->bindingStorage.isValid()) {
+ // this might be the case after an incomplete thread-move
+ // remove this object from the pending list in that case
+ if (QThread *ownThread = thread()) {
+ auto *privThread = static_cast<QThreadPrivate *>(
+ QObjectPrivate::get(ownThread));
+ privThread->removeObjectWithPendingBindingStatusChange(this);
+ }
+ }
+
// If we reached this point, we need to clear the binding data
// as the corresponding properties are no longer useful
d->clearBindingStorage();
@@ -1002,14 +1036,14 @@ QObject::~QObject()
delete sharedRefcount;
}
- if (!d->isWidget && d->isSignalConnected(0)) {
+ if (!d->wasWidget && d->isSignalConnected(0)) {
emit destroyed(this);
}
- if (d->declarativeData && QAbstractDeclarativeData::destroyed)
+ 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();
@@ -1017,7 +1051,7 @@ QObject::~QObject()
}
QBasicMutex *signalSlotMutex = signalSlotLock(this);
- QBasicMutexLocker locker(signalSlotMutex);
+ QMutexLocker locker(signalSlotMutex);
// disconnect all receivers
int receiverCount = cd->signalVectorCount();
@@ -1112,7 +1146,7 @@ QObject::~QObject()
d->setParent_helper(nullptr);
}
-QObjectPrivate::Connection::~Connection()
+inline QObjectPrivate::Connection::~Connection()
{
if (ownArgumentTypes) {
const int *v = argumentTypes.loadRelaxed();
@@ -1195,8 +1229,7 @@ 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.
@@ -1242,6 +1275,10 @@ QObjectPrivate::Connection::~Connection()
QString QObject::objectName() const
{
Q_D(const QObject);
+#if QT_CONFIG(thread)
+ if (QThread::currentThreadId() != d->threadData.loadRelaxed()->threadId.loadRelaxed()) // Unsafe code path
+ return d->extraData ? d->extraData->objectName.valueBypassingBindings() : QString();
+#endif
if (!d->extraData && QtPrivate::isAnyBindingEvaluating()) {
QObjectPrivate *dd = const_cast<QObjectPrivate *>(d);
// extraData is mutable, so this should be safe
@@ -1250,30 +1287,47 @@ QString QObject::objectName() const
return d->extraData ? d->extraData->objectName : QString();
}
-/*
+/*!
+ \fn void QObject::setObjectName(const QString &name)
Sets the object's name to \a name.
*/
-void QObject::setObjectName(const QString &name)
+void QObject::doSetObjectName(const QString &name)
{
Q_D(QObject);
- if (!d->extraData)
- d->extraData = new QObjectPrivate::ExtraData(d);
+ d->ensureExtraData();
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
}
}
+/*!
+ \overload
+ \since 6.4
+*/
+void QObject::setObjectName(QAnyStringView name)
+{
+ Q_D(QObject);
+
+ d->ensureExtraData();
+
+ d->extraData->objectName.removeBindingUnlessInWrapper();
+
+ if (d->extraData->objectName.valueBypassingBindings() != name) {
+ d->extraData->objectName.setValueBypassingBindings(name.toString());
+ d->extraData->objectName.notify(); // also emits a signal
+ }
+}
+
QBindable<QString> QObject::bindableObjectName()
{
Q_D(QObject);
- if (!d->extraData)
- d->extraData = new QObjectPrivate::ExtraData(d);
+ d->ensureExtraData();
return QBindable<QString>(&d->extraData->objectName);
}
@@ -1304,6 +1358,17 @@ QBindable<QString> QObject::bindableObjectName()
*/
/*!
+ \fn bool QObject::isQuickItemType() const
+
+ Returns \c true if the object is a QQuickItem; otherwise returns \c false.
+
+ Calling this function is equivalent to calling
+ \c{inherits("QQuickItem")}, except that it is much faster.
+
+ \since 6.4
+*/
+
+/*!
This virtual function receives events to an object and should
return true if the event \a e was recognized and processed.
@@ -1335,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;
@@ -1357,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;
@@ -1384,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()
*/
@@ -1525,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
@@ -1564,46 +1640,46 @@ 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();
QThreadData *targetData = targetThread ? QThreadData::get2(targetThread) : nullptr;
- QThreadData *thisThreadData = d->threadData.loadRelaxed();
- if (!thisThreadData->thread.loadAcquire() && currentData == targetData) {
+ QThreadData *thisThreadData = d->threadData.loadAcquire();
+ if (!thisThreadData->thread.loadRelaxed() && currentData == targetData) {
// one exception to the rule: we allow moving objects with no thread affinity to the current thread
- currentData = d->threadData;
+ currentData = thisThreadData;
} else if (thisThreadData != currentData) {
qWarning("QObject::moveToThread: Current thread (%p) is not the object's thread (%p).\n"
"Cannot move to target thread (%p)\n",
currentData->thread.loadRelaxed(), thisThreadData->thread.loadRelaxed(), targetData ? targetData->thread.loadRelaxed() : nullptr);
-#ifdef Q_OS_MAC
+#ifdef Q_OS_DARWIN
qWarning("You might be loading two sets of Qt binaries into the same process. "
"Check that all plugins are compiled against the right Qt binaries. Export "
"DYLD_PRINT_LIBRARIES=1 and check that only one set of binaries are being loaded.");
#endif
- return;
+ return false;
}
// prepare to move
@@ -1622,12 +1698,22 @@ void QObject::moveToThread(QThread *targetThread)
currentData->ref();
// move the object
- d_func()->setThreadData_helper(currentData, targetData);
+ auto threadPrivate = targetThread
+ ? static_cast<QThreadPrivate *>(QThreadPrivate::get(targetThread))
+ : nullptr;
+ QBindingStatus *bindingStatus = threadPrivate
+ ? threadPrivate->bindingStatus()
+ : nullptr;
+ if (threadPrivate && !bindingStatus) {
+ bindingStatus = threadPrivate->addObjectWithPendingBindingStatusChange(this);
+ }
+ d_func()->setThreadData_helper(currentData, targetData, bindingStatus);
locker.unlock();
// now currentData can commit suicide if it wants to
currentData->deref();
+ return true;
}
void QObjectPrivate::moveToThread_helper()
@@ -1635,16 +1721,22 @@ void QObjectPrivate::moveToThread_helper()
Q_Q(QObject);
QEvent e(QEvent::ThreadChange);
QCoreApplication::sendEvent(q, &e);
+ bindingStorage.clear();
for (int i = 0; i < children.size(); ++i) {
QObject *child = children.at(i);
child->d_func()->moveToThread_helper();
}
}
-void QObjectPrivate::setThreadData_helper(QThreadData *currentData, QThreadData *targetData)
+void QObjectPrivate::setThreadData_helper(QThreadData *currentData, QThreadData *targetData, QBindingStatus *status)
{
Q_Q(QObject);
+ if (status) {
+ // the new thread is already running
+ this->bindingStorage.bindingStatus = status;
+ }
+
// move posted events
int eventsMoved = 0;
for (int i = 0; i < currentData->postEventList.size(); ++i) {
@@ -1664,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();
@@ -1699,23 +1791,10 @@ void QObjectPrivate::setThreadData_helper(QThreadData *currentData, QThreadData
for (int i = 0; i < children.size(); ++i) {
QObject *child = children.at(i);
- child->d_func()->setThreadData_helper(currentData, targetData);
- }
-}
-
-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);
+ child->d_func()->setThreadData_helper(currentData, targetData, status);
}
- delete timerList;
}
-
//
// The timer flag hasTimer is set when startTimer is called.
// It is not reset when killing the timer because more than
@@ -1723,13 +1802,34 @@ void QObjectPrivate::_q_reregisterTimers(void *pointer)
//
/*!
+ \fn int QObject::startTimer(int interval, Qt::TimerType timerType)
+
+ This is an overloaded function that will start a timer of type
+ \a timerType and a timeout of \a interval milliseconds. This is
+ equivalent to calling:
+ \code
+ startTimer(std::chrono::milliseconds{interval}, timerType);
+ \endcode
+
+ \sa timerEvent(), killTimer(), QChronoTimer::singleShot()
+*/
+
+int QObject::startTimer(int interval, Qt::TimerType timerType)
+{
+ return startTimer(std::chrono::milliseconds{interval}, timerType);
+}
+
+/*!
+ \since 5.9
+ \overload
+
Starts a timer and returns a timer identifier, or returns zero if
it could not start a timer.
- A timer event will occur every \a interval milliseconds until
- killTimer() is called. If \a interval is 0, then the timer event
- occurs once every time there are no more window system events to
- process.
+ A timer event will occur every \a interval until killTimer()
+ is called. If \a interval is equal to \c{std::chrono::duration::zero()},
+ then the timer event occurs once every time there are no more window
+ system events to process.
The virtual timerEvent() function is called with the QTimerEvent
event parameter class when a timer event occurs. Reimplement this
@@ -1742,26 +1842,41 @@ void QObjectPrivate::_q_reregisterTimers(void *pointer)
\snippet code/src_corelib_kernel_qobject.cpp 8
- Note that QTimer's accuracy depends on the underlying operating system and
- hardware. The \a timerType argument allows you to customize the accuracy of
+ Note that the accuracy of QChronoTimer depends on the underlying operating
+ system and hardware.
+
+ The \a timerType argument allows you to customize the accuracy of
the timer. See Qt::TimerType for information on the different timer types.
Most platforms support an accuracy of 20 milliseconds; some provide more.
If Qt is unable to deliver the requested number of timer events, it will
silently discard some.
- The QTimer class provides a high-level programming interface with
- single-shot timers and timer signals instead of events. There is
- also a QBasicTimer class that is more lightweight than QTimer and
- less clumsy than using timer IDs directly.
+ The QTimer and QChronoTimer classes provide a high-level programming
+ interface with single-shot timers and timer signals instead of
+ events. There is also a QBasicTimer class that is more lightweight than
+ QChronoTimer but less clumsy than using timer IDs directly.
- \sa timerEvent(), killTimer(), QTimer::singleShot()
-*/
+ \sa timerEvent(), killTimer(), QChronoTimer::singleShot()
-int QObject::startTimer(int interval, Qt::TimerType timerType)
+ \note Starting from Qt 6.8 the type of \a interval
+ is \c std::chrono::nanoseconds, prior to that it was \c
+ std::chrono::milliseconds. This change is backwards compatible with
+ older releases of Qt.
+
+ \note In Qt 6.8, QObject was changed to use Qt::TimerId to represent timer
+ IDs. This method converts the TimerId to int for backwards compatibility
+ reasons, however you can use Qt::TimerId to check the value returned by
+ this method, for example:
+ \snippet code/src_corelib_kernel_qobject.cpp invalid-timer-id
+
+*/
+int QObject::startTimer(std::chrono::nanoseconds interval, Qt::TimerType timerType)
{
Q_D(QObject);
- if (Q_UNLIKELY(interval < 0)) {
+ using namespace std::chrono_literals;
+
+ if (Q_UNLIKELY(interval < 0ns)) {
qWarning("QObject::startTimer: Timers cannot have negative intervals");
return 0;
}
@@ -1775,53 +1890,15 @@ int QObject::startTimer(int interval, Qt::TimerType timerType)
qWarning("QObject::startTimer: Timers cannot be started from another thread");
return 0;
}
- int timerId = thisThreadData->eventDispatcher.loadRelaxed()->registerTimer(interval, timerType, this);
- if (!d->extraData)
- d->extraData = new QObjectPrivate::ExtraData(d);
+
+ auto dispatcher = thisThreadData->eventDispatcher.loadRelaxed();
+ Qt::TimerId timerId = dispatcher->registerTimer(interval, timerType, this);
+ d->ensureExtraData();
d->extraData->runningTimers.append(timerId);
- return timerId;
+ return int(timerId);
}
/*!
- \since 5.9
- \overload
- \fn int QObject::startTimer(std::chrono::milliseconds time, Qt::TimerType timerType)
-
- Starts a timer and returns a timer identifier, or returns zero if
- it could not start a timer.
-
- A timer event will occur every \a time interval until killTimer()
- is called. If \a time is equal to \c{std::chrono::duration::zero()},
- then the timer event occurs once every time there are no more window
- system events to process.
-
- The virtual timerEvent() function is called with the QTimerEvent
- event parameter class when a timer event occurs. Reimplement this
- function to get timer events.
-
- If multiple timers are running, the QTimerEvent::timerId() can be
- used to find out which timer was activated.
-
- Example:
-
- \snippet code/src_corelib_kernel_qobject.cpp 8
-
- Note that QTimer's accuracy depends on the underlying operating system and
- hardware. The \a timerType argument allows you to customize the accuracy of
- the timer. See Qt::TimerType for information on the different timer types.
- Most platforms support an accuracy of 20 milliseconds; some provide more.
- If Qt is unable to deliver the requested number of timer events, it will
- silently discard some.
-
- The QTimer class provides a high-level programming interface with
- single-shot timers and timer signals instead of events. There is
- also a QBasicTimer class that is more lightweight than QTimer and
- less clumsy than using timer IDs directly.
-
- \sa timerEvent(), killTimer(), QTimer::singleShot()
-*/
-
-/*!
Kills the timer with timer identifier, \a id.
The timer identifier is returned by startTimer() when a timer
@@ -1832,17 +1909,26 @@ int QObject::startTimer(int interval, Qt::TimerType timerType)
void QObject::killTimer(int id)
{
+ killTimer(Qt::TimerId{id});
+}
+
+/*!
+ \since 6.8
+ \overload
+*/
+void QObject::killTimer(Qt::TimerId id)
+{
Q_D(QObject);
if (Q_UNLIKELY(thread() != QThread::currentThread())) {
qWarning("QObject::killTimer: Timers cannot be stopped from another thread");
return;
}
- if (id) {
+ if (id > Qt::TimerId::Invalid) {
int at = d->extraData ? d->extraData->runningTimers.indexOf(id) : -1;
if (at == -1) {
// timer isn't owned by this object
qWarning("QObject::killTimer(): Error: timer id %d is not valid for object %p (%s, %ls), timer has not been killed",
- id,
+ qToUnderlying(id),
this,
metaObject()->className(),
qUtf16Printable(objectName()));
@@ -1858,7 +1944,6 @@ void QObject::killTimer(int id)
}
}
-
/*!
\fn QObject *QObject::parent() const
@@ -1892,18 +1977,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
@@ -1925,11 +2011,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.
@@ -1951,6 +2058,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()
*/
@@ -1968,7 +2078,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
@@ -2012,46 +2122,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);
}
}
@@ -2078,13 +2168,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()) {
@@ -2114,7 +2204,7 @@ void QObjectPrivate::deleteChildren()
// delete children objects
// don't use qDeleteAll as the destructor of the child might
// delete siblings
- for (int i = 0; i < children.count(); ++i) {
+ for (int i = 0; i < children.size(); ++i) {
currentChildBeingDeleted = children.at(i);
children[i] = nullptr;
delete currentChildBeingDeleted;
@@ -2167,10 +2257,18 @@ 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 != parent->d_func()->threadData) {
+ if (threadData.loadRelaxed() != parent->d_func()->threadData.loadRelaxed()) {
qWarning("QObject::setParent: Cannot set parent, new parent is in a different thread");
parent = nullptr;
return;
@@ -2183,6 +2281,12 @@ void QObjectPrivate::setParent_helper(QObject *o)
}
}
}
+
+ if (receiveParentEvents) {
+ Q_ASSERT(!isWidget); // Handled in QWidget
+ QEvent e(QEvent::ParentChange);
+ QCoreApplication::sendEvent(q, &e);
+ }
}
/*!
@@ -2201,6 +2305,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:
@@ -2232,17 +2339,16 @@ void QObject::installEventFilter(QObject *obj)
Q_D(QObject);
if (!obj)
return;
- if (d->threadData != obj->d_func()->threadData) {
+ if (d->threadData.loadRelaxed() != obj->d_func()->threadData.loadRelaxed()) {
qWarning("QObject::installEventFilter(): Cannot filter events for objects in a different thread.");
return;
}
- if (!d->extraData)
- d->extraData = new QObjectPrivate::ExtraData(d);
+ 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);
}
@@ -2263,9 +2369,11 @@ void QObject::removeEventFilter(QObject *obj)
{
Q_D(QObject);
if (d->extraData) {
- for (int i = 0; i < d->extraData->eventFilters.count(); ++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;
+ }
}
}
}
@@ -2294,7 +2402,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.
@@ -2305,15 +2413,85 @@ 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
*/
void QObject::deleteLater()
{
- QCoreApplication::postEvent(this, new QDeferredDeleteEvent());
+#ifdef QT_DEBUG
+ if (qApp == this)
+ qWarning("You are deferring the delete of QCoreApplication, this may not work as expected.");
+#endif
+
+
+ // 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));
}
/*!
@@ -2326,13 +2504,12 @@ void QObject::deleteLater()
translated string is available.
Example:
- \snippet ../widgets/mainwindows/sdi/mainwindow.cpp implicit tr context
+ \snippet ../widgets/itemviews/spreadsheet/spreadsheet.cpp implicit tr context
\dots
If the same \a sourceText is used in different roles within the
same context, an additional identifying string may be passed in
- \a disambiguation (\nullptr by default). In Qt 4.4 and earlier, this was
- the preferred way to pass comments to translators.
+ \a disambiguation (\nullptr by default).
Example:
@@ -2341,8 +2518,8 @@ void QObject::deleteLater()
See \l{Writing Source Code for Translation} for a detailed description of
Qt's translation mechanisms in general, and the
- \l{Writing Source Code for Translation#Disambiguation}{Disambiguation}
- section for information on disambiguation.
+ \l{Writing Source Code for Translation#Disambiguate Identical Text}
+ {Disambiguate Identical Text} section for information on disambiguation.
\warning This method is reentrant only if all translators are
installed \e before calling this method. Installing or removing
@@ -2356,11 +2533,27 @@ void QObject::deleteLater()
Signals and slots
*****************************************************************************/
+namespace {
+// This class provides (per-thread) storage for qFlagLocation()
+class FlaggedDebugSignatures
+{
+ uint idx = 0;
+ std::array<const char *, 2> locations = {}; // one for the SIGNAL, one for the SLOT
+
+public:
+ void store(const char* method) noexcept
+ { locations[idx++ % locations.size()] = method; }
+
+ bool contains(const char *method) const noexcept
+ { return std::find(locations.begin(), locations.end(), method) != locations.end(); }
+};
+
+Q_CONSTINIT static thread_local FlaggedDebugSignatures flaggedSignatures = {};
+} // unnamed namespace
+
const char *qFlagLocation(const char *method)
{
- QThreadData *currentThreadData = QThreadData::current(false);
- if (currentThreadData != nullptr)
- currentThreadData->flaggedSignatures.store(method);
+ flaggedSignatures.store(method);
return method;
}
@@ -2372,7 +2565,7 @@ static int extract_code(const char *member)
static const char *extract_location(const char *member)
{
- if (QThreadData::current()->flaggedSignatures.contains(member)) {
+ if (flaggedSignatures.contains(member)) {
// signature includes location information after the first null-terminator
const char *location = member + qstrlen(member) + 1;
if (*location != '\0')
@@ -2409,6 +2602,7 @@ static bool check_method_code(int code, const QObject *object, const char *metho
return true;
}
+Q_DECL_COLD_FUNCTION
static void err_method_notfound(const QObject *object,
const char *method, const char *func)
{
@@ -2426,6 +2620,7 @@ static void err_method_notfound(const QObject *object,
object->metaObject()->className(), method + 1, loc ? " in " : "", loc ? loc : "");
}
+Q_DECL_COLD_FUNCTION
static void err_info_about_objects(const char *func, const QObject *sender, const QObject *receiver)
{
QString a = sender ? sender->objectName() : QString();
@@ -2462,7 +2657,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;
@@ -2504,7 +2699,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;
@@ -2563,13 +2758,13 @@ int QObject::receivers(const char *signal) const
if (!d->isSignalConnected(signal_index))
return receivers;
- if (d->declarativeData && QAbstractDeclarativeData::receivers) {
+ if (!d->isDeletingChildren && d->declarativeData && QAbstractDeclarativeData::receivers) {
receivers += QAbstractDeclarativeData::receivers(d->declarativeData, this,
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) {
@@ -2591,13 +2786,15 @@ int QObject::receivers(const char *signal) const
\snippet code/src_corelib_kernel_qobject.cpp 49
- As the code snippet above illustrates, you can use this function
- to avoid emitting a signal that nobody listens to.
+ As the code snippet above illustrates, you can use this function to avoid
+ expensive initialization or emitting a signal that nobody listens to.
+ However, in a multithreaded application, connections might change after
+ this function returns and before the signal gets emitted.
\warning This function violates the object-oriented principle of
- modularity. However, it might be useful when you need to perform
- expensive initialization only if something is connected to a
- signal.
+ modularity. In particular, this function must not be called from an
+ override of connectNotify() or disconnectNotify(), as those might get
+ called from any thread.
*/
bool QObject::isSignalConnected(const QMetaMethod &signal) const
{
@@ -2614,7 +2811,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);
}
@@ -3271,8 +3468,13 @@ bool QObject::disconnect(const QObject *sender, const QMetaMethod &signal,
signal.
\warning This function is called from the thread which performs the
- connection, which may be a different thread from the thread in
- which this object lives.
+ connection, which may be a different thread from the thread in which
+ this object lives. This function may also be called with a QObject internal
+ mutex locked. It is therefore not allowed to re-enter any QObject
+ functions, including isSignalConnected(), from your reimplementation. If
+ you lock a mutex in your reimplementation, make sure that you don't call
+ QObject functions with that mutex held in other places or it will result in
+ a deadlock.
\sa connect(), disconnectNotify()
*/
@@ -3301,12 +3503,12 @@ void QObject::connectNotify(const QMetaMethod &signal)
expensive resources.
\warning This function is called from the thread which performs the
- disconnection, which may be a different thread from the thread in
- which this object lives. This function may also be called with a QObject
- internal mutex locked. It is therefore not allowed to re-enter any
- of any QObject functions from your reimplementation and if you lock
- a mutex in your reimplementation, make sure that you don't call QObject
- functions with that mutex held in other places or it will result in
+ disconnection, which may be a different thread from the thread in which
+ this object lives. This function may also be called with a QObject internal
+ mutex locked. It is therefore not allowed to re-enter any QObject
+ functions, including isSignalConnected(), from your reimplementation. If
+ you lock a mutex in your reimplementation, make sure that you don't call
+ QObject functions with that mutex held in other places or it will result in
a deadlock.
\sa disconnect(), connectNotify()
@@ -3410,7 +3612,7 @@ QObjectPrivate::Connection *QMetaObjectPrivate::connect(const QObject *sender,
c->sender = s;
c->signal_index = signal_index;
c->receiver.storeRelaxed(r);
- QThreadData *td = r->d_func()->threadData;
+ QThreadData *td = r->d_func()->threadData.loadAcquire();
td->ref();
c->receiverThreadData.storeRelaxed(td);
c->method_relative = method_index;
@@ -3515,7 +3717,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)
@@ -3622,7 +3824,7 @@ void QMetaObject::connectSlotsByName(QObject *o)
// ...we check each object in our list, ...
bool foundIt = false;
- for (int j = 0; j < list.count(); ++j) {
+ for (int j = 0; j < list.size(); ++j) {
const QObject *co = list.at(j);
const QByteArray coName = co->objectName().toLatin1();
@@ -3696,7 +3898,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)
@@ -3704,17 +3906,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;
};
/*!
@@ -3742,7 +3941,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
@@ -3770,7 +3969,7 @@ static void queued_activate(QObject *sender, int signal, QObjectPrivate::Connect
args[n] = types[n].create(argv[n]);
}
- if (c->isSingleShot && !QObjectPrivate::disconnect(c)) {
+ if (c->isSingleShot && !QObjectPrivate::removeConnection(c)) {
delete ev;
return;
}
@@ -3823,8 +4022,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;
@@ -3878,12 +4077,12 @@ void doActivate(QObject *sender, int signal_index, void **argv)
receiver->metaObject()->className(), receiver);
}
- if (c->isSingleShot && !QObjectPrivate::disconnect(c))
+ if (c->isSingleShot && !QObjectPrivate::removeConnection(c))
continue;
QSemaphore semaphore;
{
- QBasicMutexLocker locker(signalSlotLock(receiver));
+ QMutexLocker locker(signalSlotLock(receiver));
if (!c->isSingleShot && !c->receiver.loadAcquire())
continue;
QMetaCallEvent *ev = c->isSlotObject ?
@@ -3897,10 +4096,12 @@ void doActivate(QObject *sender, int signal_index, void **argv)
#endif
}
- if (c->isSingleShot && !QObjectPrivate::disconnect(c))
+ 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};
@@ -3949,7 +4150,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);
@@ -4026,9 +4227,9 @@ int QObjectPrivate::signalIndex(const char *signalName,
Properties
*****************************************************************************/
-#ifndef QT_NO_PROPERTIES
-
/*!
+ \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
@@ -4049,17 +4250,24 @@ 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;
int id = meta->indexOfProperty(name);
if (id < 0) {
- if (!d->extraData)
- d->extraData = new QObjectPrivate::ExtraData(d);
+ d->ensureExtraData();
const int idx = d->extraData->propertyNames.indexOf(name);
@@ -4071,12 +4279,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;
}
}
@@ -4091,7 +4305,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);
}
/*!
@@ -4141,42 +4355,24 @@ QList<QByteArray> QObject::dynamicPropertyNames() const
return QList<QByteArray>();
}
-#endif // QT_NO_PROPERTIES
-
-
/*****************************************************************************
QObject debugging output routines.
*****************************************************************************/
+std::string QObjectPrivate::flagsForDumping() const
+{
+ return {};
+}
+
static void dumpRecursive(int level, const QObject *object)
{
if (object) {
- QByteArray buf;
- buf.fill(' ', level / 2 * 8);
- if (level % 2)
- buf += " ";
- QString name = object->objectName();
- QString flags = QLatin1String("");
-#if 0
- if (qApp->focusWidget() == object)
- flags += 'F';
- if (object->isWidgetType()) {
- QWidget * w = (QWidget *)object;
- if (w->isVisible()) {
- QString t("<%1,%2,%3,%4>");
- flags += t.arg(w->x()).arg(w->y()).arg(w->width()).arg(w->height());
- } else {
- flags += 'I';
- }
- }
-#endif
- qDebug("%s%s::%s %s", (const char*)buf, object->metaObject()->className(), name.toLocal8Bit().data(),
- flags.toLatin1().data());
- QObjectList children = object->children();
- if (!children.isEmpty()) {
- for (int i = 0; i < children.size(); ++i)
- dumpRecursive(level+1, children.at(i));
- }
+ const int indent = level * 4;
+ qDebug("%*s%s::%ls %s", indent, "", object->metaObject()->className(),
+ qUtf16Printable(object->objectName()),
+ QObjectPrivate::get(object)->flagsForDumping().c_str());
+ for (auto child : object->children())
+ dumpRecursive(level + 1, child);
}
}
@@ -4209,7 +4405,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");
@@ -4271,15 +4467,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
@@ -4289,19 +4493,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++}
*/
/*!
@@ -4311,15 +4518,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}
*/
@@ -4396,8 +4594,8 @@ QDebug operator<<(QDebug dbg, const QObject *o)
\since 5.5
This macro registers an enum type with the meta-object system.
- It must be placed after the enum declaration in a class that has the Q_OBJECT or the
- Q_GADGET macro. For namespaces use \l Q_ENUM_NS() instead.
+ It must be placed after the enum declaration in a class that has the Q_OBJECT,
+ Q_GADGET or Q_GADGET_EXPORT macro. For namespaces use \l Q_ENUM_NS() instead.
For example:
@@ -4515,9 +4713,16 @@ 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 instead of Q_OBJECT to enable the meta object system's support
+ 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.
+//! [qobject-macros-private-access-specifier]
+ \note This macro expansion ends with a \c private: access specifier, which makes member
+ declarations immediately after the macro private, too. If you want add public (or protected)
+ members immediately after the macro, you need to use a \c public: (or \c protected:)
+ access specifier.
+//! [qobject-macros-private-access-specifier]
+
\sa {Meta-Object System}, {Signals and Slots}, {Qt's Property System}
*/
@@ -4535,7 +4740,38 @@ QDebug operator<<(QDebug dbg, const QObject *o)
Q_GADGET makes a class member, \c{staticMetaObject}, available.
\c{staticMetaObject} is of type QMetaObject and provides access to the
- enums declared with Q_ENUMS.
+ enums declared with Q_ENUM.
+
+ \include qobject.cpp qobject-macros-private-access-specifier
+
+ \sa Q_GADGET_EXPORT
+*/
+
+/*!
+ \macro Q_GADGET_EXPORT(EXPORT_MACRO)
+ \relates QObject
+ \since 6.3
+
+ The Q_GADGET_EXPORT macro works exactly like the Q_GADGET macro.
+ However, the \c{staticMetaObject} variable that is made available (see
+ Q_GADGET) is declared with the supplied \a EXPORT_MACRO qualifier. This is
+ useful if the object needs to be exported from a dynamic library, but the
+ enclosing class as a whole should not be (e.g. because it consists of mostly
+ inline functions).
+
+ For example:
+
+ \code
+ class Point {
+ Q_GADGET_EXPORT(EXPORT_MACRO)
+ Q_PROPERTY(int x MEMBER x)
+ Q_PROPERTY(int y MEMBER y)
+ ~~~
+ \endcode
+
+ \include qobject.cpp qobject-macros-private-access-specifier
+
+ \sa Q_GADGET, {Creating Shared Libraries}
*/
/*!
@@ -4765,17 +5001,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()
@@ -4869,6 +5122,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.
*/
@@ -4934,13 +5192,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();
}
@@ -4953,11 +5210,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,
@@ -4982,14 +5238,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");
@@ -5001,24 +5253,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();
}
}
@@ -5034,13 +5282,13 @@ QMetaObject::Connection QObjectPrivate::connectImpl(const QObject *sender, int s
std::unique_ptr<QObjectPrivate::Connection> c{new QObjectPrivate::Connection};
c->sender = s;
c->signal_index = signal_index;
- QThreadData *td = r->d_func()->threadData;
+ QThreadData *td = r->d_func()->threadData.loadAcquire();
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;
@@ -5071,7 +5319,7 @@ bool QObject::disconnect(const QMetaObject::Connection &connection)
QObjectPrivate::Connection *c = static_cast<QObjectPrivate::Connection *>(connection.d_ptr);
if (!c)
return false;
- const bool disconnected = QObjectPrivate::disconnect(c);
+ const bool disconnected = QObjectPrivate::removeConnection(c);
const_cast<QMetaObject::Connection &>(connection).d_ptr = nullptr;
c->deref(); // has been removed from the QMetaObject::Connection object
return disconnected;
@@ -5189,20 +5437,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);
}
/*!
@@ -5244,7 +5491,7 @@ bool QObjectPrivate::disconnect(const QObject *sender, int signal_index, const Q
\internal
\threadsafe
*/
-bool QObjectPrivate::disconnect(QObjectPrivate::Connection *c)
+inline bool QObjectPrivate::removeConnection(QObjectPrivate::Connection *c)
{
if (!c)
return false;
@@ -5283,6 +5530,33 @@ bool QObjectPrivate::disconnect(QObjectPrivate::Connection *c)
return true;
}
+/*!
+ \internal
+
+ Used by QPropertyAdaptorSlotObject to get an existing instance for a property, if available
+ */
+QtPrivate::QPropertyAdaptorSlotObject *
+QObjectPrivate::getPropertyAdaptorSlotObject(const QMetaProperty &property)
+{
+ if (auto conns = connections.loadAcquire()) {
+ Q_Q(QObject);
+ const QMetaObject *metaObject = q->metaObject();
+ int signal_index = methodIndexToSignalIndex(&metaObject, property.notifySignalIndex());
+ 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) {
+ if (auto p = QtPrivate::QPropertyAdaptorSlotObject::cast(c->slotObj,
+ property.propertyIndex()))
+ return p;
+ }
+ }
+ }
+ return nullptr;
+}
+
/*! \class QMetaObject::Connection
\inmodule QtCore
Represents a handle to a signal-slot (or signal-functor) connection.
diff --git a/src/corelib/kernel/qobject.h b/src/corelib/kernel/qobject.h
index e75cf3217e..06cfefd61b 100644
--- a/src/corelib/kernel/qobject.h
+++ b/src/corelib/kernel/qobject.h
@@ -1,42 +1,6 @@
-/****************************************************************************
-**
-** Copyright (C) 2020 The Qt Company Ltd.
-** Copyright (C) 2013 Olivier Goffart <ogoffart@woboq.com>
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtCore module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2020 The Qt Company Ltd.
+// Copyright (C) 2013 Olivier Goffart <ogoffart@woboq.com>
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QOBJECT_H
#define QOBJECT_H
@@ -54,16 +18,15 @@
#include <QtCore/qmetatype.h>
#include <QtCore/qobject_impl.h>
-#include <QtCore/qproperty.h>
+#include <QtCore/qbindingstorage.h>
+#include <QtCore/qtcoreexports.h>
-#if __has_include(<chrono>)
-# include <chrono>
-#endif
+#include <chrono>
QT_BEGIN_NAMESPACE
-class QBindingStorage;
+template <typename T> class QBindable;
class QEvent;
class QTimerEvent;
class QChildEvent;
@@ -81,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
{
@@ -107,8 +81,12 @@ public:
uint receiveChildEvents : 1;
uint isWindow : 1; // for QWindow
uint deleteLaterCalled : 1;
- uint unused : 24;
- int postedEvents;
+ 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 receiveParentEvents: 1;
+ uint unused : 20;
+ QAtomicInt postedEvents;
QDynamicMetaObjectData *metaObject;
QBindingStorage bindingStorage;
@@ -136,45 +114,58 @@ public:
virtual bool event(QEvent *event);
virtual bool eventFilter(QObject *watched, QEvent *event);
-#if defined(QT_NO_TRANSLATION) || defined(Q_CLANG_QDOC)
+#if defined(QT_NO_TRANSLATION) || defined(Q_QDOC)
static QString tr(const char *sourceText, const char * = nullptr, int = -1)
{ return QString::fromUtf8(sourceText); }
#endif // QT_NO_TRANSLATION
QString objectName() const;
+#if QT_CORE_REMOVED_SINCE(6, 4)
void setObjectName(const QString &name);
+#endif
+ Q_WEAK_OVERLOAD
+ void setObjectName(const QString &name) { doSetObjectName(name); }
+ void setObjectName(QAnyStringView name);
QBindable<QString> bindableObjectName();
inline bool isWidgetType() const { return d_ptr->isWidget; }
inline bool isWindowType() const { return d_ptr->isWindow; }
+ inline bool isQuickItemType() const { return d_ptr->isQuickItem; }
inline bool signalsBlocked() const noexcept { return d_ptr->blockSig; }
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 __has_include(<chrono>)
- Q_ALWAYS_INLINE
- int startTimer(std::chrono::milliseconds time, Qt::TimerType timerType = Qt::CoarseTimer)
- {
- return startTimer(int(time.count()), timerType);
- }
+
+#if QT_CORE_REMOVED_SINCE(6, 8)
+ int startTimer(std::chrono::milliseconds time, Qt::TimerType timerType = Qt::CoarseTimer);
#endif
+ int startTimer(std::chrono::nanoseconds time, Qt::TimerType timerType = Qt::CoarseTimer);
+
void killTimer(int id);
+ void killTimer(Qt::TimerId id);
template<typename T>
- inline T findChild(const QString &aName = QString(), Qt::FindChildOptions options = Qt::FindChildrenRecursively) const
+ T findChild(QAnyStringView aName, Qt::FindChildOptions options = Qt::FindChildrenRecursively) const
{
typedef typename std::remove_cv<typename std::remove_pointer<T>::type>::type ObjType;
+ static_assert(QtPrivate::HasQ_OBJECT_Macro<ObjType>::Value,
+ "No Q_OBJECT in the class passed to QObject::findChild");
return static_cast<T>(qt_qFindChild_helper(this, aName, ObjType::staticMetaObject, options));
}
template<typename T>
- inline QList<T> findChildren(const QString &aName, Qt::FindChildOptions options = Qt::FindChildrenRecursively) const
+ QList<T> findChildren(QAnyStringView aName, Qt::FindChildOptions options = Qt::FindChildrenRecursively) const
{
typedef typename std::remove_cv<typename std::remove_pointer<T>::type>::type ObjType;
+ static_assert(QtPrivate::HasQ_OBJECT_Macro<ObjType>::Value,
+ "No Q_OBJECT in the class passed to QObject::findChildren");
QList<T> list;
qt_qFindChildren_helper(this, aName, ObjType::staticMetaObject,
reinterpret_cast<QList<void *> *>(&list), options);
@@ -182,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)
@@ -196,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);
@@ -219,7 +214,7 @@ public:
inline QMetaObject::Connection connect(const QObject *sender, const char *signal,
const char *member, Qt::ConnectionType type = Qt::AutoConnection) const;
-#ifdef Q_CLANG_QDOC
+#ifdef Q_QDOC
template<typename PointerToMemberFunction>
static QMetaObject::Connection connect(const QObject *sender, PointerToMemberFunction signal, const QObject *receiver, PointerToMemberFunction method, Qt::ConnectionType type = Qt::AutoConnection);
template<typename PointerToMemberFunction, typename Functor>
@@ -227,54 +222,28 @@ public:
template<typename PointerToMemberFunction, typename Functor>
static QMetaObject::Connection connect(const QObject *sender, PointerToMemberFunction signal, const QObject *context, Functor functor, Qt::ConnectionType type = Qt::AutoConnection);
#else
- //Connect a signal to a pointer to qobject member function
+ //connect with context
template <typename Func1, typename Func2>
- static inline QMetaObject::Connection connect(const typename QtPrivate::FunctionPointer<Func1>::Object *sender, Func1 signal,
- const typename QtPrivate::FunctionPointer<Func2>::Object *receiver, Func2 slot,
- Qt::ConnectionType type = Qt::AutoConnection)
+ static inline QMetaObject::Connection
+ connect(const typename QtPrivate::FunctionPointer<Func1>::Object *sender, Func1 signal,
+ const typename QtPrivate::ContextTypeForFunctor<Func2>::ContextType *context, Func2 &&slot,
+ Qt::ConnectionType type = Qt::AutoConnection)
{
typedef QtPrivate::FunctionPointer<Func1> SignalType;
- typedef QtPrivate::FunctionPointer<Func2> SlotType;
+ typedef QtPrivate::FunctionPointer<std::decay_t<Func2>> SlotType;
- static_assert(QtPrivate::HasQ_OBJECT_Macro<typename SignalType::Object>::Value,
- "No Q_OBJECT in the class with the signal");
+ if constexpr (SlotType::ArgumentCount != -1) {
+ static_assert((QtPrivate::AreArgumentsCompatible<typename SlotType::ReturnType, typename SignalType::ReturnType>::value),
+ "Return type of the slot is not compatible with the return type of the signal.");
+ } else {
+ constexpr int FunctorArgumentCount = QtPrivate::ComputeFunctorArgumentCount<std::decay_t<Func2>, typename SignalType::Arguments>::Value;
+ [[maybe_unused]]
+ constexpr int SlotArgumentCount = (FunctorArgumentCount >= 0) ? FunctorArgumentCount : 0;
+ typedef typename QtPrivate::FunctorReturnType<std::decay_t<Func2>, typename QtPrivate::List_Left<typename SignalType::Arguments, SlotArgumentCount>::Value>::Value SlotReturnType;
- //compilation error if the arguments does not match.
- static_assert(int(SignalType::ArgumentCount) >= int(SlotType::ArgumentCount),
- "The slot requires more arguments than the signal provides.");
- static_assert((QtPrivate::CheckCompatibleArguments<typename SignalType::Arguments, typename SlotType::Arguments>::value),
- "Signal and slot arguments are not compatible.");
- static_assert((QtPrivate::AreArgumentsCompatible<typename SlotType::ReturnType, typename SignalType::ReturnType>::value),
- "Return type of the slot is not compatible with the return type of the signal.");
-
- const int *types = nullptr;
- if (type == Qt::QueuedConnection || type == Qt::BlockingQueuedConnection)
- types = QtPrivate::ConnectionTypes<typename SignalType::Arguments>::types();
-
- return connectImpl(sender, reinterpret_cast<void **>(&signal),
- receiver, reinterpret_cast<void **>(&slot),
- new QtPrivate::QSlotObject<Func2, typename QtPrivate::List_Left<typename SignalType::Arguments, SlotType::ArgumentCount>::Value,
- typename SignalType::ReturnType>(slot),
- type, types, &SignalType::Object::staticMetaObject);
- }
-
- //connect to a function pointer (not a member)
- template <typename Func1, typename Func2>
- static inline typename std::enable_if<int(QtPrivate::FunctionPointer<Func2>::ArgumentCount) >= 0, QMetaObject::Connection>::type
- connect(const typename QtPrivate::FunctionPointer<Func1>::Object *sender, Func1 signal, Func2 slot)
- {
- return connect(sender, signal, sender, slot, Qt::DirectConnection);
- }
-
- //connect to a function pointer (not a member)
- template <typename Func1, typename Func2>
- static inline typename std::enable_if<int(QtPrivate::FunctionPointer<Func2>::ArgumentCount) >= 0 &&
- !QtPrivate::FunctionPointer<Func2>::IsPointerToMemberFunction, QMetaObject::Connection>::type
- connect(const typename QtPrivate::FunctionPointer<Func1>::Object *sender, Func1 signal, const QObject *context, Func2 slot,
- Qt::ConnectionType type = Qt::AutoConnection)
- {
- typedef QtPrivate::FunctionPointer<Func1> SignalType;
- typedef QtPrivate::FunctionPointer<Func2> SlotType;
+ static_assert((QtPrivate::AreArgumentsCompatible<SlotReturnType, typename SignalType::ReturnType>::value),
+ "Return type of the slot is not compatible with the return type of the signal.");
+ }
static_assert(QtPrivate::HasQ_OBJECT_Macro<typename SignalType::Object>::Value,
"No Q_OBJECT in the class with the signal");
@@ -282,67 +251,35 @@ public:
//compilation error if the arguments does not match.
static_assert(int(SignalType::ArgumentCount) >= int(SlotType::ArgumentCount),
"The slot requires more arguments than the signal provides.");
- static_assert((QtPrivate::CheckCompatibleArguments<typename SignalType::Arguments, typename SlotType::Arguments>::value),
- "Signal and slot arguments are not compatible.");
- static_assert((QtPrivate::AreArgumentsCompatible<typename SlotType::ReturnType, typename SignalType::ReturnType>::value),
- "Return type of the slot is not compatible with the return type of the signal.");
const int *types = nullptr;
if (type == Qt::QueuedConnection || type == Qt::BlockingQueuedConnection)
types = QtPrivate::ConnectionTypes<typename SignalType::Arguments>::types();
- return connectImpl(sender, reinterpret_cast<void **>(&signal), context, nullptr,
- new QtPrivate::QStaticSlotObject<Func2,
- typename QtPrivate::List_Left<typename SignalType::Arguments, SlotType::ArgumentCount>::Value,
- typename SignalType::ReturnType>(slot),
+ void **pSlot = nullptr;
+ if constexpr (std::is_member_function_pointer_v<std::decay_t<Func2>>) {
+ pSlot = const_cast<void **>(reinterpret_cast<void *const *>(&slot));
+ } else {
+ Q_ASSERT_X((type & Qt::UniqueConnection) == 0, "",
+ "QObject::connect: Unique connection requires the slot to be a pointer to "
+ "a member function of a QObject subclass.");
+ }
+
+ return connectImpl(sender, reinterpret_cast<void **>(&signal), context, pSlot,
+ QtPrivate::makeCallableObject<Func1>(std::forward<Func2>(slot)),
type, types, &SignalType::Object::staticMetaObject);
}
- //connect to a functor
+#ifndef QT_NO_CONTEXTLESS_CONNECT
+ //connect without context
template <typename Func1, typename Func2>
- static inline typename std::enable_if<
- QtPrivate::FunctionPointer<Func2>::ArgumentCount == -1 &&
- !std::is_convertible_v<Func2, const char*>, // don't match old-style connect
- QMetaObject::Connection>::type
- connect(const typename QtPrivate::FunctionPointer<Func1>::Object *sender, Func1 signal, Func2 slot)
+ static inline QMetaObject::Connection
+ connect(const typename QtPrivate::FunctionPointer<Func1>::Object *sender, Func1 signal, Func2 &&slot)
{
- return connect(sender, signal, sender, std::move(slot), Qt::DirectConnection);
- }
-
- //connect to a functor, with a "context" object defining in which event loop is going to be executed
- template <typename Func1, typename Func2>
- static inline typename std::enable_if<
- QtPrivate::FunctionPointer<Func2>::ArgumentCount == -1 &&
- !std::is_convertible_v<Func2, const char*>, // don't match old-style connect
- QMetaObject::Connection>::type
- connect(const typename QtPrivate::FunctionPointer<Func1>::Object *sender, Func1 signal, const QObject *context, Func2 slot,
- Qt::ConnectionType type = Qt::AutoConnection)
- {
- typedef QtPrivate::FunctionPointer<Func1> SignalType;
- const int FunctorArgumentCount = QtPrivate::ComputeFunctorArgumentCount<Func2 , typename SignalType::Arguments>::Value;
-
- static_assert((FunctorArgumentCount >= 0),
- "Signal and slot arguments are not compatible.");
- const int SlotArgumentCount = (FunctorArgumentCount >= 0) ? FunctorArgumentCount : 0;
- typedef typename QtPrivate::FunctorReturnType<Func2, typename QtPrivate::List_Left<typename SignalType::Arguments, SlotArgumentCount>::Value>::Value SlotReturnType;
-
- static_assert((QtPrivate::AreArgumentsCompatible<SlotReturnType, typename SignalType::ReturnType>::value),
- "Return type of the slot is not compatible with the return type of the signal.");
-
- static_assert(QtPrivate::HasQ_OBJECT_Macro<typename SignalType::Object>::Value,
- "No Q_OBJECT in the class with the signal");
-
- const int *types = nullptr;
- if (type == Qt::QueuedConnection || type == Qt::BlockingQueuedConnection)
- types = QtPrivate::ConnectionTypes<typename SignalType::Arguments>::types();
-
- return connectImpl(sender, reinterpret_cast<void **>(&signal), context, nullptr,
- new QtPrivate::QFunctorSlotObject<Func2, SlotArgumentCount,
- typename QtPrivate::List_Left<typename SignalType::Arguments, SlotArgumentCount>::Value,
- typename SignalType::ReturnType>(std::move(slot)),
- type, types, &SignalType::Object::staticMetaObject);
+ return connect(sender, signal, sender, std::forward<Func2>(slot), Qt::DirectConnection);
}
-#endif //Q_CLANG_QDOC
+#endif // QT_NO_CONTEXTLESS_CONNECT
+#endif //Q_QDOC
static bool disconnect(const QObject *sender, const char *signal,
const QObject *receiver, const char *member);
@@ -355,7 +292,7 @@ public:
{ return disconnect(this, nullptr, receiver, member); }
static bool disconnect(const QMetaObject::Connection &);
-#ifdef Q_CLANG_QDOC
+#ifdef Q_QDOC
template<typename PointerToMemberFunction>
static bool disconnect(const QObject *sender, PointerToMemberFunction signal, const QObject *receiver, PointerToMemberFunction method);
#else
@@ -388,18 +325,18 @@ public:
return disconnectImpl(sender, reinterpret_cast<void **>(&signal), receiver, zero,
&SignalType::Object::staticMetaObject);
}
-#endif //Q_CLANG_QDOC
+#endif //Q_QDOC
void dumpObjectTree() const;
void dumpObjectInfo() const;
-#ifndef QT_NO_PROPERTIES
+ 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; }
const QBindingStorage *bindingStorage() const { return &d_ptr->bindingStorage; }
-#endif // QT_NO_PROPERTIES
Q_SIGNALS:
void destroyed(QObject * = nullptr);
@@ -447,8 +384,10 @@ protected:
friend class QThreadData;
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,
@@ -465,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");
@@ -477,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");
@@ -498,7 +454,7 @@ qobject_iid_cast(const QObject *object)
return qobject_iid_cast<std::remove_cv_t<T>>(o);
}
-#if defined(Q_CLANG_QDOC)
+#if defined(Q_QDOC)
# define Q_DECLARE_INTERFACE(IFace, IId)
#elif !defined(Q_MOC_RUN)
# define Q_DECLARE_INTERFACE(IFace, IId) \
@@ -526,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)
@@ -599,11 +559,16 @@ 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; }
}
-#define Q_SET_OBJECT_NAME(obj) QT_PREPEND_NAMESPACE(QtPrivate)::deref_for_methodcall(obj).setObjectName(QLatin1String(#obj))
+#define Q_SET_OBJECT_NAME(obj) QT_PREPEND_NAMESPACE(QtPrivate)::deref_for_methodcall(obj).setObjectName(QLatin1StringView(#obj))
QT_END_NAMESPACE
diff --git a/src/corelib/kernel/qobject_impl.h b/src/corelib/kernel/qobject_impl.h
index 0c36417a24..b57d7e50cc 100644
--- a/src/corelib/kernel/qobject_impl.h
+++ b/src/corelib/kernel/qobject_impl.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtCore module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// 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 Q_QDOC
@@ -73,30 +37,6 @@ namespace QtPrivate {
{ static const int *types() { return nullptr; } };
template <typename... Args> struct ConnectionTypes<List<Args...>, true>
{ static const int *types() { static const int t[sizeof...(Args) + 1] = { (QtPrivate::QMetaTypeIdHelper<Args>::qt_metatype_id())..., 0 }; return t; } };
-
- // implementation of QSlotObjectBase for which the slot is a static function
- // Args and R are the List of arguments and the return type of the signal to which the slot is connected.
- template<typename Func, typename Args, typename R> class QStaticSlotObject : public QSlotObjectBase
- {
- typedef QtPrivate::FunctionPointer<Func> FuncType;
- Func function;
- static void impl(int which, QSlotObjectBase *this_, QObject *r, void **a, bool *ret)
- {
- switch (which) {
- case Destroy:
- delete static_cast<QStaticSlotObject*>(this_);
- break;
- case Call:
- FuncType::template call<Args, R>(static_cast<QStaticSlotObject*>(this_)->function, r, a);
- break;
- case Compare: // not implemented
- case NumOperations:
- Q_UNUSED(ret);
- }
- }
- public:
- explicit QStaticSlotObject(Func f) : QSlotObjectBase(&impl), function(f) {}
- };
}
diff --git a/src/corelib/kernel/qobject_p.h b/src/corelib/kernel/qobject_p.h
index da171f5c7f..0ab9bf02ed 100644
--- a/src/corelib/kernel/qobject_p.h
+++ b/src/corelib/kernel/qobject_p.h
@@ -1,42 +1,6 @@
-/****************************************************************************
-**
-** Copyright (C) 2019 The Qt Company Ltd.
-** Copyright (C) 2013 Olivier Goffart <ogoffart@woboq.com>
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtCore module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2019 The Qt Company Ltd.
+// Copyright (C) 2013 Olivier Goffart <ogoffart@woboq.com>
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QOBJECT_P_H
#define QOBJECT_P_H
@@ -54,17 +18,27 @@
#include <QtCore/private/qglobal_p.h>
#include "QtCore/qcoreevent.h"
+#include <QtCore/qfunctionaltools_impl.h>
#include "QtCore/qlist.h"
#include "QtCore/qobject.h"
#include "QtCore/qpointer.h"
-#include "QtCore/qreadwritelock.h"
-#include "QtCore/qsharedpointer.h"
#include "QtCore/qvariant.h"
#include "QtCore/qproperty.h"
+#include <QtCore/qshareddata.h>
#include "QtCore/private/qproperty_p.h"
+#include <string>
+
QT_BEGIN_NAMESPACE
+#ifdef Q_MOC_RUN
+#define QT_ANONYMOUS_PROPERTY(text) QT_ANONYMOUS_PROPERTY(text)
+#define QT_ANONYMOUS_PRIVATE_PROPERTY(d, text) QT_ANONYMOUS_PRIVATE_PROPERTY(d, text)
+#elif !defined QT_NO_META_MACROS
+#define QT_ANONYMOUS_PROPERTY(...) QT_ANNOTATE_CLASS(qt_anonymous_property, __VA_ARGS__)
+#define QT_ANONYMOUS_PRIVATE_PROPERTY(d, text) QT_ANNOTATE_CLASS2(qt_anonymous_private_property, d, text)
+#endif
+
class QVariant;
class QThreadData;
class QObjectConnectionListVector;
@@ -98,9 +72,9 @@ public:
class Q_CORE_EXPORT QObjectPrivate : public QObjectData
{
+public:
Q_DECLARE_PUBLIC(QObject)
-public:
struct ExtraData
{
ExtraData(QObjectPrivate *ptr) : parent(ptr) { }
@@ -117,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,
@@ -125,125 +99,20 @@ public:
QObjectPrivate *parent;
};
+ void ensureExtraData()
+ {
+ if (!extraData)
+ extraData = new ExtraData(this);
+ }
+
typedef void (*StaticMetaCallFunction)(QObject *, QMetaObject::Call, int, void **);
struct Connection;
+ struct ConnectionData;
+ struct ConnectionList;
+ struct ConnectionOrSignalVector;
struct SignalVector;
-
- struct ConnectionOrSignalVector {
- union {
- // linked list of orphaned connections that need cleaning up
- ConnectionOrSignalVector *nextInOrphanList;
- // linked list of connections connected to slots in this object
- Connection *next;
- };
-
- static SignalVector *asSignalVector(ConnectionOrSignalVector *c)
- {
- if (reinterpret_cast<quintptr>(c) & 1)
- return reinterpret_cast<SignalVector *>(reinterpret_cast<quintptr>(c) & ~quintptr(1u));
- return nullptr;
- }
- static Connection *fromSignalVector(SignalVector *v) {
- return reinterpret_cast<Connection *>(reinterpret_cast<quintptr>(v) | quintptr(1u));
- }
- };
-
- struct Connection : public ConnectionOrSignalVector
- {
- // linked list of connections connected to slots in this object, next is in base class
- Connection **prev;
- // linked list of connections connected to signals in this object
- QAtomicPointer<Connection> nextConnectionList;
- Connection *prevConnectionList;
-
- QObject *sender;
- QAtomicPointer<QObject> receiver;
- QAtomicPointer<QThreadData> receiverThreadData;
- union {
- StaticMetaCallFunction callFunction;
- QtPrivate::QSlotObjectBase *slotObj;
- };
- QAtomicPointer<const int> argumentTypes;
- QAtomicInt ref_;
- uint id = 0;
- ushort method_offset;
- ushort method_relative;
- signed int signal_index : 27; // In signal range (see QObjectPrivate::signalIndex())
- ushort connectionType : 2; // 0 == auto, 1 == direct, 2 == queued, 3 == blocking
- ushort isSlotObject : 1;
- ushort ownArgumentTypes : 1;
- ushort isSingleShot : 1;
- Connection() : ref_(2), ownArgumentTypes(true) {
- //ref_ is 2 for the use in the internal lists, and for the use in QMetaObject::Connection
- }
- ~Connection();
- int method() const { Q_ASSERT(!isSlotObject); return method_offset + method_relative; }
- void ref() { ref_.ref(); }
- void freeSlotObject()
- {
- if (isSlotObject) {
- slotObj->destroyIfLastRef();
- isSlotObject = false;
- }
- }
- void deref()
- {
- if (!ref_.deref()) {
- Q_ASSERT(!receiver.loadRelaxed());
- Q_ASSERT(!isSlotObject);
- delete this;
- }
- }
- };
- // ConnectionList is a singly-linked list
- struct ConnectionList {
- QAtomicPointer<Connection> first;
- QAtomicPointer<Connection> last;
- };
-
- struct Sender
- {
- Sender(QObject *receiver, QObject *sender, int signal)
- : receiver(receiver), sender(sender), signal(signal)
- {
- if (receiver) {
- ConnectionData *cd = receiver->d_func()->connections.loadRelaxed();
- previous = cd->currentSender;
- cd->currentSender = this;
- }
- }
- ~Sender()
- {
- if (receiver)
- receiver->d_func()->connections.loadRelaxed()->currentSender = previous;
- }
- void receiverDeleted()
- {
- Sender *s = this;
- while (s) {
- s->receiver = nullptr;
- s = s->previous;
- }
- }
- Sender *previous;
- QObject *receiver;
- QObject *sender;
- int signal;
- };
-
- struct SignalVector : public ConnectionOrSignalVector {
- quintptr allocated;
- // ConnectionList signals[]
- ConnectionList &at(int i)
- {
- return reinterpret_cast<ConnectionList *>(this + 1)[i + 1];
- }
- const ConnectionList &at(int i) const
- {
- return reinterpret_cast<const ConnectionList *>(this + 1)[i + 1];
- }
- int count() const { return static_cast<int>(allocated); }
- };
+ struct Sender;
+ struct TaggedSignalVector;
/*
This contains the all connections from and to an object.
@@ -259,86 +128,6 @@ public:
to a slot in this object. The mutex of the receiver must be locked when touching the pointers of this
linked list.
*/
- struct ConnectionData {
- // the id below is used to avoid activating new connections. When the object gets
- // deleted it's set to 0, so that signal emission stops
- QAtomicInteger<uint> currentConnectionId;
- QAtomicInt ref;
- QAtomicPointer<SignalVector> signalVector;
- Connection *senders = nullptr;
- Sender *currentSender = nullptr; // object currently activating the object
- QAtomicPointer<Connection> orphaned;
-
- ~ConnectionData()
- {
- Q_ASSERT(ref.loadRelaxed() == 0);
- auto *c = orphaned.fetchAndStoreRelaxed(nullptr);
- if (c)
- deleteOrphaned(c);
- SignalVector *v = signalVector.loadRelaxed();
- if (v)
- free(v);
- }
-
- // must be called on the senders connection data
- // assumes the senders and receivers lock are held
- void removeConnection(Connection *c);
- enum LockPolicy {
- NeedToLock,
- // Beware that we need to temporarily release the lock
- // and thus calling code must carefully consider whether
- // invariants still hold.
- AlreadyLockedAndTemporarilyReleasingLock
- };
- void cleanOrphanedConnections(QObject *sender, LockPolicy lockPolicy = NeedToLock)
- {
- if (orphaned.loadRelaxed() && ref.loadAcquire() == 1)
- cleanOrphanedConnectionsImpl(sender, lockPolicy);
- }
- void cleanOrphanedConnectionsImpl(QObject *sender, LockPolicy lockPolicy);
-
- ConnectionList &connectionsForSignal(int signal)
- {
- return signalVector.loadRelaxed()->at(signal);
- }
-
- void resizeSignalVector(uint size)
- {
- SignalVector *vector = this->signalVector.loadRelaxed();
- if (vector && vector->allocated > size)
- return;
- size = (size + 7) & ~7;
- SignalVector *newVector = reinterpret_cast<SignalVector *>(malloc(sizeof(SignalVector) + (size + 1) * sizeof(ConnectionList)));
- int start = -1;
- if (vector) {
- memcpy(newVector, vector, sizeof(SignalVector) + (vector->allocated + 1) * sizeof(ConnectionList));
- start = vector->count();
- }
- for (int i = start; i < int(size); ++i)
- newVector->at(i) = ConnectionList();
- newVector->next = nullptr;
- newVector->allocated = size;
-
- signalVector.storeRelaxed(newVector);
- if (vector) {
- Connection *o = nullptr;
- /* No ABA issue here: When adding a node, we only care about the list head, it doesn't
- * matter if the tail changes.
- */
- do {
- o = orphaned.loadRelaxed();
- vector->nextInOrphanList = o;
- } while (!orphaned.testAndSetRelease(o, ConnectionOrSignalVector::fromSignalVector(vector)));
-
- }
- }
- int signalVectorCount() const
- {
- return signalVector.loadAcquire() ? signalVector.loadRelaxed()->count() : -1;
- }
-
- static void deleteOrphaned(ConnectionOrSignalVector *c);
- };
QObjectPrivate(int version = QObjectPrivateVersion);
virtual ~QObjectPrivate();
@@ -350,14 +139,13 @@ public:
void setParent_helper(QObject *);
void moveToThread_helper();
- void setThreadData_helper(QThreadData *currentData, QThreadData *targetData);
- void _q_reregisterTimers(void *pointer);
+ void setThreadData_helper(QThreadData *currentData, QThreadData *targetData, QBindingStatus *status);
- bool isSender(const QObject *receiver, const char *signal) const;
QObjectList receiverList(const char *signal) const;
- QObjectList senderList() const;
- void addConnection(int signal, Connection *c);
+ inline void ensureConnectionData();
+ inline void addConnection(int signal, Connection *c);
+ static inline bool removeConnection(Connection *c);
static QObjectPrivate *get(QObject *o) { return o->d_func(); }
static const QObjectPrivate *get(const QObject *o) { return o->d_func(); }
@@ -372,6 +160,8 @@ public:
inline void connectNotify(const QMetaMethod &signal);
inline void disconnectNotify(const QMetaMethod &signal);
+ void reinitBindingStorageAfterThreadMove();
+
template <typename Func1, typename Func2>
static inline QMetaObject::Connection connect(const typename QtPrivate::FunctionPointer<Func1>::Object *sender, Func1 signal,
const typename QtPrivate::FunctionPointer<Func2>::Object *receiverPrivate, Func2 slot,
@@ -393,16 +183,15 @@ public:
static bool disconnect(const QObject *sender, int signal_index, void **slot);
static bool disconnect(const QObject *sender, int signal_index, const QObject *receiver,
void **slot);
- static bool disconnect(Connection *c);
- void ensureConnectionData()
- {
- if (connections.loadRelaxed())
- return;
- ConnectionData *cd = new ConnectionData;
- cd->ref.ref();
- connections.storeRelaxed(cd);
- }
+ virtual std::string flagsForDumping() const;
+
+#ifndef QT_NO_DEBUG_STREAM
+ virtual void writeToDebugStream(QDebug &) const;
+#endif
+
+ QtPrivate::QPropertyAdaptorSlotObject *
+ getPropertyAdaptorSlotObject(const QMetaProperty &property);
public:
mutable ExtraData *extraData; // extra data set by the user
@@ -426,8 +215,6 @@ public:
QAtomicPointer<QtSharedPointer::ExternalRefCountData> sharedRefcount;
};
-Q_DECLARE_TYPEINFO(QObjectPrivate::ConnectionList, Q_RELOCATABLE_TYPE);
-
/*
Catch mixing of incompatible library versions.
@@ -452,7 +239,7 @@ inline void QObjectPrivate::checkForIncompatibleLibraryVersion(int version) cons
inline bool QObjectPrivate::isDeclarativeSignalConnected(uint signal_index) const
{
- return declarativeData && QAbstractDeclarativeData::isSignalConnected
+ return !isDeletingChildren && declarativeData && QAbstractDeclarativeData::isSignalConnected
&& QAbstractDeclarativeData::isSignalConnected(declarativeData, q_func(), signal_index);
}
@@ -467,47 +254,38 @@ inline void QObjectPrivate::disconnectNotify(const QMetaMethod &signal)
}
namespace QtPrivate {
+inline const QObject *getQObject(const QObjectPrivate *d) { return d->q_func(); }
template <typename Func>
-struct FunctionStorageByValue
-{
- Func f;
- Func &func() noexcept { return f; }
-};
+using FunctionStorage = QtPrivate::CompactStorage<Func>;
-template <typename Func>
-struct FunctionStorageEmptyBaseClassOptimization : Func
+template <typename ObjPrivate> inline void assertObjectType(QObjectPrivate *d)
{
- Func &func() noexcept { return *this; }
- using Func::Func;
-};
-
-template <typename Func>
-using FunctionStorage = typename std::conditional_t<
- std::conjunction_v<
- std::is_empty<Func>,
- std::negation<std::is_final<Func>>
- >,
- FunctionStorageEmptyBaseClassOptimization<Func>,
- FunctionStorageByValue<Func>
- >;
+ using Obj = std::remove_pointer_t<decltype(std::declval<ObjPrivate *>()->q_func())>;
+ assertObjectType<Obj>(d->q_ptr);
+}
template<typename Func, typename Args, typename R>
class QPrivateSlotObject : public QSlotObjectBase, private FunctionStorage<Func>
{
typedef QtPrivate::FunctionPointer<Func> FuncType;
+#if QT_VERSION < QT_VERSION_CHECK(7, 0, 0)
static void impl(int which, QSlotObjectBase *this_, QObject *r, void **a, bool *ret)
+#else
+ static void impl(QSlotObjectBase *this_, QObject *r, void **a, int which, bool *ret)
+#endif
{
+ const auto that = static_cast<QPrivateSlotObject*>(this_);
switch (which) {
case Destroy:
- delete static_cast<QPrivateSlotObject*>(this_);
+ delete that;
break;
case Call:
- FuncType::template call<Args, R>(static_cast<QPrivateSlotObject*>(this_)->func(),
+ FuncType::template call<Args, R>(that->object(),
static_cast<typename FuncType::Object *>(QObjectPrivate::get(r)), a);
break;
case Compare:
- *ret = *reinterpret_cast<Func *>(a) == static_cast<QPrivateSlotObject*>(this_)->func();
+ *ret = *reinterpret_cast<Func *>(a) == that->object();
break;
case NumOperations: ;
}
@@ -540,7 +318,7 @@ inline QMetaObject::Connection QObjectPrivate::connect(const typename QtPrivate:
types = QtPrivate::ConnectionTypes<typename SignalType::Arguments>::types();
return QObject::connectImpl(sender, reinterpret_cast<void **>(&signal),
- receiverPrivate->q_ptr, reinterpret_cast<void **>(&slot),
+ QtPrivate::getQObject(receiverPrivate), reinterpret_cast<void **>(&slot),
new QtPrivate::QPrivateSlotObject<Func2, typename QtPrivate::List_Left<typename SignalType::Arguments, SlotType::ArgumentCount>::Value,
typename SignalType::ReturnType>(slot),
type, types, &SignalType::Object::staticMetaObject);
@@ -562,9 +340,6 @@ bool QObjectPrivate::disconnect(const typename QtPrivate::FunctionPointer< Func1
&SignalType::Object::staticMetaObject);
}
-Q_DECLARE_TYPEINFO(QObjectPrivate::Connection, Q_RELOCATABLE_TYPE);
-Q_DECLARE_TYPEINFO(QObjectPrivate::Sender, Q_RELOCATABLE_TYPE);
-
class QSemaphore;
class Q_CORE_EXPORT QAbstractMetaCallEvent : public QEvent
{
@@ -601,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,
@@ -610,9 +388,31 @@ public:
QMetaCallEvent(QtPrivate::QSlotObjectBase *slotObj,
const QObject *sender, int signalId,
int nargs);
+ QMetaCallEvent(QtPrivate::SlotObjUniquePtr slotObj,
+ const QObject *sender, int signalId,
+ int nargs);
~QMetaCallEvent() override;
+ template<typename ...Args>
+ static QMetaCallEvent *create(QtPrivate::QSlotObjectBase *slotObj, const QObject *sender,
+ int signal_index, const Args &...argv)
+ {
+ const void* const argp[] = { nullptr, std::addressof(argv)... };
+ const QMetaType metaTypes[] = { QMetaType::fromType<void>(), QMetaType::fromType<Args>()... };
+ constexpr auto argc = sizeof...(Args) + 1;
+ return create_impl(slotObj, sender, signal_index, argc, argp, metaTypes);
+ }
+ template<typename ...Args>
+ static QMetaCallEvent *create(QtPrivate::SlotObjUniquePtr slotObj, const QObject *sender,
+ int signal_index, const Args &...argv)
+ {
+ const void* const argp[] = { nullptr, std::addressof(argv)... };
+ const QMetaType metaTypes[] = { QMetaType::fromType<void>(), QMetaType::fromType<Args>()... };
+ constexpr auto argc = sizeof...(Args) + 1;
+ return create_impl(std::move(slotObj), sender, signal_index, argc, argp, metaTypes);
+ }
+
inline int id() const { return d.method_offset_ + d.method_relative_; }
inline const void * const* args() const { return d.args_; }
inline void ** args() { return d.args_; }
@@ -622,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_;
@@ -640,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:
@@ -648,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
new file mode 100644
index 0000000000..2277af0497
--- /dev/null
+++ b/src/corelib/kernel/qobject_p_p.h
@@ -0,0 +1,257 @@
+// Copyright (C) 2019 The Qt Company Ltd.
+// Copyright (C) 2013 Olivier Goffart <ogoffart@woboq.com>
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QOBJECT_P_P_H
+#define QOBJECT_P_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of qobject.cpp. This header file may change from version to version
+// without notice, or even be removed.
+//
+// We mean it.
+//
+
+// Even though this file is only used by qobject.cpp, the only reason this
+// code lives here is that some special apps/libraries for e.g., QtJambi,
+// Gammaray need access to the structs in this file.
+
+#include <QtCore/qobject.h>
+#include <QtCore/private/qobject_p.h>
+
+QT_BEGIN_NAMESPACE
+
+// ConnectionList is a singly-linked list
+struct QObjectPrivate::ConnectionList
+{
+ QAtomicPointer<Connection> first;
+ QAtomicPointer<Connection> last;
+};
+static_assert(std::is_trivially_destructible_v<QObjectPrivate::ConnectionList>);
+Q_DECLARE_TYPEINFO(QObjectPrivate::ConnectionList, Q_RELOCATABLE_TYPE);
+
+struct QObjectPrivate::TaggedSignalVector
+{
+ quintptr c;
+
+ TaggedSignalVector() = default;
+ TaggedSignalVector(std::nullptr_t) noexcept : c(0) {}
+ TaggedSignalVector(Connection *v) noexcept : c(reinterpret_cast<quintptr>(v)) { Q_ASSERT(v && (reinterpret_cast<quintptr>(v) & 0x1) == 0); }
+ TaggedSignalVector(SignalVector *v) noexcept : c(reinterpret_cast<quintptr>(v) | quintptr(1u)) { Q_ASSERT(v); }
+ explicit operator SignalVector *() const noexcept
+ {
+ if (c & 0x1)
+ return reinterpret_cast<SignalVector *>(c & ~quintptr(1u));
+ return nullptr;
+ }
+ explicit operator Connection *() const noexcept
+ {
+ return reinterpret_cast<Connection *>(c);
+ }
+ operator uintptr_t() const noexcept { return c; }
+};
+
+struct QObjectPrivate::ConnectionOrSignalVector
+{
+ union {
+ // linked list of orphaned connections that need cleaning up
+ TaggedSignalVector nextInOrphanList;
+ // linked list of connections connected to slots in this object
+ Connection *next;
+ };
+};
+static_assert(std::is_trivial_v<QObjectPrivate::ConnectionOrSignalVector>);
+
+struct QObjectPrivate::Connection : public ConnectionOrSignalVector
+{
+ // linked list of connections connected to slots in this object, next is in base class
+ Connection **prev;
+ // linked list of connections connected to signals in this object
+ QAtomicPointer<Connection> nextConnectionList;
+ Connection *prevConnectionList;
+
+ QObject *sender;
+ QAtomicPointer<QObject> receiver;
+ QAtomicPointer<QThreadData> receiverThreadData;
+ union {
+ StaticMetaCallFunction callFunction;
+ QtPrivate::QSlotObjectBase *slotObj;
+ };
+ QAtomicPointer<const int> argumentTypes;
+ QAtomicInt ref_{
+ 2
+ }; // ref_ is 2 for the use in the internal lists, and for the use in QMetaObject::Connection
+ uint id = 0;
+ ushort method_offset;
+ ushort method_relative;
+ signed int signal_index : 27; // In signal range (see QObjectPrivate::signalIndex())
+ ushort connectionType : 2; // 0 == auto, 1 == direct, 2 == queued, 3 == blocking
+ ushort isSlotObject : 1;
+ ushort ownArgumentTypes : 1;
+ ushort isSingleShot : 1;
+ Connection() : ownArgumentTypes(true) { }
+ ~Connection();
+ int method() const
+ {
+ Q_ASSERT(!isSlotObject);
+ return method_offset + method_relative;
+ }
+ void ref() { ref_.ref(); }
+ void freeSlotObject()
+ {
+ if (isSlotObject) {
+ slotObj->destroyIfLastRef();
+ isSlotObject = false;
+ }
+ }
+ void deref()
+ {
+ if (!ref_.deref()) {
+ Q_ASSERT(!receiver.loadRelaxed());
+ Q_ASSERT(!isSlotObject);
+ delete this;
+ }
+ }
+};
+Q_DECLARE_TYPEINFO(QObjectPrivate::Connection, Q_RELOCATABLE_TYPE);
+
+struct QObjectPrivate::SignalVector : public ConnectionOrSignalVector
+{
+ quintptr allocated;
+ // ConnectionList signals[]
+ ConnectionList &at(int i) { return reinterpret_cast<ConnectionList *>(this + 1)[i + 1]; }
+ const ConnectionList &at(int i) const
+ {
+ return reinterpret_cast<const ConnectionList *>(this + 1)[i + 1];
+ }
+ int count() const { return static_cast<int>(allocated); }
+};
+static_assert(
+ std::is_trivial_v<QObjectPrivate::SignalVector>); // it doesn't need to be, but it helps
+
+struct QObjectPrivate::ConnectionData
+{
+ // the id below is used to avoid activating new connections. When the object gets
+ // deleted it's set to 0, so that signal emission stops
+ QAtomicInteger<uint> currentConnectionId;
+ QAtomicInt ref;
+ QAtomicPointer<SignalVector> signalVector;
+ Connection *senders = nullptr;
+ Sender *currentSender = nullptr; // object currently activating the object
+ std::atomic<TaggedSignalVector> orphaned = {};
+
+ ~ConnectionData()
+ {
+ Q_ASSERT(ref.loadRelaxed() == 0);
+ TaggedSignalVector c = orphaned.exchange(nullptr, std::memory_order_relaxed);
+ if (c)
+ deleteOrphaned(c);
+ SignalVector *v = signalVector.loadRelaxed();
+ if (v) {
+ v->~SignalVector();
+ free(v);
+ }
+ }
+
+ // must be called on the senders connection data
+ // assumes the senders and receivers lock are held
+ void removeConnection(Connection *c);
+ enum LockPolicy {
+ NeedToLock,
+ // Beware that we need to temporarily release the lock
+ // and thus calling code must carefully consider whether
+ // invariants still hold.
+ AlreadyLockedAndTemporarilyReleasingLock
+ };
+ void cleanOrphanedConnections(QObject *sender, LockPolicy lockPolicy = NeedToLock)
+ {
+ if (orphaned.load(std::memory_order_relaxed) && ref.loadAcquire() == 1)
+ cleanOrphanedConnectionsImpl(sender, lockPolicy);
+ }
+ void cleanOrphanedConnectionsImpl(QObject *sender, LockPolicy lockPolicy);
+
+ ConnectionList &connectionsForSignal(int signal)
+ {
+ return signalVector.loadRelaxed()->at(signal);
+ }
+
+ void resizeSignalVector(uint size)
+ {
+ SignalVector *vector = this->signalVector.loadRelaxed();
+ if (vector && vector->allocated > size)
+ return;
+ size = (size + 7) & ~7;
+ void *ptr = malloc(sizeof(SignalVector) + (size + 1) * sizeof(ConnectionList));
+ auto newVector = new (ptr) SignalVector;
+
+ int start = -1;
+ if (vector) {
+ // not (yet) existing trait:
+ // static_assert(std::is_relocatable_v<SignalVector>);
+ // static_assert(std::is_relocatable_v<ConnectionList>);
+ memcpy(newVector, vector,
+ sizeof(SignalVector) + (vector->allocated + 1) * sizeof(ConnectionList));
+ start = vector->count();
+ }
+ for (int i = start; i < int(size); ++i)
+ new (&newVector->at(i)) ConnectionList();
+ newVector->next = nullptr;
+ newVector->allocated = size;
+
+ signalVector.storeRelaxed(newVector);
+ if (vector) {
+ TaggedSignalVector o = nullptr;
+ /* No ABA issue here: When adding a node, we only care about the list head, it doesn't
+ * matter if the tail changes.
+ */
+ o = orphaned.load(std::memory_order_acquire);
+ do {
+ vector->nextInOrphanList = o;
+ } while (!orphaned.compare_exchange_strong(o, TaggedSignalVector(vector), std::memory_order_release));
+ }
+ }
+ int signalVectorCount() const
+ {
+ return signalVector.loadAcquire() ? signalVector.loadRelaxed()->count() : -1;
+ }
+
+ static void deleteOrphaned(TaggedSignalVector o);
+};
+
+struct QObjectPrivate::Sender
+{
+ Sender(QObject *receiver, QObject *sender, int signal, ConnectionData *receiverConnections)
+ : receiver(receiver), sender(sender), signal(signal)
+ {
+ if (receiverConnections) {
+ previous = receiverConnections->currentSender;
+ receiverConnections->currentSender = this;
+ }
+ }
+ ~Sender()
+ {
+ if (receiver)
+ receiver->d_func()->connections.loadAcquire()->currentSender = previous;
+ }
+ void receiverDeleted()
+ {
+ Sender *s = this;
+ while (s) {
+ s->receiver = nullptr;
+ s = s->previous;
+ }
+ }
+ Sender *previous = nullptr;
+ QObject *receiver;
+ QObject *sender;
+ int signal;
+};
+Q_DECLARE_TYPEINFO(QObjectPrivate::Sender, Q_RELOCATABLE_TYPE);
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/corelib/kernel/qobjectcleanuphandler.cpp b/src/corelib/kernel/qobjectcleanuphandler.cpp
index 831874424f..f46afc2f07 100644
--- a/src/corelib/kernel/qobjectcleanuphandler.cpp
+++ b/src/corelib/kernel/qobjectcleanuphandler.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtCore module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// 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 "qobjectcleanuphandler.h"
@@ -96,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;
}
@@ -112,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/qobjectcleanuphandler.h b/src/corelib/kernel/qobjectcleanuphandler.h
index add9cf4ec5..b01ed94d47 100644
--- a/src/corelib/kernel/qobjectcleanuphandler.h
+++ b/src/corelib/kernel/qobjectcleanuphandler.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtCore module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// 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 QOBJECTCLEANUPHANDLER_H
#define QOBJECTCLEANUPHANDLER_H
diff --git a/src/corelib/kernel/qobjectdefs.h b/src/corelib/kernel/qobjectdefs.h
index 00a6b62eab..190901d5d1 100644
--- a/src/corelib/kernel/qobjectdefs.h
+++ b/src/corelib/kernel/qobjectdefs.h
@@ -1,42 +1,6 @@
-/****************************************************************************
-**
-** Copyright (C) 2020 The Qt Company Ltd.
-** Copyright (C) 2019 Intel Corporation.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtCore module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2022 The Qt Company Ltd.
+// Copyright (C) 2019 Intel Corporation.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QOBJECTDEFS_H
#define QOBJECTDEFS_H
@@ -47,6 +11,7 @@
#include <QtCore/qnamespace.h>
#include <QtCore/qobjectdefs_impl.h>
+#include <QtCore/qtcoreexports.h>
#include <QtCore/qtmetamacros.h>
QT_BEGIN_NAMESPACE
@@ -72,28 +37,31 @@ class QString;
Q_CORE_EXPORT const char *qFlagLocation(const char *method);
#ifndef QT_NO_META_MACROS
-#ifndef QT_NO_DEBUG
-# define QLOCATION "\0" __FILE__ ":" QT_STRINGIFY(__LINE__)
-# ifndef QT_NO_KEYWORDS
-# define METHOD(a) qFlagLocation("0"#a QLOCATION)
+# define QMETHOD_CODE 0 // member type codes
+# define QSLOT_CODE 1
+# define QSIGNAL_CODE 2
+# define QT_PREFIX_CODE(code, a) QT_STRINGIFY(code) #a
+# define QT_STRINGIFY_METHOD(a) QT_PREFIX_CODE(QMETHOD_CODE, a)
+# define QT_STRINGIFY_SLOT(a) QT_PREFIX_CODE(QSLOT_CODE, a)
+# define QT_STRINGIFY_SIGNAL(a) QT_PREFIX_CODE(QSIGNAL_CODE, a)
+# ifndef QT_NO_DEBUG
+# define QLOCATION "\0" __FILE__ ":" QT_STRINGIFY(__LINE__)
+# ifndef QT_NO_KEYWORDS
+# define METHOD(a) qFlagLocation(QT_STRINGIFY_METHOD(a) QLOCATION)
+# endif
+# define SLOT(a) qFlagLocation(QT_STRINGIFY_SLOT(a) QLOCATION)
+# define SIGNAL(a) qFlagLocation(QT_STRINGIFY_SIGNAL(a) QLOCATION)
+# else
+# ifndef QT_NO_KEYWORDS
+# define METHOD(a) QT_STRINGIFY_METHOD(a)
+# endif
+# define SLOT(a) QT_STRINGIFY_SLOT(a)
+# define SIGNAL(a) QT_STRINGIFY_SIGNAL(a)
# endif
-# define SLOT(a) qFlagLocation("1"#a QLOCATION)
-# define SIGNAL(a) qFlagLocation("2"#a QLOCATION)
-#else
-# ifndef QT_NO_KEYWORDS
-# define METHOD(a) "0"#a
-# endif
-# define SLOT(a) "1"#a
-# define SIGNAL(a) "2"#a
-#endif
-
-#define QMETHOD_CODE 0 // member type codes
-#define QSLOT_CODE 1
-#define QSIGNAL_CODE 2
#endif // QT_NO_META_MACROS
-#define Q_ARG(type, data) QArgument<type >(#type, data)
-#define Q_RETURN_ARG(type, data) QReturnArgument<type >(#type, data)
+#define Q_ARG(Type, data) QtPrivate::Invoke::argument<Type>(QT_STRINGIFY(Type), data)
+#define Q_RETURN_ARG(Type, data) QtPrivate::Invoke::returnArgument<Type>(QT_STRINGIFY(Type), data)
class QObject;
class QMetaMethod;
@@ -103,6 +71,7 @@ class QMetaClassInfo;
namespace QtPrivate {
class QMetaTypeInterface;
+template<typename T> constexpr const QMetaTypeInterface *qMetaTypeInterfaceForType();
}
struct QMethodRawArguments
@@ -110,6 +79,7 @@ struct QMethodRawArguments
void **arguments;
};
+#if QT_VERSION < QT_VERSION_CHECK(7, 0, 0)
class Q_CORE_EXPORT QGenericArgument
{
public:
@@ -157,6 +127,108 @@ public:
: QGenericReturnArgument(aName, static_cast<void *>(&aData))
{}
};
+#endif
+
+struct QMetaMethodArgument
+{
+ const QtPrivate::QMetaTypeInterface *metaType;
+ const char *name;
+ const void *data;
+};
+
+struct QMetaMethodReturnArgument
+{
+ const QtPrivate::QMetaTypeInterface *metaType;
+ const char *name;
+ void *data;
+};
+
+template <typename T>
+struct QTemplatedMetaMethodReturnArgument : QMetaMethodReturnArgument
+{
+ using Type = T;
+};
+
+namespace QtPrivate {
+namespace Invoke {
+#if QT_VERSION <= QT_VERSION_CHECK(7, 0, 0)
+template <typename... Args>
+using AreOldStyleArgs = std::disjunction<std::is_base_of<QGenericArgument, Args>...>;
+
+template <typename T, typename... Args> using IfNotOldStyleArgs =
+ std::enable_if_t<!AreOldStyleArgs<Args...>::value, T>;
+#else
+template <typename T, typename... Args> using IfNotOldStyleArgs = T;
+#endif
+
+template <typename T> inline QMetaMethodArgument argument(const char *name, const T &t)
+{
+ if constexpr ((std::is_lvalue_reference_v<T> && std::is_const_v<std::remove_reference_t<T>>) ||
+ !std::is_reference_v<T>) {
+ return { qMetaTypeInterfaceForType<T>(), name, std::addressof(t) };
+ } else {
+ return { nullptr, name, std::addressof(t) };
+ }
+}
+
+template <typename T>
+inline QTemplatedMetaMethodReturnArgument<T> returnArgument(const char *name, T &t)
+{
+ return { qMetaTypeInterfaceForType<T>(), name, std::addressof(t) };
+}
+
+template <typename T> inline const char *typenameHelper(const T &)
+{
+ return nullptr;
+}
+template <typename T> inline const void *dataHelper(const T &t)
+{
+ return std::addressof(t);
+}
+template <typename T> inline const QMetaTypeInterface *metaTypeHelper(const T &)
+{
+ return qMetaTypeInterfaceForType<T>();
+}
+
+inline const char *typenameHelper(QMetaMethodArgument a)
+{ return a.name; }
+inline const void *dataHelper(QMetaMethodArgument a)
+{ return a.data; }
+inline const QMetaTypeInterface *metaTypeHelper(QMetaMethodArgument a)
+{ return a.metaType; }
+
+inline const char *typenameHelper(const char *) = delete;
+template <typename T> inline const void *dataHelper(const char *) = delete;
+inline const QMetaTypeInterface *metaTypeHelper(const char *) = delete;
+inline const char *typenameHelper(const char16_t *) = delete;
+template <typename T> inline const void *dataHelper(const char16_t *) = delete;
+inline const QMetaTypeInterface *metaTypeHelper(const char16_t *) = delete;
+
+} // namespace QtPrivate::Invoke
+
+template <typename... Args> inline auto invokeMethodHelper(QMetaMethodReturnArgument r, const Args &... arguments)
+{
+ std::array params = { const_cast<const void *>(r.data), Invoke::dataHelper(arguments)... };
+ std::array names = { r.name, Invoke::typenameHelper(arguments)... };
+ std::array types = { r.metaType, Invoke::metaTypeHelper(arguments)... };
+ static_assert(params.size() == types.size());
+ static_assert(params.size() == names.size());
+
+ struct R {
+ decltype(params) parameters;
+ decltype(names) typeNames;
+ decltype(types) metaTypes;
+ constexpr qsizetype parameterCount() const { return qsizetype(parameters.size()); }
+ };
+ return R { params, names, types };
+}
+} // namespace QtPrivate
+
+template <typename T> void qReturnArg(const T &&) = delete;
+template <typename T> inline QTemplatedMetaMethodReturnArgument<T> qReturnArg(T &data)
+{
+ return QtPrivate::Invoke::returnArgument(nullptr, data);
+}
struct Q_CORE_EXPORT QMetaObject
{
@@ -169,7 +241,7 @@ struct Q_CORE_EXPORT QMetaObject
{ return const_cast<QObject *>(cast(const_cast<const QObject *>(obj))); }
const QObject *cast(const QObject *obj) const;
-#if !defined(QT_NO_TRANSLATION) || defined(Q_CLANG_QDOC)
+#if !defined(QT_NO_TRANSLATION) || defined(Q_QDOC)
QString tr(const char *s, const char *c, int n = -1) const;
#endif // QT_NO_TRANSLATION
@@ -191,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;
@@ -224,6 +297,7 @@ struct Q_CORE_EXPORT QMetaObject
static void activate(QObject *sender, const QMetaObject *, int local_signal_index, void **argv);
static void activate(QObject *sender, int signal_offset, int local_signal_index, void **argv);
+#if QT_VERSION <= QT_VERSION_CHECK(7, 0, 0)
static bool invokeMethod(QObject *obj, const char *member,
Qt::ConnectionType,
QGenericReturnArgument ret,
@@ -257,7 +331,7 @@ struct Q_CORE_EXPORT QMetaObject
static inline bool invokeMethod(QObject *obj, const char *member,
Qt::ConnectionType type,
- QGenericArgument val0 = QGenericArgument(nullptr),
+ QGenericArgument val0,
QGenericArgument val1 = QGenericArgument(),
QGenericArgument val2 = QGenericArgument(),
QGenericArgument val3 = QGenericArgument(),
@@ -273,7 +347,7 @@ struct Q_CORE_EXPORT QMetaObject
}
static inline bool invokeMethod(QObject *obj, const char *member,
- QGenericArgument val0 = QGenericArgument(nullptr),
+ QGenericArgument val0,
QGenericArgument val1 = QGenericArgument(),
QGenericArgument val2 = QGenericArgument(),
QGenericArgument val3 = QGenericArgument(),
@@ -287,89 +361,162 @@ struct Q_CORE_EXPORT QMetaObject
return invokeMethod(obj, member, Qt::AutoConnection, QGenericReturnArgument(), val0,
val1, val2, val3, val4, val5, val6, val7, val8, val9);
}
+#endif // Qt < 7.0
+
+ 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,
+ QTemplatedMetaMethodReturnArgument<ReturnArg> r, Args &&... arguments)
+ {
+ auto h = QtPrivate::invokeMethodHelper(r, std::forward<Args>(arguments)...);
+ return invokeMethodImpl(obj, member, c, h.parameterCount(), h.parameters.data(),
+ h.typeNames.data(), h.metaTypes.data());
+ }
+
+ template <typename... Args> static
+#ifdef Q_QDOC
+ bool
+#else
+ QtPrivate::Invoke::IfNotOldStyleArgs<bool, Args...>
+#endif
+ invokeMethod(QObject *obj, const char *member, Qt::ConnectionType c, Args &&... arguments)
+ {
+ QTemplatedMetaMethodReturnArgument<void> r = {};
+ return invokeMethod(obj, member, c, r, std::forward<Args>(arguments)...);
+ }
+
+ template <typename ReturnArg, typename... Args> static
+#ifdef Q_QDOC
+ bool
+#else
+ QtPrivate::Invoke::IfNotOldStyleArgs<bool, Args...>
+#endif
+ invokeMethod(QObject *obj, const char *member, QTemplatedMetaMethodReturnArgument<ReturnArg> r,
+ Args &&... arguments)
+ {
+ return invokeMethod(obj, member, Qt::AutoConnection, r, std::forward<Args>(arguments)...);
+ }
+
+ template <typename... Args> static
+#ifdef Q_QDOC
+ bool
+#else
+ QtPrivate::Invoke::IfNotOldStyleArgs<bool, Args...>
+#endif
+ invokeMethod(QObject *obj, const char *member, Args &&... arguments)
+ {
+ QTemplatedMetaMethodReturnArgument<void> r = {};
+ return invokeMethod(obj, member, Qt::AutoConnection, r, std::forward<Args>(arguments)...);
+ }
-#ifdef Q_CLANG_QDOC
+#ifdef Q_QDOC
template<typename Functor, typename FunctorReturnType>
- static bool invokeMethod(QObject *context, Functor function, Qt::ConnectionType type = Qt::AutoConnection, FunctorReturnType *ret = nullptr);
+ static bool invokeMethod(QObject *context, Functor &&function, Qt::ConnectionType type = Qt::AutoConnection, FunctorReturnType *ret = nullptr);
template<typename Functor, typename FunctorReturnType>
- static bool invokeMethod(QObject *context, Functor function, FunctorReturnType *ret);
+ static bool invokeMethod(QObject *context, Functor &&function, FunctorReturnType *ret);
+
+ template<typename Functor, typename FunctorReturnType, typename... Args>
+ static bool invokeMethod(QObject *context, Functor &&function, Qt::ConnectionType type, QTemplatedMetaMethodReturnArgument<FunctorReturnType> ret, Args &&...arguments);
+ template<typename Functor, typename FunctorReturnType, typename... Args>
+ static bool invokeMethod(QObject *context, Functor &&function, QTemplatedMetaMethodReturnArgument<FunctorReturnType> ret, Args &&...arguments);
+ template<typename Functor, typename... Args>
+ static bool invokeMethod(QObject *context, Functor &&function, Qt::ConnectionType type, Args &&...arguments);
+ template<typename Functor, typename... Args>
+ static bool invokeMethod(QObject *context, Functor &&function, Args &&...arguments);
#else
-
- // invokeMethod() for member function pointer
template <typename Func>
- static typename std::enable_if<QtPrivate::FunctionPointer<Func>::IsPointerToMemberFunction
- && !std::is_convertible<Func, const char*>::value
- && QtPrivate::FunctionPointer<Func>::ArgumentCount == 0, bool>::type
- invokeMethod(typename QtPrivate::FunctionPointer<Func>::Object *object,
- Func function,
- Qt::ConnectionType type = Qt::AutoConnection,
- typename QtPrivate::FunctionPointer<Func>::ReturnType *ret = nullptr)
+ static std::enable_if_t<!std::disjunction_v<std::is_convertible<Func, const char *>,
+ QtPrivate::Invoke::AreOldStyleArgs<Func>>,
+ bool>
+ invokeMethod(typename QtPrivate::ContextTypeForFunctor<Func>::ContextType *object,
+ Func &&function, Qt::ConnectionType type,
+ typename QtPrivate::Callable<Func>::ReturnType *ret)
{
- return invokeMethodImpl(object, new QtPrivate::QSlotObjectWithNoArgs<Func>(function), type, ret);
+ using R = typename QtPrivate::Callable<Func>::ReturnType;
+ const auto getReturnArg = [ret]() -> QTemplatedMetaMethodReturnArgument<R> {
+ if constexpr (std::is_void_v<R>)
+ return {};
+ else
+ return ret ? qReturnArg(*ret) : QTemplatedMetaMethodReturnArgument<R>{};
+ };
+ return invokeMethod(object, std::forward<Func>(function), type, getReturnArg());
}
-
template <typename Func>
- static typename std::enable_if<QtPrivate::FunctionPointer<Func>::IsPointerToMemberFunction
- && !std::is_convertible<Func, const char*>::value
- && QtPrivate::FunctionPointer<Func>::ArgumentCount == 0, bool>::type
- invokeMethod(typename QtPrivate::FunctionPointer<Func>::Object *object,
- Func function,
- typename QtPrivate::FunctionPointer<Func>::ReturnType *ret)
+ static std::enable_if_t<!std::disjunction_v<std::is_convertible<Func, const char *>,
+ QtPrivate::Invoke::AreOldStyleArgs<Func>>,
+ bool>
+ invokeMethod(typename QtPrivate::ContextTypeForFunctor<Func>::ContextType *object,
+ Func &&function, typename QtPrivate::Callable<Func>::ReturnType *ret)
{
- return invokeMethodImpl(object, new QtPrivate::QSlotObjectWithNoArgs<Func>(function), Qt::AutoConnection, ret);
+ return invokeMethod(object, std::forward<Func>(function), Qt::AutoConnection, ret);
}
- // invokeMethod() for function pointer (not member)
- template <typename Func>
- static typename std::enable_if<!QtPrivate::FunctionPointer<Func>::IsPointerToMemberFunction
- && !std::is_convertible<Func, const char*>::value
- && QtPrivate::FunctionPointer<Func>::ArgumentCount == 0, bool>::type
- invokeMethod(QObject *context, Func function,
- Qt::ConnectionType type = Qt::AutoConnection,
- typename QtPrivate::FunctionPointer<Func>::ReturnType *ret = nullptr)
+ template <typename Func, typename... Args>
+ static std::enable_if_t<!std::disjunction_v<std::is_convertible<Func, const char *>,
+ QtPrivate::Invoke::AreOldStyleArgs<Args...>>,
+ bool>
+ invokeMethod(typename QtPrivate::ContextTypeForFunctor<Func>::ContextType *object,
+ Func &&function, Qt::ConnectionType type,
+ QTemplatedMetaMethodReturnArgument<
+ typename QtPrivate::Callable<Func, Args...>::ReturnType>
+ ret,
+ Args &&...args)
{
- return invokeMethodImpl(context, new QtPrivate::QFunctorSlotObjectWithNoArgsImplicitReturn<Func>(function), type, ret);
+ return invokeMethodCallableHelper(object, std::forward<Func>(function), type, ret,
+ std::forward<Args>(args)...);
}
- template <typename Func>
- static typename std::enable_if<!QtPrivate::FunctionPointer<Func>::IsPointerToMemberFunction
- && !std::is_convertible<Func, const char*>::value
- && QtPrivate::FunctionPointer<Func>::ArgumentCount == 0, bool>::type
- invokeMethod(QObject *context, Func function,
- typename QtPrivate::FunctionPointer<Func>::ReturnType *ret)
+ template <typename Func, typename... Args>
+ static std::enable_if_t<!std::disjunction_v<std::is_convertible<Func, const char *>,
+ QtPrivate::Invoke::AreOldStyleArgs<Args...>>,
+ bool>
+ invokeMethod(typename QtPrivate::ContextTypeForFunctor<Func>::ContextType *object,
+ Func &&function, Qt::ConnectionType type, Args &&...args)
{
- return invokeMethodImpl(context, new QtPrivate::QFunctorSlotObjectWithNoArgsImplicitReturn<Func>(function), Qt::AutoConnection, ret);
+ using R = typename QtPrivate::Callable<Func, Args...>::ReturnType;
+ QTemplatedMetaMethodReturnArgument<R> r{ QtPrivate::qMetaTypeInterfaceForType<R>(), nullptr,
+ nullptr };
+ return invokeMethod(object, std::forward<Func>(function), type, r,
+ std::forward<Args>(args)...);
}
- // invokeMethod() for Functor
- template <typename Func>
- static typename std::enable_if<!QtPrivate::FunctionPointer<Func>::IsPointerToMemberFunction
- && QtPrivate::FunctionPointer<Func>::ArgumentCount == -1
- && !std::is_convertible<Func, const char*>::value, bool>::type
- invokeMethod(QObject *context, Func function,
- Qt::ConnectionType type = Qt::AutoConnection, decltype(function()) *ret = nullptr)
+ template <typename Func, typename... Args>
+ static std::enable_if_t<!std::disjunction_v<std::is_convertible<Func, const char *>,
+ QtPrivate::Invoke::AreOldStyleArgs<Args...>>,
+ bool>
+ invokeMethod(typename QtPrivate::ContextTypeForFunctor<Func>::ContextType *object,
+ Func &&function,
+ QTemplatedMetaMethodReturnArgument<
+ typename QtPrivate::Callable<Func, Args...>::ReturnType>
+ ret,
+ Args &&...args)
{
- return invokeMethodImpl(context,
- new QtPrivate::QFunctorSlotObjectWithNoArgs<Func, decltype(function())>(std::move(function)),
- type,
- ret);
+ return invokeMethod(object, std::forward<Func>(function), Qt::AutoConnection, ret,
+ std::forward<Args>(args)...);
}
- template <typename Func>
- static typename std::enable_if<!QtPrivate::FunctionPointer<Func>::IsPointerToMemberFunction
- && QtPrivate::FunctionPointer<Func>::ArgumentCount == -1
- && !std::is_convertible<Func, const char*>::value, bool>::type
- invokeMethod(QObject *context, Func function, decltype(function()) *ret)
+ template <typename Func, typename... Args>
+ static std::enable_if_t<!std::disjunction_v<std::is_convertible<Func, const char *>,
+ QtPrivate::Invoke::AreOldStyleArgs<Args...>>,
+ bool>
+ invokeMethod(typename QtPrivate::ContextTypeForFunctor<Func>::ContextType *object,
+ Func &&function, Args &&...args)
{
- return invokeMethodImpl(context,
- new QtPrivate::QFunctorSlotObjectWithNoArgs<Func, decltype(function())>(std::move(function)),
- Qt::AutoConnection,
- ret);
+ using R = typename QtPrivate::Callable<Func, Args...>::ReturnType;
+ QTemplatedMetaMethodReturnArgument<R> r{ QtPrivate::qMetaTypeInterfaceForType<R>(), nullptr,
+ nullptr };
+ return invokeMethod(object, std::forward<Func>(function), Qt::AutoConnection, r,
+ std::forward<Args>(args)...);
}
#endif
- QObject *newInstance(QGenericArgument val0 = QGenericArgument(nullptr),
+#if QT_VERSION < QT_VERSION_CHECK(7, 0, 0)
+ QObject *newInstance(QGenericArgument val0,
QGenericArgument val1 = QGenericArgument(),
QGenericArgument val2 = QGenericArgument(),
QGenericArgument val3 = QGenericArgument(),
@@ -379,6 +526,20 @@ struct Q_CORE_EXPORT QMetaObject
QGenericArgument val7 = QGenericArgument(),
QGenericArgument val8 = QGenericArgument(),
QGenericArgument val9 = QGenericArgument()) const;
+#endif
+
+ template <typename... Args>
+#ifdef Q_QDOC
+ QObject *
+#else
+ QtPrivate::Invoke::IfNotOldStyleArgs<QObject *, Args...>
+#endif
+ newInstance(Args &&... arguments) const
+ {
+ auto h = QtPrivate::invokeMethodHelper(QMetaMethodReturnArgument{}, std::forward<Args>(arguments)...);
+ return newInstanceImpl(this, h.parameterCount(), h.parameters.data(),
+ h.typeNames.data(), h.metaTypes.data());
+ }
enum Call {
InvokeMetaMethod,
@@ -389,7 +550,9 @@ struct Q_CORE_EXPORT QMetaObject
IndexOfMethod,
RegisterPropertyMetaType,
RegisterMethodArgumentMetaType,
- BindableProperty
+ BindableProperty,
+ CustomCall,
+ ConstructInPlace,
};
int static_metacall(Call, int, void **) const;
@@ -401,6 +564,7 @@ struct Q_CORE_EXPORT QMetaObject
}
struct SuperData {
+ using Getter = const QMetaObject *(*)();
const QMetaObject *direct;
SuperData() = default;
constexpr SuperData(std::nullptr_t) : direct(nullptr) {}
@@ -409,7 +573,6 @@ struct Q_CORE_EXPORT QMetaObject
constexpr const QMetaObject *operator->() const { return operator const QMetaObject *(); }
#ifdef QT_NO_DATA_RELOCATION
- using Getter = const QMetaObject *(*)();
Getter indirect = nullptr;
constexpr SuperData(Getter g) : direct(nullptr), indirect(g) {}
constexpr operator const QMetaObject *() const
@@ -417,6 +580,7 @@ struct Q_CORE_EXPORT QMetaObject
template <const QMetaObject &MO> static constexpr SuperData link()
{ return SuperData(QMetaObject::staticMetaObject<MO>); }
#else
+ constexpr SuperData(Getter g) : direct(g()) {}
constexpr operator const QMetaObject *() const
{ return direct; }
template <const QMetaObject &MO> static constexpr SuperData link()
@@ -436,8 +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 {
@@ -461,9 +663,9 @@ public:
operator RestrictedBool() const { return d_ptr && isConnected_helper() ? &Connection::d_ptr : nullptr; }
#endif
- Connection(Connection &&other) noexcept : d_ptr(qExchange(other.d_ptr, nullptr)) {}
+ Connection(Connection &&other) noexcept : d_ptr(std::exchange(other.d_ptr, nullptr)) {}
QT_MOVE_ASSIGNMENT_OPERATOR_IMPL_VIA_PURE_SWAP(Connection)
- void swap(Connection &other) noexcept { qSwap(d_ptr, other.d_ptr); }
+ void swap(Connection &other) noexcept { qt_ptr_swap(d_ptr, other.d_ptr); }
};
inline void swap(QMetaObject::Connection &lhs, QMetaObject::Connection &rhs) noexcept
@@ -475,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 a3068e1712..1e953f29b6 100644
--- a/src/corelib/kernel/qobjectdefs_impl.h
+++ b/src/corelib/kernel/qobjectdefs_impl.h
@@ -1,42 +1,6 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Copyright (C) 2013 Olivier Goffart <ogoffart@woboq.com>
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtCore module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// Copyright (C) 2013 Olivier Goffart <ogoffart@woboq.com>
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QOBJECTDEFS_H
#error Do not include qobjectdefs_impl.h directly
@@ -48,8 +12,15 @@
#pragma qt_sync_stop_processing
#endif
+#include <QtCore/qfunctionaltools_impl.h>
+
+#include <memory>
+
QT_BEGIN_NAMESPACE
class QObject;
+class QObjectPrivate;
+class QMetaMethod;
+class QByteArray;
namespace QtPrivate {
template <typename T> struct RemoveRef { typedef T Type; };
@@ -64,37 +35,43 @@ namespace QtPrivate {
the list composed of the first N element of the list
*/
// With variadic template, lists are represented using a variadic template argument instead of the lisp way
- template <typename...> struct List {};
- template <typename Head, typename... Tail> struct List<Head, Tail...> { typedef Head Car; typedef List<Tail...> Cdr; };
+ template <typename... Ts> struct List { static constexpr size_t size = sizeof...(Ts); };
+ template<typename T> struct SizeOfList { static constexpr size_t value = 1; };
+ template<> struct SizeOfList<List<>> { static constexpr size_t value = 0; };
+ template<typename ...Ts> struct SizeOfList<List<Ts...>> { static constexpr size_t value = List<Ts...>::size; };
+ template <typename Head, typename... Tail> struct List<Head, Tail...> {
+ static constexpr size_t size = 1 + sizeof...(Tail);
+ typedef Head Car; typedef List<Tail...> Cdr;
+ };
template <typename, typename> struct List_Append;
template <typename... L1, typename...L2> struct List_Append<List<L1...>, List<L2...>> { typedef List<L1..., L2...> Value; };
template <typename L, int N> struct List_Left {
typedef typename List_Append<List<typename L::Car>,typename List_Left<typename L::Cdr, N - 1>::Value>::Value Value;
};
template <typename L> struct List_Left<L, 0> { typedef List<> Value; };
- // List_Select<L,N> returns (via typedef Value) the Nth element of the list L
- template <typename L, int N> struct List_Select { typedef typename List_Select<typename L::Cdr, N - 1>::Value Value; };
- template <typename L> struct List_Select<L,0> { typedef typename L::Car Value; };
/*
- trick to set the return value of a slot that works even if the signal or the slot returns void
- to be used like function(), ApplyReturnValue<ReturnType>(&return_value)
- if function() returns a value, the operator,(T, ApplyReturnValue<ReturnType>) is called, but if it
- returns void, the builtin one is used without an error.
- */
- template <typename T>
- struct ApplyReturnValue {
- void *data;
- explicit ApplyReturnValue(void *data_) : data(data_) {}
+ This is used to store the return value from a slot, whether the caller
+ wants to store this value (QMetaObject::invokeMethod() with
+ qReturnArg() or non-void signal ) or not.
+ */
+ struct FunctorCallBase
+ {
+ template <typename R, typename Lambda>
+ static void call_internal(void **args, Lambda &&fn) noexcept(noexcept(fn()))
+ {
+ using SlotRet = decltype(fn());
+ if constexpr (std::is_void_v<R> || std::is_void_v<SlotRet>) {
+ Q_UNUSED(args);
+ } else {
+ if (args[0]) {
+ *reinterpret_cast<R *>(args[0]) = fn();
+ return;
+ }
+ }
+ fn();
+ }
};
- template<typename T, typename U>
- void operator,(T &&value, const ApplyReturnValue<U> &container) {
- if (container.data)
- *reinterpret_cast<U *>(container.data) = std::forward<T>(value);
- }
- template<typename T>
- void operator,(T, const ApplyReturnValue<void> &) {}
-
/*
The FunctionPointer<Func> struct is a type trait for function pointer.
@@ -110,7 +87,7 @@ namespace QtPrivate {
and args is the array of pointer to arguments, as used in qt_metacall
The Functor<Func,N> struct is the helper to call a functor of N argument.
- its call function is the same as the FunctionPointer::call function.
+ Its call function is the same as the FunctionPointer::call function.
*/
template<class T> using InvokeGenSeq = typename T::Type;
@@ -139,35 +116,75 @@ namespace QtPrivate {
template<typename Func> struct FunctionPointer { enum {ArgumentCount = -1, IsPointerToMemberFunction = false}; };
+ template<typename ObjPrivate> inline void assertObjectType(QObjectPrivate *d);
+ template<typename Obj> inline void assertObjectType(QObject *o)
+ {
+ // ensure all three compile
+ [[maybe_unused]] auto staticcast = [](QObject *obj) { return static_cast<Obj *>(obj); };
+ [[maybe_unused]] auto qobjcast = [](QObject *obj) { return Obj::staticMetaObject.cast(obj); };
+#ifdef __cpp_rtti
+ [[maybe_unused]] auto dyncast = [](QObject *obj) { return dynamic_cast<Obj *>(obj); };
+ auto cast = dyncast;
+#else
+ auto cast = qobjcast;
+#endif
+ Q_ASSERT_X(cast(o), Obj::staticMetaObject.className(),
+ "Called object is not of the correct type (class destructor may have already run)");
+ }
+
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...)> {
- static void call(SlotRet (Obj::*f)(SlotArgs...), Obj *o, void **arg) {
- (o->*f)((*reinterpret_cast<typename RemoveRef<SignalArgs>::Type *>(arg[II+1]))...), ApplyReturnValue<R>(arg[0]);
+ 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);
+ 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> {
- static void call(SlotRet (Obj::*f)(SlotArgs...) const, Obj *o, void **arg) {
- (o->*f)((*reinterpret_cast<typename RemoveRef<SignalArgs>::Type *>(arg[II+1]))...), ApplyReturnValue<R>(arg[0]);
+ 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);
+ 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> {
- static void call(SlotRet (Obj::*f)(SlotArgs...) noexcept, Obj *o, void **arg) {
- (o->*f)((*reinterpret_cast<typename RemoveRef<SignalArgs>::Type *>(arg[II+1]))...), ApplyReturnValue<R>(arg[0]);
+ 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);
+ 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> {
- static void call(SlotRet (Obj::*f)(SlotArgs...) const noexcept, Obj *o, void **arg) {
- (o->*f)((*reinterpret_cast<typename RemoveRef<SignalArgs>::Type *>(arg[II+1]))...), ApplyReturnValue<R>(arg[0]);
+ 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);
+ call_internal<R>(arg, [&]() noexcept {
+ return (o->*f)((*reinterpret_cast<typename RemoveRef<SignalArgs>::Type *>(arg[II+1]))...);
+ });
}
};
@@ -245,14 +262,6 @@ namespace QtPrivate {
}
};
- template<typename Function, int N> struct Functor
- {
- template <typename SignalArgs, typename R>
- static void call(Function &f, void *, void **arg) {
- FunctorCall<typename Indexes<N>::Value, SignalArgs, R, Function>::call(f, arg);
- }
- };
-
// Traits to detect if there is a conversion between two types,
// and that conversion does not include a narrowing conversion.
template <typename T>
@@ -286,10 +295,9 @@ namespace QtPrivate {
static_assert(CheckCompatibleArguments<FunctionPointer<Signal>::Arguments, FunctionPointer<Slot>::Arguments>::value)
*/
template<typename A1, typename A2> struct AreArgumentsCompatible {
- static int test(const typename RemoveRef<A2>::Type&);
+ static int test(const std::remove_reference_t<A2>&);
static char test(...);
- static const typename RemoveRef<A1>::Type &dummy();
- enum { value = sizeof(test(dummy())) == sizeof(int) };
+ enum { value = sizeof(test(std::declval<std::remove_reference_t<A1>>())) == sizeof(int) };
#ifdef QT_NO_NARROWING_CONVERSIONS_IN_CONNECT
using AreArgumentsConvertibleWithoutNarrowing = AreArgumentsConvertibleWithoutNarrowingBase<std::decay_t<A1>, std::decay_t<A2>>;
static_assert(AreArgumentsConvertibleWithoutNarrowing::value, "Signal and slot arguments are not compatible (narrowing)");
@@ -328,11 +336,10 @@ namespace QtPrivate {
template <typename Functor, typename... ArgList> struct ComputeFunctorArgumentCount<Functor, List<ArgList...>>
{
- template <typename D> static D dummy();
- template <typename F> static auto test(F f) -> decltype(((f.operator()((dummy<ArgList>())...)), int()));
+ template <typename F> static auto test(F f) -> decltype(((f.operator()((std::declval<ArgList>())...)), int()));
static char test(...);
enum {
- Ok = sizeof(test(dummy<Functor>())) == sizeof(int),
+ Ok = sizeof(test(std::declval<Functor>())) == sizeof(int),
Value = Ok ? int(sizeof...(ArgList)) : int(ComputeFunctorArgumentCountHelper<Functor, List<ArgList...>, Ok>::Value)
};
};
@@ -340,19 +347,115 @@ namespace QtPrivate {
/* get the return type of a functor, given the signal argument list */
template <typename Functor, typename ArgList> struct FunctorReturnType;
template <typename Functor, typename ... ArgList> struct FunctorReturnType<Functor, List<ArgList...>> {
- template <typename D> static D dummy();
- typedef decltype(dummy<Functor>().operator()((dummy<ArgList>())...)) Value;
+ typedef decltype(std::declval<Functor>().operator()((std::declval<ArgList>())...)) Value;
+ };
+
+ template<typename Func, typename... Args>
+ struct FunctorCallable
+ {
+ using ReturnType = decltype(std::declval<Func>()(std::declval<Args>()...));
+ using Function = ReturnType(*)(Args...);
+ enum {ArgumentCount = sizeof...(Args)};
+ using Arguments = QtPrivate::List<Args...>;
+
+ template <typename SignalArgs, typename R>
+ static void call(Func &f, void *, void **arg) {
+ FunctorCall<typename Indexes<ArgumentCount>::Value, SignalArgs, R, Func>::call(f, arg);
+ }
};
+ template <typename Functor, typename... Args>
+ struct HasCallOperatorAcceptingArgs
+ {
+ private:
+ template <typename F, typename = void>
+ struct Test : std::false_type
+ {
+ };
+ // We explicitly use .operator() to not return true for pointers to free/static function
+ template <typename F>
+ struct Test<F, std::void_t<decltype(std::declval<F>().operator()(std::declval<Args>()...))>>
+ : std::true_type
+ {
+ };
+
+ public:
+ using Type = Test<Functor>;
+ static constexpr bool value = Type::value;
+ };
+
+ template <typename Functor, typename... Args>
+ constexpr bool
+ HasCallOperatorAcceptingArgs_v = HasCallOperatorAcceptingArgs<Functor, Args...>::value;
+
+ template <typename Func, typename... Args>
+ struct CallableHelper
+ {
+ private:
+ // Could've been std::conditional_t, but that requires all branches to
+ // be valid
+ static auto Resolve(std::true_type CallOperator) -> FunctorCallable<Func, Args...>;
+ static auto Resolve(std::false_type CallOperator) -> FunctionPointer<std::decay_t<Func>>;
+
+ public:
+ using Type = decltype(Resolve(typename HasCallOperatorAcceptingArgs<std::decay_t<Func>,
+ Args...>::Type{}));
+ };
+
+ template<typename Func, typename... Args>
+ struct Callable : CallableHelper<Func, Args...>::Type
+ {};
+ template<typename Func, typename... Args>
+ struct Callable<Func, List<Args...>> : CallableHelper<Func, Args...>::Type
+ {};
+
+ /*
+ Wrapper around ComputeFunctorArgumentCount and CheckCompatibleArgument,
+ depending on whether \a Functor is a PMF or not. Returns -1 if \a Func is
+ not compatible with the \a ExpectedArguments, otherwise returns >= 0.
+ */
+ template<typename Prototype, typename Functor>
+ inline constexpr std::enable_if_t<!std::disjunction_v<std::is_convertible<Prototype, const char *>,
+ std::is_same<std::decay_t<Prototype>, QMetaMethod>,
+ std::is_convertible<Functor, const char *>,
+ std::is_same<std::decay_t<Functor>, QMetaMethod>
+ >,
+ int>
+ countMatchingArguments()
+ {
+ using ExpectedArguments = typename QtPrivate::FunctionPointer<Prototype>::Arguments;
+ using Actual = std::decay_t<Functor>;
+
+ if constexpr (QtPrivate::FunctionPointer<Actual>::IsPointerToMemberFunction
+ || QtPrivate::FunctionPointer<Actual>::ArgumentCount >= 0) {
+ // PMF or free function
+ using ActualArguments = typename QtPrivate::FunctionPointer<Actual>::Arguments;
+ if constexpr (QtPrivate::CheckCompatibleArguments<ExpectedArguments, ActualArguments>::value)
+ return QtPrivate::FunctionPointer<Actual>::ArgumentCount;
+ else
+ return -1;
+ } else {
+ // lambda or functor
+ return QtPrivate::ComputeFunctorArgumentCount<Actual, ExpectedArguments>::Value;
+ }
+ }
+
// internal base class (interface) containing functions required to call a slot managed by a pointer to function.
- class QSlotObjectBase {
- QAtomicInt m_ref;
- // don't use virtual functions here; we don't want the
+ class QSlotObjectBase
+ {
+ // Don't use virtual functions here; we don't want the
// compiler to create tons of per-polymorphic-class stuff that
// we'll never need. We just use one function pointer, and the
// Operations enum below to distinguish requests
+#if QT_VERSION < QT_VERSION_CHECK(7, 0, 0)
+ QAtomicInt m_ref = 1;
typedef void (*ImplFn)(int which, QSlotObjectBase* this_, QObject *receiver, void **args, bool *ret);
const ImplFn m_impl;
+#else
+ using ImplFn = void (*)(QSlotObjectBase* this_, QObject *receiver, void **args, int which, bool *ret);
+ const ImplFn m_impl;
+ QAtomicInt m_ref = 1;
+#endif
protected:
// The operations that can be requested by calls to m_impl,
// see the member functions that call m_impl below for details
@@ -364,80 +467,197 @@ namespace QtPrivate {
NumOperations
};
public:
- explicit QSlotObjectBase(ImplFn fn) : m_ref(1), m_impl(fn) {}
+ explicit QSlotObjectBase(ImplFn fn) : m_impl(fn) {}
+
+ // A custom deleter compatible with std protocols (op()()) we well as
+ // the legacy QScopedPointer protocol (cleanup()).
+ struct Deleter {
+ void operator()(QSlotObjectBase *p) const noexcept
+ { if (p) p->destroyIfLastRef(); }
+ // for the non-standard QScopedPointer protocol:
+ static void cleanup(QSlotObjectBase *p) noexcept { Deleter{}(p); }
+ };
- inline int ref() noexcept { return m_ref.ref(); }
+ bool ref() noexcept { return m_ref.ref(); }
+#if QT_VERSION < QT_VERSION_CHECK(7, 0, 0)
inline void destroyIfLastRef() noexcept
{ if (!m_ref.deref()) m_impl(Destroy, this, nullptr, nullptr, nullptr); }
inline bool compare(void **a) { bool ret = false; m_impl(Compare, this, nullptr, a, &ret); return ret; }
- inline void call(QObject *r, void **a) { m_impl(Call, this, r, a, nullptr); }
+ inline void call(QObject *r, void **a) { m_impl(Call, this, r, a, nullptr); }
+#else
+ inline void destroyIfLastRef() noexcept
+ { if (!m_ref.deref()) m_impl(this, nullptr, nullptr, Destroy, nullptr); }
+
+ inline bool compare(void **a)
+ {
+ bool ret = false;
+ m_impl(this, nullptr, a, Compare, &ret);
+ return ret;
+ }
+ inline void call(QObject *r, void **a) { m_impl(this, r, a, Call, nullptr); }
+#endif
+ bool isImpl(ImplFn f) const { return m_impl == f; }
protected:
~QSlotObjectBase() {}
private:
Q_DISABLE_COPY_MOVE(QSlotObjectBase)
};
- // implementation of QSlotObjectBase for which the slot is a pointer to member function of a QObject
- // Args and R are the List of arguments and the return type of the signal to which the slot is connected.
- template<typename Func, typename Args, typename R> class QSlotObject : public QSlotObjectBase
+ using SlotObjUniquePtr = std::unique_ptr<QSlotObjectBase,
+ QSlotObjectBase::Deleter>;
+ inline SlotObjUniquePtr copy(const SlotObjUniquePtr &other) noexcept
{
- typedef QtPrivate::FunctionPointer<Func> FuncType;
- Func function;
- static void impl(int which, QSlotObjectBase *this_, QObject *r, void **a, bool *ret)
+ if (other)
+ other->ref();
+ return SlotObjUniquePtr{other.get()};
+ }
+
+ class SlotObjSharedPtr {
+ SlotObjUniquePtr obj;
+ public:
+ Q_NODISCARD_CTOR Q_IMPLICIT SlotObjSharedPtr() noexcept = default;
+ Q_NODISCARD_CTOR Q_IMPLICIT SlotObjSharedPtr(std::nullptr_t) noexcept : SlotObjSharedPtr() {}
+ Q_NODISCARD_CTOR explicit SlotObjSharedPtr(SlotObjUniquePtr o)
+ : obj(std::move(o))
{
- switch (which) {
- case Destroy:
- delete static_cast<QSlotObject*>(this_);
- break;
- case Call:
- FuncType::template call<Args, R>(static_cast<QSlotObject*>(this_)->function, static_cast<typename FuncType::Object *>(r), a);
- break;
- case Compare:
- *ret = *reinterpret_cast<Func *>(a) == static_cast<QSlotObject*>(this_)->function;
- break;
- case NumOperations: ;
- }
+ // does NOT ref() (takes unique_ptr by value)
+ // (that's why (QSlotObjectBase*) ctor doesn't exisit: don't know whether that one _should_)
}
- public:
- explicit QSlotObject(Func f) : QSlotObjectBase(&impl), function(f) {}
+ Q_NODISCARD_CTOR SlotObjSharedPtr(const SlotObjSharedPtr &other) noexcept
+ : obj{copy(other.obj)} {}
+ SlotObjSharedPtr &operator=(const SlotObjSharedPtr &other) noexcept
+ { auto copy = other; swap(copy); return *this; }
+
+ Q_NODISCARD_CTOR SlotObjSharedPtr(SlotObjSharedPtr &&other) noexcept = default;
+ SlotObjSharedPtr &operator=(SlotObjSharedPtr &&other) noexcept = default;
+ ~SlotObjSharedPtr() = default;
+
+ void swap(SlotObjSharedPtr &other) noexcept { obj.swap(other.obj); }
+
+ auto get() const noexcept { return obj.get(); }
+ auto operator->() const noexcept { return get(); }
+
+ explicit operator bool() const noexcept { return bool(obj); }
};
- // implementation of QSlotObjectBase for which the slot is a functor (or lambda)
- // N is the number of arguments
+
+
+ // Implementation of QSlotObjectBase for which the slot is a callable (function, PMF, functor, or lambda).
// Args and R are the List of arguments and the return type of the signal to which the slot is connected.
- template<typename Func, int N, typename Args, typename R> class QFunctorSlotObject : public QSlotObjectBase
+ template <typename Func, typename Args, typename R>
+ class QCallableObject : public QSlotObjectBase,
+ private QtPrivate::CompactStorage<std::decay_t<Func>>
{
- typedef QtPrivate::Functor<Func, N> FuncType;
- Func function;
- static void impl(int which, QSlotObjectBase *this_, QObject *r, void **a, bool *ret)
+ using FunctorValue = std::decay_t<Func>;
+ using Storage = QtPrivate::CompactStorage<FunctorValue>;
+ using FuncType = Callable<Func, Args>;
+
+#if QT_VERSION < QT_VERSION_CHECK(7, 0, 0)
+ Q_DECL_HIDDEN static void impl(int which, QSlotObjectBase *this_, QObject *r, void **a, bool *ret)
+#else
+ // Design note: the first three arguments match those for typical Call
+ // and Destroy uses. We return void to enable tail call optimization
+ // for those too.
+ Q_DECL_HIDDEN static void impl(QSlotObjectBase *this_, QObject *r, void **a, int which, bool *ret)
+#endif
{
+ const auto that = static_cast<QCallableObject*>(this_);
switch (which) {
case Destroy:
- delete static_cast<QFunctorSlotObject*>(this_);
+ delete that;
break;
case Call:
- FuncType::template call<Args, R>(static_cast<QFunctorSlotObject*>(this_)->function, r, a);
+ if constexpr (std::is_member_function_pointer_v<FunctorValue>)
+ FuncType::template call<Args, R>(that->object(), static_cast<typename FuncType::Object *>(r), a);
+ else
+ FuncType::template call<Args, R>(that->object(), r, a);
break;
- case Compare: // not implemented
+ case Compare:
+ if constexpr (std::is_member_function_pointer_v<FunctorValue>) {
+ *ret = *reinterpret_cast<FunctorValue *>(a) == that->object();
+ break;
+ }
+ // not implemented otherwise
+ Q_FALLTHROUGH();
case NumOperations:
Q_UNUSED(ret);
}
}
public:
- explicit QFunctorSlotObject(Func f) : QSlotObjectBase(&impl), function(std::move(f)) {}
+ explicit QCallableObject(Func &&f) : QSlotObjectBase(&impl), Storage{std::move(f)} {}
+ explicit QCallableObject(const Func &f) : QSlotObjectBase(&impl), Storage{f} {}
};
- // typedefs for readability for when there are no parameters
+ // Helper to detect the context object type based on the functor type:
+ // QObject for free functions and lambdas; the callee for member function
+ // pointers. The default declaration doesn't have the ContextType typedef,
+ // and so non-functor APIs (like old-style string-based slots) are removed
+ // from the overload set.
+ template <typename Func, typename = void>
+ struct ContextTypeForFunctor {};
+
template <typename Func>
- using QSlotObjectWithNoArgs = QSlotObject<Func,
- QtPrivate::List<>,
- typename QtPrivate::FunctionPointer<Func>::ReturnType>;
+ struct ContextTypeForFunctor<Func,
+ std::enable_if_t<!std::disjunction_v<std::is_convertible<Func, const char *>,
+ std::is_member_function_pointer<Func>
+ >
+ >
+ >
+ {
+ using ContextType = QObject;
+ };
+ template <typename Func>
+ struct ContextTypeForFunctor<Func,
+ std::enable_if_t<std::conjunction_v<std::negation<std::is_convertible<Func, const char *>>,
+ std::is_member_function_pointer<Func>,
+ std::is_convertible<typename QtPrivate::FunctionPointer<Func>::Object *, QObject *>
+ >
+ >
+ >
+ {
+ using ContextType = typename QtPrivate::FunctionPointer<Func>::Object;
+ };
- template <typename Func, typename R>
- using QFunctorSlotObjectWithNoArgs = QFunctorSlotObject<Func, 0, QtPrivate::List<>, R>;
+ /*
+ Returns a suitable QSlotObjectBase object that holds \a func, if possible.
- template <typename Func>
- using QFunctorSlotObjectWithNoArgsImplicitReturn = QFunctorSlotObjectWithNoArgs<Func, typename QtPrivate::FunctionPointer<Func>::ReturnType>;
+ Not available (and thus produces compile-time errors) if the Functor provided is
+ not compatible with the expected Prototype.
+ */
+ template <typename Prototype, typename Functor>
+ static constexpr std::enable_if_t<QtPrivate::countMatchingArguments<Prototype, Functor>() >= 0,
+ QtPrivate::QSlotObjectBase *>
+ makeCallableObject(Functor &&func)
+ {
+ using ExpectedSignature = QtPrivate::FunctionPointer<Prototype>;
+ using ExpectedReturnType = typename ExpectedSignature::ReturnType;
+ using ExpectedArguments = typename ExpectedSignature::Arguments;
+
+ using ActualSignature = QtPrivate::FunctionPointer<Functor>;
+ constexpr int MatchingArgumentCount = QtPrivate::countMatchingArguments<Prototype, Functor>();
+ using ActualArguments = typename QtPrivate::List_Left<ExpectedArguments, MatchingArgumentCount>::Value;
+
+ static_assert(int(ActualSignature::ArgumentCount) <= int(ExpectedSignature::ArgumentCount),
+ "Functor requires more arguments than what can be provided.");
+
+ // NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks)
+ return new QtPrivate::QCallableObject<std::decay_t<Functor>, ActualArguments, ExpectedReturnType>(std::forward<Functor>(func));
+ }
+
+ template<typename Prototype, typename Functor, typename = void>
+ struct AreFunctionsCompatible : std::false_type {};
+ template<typename Prototype, typename Functor>
+ struct AreFunctionsCompatible<Prototype, Functor, std::enable_if_t<
+ std::is_same_v<decltype(QtPrivate::makeCallableObject<Prototype>(std::forward<Functor>(std::declval<Functor>()))),
+ QtPrivate::QSlotObjectBase *>>
+ > : std::true_type {};
+
+ template<typename Prototype, typename Functor>
+ inline constexpr bool AssertCompatibleFunctions() {
+ static_assert(AreFunctionsCompatible<Prototype, Functor>::value,
+ "Functor is not compatible with expected prototype!");
+ return true;
+ }
}
QT_END_NAMESPACE
diff --git a/src/corelib/kernel/qpermissions.cpp b/src/corelib/kernel/qpermissions.cpp
new file mode 100644
index 0000000000..d0d2d9eb10
--- /dev/null
+++ b/src/corelib/kernel/qpermissions.cpp
@@ -0,0 +1,693 @@
+// 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
+
+#include "qpermissions.h"
+#include "qpermissions_p.h"
+#include "qhashfunctions.h"
+
+#include <QtCore/qshareddata.h>
+#include <QtCore/qdebug.h>
+
+QT_BEGIN_NAMESPACE
+
+Q_LOGGING_CATEGORY(lcPermissions, "qt.permissions", QtWarningMsg);
+
+/*!
+ \page permissions.html
+ \title Application Permissions
+ \brief Managing application permissions
+
+ Many features of today's devices and operating systems can have
+ significant privacy, security, and performance implications if
+ misused. It's therefore increasingly common for platforms to
+ require explicit consent from the user before accessing these
+ features.
+
+ The Qt permission APIs allow the application to check or request
+ permission for such features in a cross platform manner.
+
+ \section1 Usage
+
+ A feature that commonly requires user consent is access to the
+ microphone of the device. An application for recording voice
+ memos would perhaps look something like this initially:
+
+ \code
+ void VoiceMemoWidget::onRecordingInitiated()
+ {
+ m_microphone->startRecording();
+ }
+ \endcode
+
+ To ensure this application works well on platforms that
+ require user consent for microphone access we would extend
+ it like this:
+
+ \code
+ void VoiceMemoWidget::onRecordingInitiated()
+ {
+ QMicrophonePermission microphonePermission;
+ switch (qApp->checkPermission(microphonePermission)) {
+ case Qt::PermissionStatus::Undetermined:
+ qApp->requestPermission(microphonePermission, this,
+ &VoiceMemoWidget::onRecordingInitiated);
+ return;
+ case Qt::PermissionStatus::Denied:
+ m_permissionInstructionsDialog->show();
+ return;
+ case Qt::PermissionStatus::Granted:
+ m_microphone->startRecording();
+ }
+ }
+ \endcode
+
+ We first check if we already know the status of the microphone permission.
+ If we don't we initiate a permission request to determine the current
+ status, which will potentially ask the user for consent. We connect the
+ result of the request to the slot we're already in, so that we get another
+ chance at evaluating the permission status.
+
+ Once the permission status is known, either because we had been granted or
+ denied permission at an earlier time, or after getting the result back from
+ the request we just initiated, we redirect the user to a dialog explaining
+ why we can not record voice memos at this time (if the permission was denied),
+ or proceed to using the microphone (if permission was granted).
+
+ \note On \macOS and iOS permissions can currently only be requested for
+ GUI applications.
+
+ \section2 Declaring Permissions
+
+ Some platforms require that the permissions you request are declared
+ up front at build time.
+
+ \section3 Apple platforms
+ \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
+ file, describing why the application needs to access the given
+ permission. For example:
+
+ \badcode
+ <key>NSMicrophoneUsageDescription</key>
+ <string>The microphone is used to record voice memos.</string>
+ \endcode
+
+ The relevant usage description keys are described in the documentation
+ for each permission type.
+
+ \sa {Information Property List Files}.
+
+ \section3 Android
+ \target android-uses-permission
+
+ Each permission you request must be accompanied by a \c uses-permission
+ entry in the application's \c AndroidManifest.xml file. For example:
+
+ \badcode
+ <manifest ...>
+ <uses-permission android:name="android.permission.RECORD_AUDIO"/>
+ </manifest>
+ \endcode
+
+ The relevant permission names are described in the documentation
+ for each permission type.
+
+ \sa {Qt Creator: Editing Manifest Files}.
+
+ \section1 Available Permissions
+
+ The following permissions types are available:
+
+ \annotatedlist permissions
+
+ \section1 Best Practices
+
+ To ensure the best possible user experience for the end user we recommend
+ adopting the following best practices for managing application permissions:
+
+ \list
+
+ \li Request the minimal set of permissions needed. For example, if you only
+ need access to the microphone, do \e not request camera permission just in case.
+ Use the properties of individual permission types to limit the permission scope
+ even further, for example QContactsPermission::setReadOnly() to request read
+ only access.
+
+ \li Request permissions in response to specific actions by the user. For example,
+ defer requesting microphone permission until the user presses the button to record
+ audio. Associating the permission request to a specific action gives the user a clearer
+ context of why the permission is needed. Do \e not request all needed permission on
+ startup.
+
+ \li Present extra context and explanation if needed. Sometimes the action by the user
+ is not enough context. Consider presenting an explanation-dialog after the user has
+ initiated the action, but before requesting the permission, so the user is aware of
+ what's about to happen when the system permission dialog subsequently pops up.
+
+ \li Be transparent and explicit about why permissions are needed. In explanation
+ dialogs and usage descriptions, be transparent about why the particular permission
+ is needed for your application to provide a specific feature, so users can make
+ informed decisions.
+
+ \li Account for denied permissions. The permissions you request may be denied
+ for various reasons. You should always account for this situation, by gracefully
+ degrading the experience of your application, and presenting clear explanations
+ the user about the situation.
+
+ \li Never request permissions from a library. The request of permissions should
+ be done as close as possible to the user, where the information needed to make
+ good decisions on the points above is available. Libraries can check permissions,
+ to ensure they have the prerequisites for doing their work, but if the permission
+ is undetermined or denied this should be reflected through the library's API,
+ so that the application in turn can request the necessary permissions.
+
+ \endlist
+*/
+
+
+/*!
+ \class QPermission
+ \inmodule QtCore
+ \inheaderfile QPermissions
+ \since 6.5
+ \brief An opaque wrapper of a typed permission.
+
+ The QPermission class is an opaque wrapper of a \l{typed permission},
+ used when checking or requesting permissions. You do not need to construct
+ this type explicitly, as the type is automatically used when checking or
+ requesting permissions:
+
+ \code
+ qApp->checkPermission(QCameraPermission{});
+ \endcode
+
+ When requesting permissions, the given functor will
+ be passed an instance of a QPermission, which can be used
+ to check the result of the request:
+
+ \code
+ qApp->requestPermission(QCameraPermission{}, [](const QPermission &permission) {
+ if (permission.status() == Qt::PermissionStatus:Granted)
+ takePhoto();
+ });
+ \endcode
+
+ To inspect the properties of the original, typed permission,
+ use the \l {QPermission::}{value()} function:
+
+ \code
+ QLocationPermission locationPermission;
+ locationPermission.setAccuracy(QLocationPermission::Precise);
+ qApp->requestPermission(locationPermission, this, &LocationWidget::permissionUpdated);
+ \endcode
+
+ \code
+ void LocationWidget::permissionUpdated(const QPermission &permission)
+ {
+ if (permission.status() != Qt::PermissionStatus:Granted)
+ return;
+ auto locationPermission = permission.value<QLocationPermission>();
+ if (!locationPermission || locationPermission->accuracy() != QLocationPermission::Precise)
+ return;
+ updatePreciseLocation();
+ }
+ \endcode
+
+ \target typed permission
+ \section2 Typed Permissions
+
+ The following permissions are available:
+
+ \annotatedlist permissions
+
+ \sa {Application Permissions}
+*/
+
+/*!
+ \fn template <typename T, QPermission::if_permission<T>> QPermission::QPermission(const T &type)
+
+ Constructs a permission from the given \l{typed permission} \a type.
+
+ You do not need to construct this type explicitly, as the type is automatically
+ used when checking or requesting permissions.
+
+ This constructor participates in overload resolution only if \c T is one of
+ the \l{typed permission} classes:
+
+ \annotatedlist permissions
+*/
+
+/*!
+ \fn template <typename 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.
+
+ Use type() for dynamically choosing which typed permission to request.
+
+ This function participates in overload resolution only if \c T is one of
+ the \l{typed permission} classes:
+
+ \annotatedlist permissions
+*/
+
+/*!
+ \fn Qt::PermissionStatus QPermission::status() const
+ Returns the status of the permission.
+*/
+
+/*!
+ \fn QMetaType QPermission::type() const
+ Returns the type of the permission.
+*/
+
+/*
+ \internal
+*/
+const void *QPermission::data(QMetaType requestedType) const
+{
+ const auto actualType = type();
+ if (requestedType != actualType)
+ return nullptr;
+ return m_data.data();
+}
+
+// check alignof(AlignmentCheck) instead of alignof(void*), in case
+// pointers have different alignment inside structs:
+struct AlignmentCheck { void *p; };
+
+#define QT_PERMISSION_IMPL_COMMON(ClassName) \
+ /* Class##Private is unused until we need it: */ \
+ static_assert(sizeof(ClassName) == sizeof(void*), \
+ "You have added too many members to " #ClassName "::ShortData. " \
+ "Decrease their size or switch to using a d-pointer."); \
+ static_assert(alignof(ClassName) == alignof(AlignmentCheck), \
+ "You have added members to " #ClassName "::ShortData that are overaligned. " \
+ "Decrease their alignment or switch to using a d-pointer."); \
+ ClassName::ClassName(const ClassName &other) noexcept = default; \
+ ClassName::~ClassName() = default; \
+ ClassName &ClassName::operator=(const ClassName &other) noexcept = default; \
+ ClassName::ClassName() \
+ /* impl supplied by caller */
+
+
+/*!
+ \class QCameraPermission
+ \brief Access the camera for taking pictures or videos.
+
+ \section1 Requirements
+
+ \include permissions.qdocinc begin-usage-declarations
+ \row
+ \li Apple
+ \li \l{apple-usage-description}{Usage description}
+ \li \c NSCameraUsageDescription
+ \row
+ \li Android
+ \li \l{android-uses-permission}{\c{uses-permission}}
+ \li \c android.permission.CAMERA
+ \include permissions.qdocinc end-usage-declarations
+
+ \include permissions.qdocinc permission-metadata
+*/
+
+QT_PERMISSION_IMPL_COMMON(QCameraPermission)
+ : u{} // stateless, atm
+{}
+
+/*!
+ \class QMicrophonePermission
+ \brief Access the microphone for monitoring or recording sound.
+
+ \section1 Requirements
+
+ \include permissions.qdocinc begin-usage-declarations
+ \row
+ \li Apple
+ \li \l{apple-usage-description}{Usage description}
+ \li \c NSMicrophoneUsageDescription
+ \row
+ \li Android
+ \li \l{android-uses-permission}{\c{uses-permission}}
+ \li \c android.permission.RECORD_AUDIO
+ \include permissions.qdocinc end-usage-declarations
+
+ \include permissions.qdocinc permission-metadata
+*/
+
+QT_PERMISSION_IMPL_COMMON(QMicrophonePermission)
+ : u{} // stateless, atm
+{}
+
+/*!
+ \class QBluetoothPermission
+ \brief Access Bluetooth peripherals.
+
+ \section1 Requirements
+
+ \include permissions.qdocinc begin-usage-declarations
+ \row
+ \li Apple
+ \li \l{apple-usage-description}{Usage description}
+ \li \c NSBluetoothAlwaysUsageDescription
+ \row
+ \li Android
+ \li \l{android-uses-permission}{\c{uses-permission}}
+ \li Up to Android 11 (API Level < 31):
+ \list
+ \li \c android.permission.BLUETOOTH
+ \li \c android.permission.ACCESS_FINE_LOCATION
+ \endlist
+
+ Starting from Android 12 (API Level >= 31):
+ \list
+ \li \c android.permission.BLUETOOTH_ADVERTISE
+ \li \c android.permission.BLUETOOTH_CONNECT
+ \li \c android.permission.BLUETOOTH_SCAN
+ \li \c android.permission.ACCESS_FINE_LOCATION
+ \endlist
+ \include permissions.qdocinc end-usage-declarations
+
+ \note Currently on Android the \c android.permission.ACCESS_FINE_LOCATION
+ permission is requested together with Bluetooth permissions. This is
+ required for Bluetooth to work properly, unless the application provides a
+ strong assertion in the application manifest that it does not use Bluetooth
+ to derive a physical location. This permission coupling may change in
+ future.
+
+ \include permissions.qdocinc permission-metadata
+*/
+
+QT_PERMISSION_IMPL_COMMON(QBluetoothPermission)
+ : u{ShortData{CommunicationMode::Default, {}}}
+{}
+
+/*!
+ \enum QBluetoothPermission::CommunicationMode
+ \since 6.6
+
+ This enum is used to control the allowed Bluetooth communication modes.
+
+ \value Access Allow this device to access other Bluetooth devices. This
+ includes scanning for nearby devices and connecting to them.
+ \value Advertise Allow other Bluetooth devices to discover this device.
+ \value Default This configuration is used by default.
+
+ \note The fine-grained permissions are currently supported only on
+ Android 12 and newer. On older Android versions, as well as on Apple
+ operating systems, any mode results in full Bluetooth access.
+
+ \note For now the \c Access mode on Android also requests the
+ \l {QLocationPermission::Precise}{precise location} permission.
+ This permission coupling may change in the future.
+*/
+
+/*!
+ \since 6.6
+
+ Sets the allowed Bluetooth communication modes to \a modes.
+
+ \note A default-constructed instance of \l {QBluetoothPermission::}
+ {CommunicationModes} has no sense, so an attempt to set such a mode will
+ raise a \c {qWarning()} and fall back to using the
+ \l {QBluetoothPermission::}{Default} mode.
+*/
+void QBluetoothPermission::setCommunicationModes(CommunicationModes modes)
+{
+ if (modes == CommunicationModes{}) {
+ qCWarning(lcPermissions, "QBluetoothPermission: trying to set an invalid empty mode. "
+ "Falling back to CommunicationMode::Default.");
+ u.data.mode = Default;
+ } else {
+ u.data.mode = static_cast<CommunicationMode>(modes.toInt());
+ }
+}
+
+/*!
+ \since 6.6
+
+ Returns the allowed Bluetooth communication modes.
+*/
+QBluetoothPermission::CommunicationModes QBluetoothPermission::communicationModes() const
+{
+ return u.data.mode;
+}
+
+/*!
+ \class QLocationPermission
+ \brief Access the user's location.
+
+ By default the request is for approximate accuracy,
+ and only while the application is in use. Use
+ setAccuracy() and/or setAvailability() to override
+ the default.
+
+ \section1 Requirements
+
+ \include permissions.qdocinc begin-usage-declarations
+ \row
+ \li \macos
+ \li \l{apple-usage-description}{Usage description}
+ \li \c NSLocationUsageDescription
+ \row
+ \li iOS
+ \li \l{apple-usage-description}{Usage description}
+ \li \c NSLocationWhenInUseUsageDescription, and
+ \c NSLocationAlwaysAndWhenInUseUsageDescription if requesting
+ QLocationPermission::Always
+ \row
+ \li Android
+ \li \l{android-uses-permission}{\c{uses-permission}}
+ \li \list
+ \li \c android.permission.ACCESS_FINE_LOCATION for QLocationPermission::Precise
+ \li \c android.permission.ACCESS_COARSE_LOCATION for QLocationPermission::Approximate
+ \li \c android.permission.ACCESS_BACKGROUND_LOCATION for QLocationPermission::Always
+ \endlist
+ \note QLocationPermission::Always \c uses-permission string has
+ to be combined with one or both of QLocationPermission::Precise
+ and QLocationPermission::Approximate strings.
+ \include permissions.qdocinc end-usage-declarations
+
+ \include permissions.qdocinc permission-metadata
+*/
+
+QT_PERMISSION_IMPL_COMMON(QLocationPermission)
+ : u{ShortData{Accuracy::Approximate, Availability::WhenInUse, {}}}
+{}
+
+/*!
+ \enum QLocationPermission::Accuracy
+
+ This enum is used to control the accuracy of the location data.
+
+ \value Approximate An approximate location is requested.
+ \value Precise A precise location is requested.
+*/
+
+/*!
+ \enum QLocationPermission::Availability
+
+ This enum is used to control the availability of the location data.
+
+ \value WhenInUse The location is only available only when the
+ application is in use.
+ \value Always The location is available at all times, including when
+ the application is in the background.
+*/
+
+/*!
+ Sets the desired \a accuracy of the request.
+*/
+void QLocationPermission::setAccuracy(Accuracy accuracy)
+{
+ u.data.accuracy = accuracy;
+}
+
+/*!
+ Returns the accuracy of the request.
+*/
+QLocationPermission::Accuracy QLocationPermission::accuracy() const
+{
+ return u.data.accuracy;
+}
+
+/*!
+ Sets the desired \a availability of the request.
+*/
+void QLocationPermission::setAvailability(Availability availability)
+{
+ u.data.availability = availability;
+}
+
+/*!
+ Returns the availability of the request.
+*/
+QLocationPermission::Availability QLocationPermission::availability() const
+{
+ return u.data.availability;
+}
+
+/*!
+ \class QContactsPermission
+ \brief Access the user's contacts.
+
+ By default the request is for read-only access.
+ Use setAccessMode() to override the default.
+
+ \section1 Requirements
+
+ \include permissions.qdocinc begin-usage-declarations
+ \row
+ \li Apple
+ \li \l{apple-usage-description}{Usage description}
+ \li \c NSContactsUsageDescription
+ \row
+ \li Android
+ \li \l{android-uses-permission}{\c{uses-permission}}
+ \li \c android.permission.READ_CONTACTS. \c android.permission.WRITE_CONTACTS if
+ QContactsPermission::accessMode() is set to AccessMode::ReadWrite.
+ \include permissions.qdocinc end-usage-declarations
+
+ \include permissions.qdocinc permission-metadata
+*/
+
+/*!
+ \enum QContactsPermission::AccessMode
+
+ This enum is used to control access to the contacts data.
+
+ \value ReadOnly Read-only access to the contacts data (the default).
+ \value ReadWrite Read and write access to the contacts data.
+
+ \sa setAccessMode, accessMode
+*/
+
+QT_PERMISSION_IMPL_COMMON(QContactsPermission)
+ : u{ShortData{AccessMode::ReadOnly, {}}}
+{}
+
+/*!
+ Sets whether the request is for read-write (\a mode == AccessMode::ReadOnly) or
+ read-only (\a mode == AccessMode::ReadOnly) access to the contacts.
+*/
+void QContactsPermission::setAccessMode(AccessMode mode)
+{
+ u.data.mode = mode;
+}
+
+/*!
+ Returns AccessMode::ReadWrite when the request is for read-write and
+ AccessMode::ReadOnly when it is for read-only access to the contacts.
+*/
+QContactsPermission::AccessMode QContactsPermission::accessMode() const
+{
+ return u.data.mode;
+}
+
+/*!
+ \class QCalendarPermission
+ \brief Access the user's calendar.
+
+ By default the request is for read-only access.
+ Use setAccessMode() to override the default.
+
+ \section1 Requirements
+
+ \include permissions.qdocinc begin-usage-declarations
+ \row
+ \li Apple
+ \li \l{apple-usage-description}{Usage description}
+ \li \c NSCalendarsUsageDescription
+ \row
+ \li Android
+ \li \l{android-uses-permission}{\c{uses-permission}}
+ \li \c android.permission.READ_CALENDAR. \c android.permission.WRITE_CALENDAR if
+ QCalendarPermission::accessMode() is set to AccessMode::ReadWrite.
+ \include permissions.qdocinc end-usage-declarations
+
+ \include permissions.qdocinc permission-metadata
+*/
+
+/*!
+ \enum QCalendarPermission::AccessMode
+
+ This enum is used to control access to the calendar data.
+
+ \value ReadOnly Read-only access to the calendar data (the default).
+ \value ReadWrite Read and write access to the calendar data.
+
+ \sa setAccessMode, accessMode
+*/
+
+QT_PERMISSION_IMPL_COMMON(QCalendarPermission)
+ : u{ShortData{AccessMode::ReadOnly, {}}}
+{}
+
+/*!
+ Sets whether the request is for read-write (\a mode == AccessMode::ReadOnly) or
+ read-only (\a mode == AccessMode::ReadOnly) access to the calendar.
+*/
+void QCalendarPermission::setAccessMode(AccessMode mode)
+{
+ u.data.mode = mode;
+}
+
+/*!
+ Returns AccessMode::ReadWrite when the request is for read-write and
+ AccessMode::ReadOnly when it is for read-only access to the calendar.
+*/
+QCalendarPermission::AccessMode QCalendarPermission::accessMode() const
+{
+ return u.data.mode;
+}
+
+/*!
+ * \internal
+*/
+
+QPermissionPlugin::~QPermissionPlugin() = default;
+
+#ifndef QT_NO_DEBUG_STREAM
+QDebug operator<<(QDebug debug, const QPermission &permission)
+{
+ const auto verbosity = debug.verbosity();
+ QDebugStateSaver saver(debug);
+ debug.nospace().setVerbosity(0);
+ if (verbosity >= QDebug::DefaultVerbosity)
+ debug << permission.type().name() << "(";
+ debug << permission.status();
+ if (verbosity >= QDebug::DefaultVerbosity)
+ debug << ")";
+ return debug;
+}
+#endif
+
+#undef QT_PERMISSION_IMPL_COMMON
+
+#if !defined(Q_OS_DARWIN) && !defined(Q_OS_ANDROID) && !defined(Q_OS_WASM)
+// Default backend for platforms without a permission implementation.
+// Always returns Granted, to match behavior when not using permission APIs
+// https://bugreports.qt.io/browse/QTBUG-90498?focusedCommentId=725085#comment-725085
+namespace QPermissions::Private
+{
+ Qt::PermissionStatus checkPermission(const QPermission &permission)
+ {
+ qCDebug(lcPermissions) << "No permission backend on this platform."
+ << "Optimistically returning Granted for" << permission;
+ return Qt::PermissionStatus::Granted;
+ }
+
+ void requestPermission(const QPermission &permission, const PermissionCallback &callback)
+ {
+ qCDebug(lcPermissions) << "No permission backend on this platform."
+ << "Optimistically returning Granted for" << permission;
+ callback(Qt::PermissionStatus::Granted);
+ }
+}
+#endif
+
+QT_END_NAMESPACE
+
+#include "moc_qpermissions.cpp"
diff --git a/src/corelib/kernel/qpermissions.h b/src/corelib/kernel/qpermissions.h
new file mode 100644
index 0000000000..9573e377e5
--- /dev/null
+++ b/src/corelib/kernel/qpermissions.h
@@ -0,0 +1,219 @@
+// 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 QPERMISSIONS_H
+#define QPERMISSIONS_H
+
+#if 0
+#pragma qt_class(QPermissions)
+#endif
+
+#include <QtCore/qglobal.h>
+#include <QtCore/qtmetamacros.h>
+#include <QtCore/qvariant.h>
+
+#include <QtCore/qshareddata_impl.h>
+#include <QtCore/qtypeinfo.h>
+#include <QtCore/qmetatype.h>
+
+#include <optional>
+
+#if !defined(Q_QDOC)
+QT_REQUIRE_CONFIG(permissions);
+#endif
+
+QT_BEGIN_NAMESPACE
+
+#ifndef QT_NO_DEBUG_STREAM
+class QDebug;
+#endif
+
+struct QMetaObject;
+class QCoreApplication;
+
+class QPermission
+{
+ template <typename T, typename Enable = void>
+ static constexpr inline bool is_permission_v = false;
+
+ template <typename T>
+ using if_permission = std::enable_if_t<is_permission_v<T>, bool>;
+public:
+ explicit QPermission() = default;
+
+ template <typename T, if_permission<T> = true>
+ QPermission(const T &t) : m_data(QVariant::fromValue(t)) {}
+
+ Qt::PermissionStatus status() const { return m_status; }
+
+ QMetaType type() const { return m_data.metaType(); }
+
+ template <typename T, if_permission<T> = true>
+ std::optional<T> value() const
+ {
+ if (auto p = data(QMetaType::fromType<T>()))
+ return *static_cast<const T *>(p);
+ return std::nullopt;
+ }
+
+#ifndef QT_NO_DEBUG_STREAM
+ friend Q_CORE_EXPORT QDebug operator<<(QDebug debug, const QPermission &);
+#endif
+
+private:
+ Q_CORE_EXPORT const void *data(QMetaType id) const;
+
+ Qt::PermissionStatus m_status = Qt::PermissionStatus::Undetermined;
+ QVariant m_data;
+
+ friend class QCoreApplication;
+};
+
+template <typename T>
+constexpr bool QPermission::is_permission_v<T, typename T::QtPermissionHelper> = true;
+
+#define QT_PERMISSION(ClassName) \
+ using QtPermissionHelper = void; \
+ friend class QPermission; \
+ union U { \
+ U() : d(nullptr) {} \
+ U(ShortData _data) : data(_data) {} \
+ U(ClassName##Private *_d) : d(_d) {} \
+ ShortData data; \
+ ClassName##Private *d; \
+ } u; \
+public: \
+ Q_CORE_EXPORT ClassName(); \
+ Q_CORE_EXPORT ClassName(const ClassName &other) noexcept; \
+ ClassName(ClassName &&other) noexcept \
+ : u{other.u} { other.u.d = nullptr; } \
+ Q_CORE_EXPORT ~ClassName(); \
+ Q_CORE_EXPORT ClassName &operator=(const ClassName &other) noexcept; \
+ QT_MOVE_ASSIGNMENT_OPERATOR_IMPL_VIA_MOVE_AND_SWAP(ClassName) \
+ void swap(ClassName &other) noexcept { std::swap(u, other.u); } \
+private: \
+ /*end*/
+
+class QLocationPermissionPrivate;
+class QLocationPermission
+{
+ Q_GADGET_EXPORT(Q_CORE_EXPORT)
+public:
+ enum Accuracy : quint8 {
+ Approximate,
+ Precise,
+ };
+ Q_ENUM(Accuracy)
+
+ Q_CORE_EXPORT void setAccuracy(Accuracy accuracy);
+ Q_CORE_EXPORT Accuracy accuracy() const;
+
+ enum Availability : quint8 {
+ WhenInUse,
+ Always,
+ };
+ Q_ENUM(Availability)
+
+ Q_CORE_EXPORT void setAvailability(Availability availability);
+ Q_CORE_EXPORT Availability availability() const;
+
+private:
+ struct ShortData {
+ Accuracy accuracy;
+ Availability availability;
+ char reserved[sizeof(void*) - sizeof(accuracy) - sizeof(availability)];
+ };
+ QT_PERMISSION(QLocationPermission)
+};
+Q_DECLARE_SHARED(QLocationPermission)
+
+class QCalendarPermissionPrivate;
+class QCalendarPermission
+{
+ Q_GADGET_EXPORT(Q_CORE_EXPORT)
+public:
+ enum AccessMode : quint8 {
+ ReadOnly,
+ ReadWrite,
+ };
+ Q_ENUM(AccessMode)
+
+ Q_CORE_EXPORT void setAccessMode(AccessMode mode);
+ Q_CORE_EXPORT AccessMode accessMode() const;
+
+private:
+ struct ShortData {
+ AccessMode mode;
+ char reserved[sizeof(void*) - sizeof(mode)];
+ };
+ QT_PERMISSION(QCalendarPermission)
+};
+Q_DECLARE_SHARED(QCalendarPermission)
+
+class QContactsPermissionPrivate;
+class QContactsPermission
+{
+ Q_GADGET_EXPORT(Q_CORE_EXPORT)
+public:
+ enum AccessMode : quint8 {
+ ReadOnly,
+ ReadWrite,
+ };
+ Q_ENUM(AccessMode)
+
+ Q_CORE_EXPORT void setAccessMode(AccessMode mode);
+ Q_CORE_EXPORT AccessMode accessMode() const;
+
+private:
+ struct ShortData {
+ AccessMode mode;
+ char reserved[sizeof(void*) - sizeof(mode)];
+ };
+ QT_PERMISSION(QContactsPermission)
+};
+Q_DECLARE_SHARED(QContactsPermission)
+
+class QBluetoothPermissionPrivate;
+class QBluetoothPermission
+{
+ Q_GADGET_EXPORT(Q_CORE_EXPORT)
+public:
+ enum CommunicationMode : quint8 {
+ Access = 0x01,
+ Advertise = 0x02,
+ Default = Access | Advertise,
+ };
+ Q_DECLARE_FLAGS(CommunicationModes, CommunicationMode)
+ Q_FLAG(CommunicationModes)
+
+ Q_CORE_EXPORT void setCommunicationModes(CommunicationModes modes);
+ Q_CORE_EXPORT CommunicationModes communicationModes() const;
+
+private:
+ struct ShortData {
+ CommunicationMode mode;
+ char reserved[sizeof(void*) - sizeof(mode)];
+ };
+ QT_PERMISSION(QBluetoothPermission)
+};
+Q_DECLARE_OPERATORS_FOR_FLAGS(QBluetoothPermission::CommunicationModes)
+Q_DECLARE_SHARED(QBluetoothPermission)
+
+#define Q_DECLARE_MINIMAL_PERMISSION(ClassName) \
+ class ClassName##Private; \
+ class ClassName \
+ { \
+ struct ShortData { char reserved[sizeof(void*)]; }; \
+ QT_PERMISSION(ClassName) \
+ }; \
+ Q_DECLARE_SHARED(ClassName)
+
+Q_DECLARE_MINIMAL_PERMISSION(QCameraPermission)
+Q_DECLARE_MINIMAL_PERMISSION(QMicrophonePermission)
+
+#undef QT_PERMISSION
+#undef Q_DECLARE_MINIMAL_PERMISSION
+
+QT_END_NAMESPACE
+
+#endif // QPERMISSIONS_H
diff --git a/src/corelib/kernel/qpermissions_android.cpp b/src/corelib/kernel/qpermissions_android.cpp
new file mode 100644
index 0000000000..6c21ded72c
--- /dev/null
+++ b/src/corelib/kernel/qpermissions_android.cpp
@@ -0,0 +1,189 @@
+// 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
+
+#include "qpermissions.h"
+#include "qpermissions_p.h"
+
+#include <QtCore/qstringlist.h>
+#include <QtCore/qfuture.h>
+#include <QtCore/qhash.h>
+
+#include "private/qandroidextras_p.h"
+
+QT_BEGIN_NAMESPACE
+
+using namespace Qt::StringLiterals;
+
+static QStringList nativeLocationPermission(const QLocationPermission &permission)
+{
+ QStringList nativeLocationPermissionList;
+ const int sdkVersion = QtAndroidPrivate::androidSdkVersion();
+ static QString backgroundLocation = u"android.permission.ACCESS_BACKGROUND_LOCATION"_s;
+ static QString fineLocation = u"android.permission.ACCESS_FINE_LOCATION"_s;
+ static QString coarseLocation = u"android.permission.ACCESS_COARSE_LOCATION"_s;
+
+ // Since Android API 30, background location cannot be requested along
+ // with fine or coarse location, but it should be requested separately after
+ // the latter have been granted, see
+ // https://developer.android.com/training/location/permissions
+ if (sdkVersion < 30 || permission.availability() == QLocationPermission::WhenInUse) {
+ if (permission.accuracy() == QLocationPermission::Approximate) {
+ nativeLocationPermissionList << coarseLocation;
+ } else {
+ nativeLocationPermissionList << fineLocation;
+ // Since Android API 31, if precise location is requested, it's advised
+ // to request both fine and coarse location permissions, see
+ // https://developer.android.com/training/location/permissions#approximate-request
+ if (sdkVersion >= 31)
+ nativeLocationPermissionList << coarseLocation;
+ }
+ }
+
+ // NOTE: before Android API 29, background permission doesn't exist yet.
+
+ // Keep the background permission in front to be able to use first()
+ // on the list in checkPermission() because it takes single permission.
+ if (sdkVersion >= 29 && permission.availability() == QLocationPermission::Always)
+ nativeLocationPermissionList.prepend(backgroundLocation);
+
+ return nativeLocationPermissionList;
+}
+
+static QStringList nativeBluetoothPermission(const QBluetoothPermission &permission)
+{
+ // See https://developer.android.com/guide/topics/connectivity/bluetooth/permissions
+ // for the details.
+
+ // API Level < 31
+ static QString bluetoothGeneral = u"android.permission.BLUETOOTH"_s;
+ // API Level >= 31
+ static QString bluetoothScan = u"android.permission.BLUETOOTH_SCAN"_s;
+ static QString bluetoothAdvertise = u"android.permission.BLUETOOTH_ADVERTISE"_s;
+ static QString bluetoothConnect = u"android.permission.BLUETOOTH_CONNECT"_s;
+ // Fine location is currently required for ALL API levels, but that is not
+ // strictly necessary for API Level >= 31. See QTBUG-112164.
+ static QString fineLocation = u"android.permission.ACCESS_FINE_LOCATION"_s;
+
+ if (QtAndroidPrivate::androidSdkVersion() < 31) {
+ return {bluetoothGeneral, fineLocation};
+ } else {
+ const auto modes = permission.communicationModes();
+ QStringList permissionList;
+ if (modes & QBluetoothPermission::Advertise)
+ permissionList << bluetoothAdvertise;
+ if (modes & QBluetoothPermission::Access)
+ permissionList << bluetoothScan << bluetoothConnect << fineLocation;
+ return permissionList;
+ }
+}
+
+static QStringList nativeStringsFromPermission(const QPermission &permission)
+{
+ const auto id = permission.type().id();
+ if (id == qMetaTypeId<QLocationPermission>()) {
+ return nativeLocationPermission(*permission.value<QLocationPermission>());
+ } else if (id == qMetaTypeId<QCameraPermission>()) {
+ return { u"android.permission.CAMERA"_s };
+ } else if (id == qMetaTypeId<QMicrophonePermission>()) {
+ return { u"android.permission.RECORD_AUDIO"_s };
+ } else if (id == qMetaTypeId<QBluetoothPermission>()) {
+ return nativeBluetoothPermission(*permission.value<QBluetoothPermission>());
+ } else if (id == qMetaTypeId<QContactsPermission>()) {
+ const auto readContactsString = u"android.permission.READ_CONTACTS"_s;
+ switch (permission.value<QContactsPermission>()->accessMode()) {
+ case QContactsPermission::AccessMode::ReadOnly:
+ return { readContactsString };
+ case QContactsPermission::AccessMode::ReadWrite:
+ return { readContactsString, u"android.permission.WRITE_CONTACTS"_s };
+ }
+ Q_UNREACHABLE_RETURN({});
+ } else if (id == qMetaTypeId<QCalendarPermission>()) {
+ const auto readContactsString = u"android.permission.READ_CALENDAR"_s;
+ switch (permission.value<QCalendarPermission>()->accessMode()) {
+ case QCalendarPermission::AccessMode::ReadOnly:
+ return { readContactsString };
+ case QCalendarPermission::AccessMode::ReadWrite:
+ return { readContactsString, u"android.permission.WRITE_CALENDAR"_s };
+ }
+ Q_UNREACHABLE_RETURN({});
+ }
+
+ return {};
+}
+
+static Qt::PermissionStatus
+permissionStatusForAndroidResult(QtAndroidPrivate::PermissionResult result)
+{
+ switch (result) {
+ case QtAndroidPrivate::PermissionResult::Authorized: return Qt::PermissionStatus::Granted;
+ case QtAndroidPrivate::PermissionResult::Denied: return Qt::PermissionStatus::Denied;
+ default: return Qt::PermissionStatus::Undetermined;
+ }
+}
+
+using PermissionStatusHash = QHash<int, Qt::PermissionStatus>;
+Q_GLOBAL_STATIC_WITH_ARGS(PermissionStatusHash, g_permissionStatusHash, ({
+ { qMetaTypeId<QCameraPermission>(), Qt::PermissionStatus::Undetermined },
+ { qMetaTypeId<QMicrophonePermission>(), Qt::PermissionStatus::Undetermined },
+ { qMetaTypeId<QBluetoothPermission>(), Qt::PermissionStatus::Undetermined },
+ { qMetaTypeId<QContactsPermission>(), Qt::PermissionStatus::Undetermined },
+ { qMetaTypeId<QCalendarPermission>(), Qt::PermissionStatus::Undetermined },
+ { qMetaTypeId<QLocationPermission>(), Qt::PermissionStatus::Undetermined }
+}));
+
+static Qt::PermissionStatus
+getCombinedStatus(const QList<QtAndroidPrivate::PermissionResult> &androidResults)
+{
+ // Android returns only Denied or Granted
+ for (const auto &result : androidResults) {
+ const auto status = permissionStatusForAndroidResult(result);
+ if (status == Qt::PermissionStatus::Denied)
+ return status;
+ }
+ return Qt::PermissionStatus::Granted;
+}
+
+namespace QPermissions::Private
+{
+ Qt::PermissionStatus checkPermission(const QPermission &permission)
+ {
+ const auto nativePermissionList = nativeStringsFromPermission(permission);
+ if (nativePermissionList.isEmpty())
+ return Qt::PermissionStatus::Granted;
+
+ QList<QtAndroidPrivate::PermissionResult> androidResults;
+ androidResults.reserve(nativePermissionList.size());
+ for (const auto &nativePermission : nativePermissionList)
+ androidResults.push_back(QtAndroidPrivate::checkPermission(nativePermission).result());
+
+ const auto status = getCombinedStatus(androidResults);
+ const auto it = g_permissionStatusHash->constFind(permission.type().id());
+ const bool foundStatus = (it != g_permissionStatusHash->constEnd());
+ const bool itUndetermined = foundStatus && (*it) == Qt::PermissionStatus::Undetermined;
+ if (status == Qt::PermissionStatus::Denied && itUndetermined)
+ return Qt::PermissionStatus::Undetermined;
+ return status;
+ }
+
+ void requestPermission(const QPermission &permission,
+ const QPermissions::Private::PermissionCallback &callback)
+ {
+ const auto nativePermissionList = nativeStringsFromPermission(permission);
+ if (nativePermissionList.isEmpty()) {
+ callback(Qt::PermissionStatus::Granted);
+ return;
+ }
+
+ QtAndroidPrivate::requestPermissions(nativePermissionList).then(qApp,
+ [callback, permission](QFuture<QtAndroidPrivate::PermissionResult> future) {
+ const auto androidResults = future.isValid() ? future.results()
+ : QList{QtAndroidPrivate::Denied};
+ const auto status = getCombinedStatus(androidResults);
+ g_permissionStatusHash->insert(permission.type().id(), status);
+ callback(status);
+ }
+ );
+ }
+}
+
+QT_END_NAMESPACE
diff --git a/src/corelib/kernel/qpermissions_darwin.mm b/src/corelib/kernel/qpermissions_darwin.mm
new file mode 100644
index 0000000000..ae2cb2c423
--- /dev/null
+++ b/src/corelib/kernel/qpermissions_darwin.mm
@@ -0,0 +1,88 @@
+// 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
+
+#include "qpermissions.h"
+#include "qpermissions_p.h"
+
+#include <QtCore/private/qfactoryloader_p.h>
+#include <QtCore/private/qcoreapplication_p.h>
+#include <QtCore/qcborarray.h>
+
+QT_BEGIN_NAMESPACE
+
+using namespace Qt::StringLiterals;
+
+namespace {
+
+Q_GLOBAL_STATIC_WITH_ARGS(QFactoryLoader, pluginLoader,
+ (QPermissionPluginInterface_iid, QLatin1String("/permissions"), Qt::CaseInsensitive))
+
+QPermissionPlugin *permissionPlugin(const QPermission &permission)
+{
+ static QMutex mutex;
+ QMutexLocker locker(&mutex);
+
+ const char *permissionType = permission.type().name();
+ qCDebug(lcPermissions, "Looking for permission plugin for %s", permissionType);
+
+ if (Q_UNLIKELY(!pluginLoader)) {
+ qCWarning(lcPermissions, "Cannot check or request permissions during application shutdown");
+ return nullptr;
+ }
+
+ auto metaDataList = pluginLoader()->metaData();
+ for (int i = 0; i < metaDataList.size(); ++i) {
+ auto metaData = metaDataList.at(i).value(QtPluginMetaDataKeys::MetaData).toMap();
+ auto permissions = metaData.value("Permissions"_L1).toArray();
+ if (permissions.contains(QString::fromUtf8(permissionType))) {
+ auto className = metaDataList.at(i).value(QtPluginMetaDataKeys::ClassName).toString();
+ qCDebug(lcPermissions) << "Found matching plugin" << qUtf8Printable(className);
+ auto *plugin = static_cast<QPermissionPlugin*>(pluginLoader()->instance(i));
+ if (!plugin->parent()) {
+ // We want to re-parent the plugin to the factory loader, so that it's
+ // cleaned up properly. To do so we first need to move the plugin to the
+ // same thread as the factory loader, as the plugin might be instantiated
+ // on a secondary thread if triggered from a checkPermission call (which
+ // is allowed on any thread).
+ plugin->moveToThread(pluginLoader->thread());
+
+ // Also, as setParent will involve sending a ChildAdded event to the parent,
+ // we need to make the call on the same thread as the parent lives, as events
+ // are not allowed to be sent to an object owned by another thread.
+ QMetaObject::invokeMethod(plugin, [=] {
+ plugin->setParent(pluginLoader);
+ });
+ }
+ return plugin;
+ }
+ }
+
+ qCWarning(lcPermissions).nospace() << "Could not find permission plugin for "
+ << permission.type().name() << ". Please make sure you have included the "
+ << "required usage description in your Info.plist";
+
+ return nullptr;
+}
+
+} // Unnamed namespace
+
+namespace QPermissions::Private
+{
+ Qt::PermissionStatus checkPermission(const QPermission &permission)
+ {
+ if (auto *plugin = permissionPlugin(permission))
+ return plugin->checkPermission(permission);
+ else
+ return Qt::PermissionStatus::Denied;
+ }
+
+ void requestPermission(const QPermission &permission, const QPermissions::Private::PermissionCallback &callback)
+ {
+ if (auto *plugin = permissionPlugin(permission))
+ plugin->requestPermission(permission, callback);
+ else
+ callback(Qt::PermissionStatus::Denied);
+ }
+}
+
+QT_END_NAMESPACE
diff --git a/src/corelib/kernel/qpermissions_p.h b/src/corelib/kernel/qpermissions_p.h
new file mode 100644
index 0000000000..36f497f198
--- /dev/null
+++ b/src/corelib/kernel/qpermissions_p.h
@@ -0,0 +1,55 @@
+// 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 QPERMISSIONS_P_H
+#define QPERMISSIONS_P_H
+
+#include "qpermissions.h"
+
+#include <private/qglobal_p.h>
+#include <QtCore/qloggingcategory.h>
+
+#include <QtCore/QObject>
+
+#include <functional>
+
+QT_REQUIRE_CONFIG(permissions);
+
+//
+// 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.
+//
+
+QT_BEGIN_NAMESPACE
+
+Q_DECLARE_EXPORTED_LOGGING_CATEGORY(lcPermissions, Q_CORE_EXPORT)
+
+namespace QPermissions::Private
+{
+ using PermissionCallback = std::function<void(Qt::PermissionStatus)>;
+
+ Qt::PermissionStatus checkPermission(const QPermission &permission);
+ void requestPermission(const QPermission &permission, const PermissionCallback &callback);
+}
+
+#define QPermissionPluginInterface_iid "org.qt-project.QPermissionPluginInterface.6.5"
+
+class Q_CORE_EXPORT QPermissionPlugin : public QObject
+{
+public:
+ virtual ~QPermissionPlugin();
+
+ virtual Qt::PermissionStatus checkPermission(const QPermission &permission) = 0;
+ virtual void requestPermission(const QPermission &permission,
+ const QPermissions::Private::PermissionCallback &callback) = 0;
+};
+
+QT_END_NAMESPACE
+
+#endif // QPERMISSIONS_P_H
diff --git a/src/corelib/kernel/qpermissions_wasm.cpp b/src/corelib/kernel/qpermissions_wasm.cpp
new file mode 100644
index 0000000000..846e62ccf7
--- /dev/null
+++ b/src/corelib/kernel/qpermissions_wasm.cpp
@@ -0,0 +1,275 @@
+// 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
+
+#include <private/qpermissions_p.h>
+#include <private/qstdweb_p.h>
+
+#include <qglobalstatic.h>
+#include <qpermissions.h>
+#include <qmetaobject.h>
+#include <qnamespace.h>
+#include <qmetatype.h>
+#include <qstring.h>
+#include <qtimer.h>
+#include <qhash.h>
+
+#include <emscripten.h>
+#include <emscripten/bind.h>
+#include <emscripten/val.h>
+
+#include <utility>
+#include <string>
+#include <queue>
+
+QT_BEGIN_NAMESPACE
+
+using namespace QPermissions::Private;
+using namespace emscripten;
+
+namespace
+{
+ constexpr const char *wapiGranted = "granted";
+ constexpr const char *wapiDenied = "denied";
+ constexpr const char *wapiPrompt = "prompt";
+ constexpr const char *wapiCamera = "camera";
+ constexpr const char *wapiVideoCapture = "video_capture"; // Alternative to "camera".
+ constexpr const char *wapiMicrophone = "microphone";
+ constexpr const char *wapiAudioCapture = "audio_capture"; // Alternative to "microphone".
+ constexpr const char *wapiGeolocation = "geolocation";
+
+ void updatePermission(const std::string &name, const std::string &state,
+ PermissionCallback callback);
+
+ void checkPermission(const std::string &permissionName)
+ {
+ val permissions = val::global("navigator")["permissions"];
+ if (permissions.isUndefined() || permissions.isNull())
+ return;
+
+ qstdweb::PromiseCallbacks callbacks;
+ callbacks.thenFunc = [permissionName](val permissionState)
+ {
+ updatePermission(permissionName, permissionState["state"].as<std::string>(), {});
+ };
+ callbacks.catchFunc = [permissionName](val)
+ {
+ updatePermission(permissionName, wapiDenied, {});
+ };
+
+ val query = val::object();
+ query.set("name", val(permissionName));
+
+ qstdweb::Promise::make(permissions, QStringLiteral("query"), callbacks, query);
+ }
+
+ void checkPermissions()
+ {
+ checkPermission(wapiCamera);
+ checkPermission(wapiMicrophone);
+ checkPermission(wapiGeolocation);
+ }
+
+ void bootstrapCheckPermissions()
+ {
+ QTimer::singleShot(0, []{checkPermissions();});
+ }
+
+ Q_CONSTRUCTOR_FUNCTION(bootstrapCheckPermissions);
+
+ int permissionTypeIdFromString(const std::string &permission)
+ {
+ if (permission == wapiCamera || permission == wapiVideoCapture)
+ return qMetaTypeId<QCameraPermission>();
+ if (permission == wapiMicrophone || permission == wapiAudioCapture)
+ return qMetaTypeId<QMicrophonePermission>();
+ if (permission == wapiGeolocation)
+ return qMetaTypeId<QLocationPermission>();
+
+ qCWarning(lcPermissions, "Unknown permission type '%s'", permission.c_str());
+
+ return -1;
+ }
+
+ Qt::PermissionStatus permissionStatusFromString(const std::string &state)
+ {
+ if (state == wapiGranted)
+ return Qt::PermissionStatus::Granted;
+ if (state == wapiDenied)
+ return Qt::PermissionStatus::Denied;
+ if (state == wapiPrompt)
+ return Qt::PermissionStatus::Undetermined;
+
+ qCWarning(lcPermissions, "Unknown permission state '%s'", state.c_str());
+
+ return Qt::PermissionStatus::Denied;
+ }
+
+ using PermissionHash = QHash<int, Qt::PermissionStatus>;
+ Q_GLOBAL_STATIC(PermissionHash, permissionStatuses);
+
+ void updatePermission(const std::string &name, const std::string &state, PermissionCallback callback)
+ {
+ qCDebug(lcPermissions) << "Updating" << name << "permission to" << state;
+
+ const int type = permissionTypeIdFromString(name);
+ if (type == -1)
+ return; // Unknown permission type
+
+ const auto status = permissionStatusFromString(state);
+ (*permissionStatuses)[type] = status;
+
+ if (callback)
+ callback(status);
+ }
+
+ void requestMediaDevicePermission(const std::string &device, const PermissionCallback &cb)
+ {
+ Q_ASSERT(cb);
+
+ val mediaDevices = val::global("navigator")["mediaDevices"];
+ if (mediaDevices.isUndefined())
+ return cb(Qt::PermissionStatus::Denied);
+
+ qstdweb::PromiseCallbacks queryCallbacks;
+ queryCallbacks.thenFunc = [device, cb](val)
+ {
+ updatePermission(device, wapiGranted, cb);
+ };
+ queryCallbacks.catchFunc = [device, cb](val error)
+ {
+ if (error["name"].as<std::string>() == "NotAllowedError")
+ return updatePermission(device, wapiDenied, cb);
+ updatePermission(device, wapiPrompt, cb);
+ };
+
+ val constraint = val::object();
+ if (device == wapiCamera)
+ constraint.set("video", true);
+ else
+ constraint.set("audio", true);
+
+ qstdweb::Promise::make(mediaDevices, QStringLiteral("getUserMedia"), queryCallbacks, constraint);
+ }
+
+ using GeoRequest = std::pair<QPermission, PermissionCallback>;
+ Q_GLOBAL_STATIC(std::deque<GeoRequest>, geolocationRequestQueue);
+
+ bool processingLocationRequest = false;
+
+ void processNextGeolocationRequest();
+
+ void geolocationSuccess(val position)
+ {
+ Q_UNUSED(position);
+ Q_ASSERT(geolocationRequestQueue->size());
+
+ processingLocationRequest = false;
+
+ auto cb = geolocationRequestQueue->front().second;
+ geolocationRequestQueue->pop_front();
+ updatePermission(wapiGeolocation, wapiGranted, cb);
+ processNextGeolocationRequest();
+ }
+
+ void geolocationError(val error)
+ {
+ Q_ASSERT(geolocationRequestQueue->size());
+
+ static int deniedError = []
+ {
+ val posErr = val::global("GeolocationPositionError");
+ if (posErr.isUndefined() || posErr.isNull())
+ return 1;
+ return posErr["PERMISSION_DENIED"].as<int>();
+ }();
+
+ processingLocationRequest = false;
+
+ auto cb = geolocationRequestQueue->front().second;
+ geolocationRequestQueue->pop_front();
+
+ const int errorCode = error["code"].as<int>();
+ updatePermission(wapiGeolocation, errorCode == deniedError ? wapiDenied : wapiPrompt, cb);
+ processNextGeolocationRequest();
+ }
+
+ EMSCRIPTEN_BINDINGS(qt_permissions) {
+ function("qtLocationSuccess", &geolocationSuccess);
+ function("qtLocationError", &geolocationError);
+ }
+
+ void processNextGeolocationRequest()
+ {
+ if (processingLocationRequest)
+ return;
+
+ if (geolocationRequestQueue->empty())
+ return;
+
+ processingLocationRequest = true;
+
+ val geolocation = val::global("navigator")["geolocation"];
+ Q_ASSERT(!geolocation.isUndefined());
+ Q_ASSERT(!geolocation.isNull());
+
+ const auto &permission = geolocationRequestQueue->front().first;
+ const auto locationPermission = *permission.value<QLocationPermission>();
+ const bool highAccuracy = locationPermission.accuracy() == QLocationPermission::Precise;
+
+ val options = val::object();
+ options.set("enableHighAccuracy", highAccuracy ? true : false);
+ geolocation.call<void>("getCurrentPosition", val::module_property("qtLocationSuccess"),
+ val::module_property("qtLocationError"), options);
+ }
+
+ void requestGeolocationPermission(const QPermission &permission, const PermissionCallback &cb)
+ {
+ Q_ASSERT(cb);
+ Q_UNUSED(permission);
+ Q_UNUSED(cb);
+
+ val geolocation = val::global("navigator")["geolocation"];
+ if (geolocation.isUndefined() || geolocation.isNull())
+ return cb(Qt::PermissionStatus::Denied);
+
+ if (processingLocationRequest)
+ qCWarning(lcPermissions, "Permission to access location requested, while another request is in progress");
+
+ geolocationRequestQueue->push_back(std::make_pair(permission, cb));
+ processNextGeolocationRequest();
+ }
+} // Unnamed namespace
+
+namespace QPermissions::Private
+{
+ Qt::PermissionStatus checkPermission(const QPermission &permission)
+ {
+ const auto it = permissionStatuses->find(permission.type().id());
+ return it != permissionStatuses->end() ? it.value() : Qt::PermissionStatus::Undetermined;
+ }
+
+ void requestPermission(const QPermission &permission, const PermissionCallback &callback)
+ {
+ Q_ASSERT(permission.type().isValid());
+ Q_ASSERT(callback);
+
+ const auto status = checkPermission(permission);
+ if (status != Qt::PermissionStatus::Undetermined)
+ return callback(status);
+
+ const int requestedTypeId = permission.type().id();
+ if (requestedTypeId == qMetaTypeId<QCameraPermission>())
+ return requestMediaDevicePermission(wapiCamera, callback);
+
+ if (requestedTypeId == qMetaTypeId<QMicrophonePermission>())
+ return requestMediaDevicePermission(wapiMicrophone, callback);
+
+ if (requestedTypeId == qMetaTypeId<QLocationPermission>())
+ return requestGeolocationPermission(permission, callback);
+
+ (*permissionStatuses)[requestedTypeId] = Qt::PermissionStatus::Denied;
+ callback(Qt::PermissionStatus::Denied);
+ }
+}
+
+QT_END_NAMESPACE
diff --git a/src/corelib/kernel/qpointer.h b/src/corelib/kernel/qpointer.h
index b877b56133..39e4ba3e0f 100644
--- a/src/corelib/kernel/qpointer.h
+++ b/src/corelib/kernel/qpointer.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtCore module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// 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 QPOINTER_H
#define QPOINTER_H
@@ -54,15 +18,48 @@ class QPointer
{
static_assert(!std::is_pointer<T>::value, "QPointer's template type must not be a pointer type");
+ template <typename X>
+ using if_convertible = std::enable_if_t<std::is_convertible_v<X*, T*>, bool>;
+ template <typename X>
+ friend class QPointer;
+
using QObjectType =
typename std::conditional<std::is_const<T>::value, const QObject, QObject>::type;
QWeakPointer<QObjectType> wp;
public:
- QPointer() = default;
+ Q_NODISCARD_CTOR
+ QPointer() noexcept = default;
+ Q_NODISCARD_CTOR
+ constexpr QPointer(std::nullptr_t) noexcept : QPointer{} {}
+ Q_WEAK_OVERLOAD
+ Q_NODISCARD_CTOR
inline QPointer(T *p) : wp(p, true) { }
// compiler-generated copy/move ctor/assignment operators are fine!
// compiler-generated dtor is fine!
+ template <typename X, if_convertible<X> = true>
+ Q_NODISCARD_CTOR
+ QPointer(QPointer<X> &&other) noexcept
+ : wp(std::exchange(other.wp, nullptr).internalData(), true) {}
+ template <typename X, if_convertible<X> = true>
+ Q_NODISCARD_CTOR
+ QPointer(const QPointer<X> &other) noexcept
+ : wp(other.wp.internalData(), true) {}
+
+ template <typename X, if_convertible<X> = true>
+ QPointer &operator=(const QPointer<X> &other) noexcept
+ {
+ QPointer(other).swap(*this);
+ return *this;
+ }
+
+ template <typename X, if_convertible<X> = true>
+ QPointer &operator=(QPointer<X> &&other) noexcept
+ {
+ QPointer(std::move(other)).swap(*this);
+ return *this;
+ }
+
#ifdef Q_QDOC
// Stop qdoc from complaining about missing function
~QPointer();
@@ -73,27 +70,30 @@ 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(); }
+ friend void swap(QPointer &lhs, QPointer &rhs) noexcept
+ { lhs.swap(rhs); }
+
#define DECLARE_COMPARE_SET(T1, A1, T2, A2) \
- friend bool operator==(T1, T2) \
+ friend bool operator==(T1, T2) noexcept \
{ return A1 == A2; } \
- friend bool operator!=(T1, T2) \
+ friend bool operator!=(T1, T2) noexcept \
{ return A1 != A2; }
#define DECLARE_TEMPLATE_COMPARE_SET(T1, A1, T2, A2) \
@@ -122,10 +122,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 737d73c4b8..9e4c9b2658 100644
--- a/src/corelib/kernel/qpointer.cpp
+++ b/src/corelib/kernel/qpointer.qdoc
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtCore module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
/*!
\class QPointer
@@ -112,6 +76,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.
@@ -134,6 +99,42 @@
*/
/*!
+ \fn template <class T> template <typename X, QPointer<T>::if_convertible<X> = true> QPointer<T>::QPointer(QPointer<X> &&other)
+ \fn template <class T> template <typename X, QPointer<T>::if_convertible<X> = true> QPointer<T>::QPointer(const QPointer<X> &other)
+ \since 6.6
+
+ Conversion constructor. Constructs a new QPointer by moving or copying from
+ \a other.
+
+ The moved-from QPointer is reset to nullptr.
+
+ \note These constructors participate in overload resolution only if \c{X*}
+ is convertible to \c{T*}.
+*/
+
+/*!
+ \fn template <class T> template <typename X, QPointer<T>::if_convertible<X> = true> QPointer<T> &QPointer<T>::operator=(const QPointer<X> &other)
+ \since 6.6
+
+ Conversion assignment operator. Makes this guarded pointer guard the
+ same object guarded by \a other.
+
+ \note This operator participates in overload resolution only if \c{X*}
+ is convertible to \c{T*}.
+*/
+
+/*!
+ \fn template <class T> template <typename X, QPointer<T>::if_convertible<X> = true> &QPointer<T>::operator=(QPointer<X> &&other)
+ \since 6.6.1
+
+ Conversion move-assignment operator. Makes this guarded pointer guard the
+ same object guarded by \a other and resets \a other to nullptr.
+
+ \note This operator participates in overload resolution only if \c{X*}
+ is convertible to \c{T*}.
+*/
+
+/*!
\fn template <class T> void QPointer<T>::swap(QPointer &other)
\since 5.6
@@ -201,7 +202,7 @@
*/
/*!
- \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 *o, const QPointer<T> &p)
Equality operator. Returns \c true if \a o and the guarded
pointer \a p are pointing to the same object, otherwise
@@ -209,7 +210,7 @@
*/
/*!
- \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> &p, X *o)
Equality operator. Returns \c true if \a o and the guarded
pointer \a p are pointing to the same object, otherwise
@@ -217,7 +218,7 @@
*/
/*!
- \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> &p1, const QPointer<X> &p2)
Equality operator. Returns \c true if the guarded pointers \a p1 and \a p2
are pointing to the same object, otherwise
@@ -240,21 +241,21 @@
*/
/*!
- \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> &p, X *o)
Inequality operator. Returns \c true if \a o and the guarded
pointer \a p 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 *o, const QPointer<T> &p)
Inequality operator. Returns \c true if \a o and the guarded
pointer \a p 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> &p1, const QPointer<X> &p2)
Inequality operator. Returns \c true if the guarded pointers \a p1 and
\a p2 are not pointing to the same object, otherwise
diff --git a/src/corelib/kernel/qpoll.cpp b/src/corelib/kernel/qpoll.cpp
index f41c413db6..58fb0234b4 100644
--- a/src/corelib/kernel/qpoll.cpp
+++ b/src/corelib/kernel/qpoll.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtCore module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// 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 "qcore_unix_p.h"
@@ -90,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) {
@@ -145,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);
}
@@ -192,6 +156,11 @@ int qt_poll(struct pollfd *fds, nfds_t nfds, const struct timespec *timeout_ts)
if (fds[i].fd < 0)
continue;
+ if (fds[i].fd > FD_SETSIZE) {
+ errno = EINVAL;
+ return -1;
+ }
+
if (fds[i].events & QT_POLL_EVENTS_MASK)
continue;
diff --git a/src/corelib/kernel/qpoll_p.h b/src/corelib/kernel/qpoll_p.h
index cb6ad82ae7..7c7fa7a5a3 100644
--- a/src/corelib/kernel/qpoll_p.h
+++ b/src/corelib/kernel/qpoll_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtCore module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// 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 QPOLL_P_H
#define QPOLL_P_H
@@ -51,7 +15,7 @@
// We mean it.
//
-#include <QtCore/qglobal.h>
+#include <QtCore/private/qglobal_p.h>
QT_BEGIN_NAMESPACE
diff --git a/src/corelib/kernel/qproperty.cpp b/src/corelib/kernel/qproperty.cpp
index 6a129daa7a..caa9fce787 100644
--- a/src/corelib/kernel/qproperty.cpp
+++ b/src/corelib/kernel/qproperty.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2020 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtCore module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2020 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 "qproperty.h"
#include "qproperty_p.h"
@@ -44,6 +8,9 @@
#include <QScopeGuard>
#include <QtCore/qloggingcategory.h>
#include <QThread>
+#include <QtCore/qmetaobject.h>
+
+#include "qobject_p.h"
QT_BEGIN_NAMESPACE
@@ -60,9 +27,9 @@ void QPropertyBindingPrivatePtr::reset(QtPrivate::RefCounted *ptr) noexcept
{
if (ptr != d) {
if (ptr)
- ptr->ref++;
- auto *old = qExchange(d, ptr);
- if (old && (--old->ref == 0))
+ ptr->addRef();
+ auto *old = std::exchange(d, ptr);
+ if (old && !old->deref())
QPropertyBindingPrivate::destroyAndFreeMemory(static_cast<QPropertyBindingPrivate *>(d));
}
}
@@ -154,7 +121,7 @@ struct QPropertyDelayedNotifications
Change notifications are sent later with notify (following the logic of separating
binding updates and notifications used in non-deferred updates).
*/
- void evaluateBindings(int index, QBindingStatus *status) {
+ void evaluateBindings(PendingBindingObserverList &bindingObservers, qsizetype index, QBindingStatus *status) {
auto *delayed = delayedProperties + index;
auto *bindingData = delayed->originalBindingData;
if (!bindingData)
@@ -170,7 +137,7 @@ struct QPropertyDelayedNotifications
QPropertyBindingDataPointer bindingDataPointer{bindingData};
QPropertyObserverPointer observer = bindingDataPointer.firstObserver();
if (observer)
- observer.evaluateBindings(status);
+ observer.evaluateBindings(bindingObservers, status);
}
/*!
@@ -182,23 +149,23 @@ struct QPropertyDelayedNotifications
\li sends any pending notifications.
\endlist
*/
- void notify(int index) {
+ void notify(qsizetype index) {
auto *delayed = delayedProperties + index;
- auto *bindingData = delayed->originalBindingData;
- if (!bindingData)
+ if (delayed->d_ptr & QPropertyBindingData::BindingBit)
+ return; // already handled
+ if (!delayed->originalBindingData)
return;
-
delayed->originalBindingData = nullptr;
+
+ QPropertyObserverPointer observer { reinterpret_cast<QPropertyObserver *>(delayed->d_ptr & ~QPropertyBindingData::DelayedNotificationBit) };
delayed->d_ptr = 0;
- QPropertyBindingDataPointer bindingDataPointer{bindingData};
- QPropertyObserverPointer observer = bindingDataPointer.firstObserver();
if (observer)
observer.notify(delayed->propertyData);
}
};
-static thread_local QBindingStatus bindingStatus;
+Q_CONSTINIT static thread_local QBindingStatus bindingStatus;
/*!
\since 6.2
@@ -218,7 +185,7 @@ static thread_local QBindingStatus bindingStatus;
properties need to be updated, preventing any external observer from noticing an inconsistent
state.
- \sa Qt::endPropertyUpdateGroup
+ \sa Qt::endPropertyUpdateGroup, QScopedPropertyUpdateGroup
*/
void Qt::beginPropertyUpdateGroup()
{
@@ -238,7 +205,7 @@ void Qt::beginPropertyUpdateGroup()
\warning Calling endPropertyUpdateGroup without a preceding call to beginPropertyUpdateGroup
results in undefined behavior.
- \sa Qt::beginPropertyUpdateGroup
+ \sa Qt::beginPropertyUpdateGroup, QScopedPropertyUpdateGroup
*/
void Qt::endPropertyUpdateGroup()
{
@@ -249,24 +216,69 @@ void Qt::endPropertyUpdateGroup()
if (--data->ref)
return;
groupUpdateData = nullptr;
+ // ensures that bindings are kept alive until endPropertyUpdateGroup concludes
+ PendingBindingObserverList bindingObservers;
// update all delayed properties
auto start = data;
while (data) {
- for (int i = 0; i < data->used; ++i)
- data->evaluateBindings(i, status);
+ for (qsizetype i = 0; i < data->used; ++i)
+ data->evaluateBindings(bindingObservers, i, status);
data = data->next;
}
- // notify all delayed properties
+ // notify all delayed notifications from binding evaluation
+ for (const QBindingObserverPtr &observer: bindingObservers) {
+ QPropertyBindingPrivate *binding = observer.binding();
+ binding->notifyNonRecursive();
+ }
+ // do the same for properties which only have observers
data = start;
while (data) {
- for (int i = 0; i < data->used; ++i)
+ for (qsizetype i = 0; i < data->used; ++i)
data->notify(i);
- auto *next = data->next;
- delete data;
- data = next;
+ delete std::exchange(data, data->next);
}
}
+/*!
+ \since 6.6
+ \class QScopedPropertyUpdateGroup
+ \inmodule QtCore
+ \ingroup tools
+ \brief RAII class around Qt::beginPropertyUpdateGroup()/Qt::endPropertyUpdateGroup().
+
+ This class calls Qt::beginPropertyUpdateGroup() in its constructor and
+ Qt::endPropertyUpdateGroup() in its destructor, making sure the latter
+ function is reliably called even in the presence of early returns or thrown
+ exceptions.
+
+ \note Qt::endPropertyUpdateGroup() may re-throw exceptions thrown by
+ binding evaluations. This means your application may crash
+ (\c{std::terminate()} called) if another exception is causing
+ QScopedPropertyUpdateGroup's destructor to be called during stack
+ unwinding. If you expect exceptions from binding evaluations, use manual
+ Qt::endPropertyUpdateGroup() calls and \c{try}/\c{catch} blocks.
+
+ \sa QProperty
+*/
+
+/*!
+ \fn QScopedPropertyUpdateGroup::QScopedPropertyUpdateGroup()
+
+ Calls Qt::beginPropertyUpdateGroup().
+*/
+
+/*!
+ \fn QScopedPropertyUpdateGroup::~QScopedPropertyUpdateGroup()
+
+ Calls Qt::endPropertyUpdateGroup().
+*/
+
+
+// check everything stored in QPropertyBindingPrivate's union is trivially destructible
+// (though the compiler would also complain if that weren't the case)
+static_assert(std::is_trivially_destructible_v<QPropertyBindingSourceLocation>);
+static_assert(std::is_trivially_destructible_v<std::byte[sizeof(QPropertyBindingSourceLocation)]>);
+
QPropertyBindingPrivate::~QPropertyBindingPrivate()
{
if (firstObserver)
@@ -276,25 +288,51 @@ QPropertyBindingPrivate::~QPropertyBindingPrivate()
+ QPropertyBindingPrivate::getSizeEnsuringAlignment());
}
+void QPropertyBindingPrivate::clearDependencyObservers() {
+ for (size_t i = 0; i < qMin(dependencyObserverCount, inlineDependencyObservers.size()); ++i) {
+ QPropertyObserverPointer p{&inlineDependencyObservers[i]};
+ p.unlink_fast();
+ }
+ if (heapObservers)
+ heapObservers->clear();
+ dependencyObserverCount = 0;
+}
+
+QPropertyObserverPointer QPropertyBindingPrivate::allocateDependencyObserver_slow()
+{
+ ++dependencyObserverCount;
+ if (!heapObservers)
+ heapObservers.reset(new std::vector<QPropertyObserver>());
+ return {&heapObservers->emplace_back()};
+}
+
void QPropertyBindingPrivate::unlinkAndDeref()
{
clearDependencyObservers();
propertyDataPtr = nullptr;
- if (--ref == 0)
+ if (!deref())
destroyAndFreeMemory(this);
}
-void QPropertyBindingPrivate::evaluateRecursive(QBindingStatus *status)
+bool QPropertyBindingPrivate::evaluateRecursive(PendingBindingObserverList &bindingObservers, QBindingStatus *status)
{
if (!status)
status = &bindingStatus;
- return evaluateRecursive_inline(status);
+ return evaluateRecursive_inline(bindingObservers, status);
}
-void QPropertyBindingPrivate::notifyRecursive()
+void QPropertyBindingPrivate::notifyNonRecursive(const PendingBindingObserverList &bindingObservers)
+{
+ notifyNonRecursive();
+ for (auto &&bindingObserver: bindingObservers) {
+ bindingObserver.binding()->notifyNonRecursive();
+ }
+}
+
+QPropertyBindingPrivate::NotificationState QPropertyBindingPrivate::notifyNonRecursive()
{
if (!pendingNotify)
- return;
+ return Delayed;
pendingNotify = false;
Q_ASSERT(!updating);
updating = true;
@@ -305,6 +343,7 @@ void QPropertyBindingPrivate::notifyRecursive()
if (hasStaticObserver)
staticObserverCallback(propertyDataPtr);
updating = false;
+ return Sent;
}
/*!
@@ -416,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
{
@@ -428,6 +467,8 @@ QMetaType QUntypedPropertyBinding::valueMetaType() const
QPropertyBindingData::~QPropertyBindingData()
{
QPropertyBindingDataPointer d{this};
+ if (isNotificationDelayed())
+ proxyData()->originalBindingData = nullptr;
for (auto observer = d.firstObserver(); observer;) {
auto next = observer.nextObserver();
observer.unlink();
@@ -474,8 +515,9 @@ QUntypedPropertyBinding QPropertyBindingData::setBinding(const QUntypedPropertyB
newBindingRaw->prependObserver(observer);
newBindingRaw->setStaticObserver(staticObserverCallback, guardCallback);
- newBindingRaw->evaluateRecursive();
- newBindingRaw->notifyRecursive();
+ PendingBindingObserverList bindingObservers;
+ newBindingRaw->evaluateRecursive(bindingObservers);
+ newBindingRaw->notifyNonRecursive(bindingObservers);
} else if (observer) {
d.setObservers(observer.ptr);
} else {
@@ -561,6 +603,11 @@ void QPropertyBindingData::registerWithCurrentlyEvaluatingBinding_helper(Binding
{
QPropertyBindingDataPointer d{this};
+ if (currentState->alreadyCaptureProperties.contains(this))
+ return;
+ else
+ currentState->alreadyCaptureProperties.push_back(this);
+
QPropertyObserverPointer dependencyObserver = currentState->binding->allocateDependencyObserver();
Q_ASSERT(QPropertyObserver::ObserverNotifiesBinding == 0);
dependencyObserver.setBindingToNotify_unsafe(currentState->binding);
@@ -578,18 +625,31 @@ void QPropertyBindingData::notifyObservers(QUntypedPropertyData *propertyDataPtr
return;
QPropertyBindingDataPointer d{this};
+ PendingBindingObserverList bindingObservers;
if (QPropertyObserverPointer observer = d.firstObserver()) {
- if (notifyObserver_helper(propertyDataPtr, observer, storage) == Evaluated) {
- // evaluateBindings() can trash the observers. We need to re-fetch here.
+ if (notifyObserver_helper(propertyDataPtr, storage, observer, bindingObservers) == Evaluated) {
+ /* evaluateBindings() can trash the observers. We need to re-fetch here.
+ "this" might also no longer be valid in case we have a QObjectBindableProperty
+ and consequently d isn't either (this happens when binding evaluation has
+ caused the binding storage to resize.
+ If storage is nullptr, then there is no dynamically resizable storage,
+ and we cannot run into the issue.
+ */
+ if (storage)
+ d = QPropertyBindingDataPointer {storage->bindingData(propertyDataPtr)};
if (QPropertyObserverPointer observer = d.firstObserver())
observer.notify(propertyDataPtr);
+ for (auto &&bindingObserver: bindingObservers)
+ bindingObserver.binding()->notifyNonRecursive();
}
}
}
-QPropertyBindingData::NotificationResult QPropertyBindingData::notifyObserver_helper(
- QUntypedPropertyData *propertyDataPtr, QPropertyObserverPointer observer,
- QBindingStorage *storage) const
+QPropertyBindingData::NotificationResult QPropertyBindingData::notifyObserver_helper
+(
+ QUntypedPropertyData *propertyDataPtr, QBindingStorage *storage,
+ QPropertyObserverPointer observer,
+ PendingBindingObserverList &bindingObservers) const
{
#ifdef QT_HAS_FAST_CURRENT_THREAD_ID
QBindingStatus *status = storage ? storage->bindingStatus : nullptr;
@@ -604,17 +664,10 @@ QPropertyBindingData::NotificationResult QPropertyBindingData::notifyObserver_he
return Delayed;
}
- observer.evaluateBindings(status);
+ observer.evaluateBindings(bindingObservers, status);
return Evaluated;
}
-int QPropertyBindingDataPointer::observerCount() const
-{
- int count = 0;
- for (auto observer = firstObserver(); observer; observer = observer.nextObserver())
- ++count;
- return count;
-}
QPropertyObserver::QPropertyObserver(ChangeHandler changeHandler)
{
@@ -622,11 +675,15 @@ QPropertyObserver::QPropertyObserver(ChangeHandler changeHandler)
d.setChangeHandler(changeHandler);
}
+#if QT_DEPRECATED_SINCE(6, 6)
QPropertyObserver::QPropertyObserver(QUntypedPropertyData *data)
{
+ QT_WARNING_PUSH QT_WARNING_DISABLE_DEPRECATED
aliasData = data;
next.setTag(ObserverIsAlias);
+ QT_WARNING_POP
}
+#endif
/*! \internal
*/
@@ -696,16 +753,9 @@ void QPropertyObserverPointer::setChangeHandler(QPropertyObserver::ChangeHandler
ptr->next.setTag(QPropertyObserver::ObserverNotifiesChangeHandler);
}
-void QPropertyObserverPointer::setBindingToNotify(QPropertyBindingPrivate *binding)
-{
- Q_ASSERT(ptr->next.tag() != QPropertyObserver::ObserverIsPlaceholder);
- ptr->binding = binding;
- ptr->next.setTag(QPropertyObserver::ObserverNotifiesBinding);
-}
-
/*!
\internal
- The same as as setBindingToNotify, but assumes that the tag is already correct.
+ The same as setBindingToNotify, but assumes that the tag is already correct.
*/
void QPropertyObserverPointer::setBindingToNotify_unsafe(QPropertyBindingPrivate *binding)
{
@@ -744,7 +794,7 @@ void QPropertyObserverPointer::noSelfDependencies(QPropertyBindingPrivate *bindi
}
#endif
-void QPropertyObserverPointer::evaluateBindings(QBindingStatus *status)
+void QPropertyObserverPointer::evaluateBindings(PendingBindingObserverList &bindingObservers, QBindingStatus *status)
{
Q_ASSERT(status);
auto observer = const_cast<QPropertyObserver*>(ptr);
@@ -755,7 +805,9 @@ void QPropertyObserverPointer::evaluateBindings(QBindingStatus *status)
if (QPropertyObserver::ObserverTag(observer->next.tag()) == QPropertyObserver::ObserverNotifiesBinding) {
auto bindingToEvaluate = observer->binding;
QPropertyObserverNodeProtector protector(observer);
- bindingToEvaluate->evaluateRecursive_inline(status);
+ QBindingObserverPtr bindingObserver(observer); // binding must not be gone after evaluateRecursive_inline
+ if (bindingToEvaluate->evaluateRecursive_inline(bindingObservers, status))
+ bindingObservers.push_back(std::move(bindingObserver));
next = protector.next();
}
@@ -1101,7 +1153,8 @@ QString QPropertyBindingError::description() const
\ingroup tools
- QBindable\<T\> helps to integrate Qt's traditional Q_PROPERTY with binding-enabled properties.
+ QBindable\<T\> helps to integrate Qt's traditional Q_PROPERTY with
+ \l {Qt Bindable Properties}{binding-enabled} properties.
If a property is backed by a QProperty, QObjectBindableProperty or QObjectComputedProperty,
you can add \c BINDABLE bindablePropertyName to the Q_PROPERTY
declaration, where bindablePropertyName is a function returning an instance of QBindable
@@ -1112,7 +1165,40 @@ QString QPropertyBindingError::description() const
\snippet code/src_corelib_kernel_qproperty.cpp 0
\snippet code/src_corelib_kernel_qproperty.cpp 3
- \sa QMetaProperty::isBindable, QProperty, QObjectBindableProperty
+ \sa QMetaProperty::isBindable, QProperty, QObjectBindableProperty,
+ QObjectComputedProperty, {Qt Bindable Properties}
+*/
+
+/*!
+ \fn template<typename T> QBindable<T>::QBindable(QObject *obj, const char *property)
+
+ Constructs a QBindable for the \l Q_PROPERTY \a property on \a obj. The property must
+ have a notify signal but does not need to have \c BINDABLE in its \c Q_PROPERTY
+ definition, so even binding unaware \c {Q_PROPERTY}s can be bound or used in binding
+ expressions. You must use \c QBindable::value() in binding expressions instead of the
+ normal property \c READ function (or \c MEMBER) to enable dependency tracking if the
+ property is not \c BINDABLE. When binding using a lambda, you may prefer to capture the
+ QBindable by value to avoid the cost of calling this constructor in the binding
+ expression.
+ This constructor should not be used to implement \c BINDABLE for a Q_PROPERTY, as the
+ resulting Q_PROPERTY will not support dependency tracking. To make a property that is
+ 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 \l QBindable::QBindable(QObject *obj, const char *property)
*/
/*!
@@ -1193,6 +1279,15 @@ QString QPropertyBindingError::description() const
dynamically, the binding expression. It is represented as a C++ lambda and
can be used to express relationships between different properties in your
application.
+
+ \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, 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.
*/
/*!
@@ -1223,20 +1318,20 @@ QString QPropertyBindingError::description() const
/*!
\fn template <typename T> QProperty<T>::QProperty(const QPropertyBinding<T> &binding)
- Constructs a property that is tied to the provided \a binding expression. The
- first time the property value is read, the binding is evaluated. Whenever a
- dependency of the binding changes, the binding will be re-evaluated the next
- time the value of this property is read.
+ Constructs a property that is tied to the provided \a binding expression.
+ The property's value is set to the result of evaluating the new binding.
+ Whenever a dependency of the binding changes, the binding will be re-evaluated,
+ and the property's value gets updated accordingly.
*/
/*!
\fn template <typename T> template <typename Functor> QProperty<T>::QProperty(Functor &&f)
- Constructs a property that is tied to the provided binding expression \a f. The
- first time the property value is read, the binding is evaluated. Whenever a
- dependency of the binding changes, the binding will be re-evaluated the next
- time the value of this property is read.
-*/
+ Constructs a property that is tied to the provided binding expression \a f.
+ The property's value is set to the result of evaluating the new binding.
+ Whenever a dependency of the binding changes, the binding will be re-evaluated,
+ and the property's value gets updated accordingly.
+ */
/*!
\fn template <typename T> QProperty<T>::~QProperty()
@@ -1267,23 +1362,13 @@ QString QPropertyBindingError::description() const
*/
/*!
- \fn template <typename T> QProperty<T> &QProperty<T>::operator=(const QPropertyBinding<T> &newBinding)
-
- Associates the value of this property with the provided \a newBinding
- expression and returns a reference to this property. The first time the
- property value is read, the binding is evaluated. Whenever a dependency of the
- binding changes, the binding will be re-evaluated the next time the value of
- this property is read.
-*/
-
-/*!
\fn template <typename T> QPropertyBinding<T> QProperty<T>::setBinding(const QPropertyBinding<T> &newBinding)
Associates the value of this property with the provided \a newBinding
- expression and returns the previously associated binding. The first time the
- property value is read, the binding is evaluated. Whenever a dependency of the
- binding changes, the binding will be re-evaluated the next time the value of
- this property is read.
+ expression and returns the previously associated binding. The property's value
+ is set to the result of evaluating the new binding. Whenever a dependency of
+ the binding changes, the binding will be re-evaluated, and the property's
+ value gets updated accordingly.
*/
/*!
@@ -1291,10 +1376,10 @@ QString QPropertyBindingError::description() const
\overload
Associates the value of this property with the provided functor \a f and
- returns the previously associated binding. The first time the property value
- is read, the binding is evaluated by invoking the call operator () of \a f.
- Whenever a dependency of the binding changes, the binding will be re-evaluated
- the next time the value of this property is read.
+ returns the previously associated binding. The property's value is set to the
+ result of evaluating the new binding. Whenever a dependency of the binding
+ changes, the binding will be re-evaluated, and the property's value gets
+ updated accordingly.
\sa {Formulating a Property Binding}
*/
@@ -1304,9 +1389,10 @@ QString QPropertyBindingError::description() const
\overload
Associates the value of this property with the provided \a newBinding
- expression. The first time the property value is read, the binding is evaluated.
- Whenever a dependency of the binding changes, the binding will be re-evaluated
- the next time the value of this property is read.
+ expression. The property's value is set to the result of evaluating the new
+ binding. Whenever a dependency of the binding changes, the binding will be
+ re-evaluated, and the property's value gets updated accordingly.
+
Returns true if the type of this property is the same as the type the binding
function returns; false otherwise.
@@ -1335,27 +1421,29 @@ QString QPropertyBindingError::description() const
the value of the property changes. On each value change, the handler
is either called immediately, or deferred, depending on the context.
- The callback \a f is expected to be a type that has a plain call operator () without any
- parameters. This means that you can provide a C++ lambda expression, a std::function
- or even a custom struct with a call operator.
+ The callback \a f is expected to be a type that has a plain call operator
+ \c{()} without any parameters. This means that you can provide a C++ lambda
+ expression, a std::function or even a custom struct with a call operator.
- The returned property change handler object keeps track of the registration. When it
- goes out of scope, the callback is de-registered.
+ The returned property change handler object keeps track of the registration.
+ When it goes out of scope, the callback is de-registered.
*/
/*!
\fn template <typename T> template <typename Functor> QPropertyChangeHandler<T, Functor> QProperty<T>::subscribe(Functor f)
- Subscribes the given functor \a f as a callback that is called immediately and whenever
- the value of the property changes in the future. On each value change, the handler
- is either called immediately, or deferred, depending on the context.
+ Subscribes the given functor \a f as a callback that is called immediately and
+ whenever the value of the property changes in the future. On each value
+ change, the handler is either called immediately, or deferred, depending on
+ the context.
- The callback \a f is expected to be a type that can be copied and has a plain call
- operator() without any parameters. This means that you can provide a C++ lambda expression,
- a std::function or even a custom struct with a call operator.
+ The callback \a f is expected to be a type that can be copied and has a plain
+ call operator() without any parameters. This means that you can provide a C++
+ lambda expression, a std::function or even a custom struct with a call
+ operator.
- The returned property change handler object keeps track of the subscription. When it
- goes out of scope, the callback is unsubscribed.
+ The returned property change handler object keeps track of the subscription.
+ When it goes out of scope, the callback is unsubscribed.
*/
/*!
@@ -1364,15 +1452,16 @@ QString QPropertyBindingError::description() const
Subscribes the given functor \a f as a callback that is called whenever
the value of the property changes.
- The callback \a f is expected to be a type that has a plain call operator () without any
- parameters. This means that you can provide a C++ lambda expression, a std::function
- or even a custom struct with a call operator.
+ The callback \a f is expected to be a type that has a plain call operator
+ \c{()} without any parameters. This means that you can provide a C++ lambda
+ expression, a std::function or even a custom struct with a call operator.
- The returned property change handler object keeps track of the subscription. When it
- goes out of scope, the callback is unsubscribed.
+ The returned property change handler object keeps track of the subscription.
+ When it goes out of scope, the callback is unsubscribed.
- This method is in some cases easier to use than onValueChanged(), as the returned object is not a template.
- It can therefore more easily be stored, e.g. as a member in a class.
+ This method is in some cases easier to use than onValueChanged(), as the
+ returned object is not a template. It can therefore more easily be stored,
+ e.g. as a member in a class.
\sa onValueChanged(), subscribe()
*/
@@ -1385,8 +1474,9 @@ QString QPropertyBindingError::description() const
/*!
\class QObjectBindableProperty
\inmodule QtCore
- \brief The QObjectBindableProperty class is a template class that enables automatic property bindings
- for property data stored in QObject derived classes.
+ \brief The QObjectBindableProperty class is a template class that enables
+ automatic property bindings for property data stored in QObject derived
+ classes.
\since 6.0
\ingroup tools
@@ -1399,9 +1489,9 @@ QString QPropertyBindingError::description() const
The extra template parameters are used to identify the surrounding
class and a member function of that class acting as a change handler.
- You can use QObjectBindableProperty to add binding support to code that uses Q_PROPERTY.
- The getter and setter methods must be adapted carefully according to the
- rules described in \l {Bindable Property Getters and Setters}.
+ You can use QObjectBindableProperty to add binding support to code that uses
+ Q_PROPERTY. The getter and setter methods must be adapted carefully according
+ to the rules described in \l {Bindable Property Getters and Setters}.
In order to invoke the change signal on property changes, use
QObjectBindableProperty and pass the change signal as a callback.
@@ -1410,8 +1500,8 @@ QString QPropertyBindingError::description() const
\snippet code/src_corelib_kernel_qproperty.cpp 4
- QObjectBindableProperty is usually not used directly, instead an instance of it is created by
- using the Q_OBJECT_BINDABLE_PROPERTY macro.
+ QObjectBindableProperty is usually not used directly, instead an instance of
+ it is created by using the Q_OBJECT_BINDABLE_PROPERTY macro.
Use the Q_OBJECT_BINDABLE_PROPERTY macro in the class declaration to declare
the property as bindable.
@@ -1430,23 +1520,27 @@ QString QPropertyBindingError::description() const
\snippet code/src_corelib_kernel_qproperty.cpp 2
- The change handler can optionally accept one argument, of the same type as the property,
- in which case it is passed the new value of the property. Otherwise, it should take no
- arguments.
+ The change handler can optionally accept one argument, of the same type as the
+ property, in which case it is passed the new value of the property. Otherwise,
+ it should take no arguments.
If the property does not need a changed notification, you can leave out the
"NOTIFY xChanged" in the Q_PROPERTY macro as well as the last argument
of the Q_OBJECT_BINDABLE_PROPERTY and Q_OBJECT_BINDABLE_PROPERTY_WITH_ARGS
macros.
+
+ \sa Q_OBJECT_BINDABLE_PROPERTY, Q_OBJECT_BINDABLE_PROPERTY_WITH_ARGS,
+ QProperty, QObjectComputedProperty, {Qt's Property System}, {Qt Bindable
+ Properties}
*/
/*!
\macro Q_OBJECT_BINDABLE_PROPERTY(containingClass, type, name, signal)
\since 6.0
\relates QObjectBindableProperty
- \brief Declares a \l QObjectBindableProperty inside \a containingClass
- of type \a type with name \a name. If the optional argument \a signal is given,
- this signal will be emitted when the property is marked dirty.
+ \brief Declares a \l QObjectBindableProperty inside \a containingClass of type
+ \a type with name \a name. If the optional argument \a signal is given, this
+ signal will be emitted when the property is marked dirty.
\sa {Qt's Property System}, {Qt Bindable Properties}
*/
@@ -1556,7 +1650,6 @@ QString QPropertyBindingError::description() const
properties to the bindable property system.
\since 6.0
\ingroup tools
- \internal
QObjectComputedProperty is a read-only property which is recomputed on each read.
It does not store the computed value.
@@ -1566,42 +1659,7 @@ QString QPropertyBindingError::description() const
See the following example.
- \code
- class Client{};
-
- class MyClassPrivate : public QObjectPrivate
- {
- public:
- QList<Client> clients;
- bool hasClientsActualCalculation() const { return clients.size() > 0; }
- Q_OBJECT_COMPUTED_PROPERTY(MyClassPrivate, bool, hasClientsData,
- &MyClassPrivate::hasClientsActualCalculation)
- };
-
- class MyClass : public QObject
- {
- // add q-object macro here (confuses qdoc if we do it here)
- Q_PROPERTY(bool hasClients READ hasClients STORED false BINDABLE bindableHasClients)
- public:
- QBindable<bool> bindableHasClients()
- {
- return QBindable<bool>(&d_func()->hasClientsData);
- }
- bool hasClients() const
- {
- return d_func()->hasClientsData.value();
- }
- void addClient(const Client &c)
- {
- Q_D(MyClass);
- d->clients.push_back(c);
- // notify that the value could have changed
- d->hasClientsData.markDirty();
- }
- private:
- Q_DECLARE_PRIVATE(MyClass)
- };
- \endcode
+ \snippet code/src_corelib_kernel_qproperty.cpp 5
The rules for getters in \l {Bindable Property Getters and Setters}
also apply for QObjectComputedProperty. Especially, the getter
@@ -1616,27 +1674,26 @@ QString QPropertyBindingError::description() const
have changed. Whenever a bindable property used in the callback changes,
this happens automatically. If the result of the callback might change
because of a change in a value which is not a bindable property,
- it is the developer's responsibility to call markDirty
+ it is the developer's responsibility to call \c notify
on the QObjectComputedProperty object.
This will inform dependent properties about the potential change.
- Note that calling markDirty might trigger change handlers in dependent
+ Note that calling \c notify might trigger change handlers in dependent
properties, which might in turn use the object the QObjectComputedProperty
- is a member of. So markDirty must not be called when in a transitional
+ is a member of. So \c notify must not be called when in a transitional
or invalid state.
QObjectComputedProperty is not suitable for use with a computation that depends
on any input that might change without notice, such as the contents of a file.
- \sa Q_OBJECT_COMPUTED_PROPERTY, QObjectBindableProperty, {Qt's Property System},
- {Qt Bindable Properties}
+ \sa Q_OBJECT_COMPUTED_PROPERTY, QProperty, QObjectBindableProperty,
+ {Qt's Property System}, {Qt Bindable Properties}
*/
/*!
\macro Q_OBJECT_COMPUTED_PROPERTY(containingClass, type, name, callback)
\since 6.0
- \relates QObjectCompatProperty
- \internal
+ \relates QObjectComputedProperty
\brief Declares a \l QObjectComputedProperty inside \a containingClass
of type \a type with name \a name. The argument \a callback specifies
a GETTER function to be called when the property is evaluated.
@@ -1665,30 +1722,35 @@ QString QPropertyBindingError::description() const
/*!
\fn template <typename Class, typename T, auto offset, auto Callback> QObjectBindableProperty<Class, T, offset, Callback>::QObjectBindableProperty(Class *owner, const QPropertyBinding<T> &binding)
- Constructs a property that is tied to the provided \a binding expression. The
- first time the property value is read, the binding is evaluated. Whenever a
- dependency of the binding changes, the binding will be re-evaluated the next
- time the value of this property is read. When the property value changes \a
- owner is notified via the Callback function.
+ Constructs a property that is tied to the provided \a binding expression.
+ The property's value is set to the result of evaluating the new binding.
+ Whenever a dependency of the binding changes, the binding will be
+ re-evaluated, and the property's value gets updated accordingly.
+
+ When the property value changes, \a owner is notified via the Callback
+ function.
*/
/*!
\fn template <typename Class, typename T, auto offset, auto Callback> QObjectBindableProperty<Class, T, offset, Callback>::QObjectBindableProperty(Class *owner, QPropertyBinding<T> &&binding)
- Constructs a property that is tied to the provided \a binding expression. The
- first time the property value is read, the binding is evaluated. Whenever a
- dependency of the binding changes, the binding will be re-evaluated the next
- time the value of this property is read. When the property value changes \a
+ Constructs a property that is tied to the provided \a binding expression.
+ The property's value is set to the result of evaluating the new binding.
+ Whenever a dependency of the binding changes, the binding will be
+ re-evaluated, and the property's value gets updated accordingly.
+
+ When the property value changes, \a
owner is notified via the Callback function.
*/
/*!
\fn template <typename Class, typename T, auto offset, auto Callback> template <typename Functor> QObjectBindableProperty<Class, T, offset, Callback>::QObjectBindableProperty(Functor &&f)
- Constructs a property that is tied to the provided binding expression \a f. The
- first time the property value is read, the binding is evaluated. Whenever a
- dependency of the binding changes, the binding will be re-evaluated the next
- time the value of this property is read.
+ Constructs a property that is tied to the provided binding expression \a f.
+ The property's value is set to the result of evaluating the new binding.
+ Whenever a dependency of the binding changes, the binding will be
+ re-evaluated, and the property's value gets updated accordingly.
+
*/
/*!
@@ -1716,14 +1778,15 @@ QString QPropertyBindingError::description() const
/*!
\fn template <typename Class, typename T, auto offset, auto Callback> void QObjectBindableProperty<Class, T, offset, Callback>::notify()
- Programmatically signals a change of the property. Any binding which depend on it will
- be notified, and if the property has a signal, it will be emitted.
+ Programmatically signals a change of the property. Any binding which depend on
+ it will be notified, and if the property has a signal, it will be emitted.
- This can be useful in combination with setValueBypassingBindings to defer signalling the change
- until a class invariant has been restored.
+ This can be useful in combination with setValueBypassingBindings to defer
+ signalling the change until a class invariant has been restored.
- \note If this property has a binding (i.e. hasBinding() returns true), that binding is not reevaluated when
- notify() is called. Any binding depending on this property is still reevaluated as usual.
+ \note If this property has a binding (i.e. hasBinding() returns true), that
+ binding is not reevaluated when notify() is called. Any binding depending on
+ this property is still reevaluated as usual.
\sa Qt::beginPropertyUpdateGroup(), setValueBypassingBindings()
*/
@@ -1732,11 +1795,12 @@ QString QPropertyBindingError::description() const
\fn template <typename Class, typename T, auto offset, auto Callback> QPropertyBinding<T> QObjectBindableProperty<Class, T, offset, Callback>::setBinding(const QPropertyBinding<T> &newBinding)
Associates the value of this property with the provided \a newBinding
- expression and returns the previously associated binding. The first time the
- property value is read, the binding is evaluated. Whenever a dependency of the
- binding changes, the binding will be re-evaluated the next time the value of
- this property is read. When the property value changes, the owner is notified
- via the Callback function.
+ expression and returns the previously associated binding.
+ The property's value is set to the result of evaluating the new binding. Whenever a dependency of
+ the binding changes, the binding will be re-evaluated,
+ and the property's value gets updated accordingly.
+ When the property value changes, the owner
+ is notified via the Callback function.
*/
/*!
@@ -1744,11 +1808,13 @@ QString QPropertyBindingError::description() const
\overload
Associates the value of this property with the provided functor \a f and
- returns the previously associated binding. The first time the property value
- is read, the binding is evaluated by invoking the call operator () of \a f.
- Whenever a dependency of the binding changes, the binding will be re-evaluated
- the next time the value of this property is read. When the property value
- changes, the owner is notified via the Callback function.
+ returns the previously associated binding. The property's value is set to the
+ result of evaluating the new binding by invoking the call operator \c{()} of \a
+ f. Whenever a dependency of the binding changes, the binding will be
+ re-evaluated, and the property's value gets updated accordingly.
+
+ When the property value changes, the owner is notified via the Callback
+ function.
\sa {Formulating a Property Binding}
*/
@@ -1758,12 +1824,13 @@ QString QPropertyBindingError::description() const
\overload
Associates the value of this property with the provided \a newBinding
- expression. The first time the property value is read, the binding is evaluated.
- Whenever a dependency of the binding changes, the binding will be re-evaluated
- the next time the value of this property is read.
+ expression. The property's value is set to the result of evaluating the new
+ binding. Whenever a dependency of the binding changes, the binding will be
+ re-evaluated, and the property's value gets updated accordingly.
+
- Returns \c true if the type of this property is the same as the type the binding
- function returns; \c false otherwise.
+ Returns \c true if the type of this property is the same as the type the
+ binding function returns; \c false otherwise.
*/
/*!
@@ -1793,47 +1860,49 @@ QString QPropertyBindingError::description() const
\fn template <typename Class, typename T, auto offset, auto Callback> template <typename Functor> QPropertyChangeHandler<T, Functor> QObjectBindableProperty<Class, T, offset, Callback>::onValueChanged(Functor f)
Registers the given functor \a f as a callback that shall be called whenever
- the value of the property changes. On each value change, the handler
- is either called immediately, or deferred, depending on the context.
+ the value of the property changes. On each value change, the handler is either
+ called immediately, or deferred, depending on the context.
- The callback \a f is expected to be a type that has a plain call operator () without any
- parameters. This means that you can provide a C++ lambda expression, a std::function
- or even a custom struct with a call operator.
+ The callback \a f is expected to be a type that has a plain call operator
+ \c{()} without any parameters. This means that you can provide a C++ lambda
+ expression, a std::function or even a custom struct with a call operator.
- The returned property change handler object keeps track of the registration. When it
- goes out of scope, the callback is de-registered.
+ The returned property change handler object keeps track of the registration.
+ When it goes out of scope, the callback is de-registered.
*/
/*!
\fn template <typename Class, typename T, auto offset, auto Callback> template <typename Functor> QPropertyChangeHandler<T, Functor> QObjectBindableProperty<Class, T, offset, Callback>::subscribe(Functor f)
- Subscribes the given functor \a f as a callback that is called immediately and whenever
- the value of the property changes in the future. On each value change, the handler
- is either called immediately, or deferred, depending on the context.
+ Subscribes the given functor \a f as a callback that is called immediately and
+ whenever the value of the property changes in the future. On each value
+ change, the handler is either called immediately, or deferred, depending on
+ the context.
- The callback \a f is expected to be a type that has a plain call operator () without any
- parameters. This means that you can provide a C++ lambda expression, a std::function
- or even a custom struct with a call operator.
+ The callback \a f is expected to be a type that has a plain call operator
+ \c{()} without any parameters. This means that you can provide a C++ lambda
+ expression, a std::function or even a custom struct with a call operator.
- The returned property change handler object keeps track of the subscription. When it
- goes out of scope, the callback is unsubscribed.
+ The returned property change handler object keeps track of the subscription.
+ When it goes out of scope, the callback is unsubscribed.
*/
/*!
\fn template <typename Class, typename T, auto offset, auto Callback> template <typename Functor> QPropertyNotifier QObjectBindableProperty<Class, T, offset, Callback>::addNotifier(Functor f)
- Subscribes the given functor \a f as a callback that is called whenever
- the value of the property changes.
+ Subscribes the given functor \a f as a callback that is called whenever the
+ value of the property changes.
- The callback \a f is expected to be a type that has a plain call operator () without any
- parameters. This means that you can provide a C++ lambda expression, a std::function
- or even a custom struct with a call operator.
+ The callback \a f is expected to be a type that has a plain call operator
+ \c{()} without any parameters. This means that you can provide a C++ lambda
+ expression, a std::function or even a custom struct with a call operator.
- The returned property change handler object keeps track of the subscription. When it
- goes out of scope, the callback is unsubscribed.
+ The returned property change handler object keeps track of the subscription.
+ When it goes out of scope, the callback is unsubscribed.
- This method is in some cases easier to use than onValueChanged(), as the returned object is not a template.
- It can therefore more easily be stored, e.g. as a member in a class.
+ This method is in some cases easier to use than onValueChanged(), as the
+ returned object is not a template. It can therefore more easily be stored,
+ e.g. as a member in a class.
\sa onValueChanged(), subscribe()
*/
@@ -1846,13 +1915,15 @@ QString QPropertyBindingError::description() const
/*!
\class QPropertyChangeHandler
\inmodule QtCore
- \brief The QPropertyChangeHandler class controls the lifecycle of change callback installed on a QProperty.
+ \brief The QPropertyChangeHandler class controls the lifecycle of change
+ callback installed on a QProperty.
\ingroup tools
- QPropertyChangeHandler\<Functor\> is created when registering a
- callback on a QProperty to listen to changes to the property's value, using QProperty::onValueChanged
- and QProperty::subscribe. As long as the change handler is alive, the callback remains installed.
+ QPropertyChangeHandler\<Functor\> is created when registering a callback on a
+ QProperty to listen to changes to the property's value, using
+ QProperty::onValueChanged and QProperty::subscribe. As long as the change
+ handler is alive, the callback remains installed.
A handler instance can be transferred between C++ scopes using move semantics.
*/
@@ -1864,9 +1935,9 @@ QString QPropertyBindingError::description() const
\ingroup tools
- QPropertyNotifier is created when registering a
- callback on a QProperty to listen to changes to the property's value, using QProperty::addNotifier.
- As long as the change handler is alive, the callback remains installed.
+ QPropertyNotifier is created when registering a callback on a QProperty to
+ listen to changes to the property's value, using QProperty::addNotifier. As
+ long as the change handler is alive, the callback remains installed.
A handler instance can be transferred between C++ scopes using move semantics.
*/
@@ -1876,7 +1947,8 @@ QString QPropertyBindingError::description() const
\inmodule QtCore
\internal
- \brief The QPropertyAlias class is a safe alias for a QProperty with same template parameter.
+ \brief The QPropertyAlias class is a safe alias for a QProperty with same
+ template parameter.
\ingroup tools
@@ -1954,33 +2026,14 @@ QString QPropertyBindingError::description() const
*/
/*!
- \fn template <typename T> QPropertyAlias<T> &QPropertyAlias<T>::operator=(T &&newValue)
- \overload
-
- Assigns \a newValue to the aliased property and returns a reference to this
- QPropertyAlias.
-*/
-
-/*!
- \fn template <typename T> QPropertyAlias<T> &QPropertyAlias<T>::operator=(const QPropertyBinding<T> &newBinding)
- \overload
-
- Associates the value of the aliased property with the provided \a newBinding
- expression and returns a reference to this alias. The first time the
- property value is read, either from the property itself or from any alias, the
- binding is evaluated. Whenever a dependency of the binding changes, the
- binding will be re-evaluated the next time the value of this property is read.
-*/
-
-/*!
\fn template <typename T> QPropertyBinding<T> QPropertyAlias<T>::setBinding(const QPropertyBinding<T> &newBinding)
Associates the value of the aliased property with the provided \a newBinding
expression and returns any previous binding the associated with the aliased
- property. The first time the property value is read, either from the property
- itself or from any alias, the binding is evaluated. Whenever a dependency of
- the binding changes, the binding will be re-evaluated the next time the value
- of this property is read.
+ property.The property's value is set to the result of evaluating the new
+ binding. Whenever a dependency of the binding changes, the binding will be
+ re-evaluated, and the property's value gets updated accordingly.
+
Returns any previous binding associated with the property, or a
default-constructed QPropertyBinding<T>.
@@ -1991,10 +2044,10 @@ QString QPropertyBindingError::description() const
\overload
Associates the value of the aliased property with the provided \a newBinding
- expression. The first time the property value is read, either from the
- property itself or from any alias, the binding is evaluated. Whenever a
- dependency of the binding changes, the binding will be re-evaluated the next
- time the value of this property is read.
+ expression. The property's value is set to the result of evaluating the new
+ binding. Whenever a dependency of the binding changes, the binding will be
+ re-evaluated, and the property's value gets updated accordingly.
+
Returns true if the type of this property is the same as the type the binding
function returns; false otherwise.
@@ -2005,10 +2058,10 @@ QString QPropertyBindingError::description() const
\overload
Associates the value of the aliased property with the provided functor \a f
- expression. The first time the property value is read, either from the
- property itself or from any alias, the binding is evaluated. Whenever a
- dependency of the binding changes, the binding will be re-evaluated the next
- time the value of this property is read.
+ expression. The property's value is set to the result of evaluating the new
+ binding. Whenever a dependency of the binding changes, the binding will be
+ re-evaluated, and the property's value gets updated accordingly.
+
Returns any previous binding associated with the property, or a
default-constructed QPropertyBinding<T>.
@@ -2046,9 +2099,9 @@ QString QPropertyBindingError::description() const
the value of the aliased property changes. On each value change, the handler
is either called immediately, or deferred, depending on the context.
- The callback \a f is expected to be a type that has a plain call operator () without any
- parameters. This means that you can provide a C++ lambda expression, a std::function
- or even a custom struct with a call operator.
+ The callback \a f is expected to be a type that has a plain call operator
+ \c{()} without any parameters. This means that you can provide a C++ lambda
+ expression, a std::function or even a custom struct with a call operator.
The returned property change handler object keeps track of the registration. When it
goes out of scope, the callback is de-registered.
@@ -2057,16 +2110,17 @@ QString QPropertyBindingError::description() const
/*!
\fn template <typename T> template <typename Functor> QPropertyChangeHandler<T, Functor> QPropertyAlias<T>::subscribe(Functor f)
- Subscribes the given functor \a f as a callback that is called immediately and whenever
- the value of the aliased property changes in the future. On each value change, the handler
- is either called immediately, or deferred, depending on the context.
+ Subscribes the given functor \a f as a callback that is called immediately and
+ whenever the value of the aliased property changes in the future. On each
+ value change, the handler is either called immediately, or deferred, depending
+ on the context.
- The callback \a f is expected to be a type that has a plain call operator () without any
- parameters. This means that you can provide a C++ lambda expression, a std::function
- or even a custom struct with a call operator.
+ The callback \a f is expected to be a type that has a plain call operator
+ \c{()} without any parameters. This means that you can provide a C++ lambda
+ expression, a std::function or even a custom struct with a call operator.
- The returned property change handler object keeps track of the subscription. When it
- goes out of scope, the callback is unsubscribed.
+ The returned property change handler object keeps track of the subscription.
+ When it goes out of scope, the callback is unsubscribed.
*/
/*!
@@ -2075,15 +2129,16 @@ QString QPropertyBindingError::description() const
Subscribes the given functor \a f as a callback that is called whenever
the value of the aliased property changes.
- The callback \a f is expected to be a type that has a plain call operator () without any
- parameters. This means that you can provide a C++ lambda expression, a std::function
- or even a custom struct with a call operator.
+ The callback \a f is expected to be a type that has a plain call operator
+ \c{()} without any parameters. This means that you can provide a C++ lambda
+ expression, a std::function or even a custom struct with a call operator.
- The returned property change handler object keeps track of the subscription. When it
- goes out of scope, the callback is unsubscribed.
+ The returned property change handler object keeps track of the subscription.
+ When it goes out of scope, the callback is unsubscribed.
- This method is in some cases easier to use than onValueChanged(), as the returned object is not a template.
- It can therefore more easily be stored, e.g. as a member in a class.
+ This method is in some cases easier to use than onValueChanged(), as the
+ returned object is not a template. It can therefore more easily be stored,
+ e.g. as a member in a class.
\sa onValueChanged(), subscribe()
*/
@@ -2235,16 +2290,17 @@ QBindingStorage::~QBindingStorage()
QBindingStoragePrivate(d).destroy();
}
-void QBindingStorage::clear()
+void QBindingStorage::reinitAfterThreadMove()
{
- QBindingStoragePrivate(d).destroy();
- d = nullptr;
+ bindingStatus = &QT_PREPEND_NAMESPACE(bindingStatus);
+ Q_ASSERT(bindingStatus);
}
-// ### Unused, retained for BC with 6.0
-void QBindingStorage::maybeUpdateBindingAndRegister_helper(const QUntypedPropertyData *data) const
+void QBindingStorage::clear()
{
- registerDependency_helper(data);
+ QBindingStoragePrivate(d).destroy();
+ d = nullptr;
+ bindingStatus = nullptr;
}
void QBindingStorage::registerDependency_helper(const QUntypedPropertyData *data) const
@@ -2277,6 +2333,11 @@ QPropertyBindingData *QBindingStorage::bindingData_helper(const QUntypedProperty
return QBindingStoragePrivate(d).get(data);
}
+const QBindingStatus *QBindingStorage::status(QtPrivate::QBindingStatusAccessToken) const
+{
+ return bindingStatus;
+}
+
QPropertyBindingData *QBindingStorage::bindingData_helper(QUntypedPropertyData *data, bool create)
{
return QBindingStoragePrivate(d).get(data, create);
@@ -2354,6 +2415,165 @@ void printMetaTypeMismatch(QMetaType actual, QMetaType expected)
} // namespace BindableWarnings end
+/*!
+ \internal
+ Returns the binding statusof the current thread.
+ */
+QBindingStatus* getBindingStatus(QtPrivate::QBindingStatusAccessToken) { return &QT_PREPEND_NAMESPACE(bindingStatus); }
+
+namespace PropertyAdaptorSlotObjectHelpers {
+void getter(const QUntypedPropertyData *d, void *value)
+{
+ auto adaptor = static_cast<const QtPrivate::QPropertyAdaptorSlotObject *>(d);
+ adaptor->bindingData().registerWithCurrentlyEvaluatingBinding();
+ auto mt = adaptor->metaProperty().metaType();
+ mt.destruct(value);
+ mt.construct(value, adaptor->metaProperty().read(adaptor->object()).data());
+}
+
+void setter(QUntypedPropertyData *d, const void *value)
+{
+ auto adaptor = static_cast<QtPrivate::QPropertyAdaptorSlotObject *>(d);
+ adaptor->bindingData().removeBinding();
+ adaptor->metaProperty().write(adaptor->object(),
+ QVariant(adaptor->metaProperty().metaType(), value));
+}
+
+QUntypedPropertyBinding getBinding(const QUntypedPropertyData *d)
+{
+ auto adaptor = static_cast<const QtPrivate::QPropertyAdaptorSlotObject *>(d);
+ return QUntypedPropertyBinding(adaptor->bindingData().binding());
+}
+
+bool bindingWrapper(QMetaType type, QUntypedPropertyData *d,
+ QtPrivate::QPropertyBindingFunction binding, QUntypedPropertyData *temp,
+ void *value)
+{
+ auto adaptor = static_cast<const QtPrivate::QPropertyAdaptorSlotObject *>(d);
+ type.destruct(value);
+ type.construct(value, adaptor->metaProperty().read(adaptor->object()).data());
+ if (binding.vtable->call(type, temp, binding.functor)) {
+ adaptor->metaProperty().write(adaptor->object(), QVariant(type, value));
+ return true;
+ }
+ return false;
+}
+
+QUntypedPropertyBinding setBinding(QUntypedPropertyData *d, const QUntypedPropertyBinding &binding,
+ QPropertyBindingWrapper wrapper)
+{
+ auto adaptor = static_cast<QPropertyAdaptorSlotObject *>(d);
+ return adaptor->bindingData().setBinding(binding, d, nullptr, wrapper);
+}
+
+void setObserver(const QUntypedPropertyData *d, QPropertyObserver *observer)
+{
+ observer->setSource(static_cast<const QPropertyAdaptorSlotObject *>(d)->bindingData());
+}
+}
+
+QPropertyAdaptorSlotObject::QPropertyAdaptorSlotObject(QObject *o, const QMetaProperty &p)
+ : QSlotObjectBase(&impl), obj(o), metaProperty_(p)
+{
+}
+
+#if QT_VERSION < QT_VERSION_CHECK(7, 0, 0)
+void QPropertyAdaptorSlotObject::impl(int which, QSlotObjectBase *this_, QObject *r, void **a,
+ bool *ret)
+#else
+void QPropertyAdaptorSlotObject::impl(QSlotObjectBase *this_, QObject *r, void **a, int which,
+ bool *ret)
+#endif
+{
+ auto self = static_cast<QPropertyAdaptorSlotObject *>(this_);
+ switch (which) {
+ case Destroy:
+ delete self;
+ break;
+ case Call:
+ if (!self->bindingData_.hasBinding())
+ self->bindingData_.notifyObservers(self);
+ break;
+ case Compare:
+ case NumOperations:
+ Q_UNUSED(r);
+ Q_UNUSED(a);
+ Q_UNUSED(ret);
+ break;
+ }
+}
+
} // namespace QtPrivate end
+QUntypedBindable::QUntypedBindable(QObject *obj, const QMetaProperty &metaProperty,
+ const QtPrivate::QBindableInterface *i)
+ : iface(i)
+{
+ if (!obj)
+ return;
+
+ if (!metaProperty.isValid()) {
+ qCWarning(lcQPropertyBinding) << "QUntypedBindable: Property is not valid";
+ return;
+ }
+
+ if (metaProperty.isBindable()) {
+ *this = metaProperty.bindable(obj);
+ return;
+ }
+
+ if (!metaProperty.hasNotifySignal()) {
+ qCWarning(lcQPropertyBinding)
+ << "QUntypedBindable: Property" << metaProperty.name() << "has no notify signal";
+ return;
+ }
+
+ auto metatype = iface->metaType();
+ if (metaProperty.metaType() != metatype) {
+ qCWarning(lcQPropertyBinding) << "QUntypedBindable: Property" << metaProperty.name()
+ << "of type" << metaProperty.metaType().name()
+ << "does not match requested type" << metatype.name();
+ return;
+ }
+
+ // Test for name pointer equality proves it's exactly the same property
+ if (obj->metaObject()->property(metaProperty.propertyIndex()).name() != metaProperty.name()) {
+ qCWarning(lcQPropertyBinding) << "QUntypedBindable: Property" << metaProperty.name()
+ << "does not belong to this object";
+ return;
+ }
+
+ // Get existing binding data if it exists
+ auto adaptor = QObjectPrivate::get(obj)->getPropertyAdaptorSlotObject(metaProperty);
+
+ if (!adaptor) {
+ adaptor = new QPropertyAdaptorSlotObject(obj, metaProperty);
+
+ auto c = QObjectPrivate::connect(obj, metaProperty.notifySignalIndex(), obj, adaptor,
+ Qt::DirectConnection);
+ Q_ASSERT(c);
+ }
+
+ data = adaptor;
+}
+
+QUntypedBindable::QUntypedBindable(QObject *obj, const char *property,
+ const QtPrivate::QBindableInterface *i)
+ : QUntypedBindable(
+ obj,
+ [=]() -> QMetaProperty {
+ if (!obj)
+ return {};
+ auto propertyIndex = obj->metaObject()->indexOfProperty(property);
+ if (propertyIndex < 0) {
+ qCWarning(lcQPropertyBinding)
+ << "QUntypedBindable: No property named" << property;
+ return {};
+ }
+ return obj->metaObject()->property(propertyIndex);
+ }(),
+ i)
+{
+}
+
QT_END_NAMESPACE
diff --git a/src/corelib/kernel/qproperty.h b/src/corelib/kernel/qproperty.h
index f653d78fb6..0373867a66 100644
--- a/src/corelib/kernel/qproperty.h
+++ b/src/corelib/kernel/qproperty.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2020 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtCore module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2020 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 QPROPERTY_H
#define QPROPERTY_H
@@ -43,25 +7,38 @@
#include <QtCore/qglobal.h>
#include <QtCore/qshareddata.h>
#include <QtCore/qstring.h>
+#include <QtCore/qbindingstorage.h>
+
#include <type_traits>
#include <QtCore/qpropertyprivate.h>
-#if __has_include(<source_location>) && __cplusplus >= 202002L && !defined(Q_CLANG_QDOC)
+#if __has_include(<source_location>) && __cplusplus >= 202002L && !defined(Q_QDOC)
#include <source_location>
#if defined(__cpp_lib_source_location)
#define QT_SOURCE_LOCATION_NAMESPACE std
#define QT_PROPERTY_COLLECT_BINDING_LOCATION
-#define QT_PROPERTY_DEFAULT_BINDING_LOCATION QPropertyBindingSourceLocation(std::source_location::current())
+#if defined(Q_CC_MSVC)
+/* MSVC runs into an issue with constexpr with source location (error C7595)
+ so use the factory function as a workaround */
+# define QT_PROPERTY_DEFAULT_BINDING_LOCATION QPropertyBindingSourceLocation::fromStdSourceLocation(std::source_location::current())
+#else
+/* some versions of gcc in turn run into
+ expression ‘std::source_location::current()’ is not a constant expression
+ so don't use the workaround there */
+# define QT_PROPERTY_DEFAULT_BINDING_LOCATION QPropertyBindingSourceLocation(std::source_location::current())
+#endif
#endif
#endif
-#if !defined(QT_PROPERTY_COLLECT_BINDING_LOCATION) && __has_include(<experimental/source_location>) && !defined(Q_CLANG_QDOC)
+#if __has_include(<experimental/source_location>) && !defined(Q_QDOC)
#include <experimental/source_location>
+#if !defined(QT_PROPERTY_COLLECT_BINDING_LOCATION)
#if defined(__cpp_lib_experimental_source_location)
#define QT_SOURCE_LOCATION_NAMESPACE std::experimental
#define QT_PROPERTY_COLLECT_BINDING_LOCATION
#define QT_PROPERTY_DEFAULT_BINDING_LOCATION QPropertyBindingSourceLocation(std::experimental::source_location::current())
+#endif // defined(__cpp_lib_experimental_source_location)
#endif
#endif
@@ -76,6 +53,17 @@ Q_CORE_EXPORT void beginPropertyUpdateGroup();
Q_CORE_EXPORT void endPropertyUpdateGroup();
}
+class QScopedPropertyUpdateGroup
+{
+ Q_DISABLE_COPY_MOVE(QScopedPropertyUpdateGroup)
+public:
+ Q_NODISCARD_CTOR
+ QScopedPropertyUpdateGroup()
+ { Qt::beginPropertyUpdateGroup(); }
+ ~QScopedPropertyUpdateGroup() noexcept(false)
+ { Qt::endPropertyUpdateGroup(); }
+};
+
template <typename T>
class QPropertyData : public QUntypedPropertyData
{
@@ -102,6 +90,7 @@ public:
void setValueBypassingBindings(rvalue_ref v) { val = std::move(v); }
};
+// ### Qt 7: un-export
struct Q_CORE_EXPORT QPropertyBindingSourceLocation
{
const char *fileName = nullptr;
@@ -109,8 +98,23 @@ struct Q_CORE_EXPORT QPropertyBindingSourceLocation
quint32 line = 0;
quint32 column = 0;
QPropertyBindingSourceLocation() = default;
-#ifdef QT_PROPERTY_COLLECT_BINDING_LOCATION
- QPropertyBindingSourceLocation(const QT_SOURCE_LOCATION_NAMESPACE::source_location &cppLocation)
+#ifdef __cpp_lib_source_location
+ constexpr QPropertyBindingSourceLocation(const std::source_location &cppLocation)
+ {
+ fileName = cppLocation.file_name();
+ functionName = cppLocation.function_name();
+ line = cppLocation.line();
+ column = cppLocation.column();
+ }
+ QT_POST_CXX17_API_IN_EXPORTED_CLASS
+ static consteval QPropertyBindingSourceLocation
+ fromStdSourceLocation(const std::source_location &cppLocation)
+ {
+ return cppLocation;
+ }
+#endif
+#ifdef __cpp_lib_experimental_source_location
+ constexpr QPropertyBindingSourceLocation(const std::experimental::source_location &cppLocation)
{
fileName = cppLocation.file_name();
functionName = cppLocation.function_name();
@@ -224,7 +228,9 @@ public:
ObserverNotifiesBinding, // observer was installed to notify bindings that obsverved property changed
ObserverNotifiesChangeHandler, // observer is a change handler, which runs on every change
ObserverIsPlaceholder, // the observer before this one is currently evaluated in QPropertyObserver::notifyObservers.
- ObserverIsAlias
+#if QT_DEPRECATED_SINCE(6, 6)
+ ObserverIsAlias QT_DEPRECATED_VERSION_X_6_6("Use QProperty and add a binding to the target.")
+#endif
};
protected:
using ChangeHandler = void (*)(QPropertyObserver*, QUntypedPropertyData *);
@@ -257,14 +263,17 @@ public:
QPropertyObserver &operator=(QPropertyObserver &&other) noexcept;
~QPropertyObserver();
- template<typename Property, typename = typename Property::InheritsQUntypedPropertyData>
+ template <typename Property, QtPrivate::IsUntypedPropertyData<Property> = true>
void setSource(const Property &property)
{ setSource(property.bindingData()); }
void setSource(const QtPrivate::QPropertyBindingData &property);
protected:
QPropertyObserver(ChangeHandler changeHandler);
+#if QT_DEPRECATED_SINCE(6, 6)
+ QT_DEPRECATED_VERSION_X_6_6("This constructor was only meant for internal use. Use QProperty and add a binding to the target.")
QPropertyObserver(QUntypedPropertyData *aliasedPropertyPtr);
+#endif
QUntypedPropertyData *aliasedProperty() const
{
@@ -279,10 +288,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);
@@ -292,7 +302,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);
@@ -304,12 +315,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);
@@ -319,7 +332,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);
@@ -356,7 +371,7 @@ public:
explicit QProperty(const QPropertyBinding<T> &binding)
: QProperty()
{ setBinding(binding); }
-#ifndef Q_CLANG_QDOC
+#ifndef Q_QDOC
template <typename Functor>
explicit QProperty(Functor &&f, const QPropertyBindingSourceLocation &location = QT_PROPERTY_DEFAULT_BINDING_LOCATION,
typename std::enable_if_t<std::is_invocable_r_v<T, Functor&>> * = nullptr)
@@ -439,7 +454,7 @@ public:
return true;
}
-#ifndef Q_CLANG_QDOC
+#ifndef Q_QDOC
template <typename Functor>
QPropertyBinding<T> setBinding(Functor &&f,
const QPropertyBindingSourceLocation &location = QT_PROPERTY_DEFAULT_BINDING_LOCATION,
@@ -604,6 +619,60 @@ enum Reason { InvalidInterface, NonBindableInterface, ReadOnlyInterface };
Q_CORE_EXPORT void printUnsuitableBindableWarning(QAnyStringView prefix, Reason reason);
Q_CORE_EXPORT void printMetaTypeMismatch(QMetaType actual, QMetaType expected);
}
+
+namespace PropertyAdaptorSlotObjectHelpers {
+Q_CORE_EXPORT void getter(const QUntypedPropertyData *d, void *value);
+Q_CORE_EXPORT void setter(QUntypedPropertyData *d, const void *value);
+Q_CORE_EXPORT QUntypedPropertyBinding getBinding(const QUntypedPropertyData *d);
+Q_CORE_EXPORT bool bindingWrapper(QMetaType type, QUntypedPropertyData *d,
+ QtPrivate::QPropertyBindingFunction binding,
+ QUntypedPropertyData *temp, void *value);
+Q_CORE_EXPORT QUntypedPropertyBinding setBinding(QUntypedPropertyData *d,
+ const QUntypedPropertyBinding &binding,
+ QPropertyBindingWrapper wrapper);
+Q_CORE_EXPORT void setObserver(const QUntypedPropertyData *d, QPropertyObserver *observer);
+
+template<typename T>
+bool bindingWrapper(QMetaType type, QUntypedPropertyData *d,
+ QtPrivate::QPropertyBindingFunction binding)
+{
+ struct Data : QPropertyData<T>
+ {
+ void *data() { return &this->val; }
+ } temp;
+ return bindingWrapper(type, d, binding, &temp, temp.data());
+}
+
+template<typename T>
+QUntypedPropertyBinding setBinding(QUntypedPropertyData *d, const QUntypedPropertyBinding &binding)
+{
+ return setBinding(d, binding, &bindingWrapper<T>);
+}
+
+template<typename T>
+QUntypedPropertyBinding makeBinding(const QUntypedPropertyData *d,
+ const QPropertyBindingSourceLocation &location)
+{
+ return Qt::makePropertyBinding(
+ [d]() -> T {
+ T r;
+ getter(d, &r);
+ return r;
+ },
+ location);
+}
+
+template<class T>
+inline constexpr QBindableInterface iface = {
+ &getter,
+ &setter,
+ &getBinding,
+ &setBinding<T>,
+ &makeBinding<T>,
+ &setObserver,
+ &QMetaType::fromType<T>,
+};
+}
}
class QUntypedBindable
@@ -616,6 +685,9 @@ protected:
: data(d), iface(i)
{}
+ Q_CORE_EXPORT explicit QUntypedBindable(QObject* obj, const QMetaProperty &property, const QtPrivate::QBindableInterface *i);
+ Q_CORE_EXPORT explicit QUntypedBindable(QObject* obj, const char* property, const QtPrivate::QBindableInterface *i);
+
public:
constexpr QUntypedBindable() = default;
template<typename Property>
@@ -752,6 +824,12 @@ public:
}
}
+ explicit QBindable(QObject *obj, const QMetaProperty &property)
+ : QUntypedBindable(obj, property, &QtPrivate::PropertyAdaptorSlotObjectHelpers::iface<T>) {}
+
+ explicit QBindable(QObject *obj, const char *property)
+ : QUntypedBindable(obj, property, &QtPrivate::PropertyAdaptorSlotObjectHelpers::iface<T>) {}
+
QPropertyBinding<T> makeBinding(const QPropertyBindingSourceLocation &location = QT_PROPERTY_DEFAULT_BINDING_LOCATION) const
{
return static_cast<QPropertyBinding<T> &&>(QUntypedBindable::makeBinding(location));
@@ -781,7 +859,7 @@ public:
#endif
return QPropertyBinding<T>();
}
-#ifndef Q_CLANG_QDOC
+#ifndef Q_QDOC
template <typename Functor>
QPropertyBinding<T> setBinding(Functor &&f,
const QPropertyBindingSourceLocation &location = QT_PROPERTY_DEFAULT_BINDING_LOCATION,
@@ -811,13 +889,16 @@ public:
}
};
+#if QT_DEPRECATED_SINCE(6, 6)
template<typename T>
-class QPropertyAlias : public QPropertyObserver
+class QT_DEPRECATED_VERSION_X_6_6("Class was only meant for internal use, use a QProperty and add a binding to the target")
+QPropertyAlias : public QPropertyObserver
{
Q_DISABLE_COPY_MOVE(QPropertyAlias)
const QtPrivate::QBindableInterface *iface = nullptr;
public:
+ QT_WARNING_PUSH QT_WARNING_DISABLE_DEPRECATED
QPropertyAlias(QProperty<T> *property)
: QPropertyObserver(property),
iface(&QtPrivate::QBindableInterfaceForProperty<QProperty<T>>::iface)
@@ -826,7 +907,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)
@@ -883,7 +964,7 @@ public:
return QBindable<T>(aliasedProperty(), iface).setBinding(newBinding);
}
-#ifndef Q_CLANG_QDOC
+#ifndef Q_QDOC
template <typename Functor>
QPropertyBinding<T> setBinding(Functor &&f,
const QPropertyBindingSourceLocation &location = QT_PROPERTY_DEFAULT_BINDING_LOCATION,
@@ -926,76 +1007,16 @@ public:
template<typename Functor>
QPropertyNotifier addNotifier(Functor f)
{
- return QBindable<T>(aliasedProperty(), iface).notify(f);
+ return QBindable<T>(aliasedProperty(), iface).addNotifier(f);
}
bool isValid() const
{
return aliasedProperty() != nullptr;
}
+ QT_WARNING_POP
};
-
-namespace QtPrivate {
-
-struct BindingEvaluationState;
-struct CompatPropertySafePoint;
-}
-
-struct QBindingStatus
-{
- QtPrivate::BindingEvaluationState *currentlyEvaluatingBinding = nullptr;
- QtPrivate::CompatPropertySafePoint *currentCompatProperty = nullptr;
- Qt::HANDLE threadId = nullptr;
- QPropertyDelayedNotifications *groupUpdateData = nullptr;
-};
-
-
-struct QBindingStorageData;
-class Q_CORE_EXPORT QBindingStorage
-{
- mutable QBindingStorageData *d = nullptr;
- QBindingStatus *bindingStatus = nullptr;
-
- template<typename Class, typename T, auto Offset, auto Setter, auto Signal, auto Getter>
- friend class QObjectCompatProperty;
- friend class QObjectPrivate;
- friend class QtPrivate::QPropertyBindingData;
-public:
- QBindingStorage();
- ~QBindingStorage();
-
- bool isEmpty() { return !d; }
-
- void registerDependency(const QUntypedPropertyData *data) const
- {
- if (!bindingStatus->currentlyEvaluatingBinding)
- return;
- registerDependency_helper(data);
- }
- QtPrivate::QPropertyBindingData *bindingData(const QUntypedPropertyData *data) const
- {
- if (!d)
- return nullptr;
- return bindingData_helper(data);
- }
- // ### Qt 7: remove unused BIC shim
- void maybeUpdateBindingAndRegister(const QUntypedPropertyData *data) const { registerDependency(data); }
-
- QtPrivate::QPropertyBindingData *bindingData(QUntypedPropertyData *data, bool create)
- {
- if (!d && !create)
- return nullptr;
- return bindingData_helper(data, create);
- }
-private:
- void clear();
- void registerDependency_helper(const QUntypedPropertyData *data) const;
- // ### Unused, but keep for BC
- void maybeUpdateBindingAndRegister_helper(const QUntypedPropertyData *data) const;
- QtPrivate::QPropertyBindingData *bindingData_helper(const QUntypedPropertyData *data) const;
- QtPrivate::QPropertyBindingData *bindingData_helper(QUntypedPropertyData *data, bool create);
-};
-
+#endif // QT_DEPRECATED_SINCE(6, 6)
template<typename Class, typename T, auto Offset, auto Signal = nullptr>
class QObjectBindableProperty : public QPropertyData<T>
@@ -1035,7 +1056,7 @@ public:
explicit QObjectBindableProperty(const QPropertyBinding<T> &binding)
: QObjectBindableProperty()
{ setBinding(binding); }
-#ifndef Q_CLANG_QDOC
+#ifndef Q_QDOC
template <typename Functor>
explicit QObjectBindableProperty(Functor &&f, const QPropertyBindingSourceLocation &location = QT_PROPERTY_DEFAULT_BINDING_LOCATION,
typename std::enable_if_t<std::is_invocable_r_v<T, Functor&>> * = nullptr)
@@ -1128,7 +1149,7 @@ public:
return true;
}
-#ifndef Q_CLANG_QDOC
+#ifndef Q_QDOC
template <typename Functor>
QPropertyBinding<T> setBinding(Functor &&f,
const QPropertyBindingSourceLocation &location = QT_PROPERTY_DEFAULT_BINDING_LOCATION,
diff --git a/src/corelib/kernel/qproperty_p.h b/src/corelib/kernel/qproperty_p.h
index cafe211e71..8ae6664a2b 100644
--- a/src/corelib/kernel/qproperty_p.h
+++ b/src/corelib/kernel/qproperty_p.h
@@ -1,41 +1,5 @@
-/***************************************************************************
-**
-** Copyright (C) 2020 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtCore module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// 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 QPROPERTY_P_H
#define QPROPERTY_P_H
@@ -51,24 +15,56 @@
// We mean it.
//
-#include <qglobal.h>
+#include <private/qglobal_p.h>
#include <qproperty.h>
+#include <qmetaobject.h>
#include <qscopedpointer.h>
#include <qscopedvaluerollback.h>
+#include <qvariant.h>
#include <vector>
+#include <QtCore/QVarLengthArray>
QT_BEGIN_NAMESPACE
namespace QtPrivate {
Q_CORE_EXPORT bool isAnyBindingEvaluating();
+ struct QBindingStatusAccessToken {};
}
+
+/*!
+ \internal
+ Similar to \c QPropertyBindingPrivatePtr, but stores a
+ \c QPropertyObserver * linking to the QPropertyBindingPrivate*
+ instead of the QPropertyBindingPrivate* itself
+ */
+struct QBindingObserverPtr
+{
+private:
+ QPropertyObserver *d = nullptr;
+public:
+ QBindingObserverPtr() = default;
+ Q_DISABLE_COPY(QBindingObserverPtr);
+ void swap(QBindingObserverPtr &other) noexcept
+ { qt_ptr_swap(d, other.d); }
+ QBindingObserverPtr(QBindingObserverPtr &&other) noexcept : d(std::exchange(other.d, nullptr)) {}
+ QT_MOVE_ASSIGNMENT_OPERATOR_IMPL_VIA_MOVE_AND_SWAP(QBindingObserverPtr);
+
+
+ inline QBindingObserverPtr(QPropertyObserver *observer) noexcept;
+ inline ~QBindingObserverPtr();
+ inline QPropertyBindingPrivate *binding() const noexcept;
+ inline QPropertyObserver *operator ->();
+};
+
+using PendingBindingObserverList = QVarLengthArray<QBindingObserverPtr>;
+
// Keep all classes related to QProperty in one compilation unit. Performance of this code is crucial and
// we need to allow the compiler to inline where it makes sense.
// This is a helper "namespace"
-struct Q_AUTOTEST_EXPORT QPropertyBindingDataPointer
+struct QPropertyBindingDataPointer
{
const QtPrivate::QPropertyBindingData *ptr = nullptr;
@@ -85,10 +81,11 @@ struct Q_AUTOTEST_EXPORT QPropertyBindingDataPointer
}
static void fixupAfterMove(QtPrivate::QPropertyBindingData *ptr);
void Q_ALWAYS_INLINE addObserver(QPropertyObserver *observer);
- void setFirstObserver(QPropertyObserver *observer);
- QPropertyObserverPointer firstObserver() const;
+ inline void setFirstObserver(QPropertyObserver *observer);
+ inline QPropertyObserverPointer firstObserver() const;
+ static QPropertyProxyBindingData *proxyData(QtPrivate::QPropertyBindingData *ptr);
- int observerCount() const;
+ inline int observerCount() const;
template <typename T>
static QPropertyBindingDataPointer get(QProperty<T> &property)
@@ -97,11 +94,12 @@ struct Q_AUTOTEST_EXPORT 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
@@ -127,33 +125,55 @@ struct QPropertyObserverPointer
void unlink()
{
unlink_common();
+#if QT_DEPRECATED_SINCE(6, 6)
+ QT_WARNING_PUSH QT_WARNING_DISABLE_DEPRECATED
if (ptr->next.tag() == QPropertyObserver::ObserverIsAlias)
ptr->aliasData = nullptr;
+ QT_WARNING_POP
+#endif
}
void unlink_fast()
{
+#if QT_DEPRECATED_SINCE(6, 6)
+ QT_WARNING_PUSH QT_WARNING_DISABLE_DEPRECATED
Q_ASSERT(ptr->next.tag() != QPropertyObserver::ObserverIsAlias);
+ QT_WARNING_POP
+#endif
unlink_common();
}
- void setBindingToNotify(QPropertyBindingPrivate *binding);
+ void setBindingToNotify(QPropertyBindingPrivate *binding)
+ {
+ Q_ASSERT(ptr->next.tag() != QPropertyObserver::ObserverIsPlaceholder);
+ ptr->binding = binding;
+ ptr->next.setTag(QPropertyObserver::ObserverNotifiesBinding);
+ }
+
void setBindingToNotify_unsafe(QPropertyBindingPrivate *binding);
void setChangeHandler(QPropertyObserver::ChangeHandler changeHandler);
+ enum class Notify {Everything, OnlyChangeHandlers};
+
void notify(QUntypedPropertyData *propertyDataPtr);
#ifndef QT_NO_DEBUG
void noSelfDependencies(QPropertyBindingPrivate *binding);
#else
void noSelfDependencies(QPropertyBindingPrivate *) {}
#endif
- void evaluateBindings(QBindingStatus *status);
+ void evaluateBindings(PendingBindingObserverList &bindingObservers, QBindingStatus *status);
void observeProperty(QPropertyBindingDataPointer property);
explicit operator bool() const { return ptr != nullptr; }
QPropertyObserverPointer nextObserver() const { return {ptr->next.data()}; }
+ QPropertyBindingPrivate *binding() const
+ {
+ Q_ASSERT(ptr->next.tag() == QPropertyObserver::ObserverNotifiesBinding);
+ return ptr->binding;
+ }
+
private:
void unlink_common()
{
@@ -186,6 +206,7 @@ struct BindingEvaluationState
QPropertyBindingPrivate *binding;
BindingEvaluationState *previousState = nullptr;
BindingEvaluationState **currentState = nullptr;
+ QVarLengthArray<const QPropertyBindingData *, 8> alreadyCaptureProperties;
};
/*!
@@ -210,6 +231,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,9 +302,8 @@ protected:
is stored somewhere else. To make efficient use of the space, we instead provide a scratch space
for QQmlPropertyBinding (which stores further binding information there).
Anything stored in the union must be trivially destructible.
+ (checked in qproperty.cpp)
*/
- static_assert(std::is_trivially_destructible_v<QPropertyBindingSourceLocation>);
- static_assert(std::is_trivially_destructible_v<std::byte[sizeof(QPropertyBindingSourceLocation)]>);
using DeclarativeErrorCallback = void(*)(QPropertyBindingPrivate *);
union {
QPropertyBindingSourceLocation location;
@@ -286,6 +333,7 @@ public:
bool isUpdating() {return updating;}
void setSticky(bool keep = true) {m_sticky = keep;}
bool isSticky() {return m_sticky;}
+ void scheduleNotify() {pendingNotify = true;}
QPropertyBindingPrivate(QMetaType metaType, const QtPrivate::BindingFunctionVTable *vtable,
const QPropertyBindingSourceLocation &location, bool isQQmlPropertyBinding=false)
@@ -330,15 +378,7 @@ public:
return observers;
}
- void clearDependencyObservers() {
- for (size_t i = 0; i < qMin(dependencyObserverCount, inlineDependencyObservers.size()); ++i) {
- QPropertyObserverPointer p{&inlineDependencyObservers[i]};
- p.unlink_fast();
- }
- if (heapObservers)
- heapObservers->clear();
- dependencyObserverCount = 0;
- }
+ void clearDependencyObservers();
Q_ALWAYS_INLINE QPropertyObserverPointer allocateDependencyObserver() {
if (dependencyObserverCount < inlineDependencyObservers.size()) {
@@ -348,13 +388,7 @@ public:
return allocateDependencyObserver_slow();
}
- Q_NEVER_INLINE QPropertyObserverPointer allocateDependencyObserver_slow()
- {
- ++dependencyObserverCount;
- if (!heapObservers)
- heapObservers.reset(new std::vector<QPropertyObserver>());
- return {&heapObservers->emplace_back()};
- }
+ QPropertyObserverPointer allocateDependencyObserver_slow();
QPropertyBindingSourceLocation sourceLocation() const
{
@@ -370,10 +404,13 @@ public:
void unlinkAndDeref();
- void evaluateRecursive(QBindingStatus *status = nullptr);
- void Q_ALWAYS_INLINE evaluateRecursive_inline(QBindingStatus *status);
+ bool evaluateRecursive(PendingBindingObserverList &bindingObservers, QBindingStatus *status = nullptr);
+
+ bool Q_ALWAYS_INLINE evaluateRecursive_inline(PendingBindingObserverList &bindingObservers, QBindingStatus *status);
- void notifyRecursive();
+ void notifyNonRecursive(const PendingBindingObserverList &bindingObservers);
+ enum NotificationState : bool { Delayed, Sent };
+ NotificationState notifyNonRecursive();
static QPropertyBindingPrivate *get(const QUntypedPropertyBinding &binding)
{ return static_cast<QPropertyBindingPrivate *>(binding.d.data()); }
@@ -422,9 +459,9 @@ inline void QPropertyBindingDataPointer::fixupAfterMove(QtPrivate::QPropertyBind
{
auto &d = ptr->d_ref();
if (ptr->isNotificationDelayed()) {
- QPropertyProxyBindingData *proxyData
- = reinterpret_cast<QPropertyProxyBindingData*>(d & ~QtPrivate::QPropertyBindingData::BindingBit);
- proxyData->originalBindingData = ptr;
+ QPropertyProxyBindingData *proxy = ptr->proxyData();
+ Q_ASSERT(proxy);
+ proxy->originalBindingData = ptr;
}
// If QPropertyBindingData has been moved, and it has an observer
// we have to adjust the firstObserver's prev pointer to point to
@@ -442,6 +479,25 @@ inline QPropertyObserverPointer QPropertyBindingDataPointer::firstObserver() con
return { reinterpret_cast<QPropertyObserver *>(ptr->d()) };
}
+/*!
+ \internal
+ Returns the proxy data of \a ptr, or \c nullptr if \a ptr has no delayed notification
+ */
+inline QPropertyProxyBindingData *QPropertyBindingDataPointer::proxyData(QtPrivate::QPropertyBindingData *ptr)
+{
+ if (!ptr->isNotificationDelayed())
+ return nullptr;
+ return ptr->proxyData();
+}
+
+inline int QPropertyBindingDataPointer::observerCount() const
+{
+ int count = 0;
+ for (auto observer = firstObserver(); observer; observer = observer.nextObserver())
+ ++count;
+ return count;
+}
+
namespace QtPrivate {
Q_CORE_EXPORT bool isPropertyInBindingWrapper(const QUntypedPropertyData *property);
void Q_CORE_EXPORT initBindingStatusThreadId();
@@ -470,20 +526,23 @@ 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;
}
bool inBindingWrapper(const QBindingStorage *storage) const
{
- return storage->bindingStatus->currentCompatProperty
+ return storage->bindingStatus && storage->bindingStatus->currentCompatProperty
&& QtPrivate::isPropertyInBindingWrapper(this);
}
@@ -508,7 +567,7 @@ public:
{
const QBindingStorage *storage = qGetBindingStorage(owner());
// make sure we don't register this binding as a dependency to itself
- if (storage->bindingStatus->currentlyEvaluatingBinding && !inBindingWrapper(storage))
+ if (storage->bindingStatus && storage->bindingStatus->currentlyEvaluatingBinding && !inBindingWrapper(storage))
storage->registerDependency_helper(this);
return this->val;
}
@@ -568,7 +627,7 @@ public:
return true;
}
-#ifndef Q_CLANG_QDOC
+#ifndef Q_QDOC
template <typename Functor>
QPropertyBinding<T> setBinding(Functor &&f,
const QPropertyBindingSourceLocation &location = QT_PROPERTY_DEFAULT_BINDING_LOCATION,
@@ -607,11 +666,14 @@ public:
QPropertyBindingDataPointer d{bd};
if (QPropertyObserverPointer observer = d.firstObserver()) {
if (!inBindingWrapper(storage)) {
- if (bd->notifyObserver_helper(this, observer, storage)
+ PendingBindingObserverList bindingObservers;
+ if (bd->notifyObserver_helper(this, storage, observer, bindingObservers)
== QtPrivate::QPropertyBindingData::Evaluated) {
// evaluateBindings() can trash the observers. We need to re-fetch here.
if (QPropertyObserverPointer observer = d.firstObserver())
observer.notify(this);
+ for (auto&& bindingObserver: bindingObservers)
+ bindingObserver.binding()->notifyNonRecursive();
}
}
}
@@ -768,13 +830,13 @@ struct QUntypedBindablePrivate
}
};
-inline void QPropertyBindingPrivate::evaluateRecursive_inline(QBindingStatus *status)
+inline bool QPropertyBindingPrivate::evaluateRecursive_inline(PendingBindingObserverList &bindingObservers, QBindingStatus *status)
{
if (updating) {
error = QPropertyBindingError(QPropertyBindingError::BindingLoop);
if (isQQmlPropertyBinding)
errorCallBack(this);
- return;
+ return false;
}
/*
@@ -804,12 +866,21 @@ inline void QPropertyBindingPrivate::evaluateRecursive_inline(QBindingStatus *st
// If there was not, we must not clear it, as that only should happen in notifyRecursive
pendingNotify = pendingNotify || changed;
if (!changed || !firstObserver)
- return;
+ return changed;
firstObserver.noSelfDependencies(this);
- firstObserver.evaluateBindings(status);
+ firstObserver.evaluateBindings(bindingObservers, status);
+ return true;
}
+/*!
+ \internal
+
+ Walks through the list of property observers, and calls any ChangeHandler
+ found there.
+ It doesn't do anything with bindings, which are only handled in
+ QPropertyBindingPrivate::evaluateRecursive.
+ */
inline void QPropertyObserverPointer::notify(QUntypedPropertyData *propertyDataPtr)
{
auto observer = const_cast<QPropertyObserver*>(ptr);
@@ -848,18 +919,16 @@ inline void QPropertyObserverPointer::notify(QUntypedPropertyData *propertyDataP
break;
}
case QPropertyObserver::ObserverNotifiesBinding:
- {
- auto bindingToNotify = observer->binding;
- QPropertyObserverNodeProtector protector(observer);
- bindingToNotify->notifyRecursive();
- next = protector.next();
break;
- }
case QPropertyObserver::ObserverIsPlaceholder:
// recursion is already properly handled somewhere else
break;
+#if QT_DEPRECATED_SINCE(6, 6)
+ QT_WARNING_PUSH QT_WARNING_DISABLE_DEPRECATED
case QPropertyObserver::ObserverIsAlias:
break;
+ QT_WARNING_POP
+#endif
default: Q_UNREACHABLE();
}
observer = next;
@@ -872,6 +941,61 @@ inline QPropertyObserverNodeProtector::~QPropertyObserverNodeProtector()
d.unlink_fast();
}
+QBindingObserverPtr::QBindingObserverPtr(QPropertyObserver *observer) noexcept : d(observer)
+{
+ Q_ASSERT(d);
+ QPropertyObserverPointer{d}.binding()->addRef();
+}
+
+QBindingObserverPtr::~QBindingObserverPtr()
+{
+ if (!d)
+ return;
+
+ QPropertyBindingPrivate *bindingPrivate = binding();
+ if (!bindingPrivate->deref())
+ QPropertyBindingPrivate::destroyAndFreeMemory(bindingPrivate);
+}
+
+QPropertyBindingPrivate *QBindingObserverPtr::binding() const noexcept { return QPropertyObserverPointer{d}.binding(); }
+
+QPropertyObserver *QBindingObserverPtr::operator->() { return d; }
+
+namespace QtPrivate {
+class QPropertyAdaptorSlotObject : public QUntypedPropertyData, public QSlotObjectBase
+{
+ QPropertyBindingData bindingData_;
+ QObject *obj;
+ QMetaProperty metaProperty_;
+
+#if QT_VERSION < QT_VERSION_CHECK(7, 0, 0)
+ static void impl(int which, QSlotObjectBase *this_, QObject *r, void **a, bool *ret);
+#else
+ static void impl(QSlotObjectBase *this_, QObject *r, void **a, int which, bool *ret);
+#endif
+
+ QPropertyAdaptorSlotObject(QObject *o, const QMetaProperty& p);
+
+public:
+ static QPropertyAdaptorSlotObject *cast(QSlotObjectBase *ptr, int propertyIndex)
+ {
+ if (ptr->isImpl(&QPropertyAdaptorSlotObject::impl)) {
+ auto p = static_cast<QPropertyAdaptorSlotObject *>(ptr);
+ if (p->metaProperty_.propertyIndex() == propertyIndex)
+ return p;
+ }
+ return nullptr;
+ }
+
+ inline const QPropertyBindingData &bindingData() const { return bindingData_; }
+ inline QPropertyBindingData &bindingData() { return bindingData_; }
+ inline QObject *object() const { return obj; }
+ inline const QMetaProperty &metaProperty() const { return metaProperty_; }
+
+ friend class QT_PREPEND_NAMESPACE(QUntypedBindable);
+};
+}
+
QT_END_NAMESPACE
#endif // QPROPERTY_P_H
diff --git a/src/corelib/kernel/qpropertyprivate.h b/src/corelib/kernel/qpropertyprivate.h
index 1c7f13046f..86dc08a6bc 100644
--- a/src/corelib/kernel/qpropertyprivate.h
+++ b/src/corelib/kernel/qpropertyprivate.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2020 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtCore module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2020 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 QPROPERTYPRIVATE_H
#define QPROPERTYPRIVATE_H
@@ -54,6 +18,7 @@
#include <QtCore/qglobal.h>
#include <QtCore/qtaggedpointer.h>
#include <QtCore/qmetatype.h>
+#include <QtCore/qcontainerfwd.h>
#include <functional>
@@ -64,13 +29,20 @@ class QBindingStorage;
template<typename Class, typename T, auto Offset, auto Setter, auto Signal, auto Getter>
class QObjectCompatProperty;
+struct QBindingObserverPtr;
+using PendingBindingObserverList = QVarLengthArray<QBindingObserverPtr>;
+
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;}
};
}
@@ -93,7 +65,7 @@ public:
QPropertyBindingPrivatePtr() noexcept : d(nullptr) { }
~QPropertyBindingPrivatePtr()
{
- if (d && (--d->ref == 0))
+ if (d && !d->deref())
destroyAndFreeMemory();
}
Q_CORE_EXPORT void destroyAndFreeMemory();
@@ -114,14 +86,14 @@ public:
reset(o);
return *this;
}
- QPropertyBindingPrivatePtr(QPropertyBindingPrivatePtr &&o) noexcept : d(qExchange(o.d, nullptr)) {}
+ QPropertyBindingPrivatePtr(QPropertyBindingPrivatePtr &&o) noexcept : d(std::exchange(o.d, nullptr)) {}
QT_MOVE_ASSIGNMENT_OPERATOR_IMPL_VIA_MOVE_AND_SWAP(QPropertyBindingPrivatePtr)
operator bool () const noexcept { return d != nullptr; }
bool operator!() const noexcept { return d == nullptr; }
void swap(QPropertyBindingPrivatePtr &other) noexcept
- { qSwap(d, other.d); }
+ { qt_ptr_swap(d, other.d); }
friend bool operator==(const QPropertyBindingPrivatePtr &p1, const QPropertyBindingPrivatePtr &p2) noexcept
{ return p1.d == p2.d; }
@@ -151,15 +123,18 @@ private:
class QUntypedPropertyBinding;
class QPropertyBindingPrivate;
struct QPropertyBindingDataPointer;
+class QPropertyObserver;
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;
@@ -184,6 +159,12 @@ struct QPropertyProxyBindingData
namespace QtPrivate {
struct BindingEvaluationState;
+/* used in BindingFunctionVTable::createFor; on all other compilers, void would work, but on
+ MSVC this causes C2182 when compiling in C++20 mode. As we only need to provide some default
+ value which gets ignored, we introduce this dummy type.
+*/
+struct MSVCWorkAround {};
+
struct BindingFunctionVTable
{
using CallFn = bool(*)(QMetaType, QUntypedPropertyData *, void *);
@@ -194,7 +175,7 @@ struct BindingFunctionVTable
const MoveCtrFn moveConstruct;
const qsizetype size;
- template<typename Callable, typename PropertyType=void>
+ template<typename Callable, typename PropertyType=MSVCWorkAround>
static constexpr BindingFunctionVTable createFor()
{
static_assert (alignof(Callable) <= alignof(std::max_align_t), "Bindings do not support overaligned functors!");
@@ -205,7 +186,7 @@ struct BindingFunctionVTable
static_assert (std::is_invocable_r_v<bool, Callable, QMetaType, QUntypedPropertyData *> );
auto untypedEvaluationFunction = static_cast<Callable *>(f);
return std::invoke(*untypedEvaluationFunction, metaType, dataPtr);
- } else if constexpr (!std::is_same_v<PropertyType, void>) { // check for void to workaround MSVC issue
+ } else if constexpr (!std::is_same_v<PropertyType, MSVCWorkAround>) {
Q_UNUSED(metaType);
QPropertyData<PropertyType> *propertyPtr = static_cast<QPropertyData<PropertyType> *>(dataPtr);
// That is allowed by POSIX even if Callable is a function pointer
@@ -219,8 +200,7 @@ struct BindingFunctionVTable
return true;
} else {
// Our code will never instantiate this
- Q_UNREACHABLE();
- return false;
+ Q_UNREACHABLE_RETURN(false);
}
},
/*destroy*/[](void *f){ static_cast<Callable *>(f)->~Callable(); },
@@ -232,7 +212,7 @@ struct BindingFunctionVTable
}
};
-template<typename Callable, typename PropertyType=void>
+template<typename Callable, typename PropertyType=MSVCWorkAround>
inline constexpr BindingFunctionVTable bindingFunctionVTable = BindingFunctionVTable::createFor<Callable, PropertyType>();
@@ -329,17 +309,23 @@ private:
{
quintptr &d = d_ptr;
if (isNotificationDelayed())
- return reinterpret_cast<QPropertyProxyBindingData *>(d_ptr & ~(BindingBit|DelayedNotificationBit))->d_ptr;
+ return proxyData()->d_ptr;
return d;
}
quintptr d() const { return d_ref(); }
+ QPropertyProxyBindingData *proxyData() const
+ {
+ Q_ASSERT(isNotificationDelayed());
+ return reinterpret_cast<QPropertyProxyBindingData *>(d_ptr & ~(BindingBit|DelayedNotificationBit));
+ }
void registerWithCurrentlyEvaluatingBinding_helper(BindingEvaluationState *currentBinding) const;
void removeBinding_helper();
enum NotificationResult { Delayed, Evaluated };
NotificationResult notifyObserver_helper(
- QUntypedPropertyData *propertyDataPtr, QPropertyObserverPointer observer,
- QBindingStorage *storage) const;
+ QUntypedPropertyData *propertyDataPtr, QBindingStorage *storage,
+ QPropertyObserverPointer observer,
+ PendingBindingObserverList &bindingObservers) const;
};
template <typename T, typename Tag>
diff --git a/src/corelib/kernel/qsequentialiterable.cpp b/src/corelib/kernel/qsequentialiterable.cpp
index aedcc9b373..c331f1de63 100644
--- a/src/corelib/kernel/qsequentialiterable.cpp
+++ b/src/corelib/kernel/qsequentialiterable.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2020 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtCore module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2020 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 <QtCore/qsequentialiterable.h>
#include <QtCore/qvariant.h>
diff --git a/src/corelib/kernel/qsequentialiterable.h b/src/corelib/kernel/qsequentialiterable.h
index 7b08e2afa3..dac146d2ad 100644
--- a/src/corelib/kernel/qsequentialiterable.h
+++ b/src/corelib/kernel/qsequentialiterable.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2020 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtCore module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2020 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 QSEQUENTIALITERABLE_H
#define QSEQUENTIALITERABLE_H
diff --git a/src/corelib/kernel/qsharedmemory.cpp b/src/corelib/kernel/qsharedmemory.cpp
deleted file mode 100644
index 3dbbe75fc4..0000000000
--- a/src/corelib/kernel/qsharedmemory.cpp
+++ /dev/null
@@ -1,681 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtCore module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#include "qsharedmemory.h"
-#include "qsharedmemory_p.h"
-#include "qsystemsemaphore.h"
-#include <qdir.h>
-#include <qcryptographichash.h>
-#include <qdebug.h>
-#ifdef Q_OS_WIN
-# include <qt_windows.h>
-#endif
-
-#if defined(Q_OS_DARWIN)
-# include "qcore_mac_p.h"
-# if !defined(SHM_NAME_MAX)
- // Based on PSEMNAMLEN in XNU's posix_sem.c, which would
- // indicate the max length is 31, _excluding_ the zero
- // terminator. But in practice (possibly due to an off-
- // by-one bug in the kernel) the usable bytes are only 30.
-# define SHM_NAME_MAX 30
-# endif
-#endif
-
-QT_BEGIN_NAMESPACE
-
-#if !(defined(QT_NO_SHAREDMEMORY) && defined(QT_NO_SYSTEMSEMAPHORE))
-/*!
- \internal
-
- Generate a string from the key which can be any unicode string into
- the subset that the win/unix kernel allows.
-
- On Unix this will be a file name
- */
-QString
-QSharedMemoryPrivate::makePlatformSafeKey(const QString &key,
- const QString &prefix)
-{
- if (key.isEmpty())
- return QString();
-
- QByteArray hex = QCryptographicHash::hash(key.toUtf8(), QCryptographicHash::Sha1).toHex();
-
-#if defined(Q_OS_DARWIN) && defined(QT_POSIX_IPC)
- if (qt_apple_isSandboxed()) {
- // Sandboxed applications on Apple platforms require the shared memory name
- // to be in the form <application group identifier>/<custom identifier>.
- // Since we don't know which application group identifier the user wants
- // to apply, we instead document that requirement, and use the key directly.
- return key;
- } else {
- // The shared memory name limit on Apple platforms is very low (30 characters),
- // so we can't use the logic below of combining the prefix, key, and a hash,
- // to ensure a unique and valid name. Instead we use the first part of the
- // hash, which should still long enough to avoid collisions in practice.
- return QLatin1Char('/') + hex.left(SHM_NAME_MAX - 1);
- }
-#endif
-
- QString result = prefix;
- for (QChar ch : key) {
- if ((ch >= QLatin1Char('a') && ch <= QLatin1Char('z')) ||
- (ch >= QLatin1Char('A') && ch <= QLatin1Char('Z')))
- result += ch;
- }
- result.append(QLatin1String(hex));
-
-#ifdef Q_OS_WIN
- return result;
-#elif defined(QT_POSIX_IPC)
- return QLatin1Char('/') + result;
-#else
- return QDir::tempPath() + QLatin1Char('/') + result;
-#endif
-}
-#endif // QT_NO_SHAREDMEMORY && QT_NO_SHAREDMEMORY
-
-#ifndef QT_NO_SHAREDMEMORY
-
-/*!
- \class QSharedMemory
- \inmodule QtCore
- \since 4.4
-
- \brief The QSharedMemory class provides access to a shared memory segment.
-
- QSharedMemory provides access to a shared memory segment by multiple
- threads and processes. It also provides a way for a single thread or
- process to lock the memory for exclusive access.
-
- When using this class, be aware of the following platform
- differences:
-
- \list
-
- \li Windows: QSharedMemory does not "own" the shared memory segment.
- When all threads or processes that have an instance of QSharedMemory
- attached to a particular shared memory segment have either destroyed
- their instance of QSharedMemory or exited, the Windows kernel
- releases the shared memory segment automatically.
-
- \li Unix: QSharedMemory "owns" the shared memory segment. When the
- last thread or process that has an instance of QSharedMemory
- attached to a particular shared memory segment detaches from the
- segment by destroying its instance of QSharedMemory, the Unix kernel
- release the shared memory segment. But if that last thread or
- process crashes without running the QSharedMemory destructor, the
- shared memory segment survives the crash.
-
- \li HP-UX: Only one attach to a shared memory segment is allowed per
- process. This means that QSharedMemory should not be used across
- multiple threads in the same process in HP-UX.
-
- \li Apple platforms: Sandboxed applications (including apps
- shipped through the Apple App Store) require the use of POSIX
- shared memory (instead of System V shared memory), which adds
- a number of limitations, including:
-
- \list
-
- \li The key must be in the form \c {<application group identifier>/<custom identifier>},
- as documented \l {https://developer.apple.com/library/archive/documentation/Security/Conceptual/AppSandboxDesignGuide/AppSandboxInDepth/AppSandboxInDepth.html#//apple_ref/doc/uid/TP40011183-CH3-SW24}
- {here} and \l {https://developer.apple.com/documentation/bundleresources/entitlements/com_apple_security_application-groups}
- {here}.
-
- \li The key length is limited to 30 characters.
-
- \li On process exit, the named shared memory entries are not
- cleaned up, so restarting the application and re-creating the
- shared memory under the same name will fail. To work around this,
- fall back to attaching to the existing shared memory entry:
-
- \code
-
- QSharedMemory shm("DEVTEAMID.app-group/shared");
- if (!shm.create(42) && shm.error() == QSharedMemory::AlreadyExists)
- shm.attach();
-
- \endcode
-
-
- \endlist
-
- \endlist
-
- Remember to lock the shared memory with lock() before reading from
- or writing to the shared memory, and remember to release the lock
- with unlock() after you are done.
-
- QSharedMemory automatically destroys the shared memory segment when
- the last instance of QSharedMemory is detached from the segment, and
- no references to the segment remain.
-
- \warning QSharedMemory changes the key in a Qt-specific way, unless otherwise
- specified. Interoperation with non-Qt applications is achieved by first creating
- a default shared memory with QSharedMemory() and then setting a native key with
- setNativeKey(). When using native keys, shared memory is not protected against
- multiple accesses on it (for example, unable to lock()) and a user-defined mechanism
- should be used to achieve such protection.
- */
-
-/*!
- \overload QSharedMemory()
-
- Constructs a shared memory object with the given \a parent. The
- shared memory object's key is not set by the constructor, so the
- shared memory object does not have an underlying shared memory
- segment attached. The key must be set with setKey() or setNativeKey()
- before create() or attach() can be used.
-
- \sa setKey()
- */
-
-#ifndef QT_NO_QOBJECT
-QSharedMemory::QSharedMemory(QObject *parent)
- : QObject(*new QSharedMemoryPrivate, parent)
-{
-}
-#else
-QSharedMemory::QSharedMemory()
- : d_ptr(new QSharedMemoryPrivate)
-{
-}
-#endif
-/*!
- Constructs a shared memory object with the given \a parent and with
- its key set to \a key. Because its key is set, its create() and
- attach() functions can be called.
-
- \sa setKey(), create(), attach()
- */
-#ifndef QT_NO_QOBJECT
-QSharedMemory::QSharedMemory(const QString &key, QObject *parent)
- : QObject(*new QSharedMemoryPrivate, parent)
-{
- setKey(key);
-}
-#else
-QSharedMemory::QSharedMemory(const QString &key)
- : d_ptr(new QSharedMemoryPrivate)
-{
- setKey(key);
-}
-#endif
-
-/*!
- The destructor clears the key, which forces the shared memory object
- to \l {detach()} {detach} from its underlying shared memory
- segment. If this shared memory object is the last one connected to
- the shared memory segment, the detach() operation destroys the
- shared memory segment.
-
- \sa detach(), isAttached()
- */
-QSharedMemory::~QSharedMemory()
-{
- setKey(QString());
-}
-
-/*!
- Sets the platform independent \a key for this shared memory object. If \a key
- is the same as the current key, the function returns without doing anything.
-
- You can call key() to retrieve the platform independent key. Internally,
- QSharedMemory converts this key into a platform specific key. If you instead
- call nativeKey(), you will get the platform specific, converted key.
-
- If the shared memory object is attached to an underlying shared memory
- segment, it will \l {detach()} {detach} from it before setting the new key.
- This function does not do an attach().
-
- \sa key(), nativeKey(), isAttached()
-*/
-void QSharedMemory::setKey(const QString &key)
-{
- Q_D(QSharedMemory);
- if (key == d->key && d->makePlatformSafeKey(key) == d->nativeKey)
- return;
-
- if (isAttached())
- detach();
- d->cleanHandle();
- d->key = key;
- d->nativeKey = d->makePlatformSafeKey(key);
-}
-
-/*!
- \since 4.8
-
- Sets the native, platform specific, \a key for this shared memory object. If
- \a key is the same as the current native key, the function returns without
- doing anything. If all you want is to assign a key to a segment, you should
- call setKey() instead.
-
- You can call nativeKey() to retrieve the native key. If a native key has been
- assigned, calling key() will return a null string.
-
- If the shared memory object is attached to an underlying shared memory
- segment, it will \l {detach()} {detach} from it before setting the new key.
- This function does not do an attach().
-
- The application will not be portable if you set a native key.
-
- \sa nativeKey(), key(), isAttached()
-*/
-void QSharedMemory::setNativeKey(const QString &key)
-{
- Q_D(QSharedMemory);
- if (key == d->nativeKey && d->key.isNull())
- return;
-
- if (isAttached())
- detach();
- d->cleanHandle();
- d->key = QString();
- d->nativeKey = key;
-}
-
-bool QSharedMemoryPrivate::initKey()
-{
- if (!cleanHandle())
- return false;
-#ifndef QT_NO_SYSTEMSEMAPHORE
- systemSemaphore.setKey(QString(), 1);
- systemSemaphore.setKey(key, 1);
- if (systemSemaphore.error() != QSystemSemaphore::NoError) {
- QString function = QLatin1String("QSharedMemoryPrivate::initKey");
- errorString = QSharedMemory::tr("%1: unable to set key on lock").arg(function);
- switch(systemSemaphore.error()) {
- case QSystemSemaphore::PermissionDenied:
- error = QSharedMemory::PermissionDenied;
- break;
- case QSystemSemaphore::KeyError:
- error = QSharedMemory::KeyError;
- break;
- case QSystemSemaphore::AlreadyExists:
- error = QSharedMemory::AlreadyExists;
- break;
- case QSystemSemaphore::NotFound:
- error = QSharedMemory::NotFound;
- break;
- case QSystemSemaphore::OutOfResources:
- error = QSharedMemory::OutOfResources;
- break;
- case QSystemSemaphore::UnknownError:
- default:
- error = QSharedMemory::UnknownError;
- break;
- }
- return false;
- }
-#endif
- errorString = QString();
- error = QSharedMemory::NoError;
- return true;
-}
-
-/*!
- Returns the key assigned with setKey() to this shared memory, or a null key
- if no key has been assigned, or if the segment is using a nativeKey(). The
- key is the identifier used by Qt applications to identify the shared memory
- segment.
-
- You can find the native, platform specific, key used by the operating system
- by calling nativeKey().
-
- \sa setKey(), setNativeKey()
- */
-QString QSharedMemory::key() const
-{
- Q_D(const QSharedMemory);
- return d->key;
-}
-
-/*!
- \since 4.8
-
- Returns the native, platform specific, key for this shared memory object. The
- native key is the identifier used by the operating system to identify the
- shared memory segment.
-
- You can use the native key to access shared memory segments that have not
- been created by Qt, or to grant shared memory access to non-Qt applications.
-
- \sa setKey(), setNativeKey()
-*/
-QString QSharedMemory::nativeKey() const
-{
- Q_D(const QSharedMemory);
- return d->nativeKey;
-}
-
-/*!
- Creates a shared memory segment of \a size bytes with the key passed to the
- constructor, set with setKey() or set with setNativeKey(), then attaches to
- the new shared memory segment with the given access \a mode and returns
- \tt true. If a shared memory segment identified by the key already exists,
- the attach operation is not performed and \tt false is returned. When the
- return value is \tt false, call error() to determine which error occurred.
-
- \sa error()
- */
-bool QSharedMemory::create(qsizetype size, AccessMode mode)
-{
- Q_D(QSharedMemory);
-
- if (!d->initKey())
- return false;
-
-#ifndef QT_NO_SYSTEMSEMAPHORE
-#ifndef Q_OS_WIN
- // Take ownership and force set initialValue because the semaphore
- // might have already existed from a previous crash.
- d->systemSemaphore.setKey(d->key, 1, QSystemSemaphore::Create);
-#endif
-#endif
-
- QString function = QLatin1String("QSharedMemory::create");
-#ifndef QT_NO_SYSTEMSEMAPHORE
- QSharedMemoryLocker lock(this);
- if (!d->key.isNull() && !d->tryLocker(&lock, function))
- return false;
-#endif
-
- if (size <= 0) {
- d->error = QSharedMemory::InvalidSize;
- d->errorString =
- QSharedMemory::tr("%1: create size is less then 0").arg(function);
- return false;
- }
-
- if (!d->create(size))
- return false;
-
- return d->attach(mode);
-}
-
-/*!
- Returns the size of the attached shared memory segment. If no shared
- memory segment is attached, 0 is returned.
-
- \note The size of the segment may be larger than the requested size that was
- passed to create().
-
- \sa create(), attach()
- */
-qsizetype QSharedMemory::size() const
-{
- Q_D(const QSharedMemory);
- return d->size;
-}
-
-/*!
- \enum QSharedMemory::AccessMode
-
- \value ReadOnly The shared memory segment is read-only. Writing to
- the shared memory segment is not allowed. An attempt to write to a
- shared memory segment created with ReadOnly causes the program to
- abort.
-
- \value ReadWrite Reading and writing the shared memory segment are
- both allowed.
-*/
-
-/*!
- Attempts to attach the process to the shared memory segment
- identified by the key that was passed to the constructor or to a
- call to setKey() or setNativeKey(). The access \a mode is \l {QSharedMemory::}
- {ReadWrite} by default. It can also be \l {QSharedMemory::}
- {ReadOnly}. Returns \c true if the attach operation is successful. If
- false is returned, call error() to determine which error occurred.
- After attaching the shared memory segment, a pointer to the shared
- memory can be obtained by calling data().
-
- \sa isAttached(), detach(), create()
- */
-bool QSharedMemory::attach(AccessMode mode)
-{
- Q_D(QSharedMemory);
-
- if (isAttached() || !d->initKey())
- return false;
-#ifndef QT_NO_SYSTEMSEMAPHORE
- QSharedMemoryLocker lock(this);
- if (!d->key.isNull() && !d->tryLocker(&lock, QLatin1String("QSharedMemory::attach")))
- return false;
-#endif
-
- if (isAttached() || !d->handle())
- return false;
-
- return d->attach(mode);
-}
-
-/*!
- Returns \c true if this process is attached to the shared memory
- segment.
-
- \sa attach(), detach()
- */
-bool QSharedMemory::isAttached() const
-{
- Q_D(const QSharedMemory);
- return (nullptr != d->memory);
-}
-
-/*!
- Detaches the process from the shared memory segment. If this was the
- last process attached to the shared memory segment, then the shared
- memory segment is released by the system, i.e., the contents are
- destroyed. The function returns \c true if it detaches the shared
- memory segment. If it returns \c false, it usually means the segment
- either isn't attached, or it is locked by another process.
-
- \sa attach(), isAttached()
- */
-bool QSharedMemory::detach()
-{
- Q_D(QSharedMemory);
- if (!isAttached())
- return false;
-
-#ifndef QT_NO_SYSTEMSEMAPHORE
- QSharedMemoryLocker lock(this);
- if (!d->key.isNull() && !d->tryLocker(&lock, QLatin1String("QSharedMemory::detach")))
- return false;
-#endif
-
- return d->detach();
-}
-
-/*!
- Returns a pointer to the contents of the shared memory segment, if
- one is attached. Otherwise it returns null. Remember to lock the
- shared memory with lock() before reading from or writing to the
- shared memory, and remember to release the lock with unlock() after
- you are done.
-
- \sa attach()
- */
-void *QSharedMemory::data()
-{
- Q_D(QSharedMemory);
- return d->memory;
-}
-
-/*!
- Returns a const pointer to the contents of the shared memory
- segment, if one is attached. Otherwise it returns null. Remember to
- lock the shared memory with lock() before reading from or writing to
- the shared memory, and remember to release the lock with unlock()
- after you are done.
-
- \sa attach(), create()
- */
-const void *QSharedMemory::constData() const
-{
- Q_D(const QSharedMemory);
- return d->memory;
-}
-
-/*!
- \overload data()
- */
-const void *QSharedMemory::data() const
-{
- Q_D(const QSharedMemory);
- return d->memory;
-}
-
-#ifndef QT_NO_SYSTEMSEMAPHORE
-/*!
- This is a semaphore that locks the shared memory segment for access
- by this process and returns \c true. If another process has locked the
- segment, this function blocks until the lock is released. Then it
- acquires the lock and returns \c true. If this function returns \c false,
- it means that you have ignored a false return from create() or attach(),
- that you have set the key with setNativeKey() or that
- QSystemSemaphore::acquire() failed due to an unknown system error.
-
- \sa unlock(), data(), QSystemSemaphore::acquire()
- */
-bool QSharedMemory::lock()
-{
- Q_D(QSharedMemory);
- if (d->lockedByMe) {
- qWarning("QSharedMemory::lock: already locked");
- return true;
- }
- if (d->systemSemaphore.acquire()) {
- d->lockedByMe = true;
- return true;
- }
- QString function = QLatin1String("QSharedMemory::lock");
- d->errorString = QSharedMemory::tr("%1: unable to lock").arg(function);
- d->error = QSharedMemory::LockError;
- return false;
-}
-
-/*!
- Releases the lock on the shared memory segment and returns \c true, if
- the lock is currently held by this process. If the segment is not
- locked, or if the lock is held by another process, nothing happens
- and false is returned.
-
- \sa lock()
- */
-bool QSharedMemory::unlock()
-{
- Q_D(QSharedMemory);
- if (!d->lockedByMe)
- return false;
- d->lockedByMe = false;
- if (d->systemSemaphore.release())
- return true;
- QString function = QLatin1String("QSharedMemory::unlock");
- d->errorString = QSharedMemory::tr("%1: unable to unlock").arg(function);
- d->error = QSharedMemory::LockError;
- return false;
-}
-#endif // QT_NO_SYSTEMSEMAPHORE
-
-/*!
- \enum QSharedMemory::SharedMemoryError
-
- \value NoError No error occurred.
-
- \value PermissionDenied The operation failed because the caller
- didn't have the required permissions.
-
- \value InvalidSize A create operation failed because the requested
- size was invalid.
-
- \value KeyError The operation failed because of an invalid key.
-
- \value AlreadyExists A create() operation failed because a shared
- memory segment with the specified key already existed.
-
- \value NotFound An attach() failed because a shared memory segment
- with the specified key could not be found.
-
- \value LockError The attempt to lock() the shared memory segment
- failed because create() or attach() failed and returned false, or
- because a system error occurred in QSystemSemaphore::acquire().
-
- \value OutOfResources A create() operation failed because there was
- not enough memory available to fill the request.
-
- \value UnknownError Something else happened and it was bad.
-*/
-
-/*!
- Returns a value indicating whether an error occurred, and, if so,
- which error it was.
-
- \sa errorString()
- */
-QSharedMemory::SharedMemoryError QSharedMemory::error() const
-{
- Q_D(const QSharedMemory);
- return d->error;
-}
-
-/*!
- Returns a text description of the last error that occurred. If
- error() returns an \l {QSharedMemory::SharedMemoryError} {error
- value}, call this function to get a text string that describes the
- error.
-
- \sa error()
- */
-QString QSharedMemory::errorString() const
-{
- Q_D(const QSharedMemory);
- return d->errorString;
-}
-
-#endif // QT_NO_SHAREDMEMORY
-
-QT_END_NAMESPACE
-
-#ifndef QT_NO_QOBJECT
-#include "moc_qsharedmemory.cpp"
-#endif
diff --git a/src/corelib/kernel/qsharedmemory.h b/src/corelib/kernel/qsharedmemory.h
deleted file mode 100644
index 0197b18624..0000000000
--- a/src/corelib/kernel/qsharedmemory.h
+++ /dev/null
@@ -1,137 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtCore module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#ifndef QSHAREDMEMORY_H
-#define QSHAREDMEMORY_H
-
-#include <QtCore/qglobal.h>
-#ifndef QT_NO_QOBJECT
-# include <QtCore/qobject.h>
-#else
-# include <QtCore/qobjectdefs.h>
-# include <QtCore/qscopedpointer.h>
-# include <QtCore/qstring.h>
-#endif
-QT_BEGIN_NAMESPACE
-
-
-#ifndef QT_NO_SHAREDMEMORY
-
-class QSharedMemoryPrivate;
-
-class Q_CORE_EXPORT QSharedMemory
-#ifndef QT_NO_QOBJECT
- : public QObject
-#endif
-{
-#ifndef QT_NO_QOBJECT
- Q_OBJECT
-#endif
- Q_DECLARE_PRIVATE(QSharedMemory)
-
-public:
- enum AccessMode
- {
- ReadOnly,
- ReadWrite
- };
-
- enum SharedMemoryError
- {
- NoError,
- PermissionDenied,
- InvalidSize,
- KeyError,
- AlreadyExists,
- NotFound,
- LockError,
- OutOfResources,
- UnknownError
- };
-
-#ifndef QT_NO_QOBJECT
- QSharedMemory(QObject *parent = nullptr);
- QSharedMemory(const QString &key, QObject *parent = nullptr);
-#else
- QSharedMemory();
- QSharedMemory(const QString &key);
- static QString tr(const char * str)
- {
- return QString::fromLatin1(str);
- }
-#endif
- ~QSharedMemory();
-
- void setKey(const QString &key);
- QString key() const;
- void setNativeKey(const QString &key);
- QString nativeKey() const;
-
- bool create(qsizetype size, AccessMode mode = ReadWrite);
- qsizetype size() const;
-
- bool attach(AccessMode mode = ReadWrite);
- bool isAttached() const;
- bool detach();
-
- void *data();
- const void* constData() const;
- const void *data() const;
-
-#ifndef QT_NO_SYSTEMSEMAPHORE
- bool lock();
- bool unlock();
-#endif
-
- SharedMemoryError error() const;
- QString errorString() const;
-
-private:
- Q_DISABLE_COPY(QSharedMemory)
-#ifdef QT_NO_QOBJECT
- QScopedPointer<QSharedMemoryPrivate> d_ptr;
-#endif
-};
-
-#endif // QT_NO_SHAREDMEMORY
-
-QT_END_NAMESPACE
-
-#endif // QSHAREDMEMORY_H
-
diff --git a/src/corelib/kernel/qsharedmemory_android.cpp b/src/corelib/kernel/qsharedmemory_android.cpp
deleted file mode 100644
index 7165ebcc3d..0000000000
--- a/src/corelib/kernel/qsharedmemory_android.cpp
+++ /dev/null
@@ -1,110 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2012 Collabora Ltd, author <robin.burchell@collabora.co.uk>
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtCore module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#include "qsharedmemory.h"
-#include "qsharedmemory_p.h"
-#include <qdebug.h>
-
-#ifndef QT_NO_SHAREDMEMORY
-QT_BEGIN_NAMESPACE
-
-QSharedMemoryPrivate::QSharedMemoryPrivate()
- : QObjectPrivate(), memory(0), size(0), error(QSharedMemory::NoError),
-#ifndef QT_NO_SYSTEMSEMAPHORE
- systemSemaphore(QString()), lockedByMe(false),
-#endif
- unix_key(0)
-{
-}
-
-void QSharedMemoryPrivate::setErrorString(QLatin1String function)
-{
- Q_UNUSED(function);
- Q_UNIMPLEMENTED();
-}
-
-key_t QSharedMemoryPrivate::handle()
-{
- Q_UNIMPLEMENTED();
- return 0;
-}
-
-#endif // QT_NO_SHAREDMEMORY
-
-#if !(defined(QT_NO_SHAREDMEMORY) && defined(QT_NO_SYSTEMSEMAPHORE))
-int QSharedMemoryPrivate::createUnixKeyFile(const QString &fileName)
-{
- Q_UNUSED(fileName);
- Q_UNIMPLEMENTED();
- return 0;
-}
-#endif // QT_NO_SHAREDMEMORY && QT_NO_SYSTEMSEMAPHORE
-
-#ifndef QT_NO_SHAREDMEMORY
-
-bool QSharedMemoryPrivate::cleanHandle()
-{
- Q_UNIMPLEMENTED();
- return true;
-}
-
-bool QSharedMemoryPrivate::create(qsizetype size)
-{
- Q_UNUSED(size);
- Q_UNIMPLEMENTED();
- return false;
-}
-
-bool QSharedMemoryPrivate::attach(QSharedMemory::AccessMode mode)
-{
- Q_UNUSED(mode);
- Q_UNIMPLEMENTED();
- return false;
-}
-
-bool QSharedMemoryPrivate::detach()
-{
- Q_UNIMPLEMENTED();
- return false;
-}
-
-
-QT_END_NAMESPACE
-
-#endif // QT_NO_SHAREDMEMORY
diff --git a/src/corelib/kernel/qsharedmemory_p.h b/src/corelib/kernel/qsharedmemory_p.h
deleted file mode 100644
index e06e7e86f3..0000000000
--- a/src/corelib/kernel/qsharedmemory_p.h
+++ /dev/null
@@ -1,186 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtCore module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#ifndef QSHAREDMEMORY_P_H
-#define QSHAREDMEMORY_P_H
-
-//
-// W A R N I N G
-// -------------
-//
-// This file is not part of the Qt API. It exists purely as an
-// implementation detail. This header file may change from version to
-// version without notice, or even be removed.
-//
-// We mean it.
-//
-
-#include "qsharedmemory.h"
-
-#include <QtCore/qstring.h>
-
-#ifdef QT_NO_SHAREDMEMORY
-# ifndef QT_NO_SYSTEMSEMAPHORE
-
-QT_BEGIN_NAMESPACE
-
-namespace QSharedMemoryPrivate
-{
- int createUnixKeyFile(const QString &fileName);
- QString makePlatformSafeKey(const QString &key,
- const QString &prefix = QLatin1String("qipc_sharedmemory_"));
-}
-
-QT_END_NAMESPACE
-
-# endif
-#else
-
-#include "qsystemsemaphore.h"
-
-#ifndef QT_NO_QOBJECT
-# include "private/qobject_p.h"
-#endif
-
-#if !defined(Q_OS_WIN) && !defined(Q_OS_ANDROID) && !defined(Q_OS_INTEGRITY) && !defined(Q_OS_RTEMS)
-# include <sys/sem.h>
-#endif
-
-QT_BEGIN_NAMESPACE
-
-#ifndef QT_NO_SYSTEMSEMAPHORE
-/*!
- Helper class
- */
-class QSharedMemoryLocker
-{
-
-public:
- inline QSharedMemoryLocker(QSharedMemory *sharedMemory) : q_sm(sharedMemory)
- {
- Q_ASSERT(q_sm);
- }
-
- inline ~QSharedMemoryLocker()
- {
- if (q_sm)
- q_sm->unlock();
- }
-
- inline bool lock()
- {
- if (q_sm && q_sm->lock())
- return true;
- q_sm = nullptr;
- return false;
- }
-
-private:
- QSharedMemory *q_sm;
-};
-#endif // QT_NO_SYSTEMSEMAPHORE
-
-class Q_AUTOTEST_EXPORT QSharedMemoryPrivate
-#ifndef QT_NO_QOBJECT
- : public QObjectPrivate
-#endif
-{
-#ifndef QT_NO_QOBJECT
- Q_DECLARE_PUBLIC(QSharedMemory)
-#endif
-
-public:
- QSharedMemoryPrivate();
-
- void *memory;
- qsizetype size;
- QString key;
- QString nativeKey;
- QSharedMemory::SharedMemoryError error;
- QString errorString;
-#ifndef QT_NO_SYSTEMSEMAPHORE
- QSystemSemaphore systemSemaphore;
- bool lockedByMe;
-#endif
-
- static int createUnixKeyFile(const QString &fileName);
- static QString makePlatformSafeKey(const QString &key,
- const QString &prefix = QLatin1String("qipc_sharedmemory_"));
-#ifdef Q_OS_WIN
- Qt::HANDLE handle();
-#elif defined(QT_POSIX_IPC)
- int handle();
-#else
- key_t handle();
-#endif
- bool initKey();
- bool cleanHandle();
- bool create(qsizetype size);
- bool attach(QSharedMemory::AccessMode mode);
- bool detach();
-
- void setErrorString(QLatin1String function);
-
-#ifndef QT_NO_SYSTEMSEMAPHORE
- bool tryLocker(QSharedMemoryLocker *locker, const QString &function) {
- if (!locker->lock()) {
- errorString = QSharedMemory::tr("%1: unable to lock").arg(function);
- error = QSharedMemory::LockError;
- return false;
- }
- return true;
- }
-#endif // QT_NO_SYSTEMSEMAPHORE
-
-private:
-#ifdef Q_OS_WIN
- Qt::HANDLE hand;
-#elif defined(QT_POSIX_IPC)
- int hand;
-#else
- key_t unix_key;
-#endif
-};
-
-QT_END_NAMESPACE
-
-#endif // QT_NO_SHAREDMEMORY
-
-#endif // QSHAREDMEMORY_P_H
-
diff --git a/src/corelib/kernel/qsharedmemory_posix.cpp b/src/corelib/kernel/qsharedmemory_posix.cpp
deleted file mode 100644
index 9406e200be..0000000000
--- a/src/corelib/kernel/qsharedmemory_posix.cpp
+++ /dev/null
@@ -1,234 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2015 Konstantin Ritt <ritt.ks@gmail.com>
-** Copyright (C) 2016 The Qt Company Ltd.
-** Copyright (C) 2015 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Tobias Koenig <tobias.koenig@kdab.com>
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtCore module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#include "qplatformdefs.h"
-
-#include "qsharedmemory.h"
-#include "qsharedmemory_p.h"
-#include "qsystemsemaphore.h"
-#include <qfile.h>
-
-#include <errno.h>
-
-#ifdef QT_POSIX_IPC
-
-#ifndef QT_NO_SHAREDMEMORY
-#include <sys/types.h>
-#include <sys/mman.h>
-#include <sys/stat.h>
-#include <fcntl.h>
-#include <unistd.h>
-
-#include "private/qcore_unix_p.h"
-
-QT_BEGIN_NAMESPACE
-
-int QSharedMemoryPrivate::handle()
-{
- // don't allow making handles on empty keys
- const QString safeKey = makePlatformSafeKey(key);
- if (safeKey.isEmpty()) {
- errorString = QSharedMemory::tr("%1: key is empty").arg(QLatin1String("QSharedMemory::handle"));
- error = QSharedMemory::KeyError;
- return 0;
- }
-
- return 1;
-}
-
-bool QSharedMemoryPrivate::cleanHandle()
-{
- qt_safe_close(hand);
- hand = -1;
-
- return true;
-}
-
-bool QSharedMemoryPrivate::create(qsizetype size)
-{
- if (!handle())
- return false;
-
- const QByteArray shmName = QFile::encodeName(makePlatformSafeKey(key));
-
- int fd;
-#ifdef O_CLOEXEC
- // First try with O_CLOEXEC flag, if that fails, fall back to normal flags
- EINTR_LOOP(fd, ::shm_open(shmName.constData(), O_RDWR | O_CREAT | O_EXCL | O_CLOEXEC, 0600));
- if (fd == -1)
- EINTR_LOOP(fd, ::shm_open(shmName.constData(), O_RDWR | O_CREAT | O_EXCL, 0600));
-#else
- EINTR_LOOP(fd, ::shm_open(shmName.constData(), O_RDWR | O_CREAT | O_EXCL, 0600));
-#endif
- if (fd == -1) {
- const int errorNumber = errno;
- const QLatin1String function("QSharedMemory::attach (shm_open)");
- switch (errorNumber) {
- case EINVAL:
- errorString = QSharedMemory::tr("%1: bad name").arg(function);
- error = QSharedMemory::KeyError;
- break;
- default:
- setErrorString(function);
- }
- return false;
- }
-
- // the size may only be set once
- int ret;
- EINTR_LOOP(ret, QT_FTRUNCATE(fd, size));
- if (ret == -1) {
- setErrorString(QLatin1String("QSharedMemory::create (ftruncate)"));
- qt_safe_close(fd);
- return false;
- }
-
- qt_safe_close(fd);
-
- return true;
-}
-
-bool QSharedMemoryPrivate::attach(QSharedMemory::AccessMode mode)
-{
- const QByteArray shmName = QFile::encodeName(makePlatformSafeKey(key));
-
- const int oflag = (mode == QSharedMemory::ReadOnly ? O_RDONLY : O_RDWR);
- const mode_t omode = (mode == QSharedMemory::ReadOnly ? 0400 : 0600);
-
-#ifdef O_CLOEXEC
- // First try with O_CLOEXEC flag, if that fails, fall back to normal flags
- EINTR_LOOP(hand, ::shm_open(shmName.constData(), oflag | O_CLOEXEC, omode));
- if (hand == -1)
- EINTR_LOOP(hand, ::shm_open(shmName.constData(), oflag, omode));
-#else
- EINTR_LOOP(hand, ::shm_open(shmName.constData(), oflag, omode));
-#endif
- if (hand == -1) {
- const int errorNumber = errno;
- const QLatin1String function("QSharedMemory::attach (shm_open)");
- switch (errorNumber) {
- case EINVAL:
- errorString = QSharedMemory::tr("%1: bad name").arg(function);
- error = QSharedMemory::KeyError;
- break;
- default:
- setErrorString(function);
- }
- hand = -1;
- return false;
- }
-
- // grab the size
- QT_STATBUF st;
- if (QT_FSTAT(hand, &st) == -1) {
- setErrorString(QLatin1String("QSharedMemory::attach (fstat)"));
- cleanHandle();
- return false;
- }
- size = qsizetype(st.st_size);
-
- // grab the memory
- const int mprot = (mode == QSharedMemory::ReadOnly ? PROT_READ : PROT_READ | PROT_WRITE);
- memory = QT_MMAP(0, size_t(size), mprot, MAP_SHARED, hand, 0);
- if (memory == MAP_FAILED || !memory) {
- setErrorString(QLatin1String("QSharedMemory::attach (mmap)"));
- cleanHandle();
- memory = 0;
- size = 0;
- return false;
- }
-
-#ifdef F_ADD_SEALS
- // Make sure the shared memory region will not shrink
- // otherwise someone could cause SIGBUS on us.
- // (see http://lwn.net/Articles/594919/)
- fcntl(hand, F_ADD_SEALS, F_SEAL_SHRINK);
-#endif
-
- return true;
-}
-
-bool QSharedMemoryPrivate::detach()
-{
- // detach from the memory segment
- if (::munmap(memory, size_t(size)) == -1) {
- setErrorString(QLatin1String("QSharedMemory::detach (munmap)"));
- return false;
- }
- memory = 0;
- size = 0;
-
-#ifdef Q_OS_QNX
- // On QNX the st_nlink field of struct stat contains the number of
- // active shm_open() connections to the shared memory file, so we
- // can use it to automatically clean up the file once the last
- // user has detached from it.
-
- // get the number of current attachments
- int shm_nattch = 0;
- QT_STATBUF st;
- if (QT_FSTAT(hand, &st) == 0) {
- // subtract 2 from linkcount: one for our own open and one for the dir entry
- shm_nattch = st.st_nlink - 2;
- }
-
- cleanHandle();
-
- // if there are no attachments then unlink the shared memory
- if (shm_nattch == 0) {
- const QByteArray shmName = QFile::encodeName(makePlatformSafeKey(key));
- if (::shm_unlink(shmName.constData()) == -1 && errno != ENOENT)
- setErrorString(QLatin1String("QSharedMemory::detach (shm_unlink)"));
- }
-#else
- // On non-QNX systems (tested Linux and Haiku), the st_nlink field is always 1,
- // so we'll simply leak the shared memory files.
- cleanHandle();
-#endif
-
- return true;
-}
-
-QT_END_NAMESPACE
-
-#endif // QT_NO_SHAREDMEMORY
-
-#endif // QT_POSIX_IPC
diff --git a/src/corelib/kernel/qsharedmemory_systemv.cpp b/src/corelib/kernel/qsharedmemory_systemv.cpp
deleted file mode 100644
index 65ad23b9f6..0000000000
--- a/src/corelib/kernel/qsharedmemory_systemv.cpp
+++ /dev/null
@@ -1,260 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtCore module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#include "qplatformdefs.h"
-
-#include "qsharedmemory.h"
-#include "qsharedmemory_p.h"
-#include "qsystemsemaphore.h"
-#include <qdir.h>
-#include <qdebug.h>
-
-#include <errno.h>
-
-#ifndef QT_POSIX_IPC
-
-#ifndef QT_NO_SHAREDMEMORY
-#include <sys/types.h>
-#include <sys/ipc.h>
-#include <sys/shm.h>
-#include <sys/stat.h>
-#include <fcntl.h>
-#include <unistd.h>
-#endif //QT_NO_SHAREDMEMORY
-
-#include "private/qcore_unix_p.h"
-
-#ifndef QT_NO_SHAREDMEMORY
-QT_BEGIN_NAMESPACE
-
-/*!
- \internal
-
- If not already made create the handle used for accessing the shared memory.
-*/
-key_t QSharedMemoryPrivate::handle()
-{
- // already made
- if (unix_key)
- return unix_key;
-
- // don't allow making handles on empty keys
- if (nativeKey.isEmpty()) {
- errorString = QSharedMemory::tr("%1: key is empty").arg(QLatin1String("QSharedMemory::handle:"));
- error = QSharedMemory::KeyError;
- return 0;
- }
-
- // ftok requires that an actual file exists somewhere
- if (!QFile::exists(nativeKey)) {
- errorString = QSharedMemory::tr("%1: UNIX key file doesn't exist").arg(QLatin1String("QSharedMemory::handle:"));
- error = QSharedMemory::NotFound;
- return 0;
- }
-
- unix_key = ftok(QFile::encodeName(nativeKey).constData(), 'Q');
- if (-1 == unix_key) {
- errorString = QSharedMemory::tr("%1: ftok failed").arg(QLatin1String("QSharedMemory::handle:"));
- error = QSharedMemory::KeyError;
- unix_key = 0;
- }
- return unix_key;
-}
-
-#endif // QT_NO_SHAREDMEMORY
-
-#if !(defined(QT_NO_SHAREDMEMORY) && defined(QT_NO_SYSTEMSEMAPHORE))
-/*!
- \internal
- Creates the unix file if needed.
- returns \c true if the unix file was created.
-
- -1 error
- 0 already existed
- 1 created
- */
-int QT_PREPEND_NAMESPACE(QSharedMemoryPrivate)::createUnixKeyFile(const QString &fileName)
-{
- int fd = qt_safe_open(QFile::encodeName(fileName).constData(),
- O_EXCL | O_CREAT | O_RDWR, 0640);
- if (-1 == fd) {
- if (errno == EEXIST)
- return 0;
- return -1;
- } else {
- close(fd);
- }
- return 1;
-}
-#endif // QT_NO_SHAREDMEMORY && QT_NO_SYSTEMSEMAPHORE
-
-#ifndef QT_NO_SHAREDMEMORY
-
-bool QSharedMemoryPrivate::cleanHandle()
-{
- unix_key = 0;
- return true;
-}
-
-bool QSharedMemoryPrivate::create(qsizetype size)
-{
- // build file if needed
- bool createdFile = false;
- int built = createUnixKeyFile(nativeKey);
- if (built == -1) {
- errorString = QSharedMemory::tr("%1: unable to make key").arg(QLatin1String("QSharedMemory::handle:"));
- error = QSharedMemory::KeyError;
- return false;
- }
- if (built == 1) {
- createdFile = true;
- }
-
- // get handle
- if (!handle()) {
- if (createdFile)
- QFile::remove(nativeKey);
- return false;
- }
-
- // create
- if (-1 == shmget(unix_key, size_t(size), 0600 | IPC_CREAT | IPC_EXCL)) {
- const QLatin1String function("QSharedMemory::create");
- switch (errno) {
- case EINVAL:
- errorString = QSharedMemory::tr("%1: system-imposed size restrictions").arg(QLatin1String("QSharedMemory::handle"));
- error = QSharedMemory::InvalidSize;
- break;
- default:
- setErrorString(function);
- }
- if (createdFile && error != QSharedMemory::AlreadyExists)
- QFile::remove(nativeKey);
- return false;
- }
-
- return true;
-}
-
-bool QSharedMemoryPrivate::attach(QSharedMemory::AccessMode mode)
-{
- // grab the shared memory segment id
- int id = shmget(unix_key, 0, (mode == QSharedMemory::ReadOnly ? 0400 : 0600));
- if (-1 == id) {
- setErrorString(QLatin1String("QSharedMemory::attach (shmget)"));
- return false;
- }
-
- // grab the memory
- memory = shmat(id, nullptr, (mode == QSharedMemory::ReadOnly ? SHM_RDONLY : 0));
- if ((void *)-1 == memory) {
- memory = nullptr;
- setErrorString(QLatin1String("QSharedMemory::attach (shmat)"));
- return false;
- }
-
- // grab the size
- shmid_ds shmid_ds;
- if (!shmctl(id, IPC_STAT, &shmid_ds)) {
- size = (qsizetype)shmid_ds.shm_segsz;
- } else {
- setErrorString(QLatin1String("QSharedMemory::attach (shmctl)"));
- return false;
- }
-
- return true;
-}
-
-bool QSharedMemoryPrivate::detach()
-{
- // detach from the memory segment
- if (-1 == shmdt(memory)) {
- const QLatin1String function("QSharedMemory::detach");
- switch (errno) {
- case EINVAL:
- errorString = QSharedMemory::tr("%1: not attached").arg(function);
- error = QSharedMemory::NotFound;
- break;
- default:
- setErrorString(function);
- }
- return false;
- }
- memory = nullptr;
- size = 0;
-
- // Get the number of current attachments
- int id = shmget(unix_key, 0, 0400);
- cleanHandle();
-
- struct shmid_ds shmid_ds;
- if (0 != shmctl(id, IPC_STAT, &shmid_ds)) {
- switch (errno) {
- case EINVAL:
- return true;
- default:
- return false;
- }
- }
- // If there are no attachments then remove it.
- if (shmid_ds.shm_nattch == 0) {
- // mark for removal
- if (-1 == shmctl(id, IPC_RMID, &shmid_ds)) {
- setErrorString(QLatin1String("QSharedMemory::remove"));
- switch (errno) {
- case EINVAL:
- return true;
- default:
- return false;
- }
- }
-
- // remove file
- if (!QFile::remove(nativeKey))
- return false;
- }
- return true;
-}
-
-
-QT_END_NAMESPACE
-
-#endif // QT_NO_SHAREDMEMORY
-
-#endif // QT_POSIX_IPC
diff --git a/src/corelib/kernel/qsharedmemory_unix.cpp b/src/corelib/kernel/qsharedmemory_unix.cpp
deleted file mode 100644
index bc0f3b03ca..0000000000
--- a/src/corelib/kernel/qsharedmemory_unix.cpp
+++ /dev/null
@@ -1,116 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtCore module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#include "qplatformdefs.h"
-
-#include "qsharedmemory.h"
-#include "qsharedmemory_p.h"
-#include "qsystemsemaphore.h"
-#include <qdebug.h>
-
-#include <errno.h>
-
-#ifndef QT_NO_SHAREDMEMORY
-#include <sys/types.h>
-#ifndef QT_POSIX_IPC
-#include <sys/ipc.h>
-#include <sys/shm.h>
-#else
-#include <sys/mman.h>
-#endif
-#include <sys/stat.h>
-#include <fcntl.h>
-#include <unistd.h>
-#endif //QT_NO_SHAREDMEMORY
-
-#include "private/qcore_unix_p.h"
-
-#ifndef QT_NO_SHAREDMEMORY
-QT_BEGIN_NAMESPACE
-
-QSharedMemoryPrivate::QSharedMemoryPrivate() :
-#ifndef QT_NO_QOBJECT
- QObjectPrivate(),
-#endif
- memory(nullptr), size(0), error(QSharedMemory::NoError),
-#ifndef QT_NO_SYSTEMSEMAPHORE
- systemSemaphore(QString()), lockedByMe(false),
-#endif
-#ifndef QT_POSIX_IPC
- unix_key(0)
-#else
- hand(-1)
-#endif
-{
-}
-
-void QSharedMemoryPrivate::setErrorString(QLatin1String function)
-{
- // EINVAL is handled in functions so they can give better error strings
- switch (errno) {
- case EACCES:
- errorString = QSharedMemory::tr("%1: permission denied").arg(function);
- error = QSharedMemory::PermissionDenied;
- break;
- case EEXIST:
- errorString = QSharedMemory::tr("%1: already exists").arg(function);
- error = QSharedMemory::AlreadyExists;
- break;
- case ENOENT:
- errorString = QSharedMemory::tr("%1: doesn't exist").arg(function);
- error = QSharedMemory::NotFound;
- break;
- case EMFILE:
- case ENOMEM:
- case ENOSPC:
- errorString = QSharedMemory::tr("%1: out of resources").arg(function);
- error = QSharedMemory::OutOfResources;
- break;
- default:
- errorString = QSharedMemory::tr("%1: unknown error %2").arg(function).arg(errno);
- error = QSharedMemory::UnknownError;
-#if defined QSHAREDMEMORY_DEBUG
- qDebug() << errorString << "key" << key << "errno" << errno << EINVAL;
-#endif
- }
-}
-
-QT_END_NAMESPACE
-
-#endif // QT_NO_SHAREDMEMORY
diff --git a/src/corelib/kernel/qsharedmemory_win.cpp b/src/corelib/kernel/qsharedmemory_win.cpp
deleted file mode 100644
index 30ae5fb275..0000000000
--- a/src/corelib/kernel/qsharedmemory_win.cpp
+++ /dev/null
@@ -1,191 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtCore module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#include "qsharedmemory.h"
-#include "qsharedmemory_p.h"
-#include "qsystemsemaphore.h"
-#include <qdebug.h>
-#include <qt_windows.h>
-
-QT_BEGIN_NAMESPACE
-
-#ifndef QT_NO_SHAREDMEMORY
-
-QSharedMemoryPrivate::QSharedMemoryPrivate() :
-#ifndef QT_NO_QOBJECT
- QObjectPrivate(),
-#endif
- memory(0), size(0), error(QSharedMemory::NoError),
- systemSemaphore(QString()), lockedByMe(false), hand(0)
-{
-}
-
-void QSharedMemoryPrivate::setErrorString(QLatin1String function)
-{
- DWORD windowsError = GetLastError();
- if (windowsError == 0)
- return;
- switch (windowsError) {
- case ERROR_ALREADY_EXISTS:
- error = QSharedMemory::AlreadyExists;
- errorString = QSharedMemory::tr("%1: already exists").arg(function);
- break;
- case ERROR_FILE_NOT_FOUND:
- error = QSharedMemory::NotFound;
- errorString = QSharedMemory::tr("%1: doesn't exist").arg(function);
- break;
- case ERROR_COMMITMENT_LIMIT:
- error = QSharedMemory::InvalidSize;
- errorString = QSharedMemory::tr("%1: invalid size").arg(function);
- break;
- case ERROR_NO_SYSTEM_RESOURCES:
- case ERROR_NOT_ENOUGH_MEMORY:
- error = QSharedMemory::OutOfResources;
- errorString = QSharedMemory::tr("%1: out of resources").arg(function);
- break;
- case ERROR_ACCESS_DENIED:
- error = QSharedMemory::PermissionDenied;
- errorString = QSharedMemory::tr("%1: permission denied").arg(function);
- break;
- default:
- errorString = QSharedMemory::tr("%1: unknown error %2").arg(function).arg(windowsError);
- error = QSharedMemory::UnknownError;
-#if defined QSHAREDMEMORY_DEBUG
- qDebug() << errorString << "key" << key;
-#endif
- }
-}
-
-HANDLE QSharedMemoryPrivate::handle()
-{
- if (!hand) {
- const QLatin1String function("QSharedMemory::handle");
- if (nativeKey.isEmpty()) {
- error = QSharedMemory::KeyError;
- errorString = QSharedMemory::tr("%1: unable to make key").arg(function);
- return 0;
- }
- hand = OpenFileMapping(FILE_MAP_ALL_ACCESS, false,
- reinterpret_cast<const wchar_t *>(nativeKey.utf16()));
- if (!hand) {
- setErrorString(function);
- return 0;
- }
- }
- return hand;
-}
-
-bool QSharedMemoryPrivate::cleanHandle()
-{
- if (hand != 0 && !CloseHandle(hand)) {
- hand = 0;
- setErrorString(QLatin1String("QSharedMemory::cleanHandle"));
- return false;
- }
- hand = 0;
- return true;
-}
-
-bool QSharedMemoryPrivate::create(qsizetype size)
-{
- const QLatin1String function("QSharedMemory::create");
- if (nativeKey.isEmpty()) {
- error = QSharedMemory::KeyError;
- errorString = QSharedMemory::tr("%1: key error").arg(function);
- return false;
- }
-
- // Create the file mapping.
- DWORD high, low;
- if constexpr (sizeof(qsizetype) == 8)
- high = DWORD(quint64(size) >> 32);
- else
- high = 0;
- low = DWORD(size_t(size) & 0xffffffff);
- hand = CreateFileMapping(INVALID_HANDLE_VALUE, 0, PAGE_READWRITE, high, low,
- reinterpret_cast<const wchar_t *>(nativeKey.utf16()));
- setErrorString(function);
-
- // hand is valid when it already exists unlike unix so explicitly check
- return error != QSharedMemory::AlreadyExists && hand;
-}
-
-bool QSharedMemoryPrivate::attach(QSharedMemory::AccessMode mode)
-{
- // Grab a pointer to the memory block
- int permissions = (mode == QSharedMemory::ReadOnly ? FILE_MAP_READ : FILE_MAP_ALL_ACCESS);
- memory = (void *)MapViewOfFile(handle(), permissions, 0, 0, 0);
- if (0 == memory) {
- setErrorString(QLatin1String("QSharedMemory::attach"));
- cleanHandle();
- return false;
- }
-
- // Grab the size of the memory we have been given (a multiple of 4K on windows)
- MEMORY_BASIC_INFORMATION info;
- if (!VirtualQuery(memory, &info, sizeof(info))) {
- // Windows doesn't set an error code on this one,
- // it should only be a kernel memory error.
- error = QSharedMemory::UnknownError;
- errorString = QSharedMemory::tr("%1: size query failed").arg(QLatin1String("QSharedMemory::attach: "));
- return false;
- }
- size = qsizetype(info.RegionSize);
-
- return true;
-}
-
-bool QSharedMemoryPrivate::detach()
-{
- // umap memory
- if (!UnmapViewOfFile(memory)) {
- setErrorString(QLatin1String("QSharedMemory::detach"));
- return false;
- }
- memory = 0;
- size = 0;
-
- // close handle
- return cleanHandle();
-}
-
-#endif //QT_NO_SHAREDMEMORY
-
-
-QT_END_NAMESPACE
diff --git a/src/corelib/kernel/qsignalmapper.cpp b/src/corelib/kernel/qsignalmapper.cpp
index 0c9b0cd951..65d766db4a 100644
--- a/src/corelib/kernel/qsignalmapper.cpp
+++ b/src/corelib/kernel/qsignalmapper.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtCore module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// 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"
@@ -49,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)
@@ -165,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);
}
/*!
@@ -178,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);
}
/*!
@@ -191,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 e92b275428..af0be52ee5 100644
--- a/src/corelib/kernel/qsignalmapper.h
+++ b/src/corelib/kernel/qsignalmapper.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtCore module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// 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 QSIGNALMAPPER_H
#define QSIGNALMAPPER_H
@@ -74,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..d7e33c5221
--- /dev/null
+++ b/src/corelib/kernel/qsingleshottimer_p.h
@@ -0,0 +1,126 @@
+// 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 <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);
+
+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 8d4052b8f9..4e4cf3666b 100644
--- a/src/corelib/kernel/qsocketnotifier.cpp
+++ b/src/corelib/kernel/qsocketnotifier.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtCore module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// 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
#define BUILDING_QSOCKETNOTIFIER
#include "qsocketnotifier.h"
@@ -52,12 +16,16 @@
#include <private/qthread_p.h>
#include <QtCore/QLoggingCategory>
+#include <QtCore/qpointer.h>
QT_BEGIN_NAMESPACE
Q_DECLARE_LOGGING_CATEGORY(lcSocketNotifierDeprecation)
Q_LOGGING_CATEGORY(lcSocketNotifierDeprecation, "qt.core.socketnotifier_deprecation");
+QT_IMPL_METATYPE_EXTERN_TAGGED(QSocketNotifier::Type, QSocketNotifier_Type)
+QT_IMPL_METATYPE_EXTERN(QSocketDescriptor)
+
class QSocketNotifierPrivate : public QObjectPrivate
{
Q_DECLARE_PUBLIC(QSocketNotifier)
diff --git a/src/corelib/kernel/qsocketnotifier.h b/src/corelib/kernel/qsocketnotifier.h
index 9c806235fc..8288a6b2b5 100644
--- a/src/corelib/kernel/qsocketnotifier.h
+++ b/src/corelib/kernel/qsocketnotifier.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtCore module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// 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 QSOCKETNOTIFIER_H
#define QSOCKETNOTIFIER_H
@@ -137,7 +101,8 @@ private:
};
QT_END_NAMESPACE
-Q_DECLARE_METATYPE(QSocketNotifier::Type)
-Q_DECLARE_METATYPE(QSocketDescriptor)
+
+QT_DECL_METATYPE_EXTERN_TAGGED(QSocketNotifier::Type, QSocketNotifier_Type, Q_CORE_EXPORT)
+QT_DECL_METATYPE_EXTERN(QSocketDescriptor, Q_CORE_EXPORT)
#endif // QSOCKETNOTIFIER_H
diff --git a/src/corelib/kernel/qsystemerror.cpp b/src/corelib/kernel/qsystemerror.cpp
index 4be4874cf1..428ce62984 100644
--- a/src/corelib/kernel/qsystemerror.cpp
+++ b/src/corelib/kernel/qsystemerror.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtCore module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// 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>
#include "qsystemerror_p.h"
@@ -45,6 +9,7 @@
#endif
#ifdef Q_OS_WIN
# include <qt_windows.h>
+# include <comdef.h>
#endif
#ifndef QT_BOOTSTRAPPED
# include <qcoreapplication.h>
@@ -52,6 +17,8 @@
QT_BEGIN_NAMESPACE
+using namespace Qt::StringLiterals;
+
#if !defined(Q_OS_WIN) && QT_CONFIG(thread) && !defined(Q_OS_INTEGRITY) && !defined(Q_OS_QNX) && \
defined(_POSIX_THREAD_SAFE_FUNCTIONS) && _POSIX_VERSION >= 200112L
namespace {
@@ -80,7 +47,7 @@ static QString windowsErrorString(int errorCode)
{
QString ret;
wchar_t *string = nullptr;
- FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER|FORMAT_MESSAGE_FROM_SYSTEM,
+ FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER|FORMAT_MESSAGE_FROM_SYSTEM|FORMAT_MESSAGE_IGNORE_INSERTS,
NULL,
errorCode,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
@@ -92,11 +59,11 @@ static QString windowsErrorString(int errorCode)
if (ret.isEmpty() && errorCode == ERROR_MOD_NOT_FOUND)
ret = QString::fromLatin1("The specified module could not be found.");
- if (ret.endsWith(QLatin1String("\r\n")))
+ if (ret.endsWith("\r\n"_L1))
ret.chop(2);
if (ret.isEmpty())
ret = QString::fromLatin1("Unknown error 0x%1.")
- .arg(unsigned(errorCode), 8, 16, QLatin1Char('0'));
+ .arg(unsigned(errorCode), 8, 16, '0'_L1);
return ret;
}
#endif
@@ -152,7 +119,7 @@ QString QSystemError::string(ErrorScope errorScope, int errorCode)
qWarning("invalid error scope");
Q_FALLTHROUGH();
case NoError:
- return QLatin1String("No error");
+ return u"No error"_s;
}
}
@@ -167,6 +134,15 @@ QString QSystemError::windowsString(int errorCode)
return windowsErrorString(errorCode == -1 ? GetLastError() : errorCode);
}
+QString QSystemError::windowsComString(HRESULT hr)
+{
+ const _com_error comError(hr);
+ QString result = "COM error 0x"_L1 + QString::number(ulong(hr), 16);
+ if (const wchar_t *msg = comError.ErrorMessage())
+ result += ": "_L1 + QString::fromWCharArray(msg);
+ return result;
+}
+
QString qt_error_string(int code)
{
return windowsErrorString(code == -1 ? GetLastError() : code);
diff --git a/src/corelib/kernel/qsystemerror_p.h b/src/corelib/kernel/qsystemerror_p.h
index 8a07b5d6aa..72ced63dc5 100644
--- a/src/corelib/kernel/qsystemerror_p.h
+++ b/src/corelib/kernel/qsystemerror_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtCore module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// 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 QSYSTEMERROR_P_H
#define QSYSTEMERROR_P_H
@@ -76,10 +40,19 @@ public:
constexpr ErrorScope scope() const { return errorScope; }
constexpr int error() const { return errorCode; }
+ constexpr bool ok() const noexcept { return errorScope == NoError; }
+ static constexpr QSystemError stdError(int error)
+ { return QSystemError(error, StandardLibraryError); }
+
static Q_CORE_EXPORT QString string(ErrorScope errorScope, int errorCode);
static Q_CORE_EXPORT QString stdString(int errorCode = -1);
#ifdef Q_OS_WIN
static Q_CORE_EXPORT QString windowsString(int errorCode = -1);
+ using HRESULT = long;
+ static Q_CORE_EXPORT QString windowsComString(HRESULT hr);
+
+ static constexpr QSystemError nativeError(int error)
+ { return QSystemError(error, NativeError); }
#endif
// data members
diff --git a/src/corelib/kernel/qsystemsemaphore.cpp b/src/corelib/kernel/qsystemsemaphore.cpp
deleted file mode 100644
index 063363189d..0000000000
--- a/src/corelib/kernel/qsystemsemaphore.cpp
+++ /dev/null
@@ -1,364 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtCore module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#include "qsystemsemaphore.h"
-#include "qsystemsemaphore_p.h"
-#include <qglobal.h>
-
-QT_BEGIN_NAMESPACE
-
-#ifndef QT_NO_SYSTEMSEMAPHORE
-
-/*!
- \class QSystemSemaphore
- \inmodule QtCore
- \since 4.4
-
- \brief The QSystemSemaphore class provides a general counting system semaphore.
-
- A semaphore is a generalization of a mutex. While a mutex can be
- locked only once, a semaphore can be acquired multiple times.
- Typically, a semaphore is used to protect a certain number of
- identical resources.
-
- Like its lighter counterpart QSemaphore, a QSystemSemaphore can be
- accessed from multiple \l {QThread} {threads}. Unlike QSemaphore, a
- QSystemSemaphore can also be accessed from multiple \l {QProcess}
- {processes}. This means QSystemSemaphore is a much heavier class, so
- if your application doesn't need to access your semaphores across
- multiple processes, you will probably want to use QSemaphore.
-
- Semaphores support two fundamental operations, acquire() and release():
-
- acquire() tries to acquire one resource. If there isn't a resource
- available, the call blocks until a resource becomes available. Then
- the resource is acquired and the call returns.
-
- release() releases one resource so it can be acquired by another
- process. The function can also be called with a parameter n > 1,
- which releases n resources.
-
- A system semaphore is created with a string key that other processes
- can use to use the same semaphore.
-
- Example: Create a system semaphore
- \snippet code/src_corelib_kernel_qsystemsemaphore.cpp 0
-
- A typical application of system semaphores is for controlling access
- to a circular buffer shared by a producer process and a consumer
- processes.
-
- \section1 Platform-Specific Behavior
-
- When using this class, be aware of the following platform
- differences:
-
- \b{Windows:} QSystemSemaphore does not own its underlying system
- semaphore. Windows owns it. This means that when all instances of
- QSystemSemaphore for a particular key have been destroyed, either by
- having their destructors called, or because one or more processes
- crash, Windows removes the underlying system semaphore.
-
- \b{Unix:}
-
- \list
- \li QSystemSemaphore owns the underlying system semaphore
- in Unix systems. This means that the last process having an instance of
- QSystemSemaphore for a particular key must remove the underlying
- system semaphore in its destructor. If the last process crashes
- without running the QSystemSemaphore destructor, Unix does not
- automatically remove the underlying system semaphore, and the
- semaphore survives the crash. A subsequent process that constructs a
- QSystemSemaphore with the same key will then be given the existing
- system semaphore. In that case, if the QSystemSemaphore constructor
- has specified its \l {QSystemSemaphore::AccessMode} {access mode} as
- \l {QSystemSemaphore::} {Open}, its initial resource count will not
- be reset to the one provided but remain set to the value it received
- in the crashed process. To protect against this, the first process
- to create a semaphore for a particular key (usually a server), must
- pass its \l {QSystemSemaphore::AccessMode} {access mode} as \l
- {QSystemSemaphore::} {Create}, which will force Unix to reset the
- resource count in the underlying system semaphore.
-
- \li When a process using QSystemSemaphore terminates for
- any reason, Unix automatically reverses the effect of all acquire
- operations that were not released. Thus if the process acquires a
- resource and then exits without releasing it, Unix will release that
- resource.
-
- \endlist
-
- \b{Apple platforms:} Sandboxed applications (including apps
- shipped through the Apple App Store) require the key to
- be in the form \c {<application group identifier>/<custom identifier>},
- as documented \l {https://developer.apple.com/library/archive/documentation/Security/Conceptual/AppSandboxDesignGuide/AppSandboxInDepth/AppSandboxInDepth.html#//apple_ref/doc/uid/TP40011183-CH3-SW24}
- {here} and \l {https://developer.apple.com/documentation/bundleresources/entitlements/com_apple_security_application-groups}
- {here}, and the key length is limited to 30 characters.
-
- \sa QSharedMemory, QSemaphore
- */
-
-/*!
- Requests a system semaphore for the specified \a key. The parameters
- \a initialValue and \a mode are used according to the following
- rules, which are system dependent.
-
- In Unix, if the \a mode is \l {QSystemSemaphore::} {Open} and the
- system already has a semaphore identified by \a key, that semaphore
- is used, and the semaphore's resource count is not changed, i.e., \a
- initialValue is ignored. But if the system does not already have a
- semaphore identified by \a key, it creates a new semaphore for that
- key and sets its resource count to \a initialValue.
-
- In Unix, if the \a mode is \l {QSystemSemaphore::} {Create} and the
- system already has a semaphore identified by \a key, that semaphore
- is used, and its resource count is set to \a initialValue. If the
- system does not already have a semaphore identified by \a key, it
- creates a new semaphore for that key and sets its resource count to
- \a initialValue.
-
- In Windows, \a mode is ignored, and the system always tries to
- create a semaphore for the specified \a key. If the system does not
- already have a semaphore identified as \a key, it creates the
- semaphore and sets its resource count to \a initialValue. But if the
- system already has a semaphore identified as \a key it uses that
- semaphore and ignores \a initialValue.
-
- The \l {QSystemSemaphore::AccessMode} {mode} parameter is only used
- in Unix systems to handle the case where a semaphore survives a
- process crash. In that case, the next process to allocate a
- semaphore with the same \a key will get the semaphore that survived
- the crash, and unless \a mode is \l {QSystemSemaphore::} {Create},
- the resource count will not be reset to \a initialValue but will
- retain the initial value it had been given by the crashed process.
-
- \sa acquire(), key()
- */
-QSystemSemaphore::QSystemSemaphore(const QString &key, int initialValue, AccessMode mode)
- : d(new QSystemSemaphorePrivate)
-{
- setKey(key, initialValue, mode);
-}
-
-/*!
- The destructor destroys the QSystemSemaphore object, but the
- underlying system semaphore is not removed from the system unless
- this instance of QSystemSemaphore is the last one existing for that
- system semaphore.
-
- Two important side effects of the destructor depend on the system.
- In Windows, if acquire() has been called for this semaphore but not
- release(), release() will not be called by the destructor, nor will
- the resource be released when the process exits normally. This would
- be a program bug which could be the cause of a deadlock in another
- process trying to acquire the same resource. In Unix, acquired
- resources that are not released before the destructor is called are
- automatically released when the process exits.
-*/
-QSystemSemaphore::~QSystemSemaphore()
-{
- d->cleanHandle();
-}
-
-/*!
- \enum QSystemSemaphore::AccessMode
-
- This enum is used by the constructor and setKey(). Its purpose is to
- enable handling the problem in Unix implementations of semaphores
- that survive a crash. In Unix, when a semaphore survives a crash, we
- need a way to force it to reset its resource count, when the system
- reuses the semaphore. In Windows, where semaphores can't survive a
- crash, this enum has no effect.
-
- \value Open If the semaphore already exists, its initial resource
- count is not reset. If the semaphore does not already exist, it is
- created and its initial resource count set.
-
- \value Create QSystemSemaphore takes ownership of the semaphore and
- sets its resource count to the requested value, regardless of
- whether the semaphore already exists by having survived a crash.
- This value should be passed to the constructor, when the first
- semaphore for a particular key is constructed and you know that if
- the semaphore already exists it could only be because of a crash. In
- Windows, where a semaphore can't survive a crash, Create and Open
- have the same behavior.
-*/
-
-/*!
- This function works the same as the constructor. It reconstructs
- this QSystemSemaphore object. If the new \a key is different from
- the old key, calling this function is like calling the destructor of
- the semaphore with the old key, then calling the constructor to
- create a new semaphore with the new \a key. The \a initialValue and
- \a mode parameters are as defined for the constructor.
-
- \sa QSystemSemaphore(), key()
- */
-void QSystemSemaphore::setKey(const QString &key, int initialValue, AccessMode mode)
-{
- if (key == d->key && mode == Open)
- return;
- d->clearError();
-#if !defined(Q_OS_WIN) && !defined(QT_POSIX_IPC)
- // optimization to not destroy/create the file & semaphore
- if (key == d->key && mode == Create && d->createdSemaphore && d->createdFile) {
- d->initialValue = initialValue;
- d->unix_key = -1;
- d->handle(mode);
- return;
- }
-#endif
- d->cleanHandle();
- d->key = key;
- d->initialValue = initialValue;
- // cache the file name so it doesn't have to be generated all the time.
- d->fileName = d->makeKeyFileName();
- d->handle(mode);
-}
-
-/*!
- Returns the key assigned to this system semaphore. The key is the
- name by which the semaphore can be accessed from other processes.
-
- \sa setKey()
- */
-QString QSystemSemaphore::key() const
-{
- return d->key;
-}
-
-/*!
- Acquires one of the resources guarded by this semaphore, if there is
- one available, and returns \c true. If all the resources guarded by this
- semaphore have already been acquired, the call blocks until one of
- them is released by another process or thread having a semaphore
- with the same key.
-
- If false is returned, a system error has occurred. Call error()
- to get a value of QSystemSemaphore::SystemSemaphoreError that
- indicates which error occurred.
-
- \sa release()
- */
-bool QSystemSemaphore::acquire()
-{
- return d->modifySemaphore(-1);
-}
-
-/*!
- Releases \a n resources guarded by the semaphore. Returns \c true
- unless there is a system error.
-
- Example: Create a system semaphore having five resources; acquire
- them all and then release them all.
-
- \snippet code/src_corelib_kernel_qsystemsemaphore.cpp 1
-
- This function can also "create" resources. For example, immediately
- following the sequence of statements above, suppose we add the
- statement:
-
- \snippet code/src_corelib_kernel_qsystemsemaphore.cpp 2
-
- Ten new resources are now guarded by the semaphore, in addition to
- the five that already existed. You would not normally use this
- function to create more resources.
-
- \sa acquire()
- */
-bool QSystemSemaphore::release(int n)
-{
- if (n == 0)
- return true;
- if (n < 0) {
- qWarning("QSystemSemaphore::release: n is negative.");
- return false;
- }
- return d->modifySemaphore(n);
-}
-
-/*!
- Returns a value indicating whether an error occurred, and, if so,
- which error it was.
-
- \sa errorString()
- */
-QSystemSemaphore::SystemSemaphoreError QSystemSemaphore::error() const
-{
- return d->error;
-}
-
-/*!
- \enum QSystemSemaphore::SystemSemaphoreError
-
- \value NoError No error occurred.
-
- \value PermissionDenied The operation failed because the caller
- didn't have the required permissions.
-
- \value KeyError The operation failed because of an invalid key.
-
- \value AlreadyExists The operation failed because a system
- semaphore with the specified key already existed.
-
- \value NotFound The operation failed because a system semaphore
- with the specified key could not be found.
-
- \value OutOfResources The operation failed because there was
- not enough memory available to fill the request.
-
- \value UnknownError Something else happened and it was bad.
-*/
-
-/*!
- Returns a text description of the last error that occurred. If
- error() returns an \l {QSystemSemaphore::SystemSemaphoreError} {error
- value}, call this function to get a text string that describes the
- error.
-
- \sa error()
- */
-QString QSystemSemaphore::errorString() const
-{
- return d->errorString;
-}
-
-#endif // QT_NO_SYSTEMSEMAPHORE
-
-QT_END_NAMESPACE
diff --git a/src/corelib/kernel/qsystemsemaphore.h b/src/corelib/kernel/qsystemsemaphore.h
deleted file mode 100644
index 262d089e04..0000000000
--- a/src/corelib/kernel/qsystemsemaphore.h
+++ /dev/null
@@ -1,97 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtCore module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#ifndef QSYSTEMSEMAPHORE_H
-#define QSYSTEMSEMAPHORE_H
-
-#include <QtCore/qcoreapplication.h>
-#include <QtCore/qstring.h>
-#include <QtCore/qscopedpointer.h>
-
-QT_BEGIN_NAMESPACE
-
-
-#ifndef QT_NO_SYSTEMSEMAPHORE
-
-class QSystemSemaphorePrivate;
-
-class Q_CORE_EXPORT QSystemSemaphore
-{
- Q_DECLARE_TR_FUNCTIONS(QSystemSemaphore)
-public:
- enum AccessMode
- {
- Open,
- Create
- };
-
- enum SystemSemaphoreError
- {
- NoError,
- PermissionDenied,
- KeyError,
- AlreadyExists,
- NotFound,
- OutOfResources,
- UnknownError
- };
-
- QSystemSemaphore(const QString &key, int initialValue = 0, AccessMode mode = Open);
- ~QSystemSemaphore();
-
- void setKey(const QString &key, int initialValue = 0, AccessMode mode = Open);
- QString key() const;
-
- bool acquire();
- bool release(int n = 1);
-
- SystemSemaphoreError error() const;
- QString errorString() const;
-
-private:
- Q_DISABLE_COPY(QSystemSemaphore)
- QScopedPointer<QSystemSemaphorePrivate> d;
-};
-
-#endif // QT_NO_SYSTEMSEMAPHORE
-
-QT_END_NAMESPACE
-
-#endif // QSYSTEMSEMAPHORE_H
-
diff --git a/src/corelib/kernel/qsystemsemaphore_android.cpp b/src/corelib/kernel/qsystemsemaphore_android.cpp
deleted file mode 100644
index 149215bc30..0000000000
--- a/src/corelib/kernel/qsystemsemaphore_android.cpp
+++ /dev/null
@@ -1,83 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2012 Collabora Ltd, author <robin.burchell@collabora.co.uk>
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtCore module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#include "qsystemsemaphore.h"
-#include "qsystemsemaphore_p.h"
-
-#include <qdebug.h>
-
-#ifndef QT_NO_SYSTEMSEMAPHORE
-
-QT_BEGIN_NAMESPACE
-
-QSystemSemaphorePrivate::QSystemSemaphorePrivate() :
- unix_key(-1), semaphore(-1), createdFile(false),
- createdSemaphore(false), error(QSystemSemaphore::NoError)
-{
-}
-
-void QSystemSemaphorePrivate::setErrorString(const QString &function)
-{
- Q_UNUSED(function);
- Q_UNIMPLEMENTED();
-}
-
-key_t QSystemSemaphorePrivate::handle(QSystemSemaphore::AccessMode mode)
-{
- Q_UNUSED(mode);
- Q_UNIMPLEMENTED();
- return -1;
-}
-
-void QSystemSemaphorePrivate::cleanHandle()
-{
- Q_UNIMPLEMENTED();
-}
-
-bool QSystemSemaphorePrivate::modifySemaphore(int count)
-{
- Q_UNUSED(count);
- Q_UNIMPLEMENTED();
- return false;
-}
-
-
-QT_END_NAMESPACE
-
-#endif // QT_NO_SYSTEMSEMAPHORE
diff --git a/src/corelib/kernel/qsystemsemaphore_p.h b/src/corelib/kernel/qsystemsemaphore_p.h
deleted file mode 100644
index 56619d7318..0000000000
--- a/src/corelib/kernel/qsystemsemaphore_p.h
+++ /dev/null
@@ -1,120 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtCore module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#ifndef QSYSTEMSEMAPHORE_P_H
-#define QSYSTEMSEMAPHORE_P_H
-
-//
-// W A R N I N G
-// -------------
-//
-// This file is not part of the Qt API. It exists purely as an
-// implementation detail. This header file may change from version to
-// version without notice, or even be removed.
-//
-// We mean it.
-//
-
-#include "qsystemsemaphore.h"
-
-#ifndef QT_NO_SYSTEMSEMAPHORE
-
-#include "qcoreapplication.h"
-#include "qsharedmemory_p.h"
-#include <sys/types.h>
-#ifdef QT_POSIX_IPC
-# include <semaphore.h>
-#endif
-
-QT_BEGIN_NAMESPACE
-
-class QSystemSemaphorePrivate
-{
-
-public:
- QSystemSemaphorePrivate();
-
- QString makeKeyFileName()
- {
- return QSharedMemoryPrivate::makePlatformSafeKey(key, QLatin1String("qipc_systemsem_"));
- }
-
- inline void setError(QSystemSemaphore::SystemSemaphoreError e, const QString &message)
- { error = e; errorString = message; }
- inline void clearError()
- { setError(QSystemSemaphore::NoError, QString()); }
-
-#ifdef Q_OS_WIN
- Qt::HANDLE handle(QSystemSemaphore::AccessMode mode = QSystemSemaphore::Open);
- void setErrorString(const QString &function);
-#elif defined(QT_POSIX_IPC)
- bool handle(QSystemSemaphore::AccessMode mode = QSystemSemaphore::Open);
- void setErrorString(const QString &function);
-#else
- key_t handle(QSystemSemaphore::AccessMode mode = QSystemSemaphore::Open);
- void setErrorString(const QString &function);
-#endif
- void cleanHandle();
- bool modifySemaphore(int count);
-
- QString key;
- QString fileName;
- int initialValue;
-#ifdef Q_OS_WIN
- Qt::HANDLE semaphore;
- Qt::HANDLE semaphoreLock;
-#elif defined(QT_POSIX_IPC)
- sem_t *semaphore;
- bool createdSemaphore;
-#else
- key_t unix_key;
- int semaphore;
- bool createdFile;
- bool createdSemaphore;
-#endif
- QString errorString;
- QSystemSemaphore::SystemSemaphoreError error;
-};
-
-QT_END_NAMESPACE
-
-#endif // QT_NO_SYSTEMSEMAPHORE
-
-#endif // QSYSTEMSEMAPHORE_P_H
-
diff --git a/src/corelib/kernel/qsystemsemaphore_posix.cpp b/src/corelib/kernel/qsystemsemaphore_posix.cpp
deleted file mode 100644
index 567e154e0d..0000000000
--- a/src/corelib/kernel/qsystemsemaphore_posix.cpp
+++ /dev/null
@@ -1,186 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2015 Konstantin Ritt <ritt.ks@gmail.com>
-** Copyright (C) 2016 The Qt Company Ltd.
-** Copyright (C) 2015 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Tobias Koenig <tobias.koenig@kdab.com>
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtCore module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#include "qsystemsemaphore.h"
-#include "qsystemsemaphore_p.h"
-
-#include <qdebug.h>
-#include <qfile.h>
-#include <qcoreapplication.h>
-
-#ifdef QT_POSIX_IPC
-
-#ifndef QT_NO_SYSTEMSEMAPHORE
-
-#include <sys/types.h>
-#include <fcntl.h>
-#include <errno.h>
-
-#include "private/qcore_unix_p.h"
-
-// OpenBSD 4.2 doesn't define EIDRM, see BUGS section:
-// http://www.openbsd.org/cgi-bin/man.cgi?query=semop&manpath=OpenBSD+4.2
-#if defined(Q_OS_OPENBSD) && !defined(EIDRM)
-#define EIDRM EINVAL
-#endif
-
-QT_BEGIN_NAMESPACE
-
-bool QSystemSemaphorePrivate::handle(QSystemSemaphore::AccessMode mode)
-{
- if (semaphore != SEM_FAILED)
- return true; // we already have a semaphore
-
- if (fileName.isEmpty()) {
- errorString = QSystemSemaphore::tr("%1: key is empty").arg(QLatin1String("QSystemSemaphore::handle"));
- error = QSystemSemaphore::KeyError;
- return false;
- }
-
- const QByteArray semName = QFile::encodeName(fileName);
-
- // Always try with O_EXCL so we know whether we created the semaphore.
- int oflag = O_CREAT | O_EXCL;
- for (int tryNum = 0, maxTries = 1; tryNum < maxTries; ++tryNum) {
- do {
- semaphore = ::sem_open(semName.constData(), oflag, 0600, initialValue);
- } while (semaphore == SEM_FAILED && errno == EINTR);
- if (semaphore == SEM_FAILED && errno == EEXIST) {
- if (mode == QSystemSemaphore::Create) {
- if (::sem_unlink(semName.constData()) == -1 && errno != ENOENT) {
- setErrorString(QLatin1String("QSystemSemaphore::handle (sem_unlink)"));
- return false;
- }
- // Race condition: the semaphore might be recreated before
- // we call sem_open again, so we'll retry several times.
- maxTries = 3;
- } else {
- // Race condition: if it no longer exists at the next sem_open
- // call, we won't realize we created it, so we'll leak it later.
- oflag &= ~O_EXCL;
- maxTries = 2;
- }
- } else {
- break;
- }
- }
-
- if (semaphore == SEM_FAILED) {
- setErrorString(QLatin1String("QSystemSemaphore::handle"));
- return false;
- }
-
- createdSemaphore = (oflag & O_EXCL) != 0;
-
- return true;
-}
-
-void QSystemSemaphorePrivate::cleanHandle()
-{
- if (semaphore != SEM_FAILED) {
- if (::sem_close(semaphore) == -1) {
- setErrorString(QLatin1String("QSystemSemaphore::cleanHandle (sem_close)"));
-#if defined QSYSTEMSEMAPHORE_DEBUG
- qDebug("QSystemSemaphore::cleanHandle sem_close failed.");
-#endif
- }
- semaphore = SEM_FAILED;
- }
-
- if (createdSemaphore) {
- if (::sem_unlink(QFile::encodeName(fileName).constData()) == -1 && errno != ENOENT) {
- setErrorString(QLatin1String("QSystemSemaphore::cleanHandle (sem_unlink)"));
-#if defined QSYSTEMSEMAPHORE_DEBUG
- qDebug("QSystemSemaphore::cleanHandle sem_unlink failed.");
-#endif
- }
- createdSemaphore = false;
- }
-}
-
-bool QSystemSemaphorePrivate::modifySemaphore(int count)
-{
- if (!handle())
- return false;
-
- if (count > 0) {
- int cnt = count;
- do {
- if (::sem_post(semaphore) == -1) {
- setErrorString(QLatin1String("QSystemSemaphore::modifySemaphore (sem_post)"));
-#if defined QSYSTEMSEMAPHORE_DEBUG
- qDebug("QSystemSemaphore::modify sem_post failed %d %d", count, errno);
-#endif
- // rollback changes to preserve the SysV semaphore behavior
- for ( ; cnt < count; ++cnt) {
- int res;
- EINTR_LOOP(res, ::sem_wait(semaphore));
- }
- return false;
- }
- --cnt;
- } while (cnt > 0);
- } else {
- int res;
- EINTR_LOOP(res, ::sem_wait(semaphore));
- if (res == -1) {
- // If the semaphore was removed be nice and create it and then modifySemaphore again
- if (errno == EINVAL || errno == EIDRM) {
- semaphore = SEM_FAILED;
- return modifySemaphore(count);
- }
- setErrorString(QLatin1String("QSystemSemaphore::modifySemaphore (sem_wait)"));
-#if defined QSYSTEMSEMAPHORE_DEBUG
- qDebug("QSystemSemaphore::modify sem_wait failed %d %d", count, errno);
-#endif
- return false;
- }
- }
-
- clearError();
- return true;
-}
-
-QT_END_NAMESPACE
-
-#endif // QT_NO_SYSTEMSEMAPHORE
-
-#endif // QT_POSIX_IPC
diff --git a/src/corelib/kernel/qsystemsemaphore_systemv.cpp b/src/corelib/kernel/qsystemsemaphore_systemv.cpp
deleted file mode 100644
index 1c9fc1f2f7..0000000000
--- a/src/corelib/kernel/qsystemsemaphore_systemv.cpp
+++ /dev/null
@@ -1,224 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtCore module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#include "qsystemsemaphore.h"
-#include "qsystemsemaphore_p.h"
-
-#include <qdebug.h>
-#include <qfile.h>
-#include <qcoreapplication.h>
-
-#ifndef QT_POSIX_IPC
-
-#ifndef QT_NO_SYSTEMSEMAPHORE
-
-#include <sys/types.h>
-#include <sys/ipc.h>
-#include <sys/sem.h>
-#include <fcntl.h>
-#include <errno.h>
-
-#if defined(Q_OS_DARWIN)
-#include "qcore_mac_p.h"
-#endif
-
-#include "private/qcore_unix_p.h"
-
-// OpenBSD 4.2 doesn't define EIDRM, see BUGS section:
-// http://www.openbsd.org/cgi-bin/man.cgi?query=semop&manpath=OpenBSD+4.2
-#if defined(Q_OS_OPENBSD) && !defined(EIDRM)
-#define EIDRM EINVAL
-#endif
-
-QT_BEGIN_NAMESPACE
-
-/*!
- \internal
-
- Setup unix_key
- */
-key_t QSystemSemaphorePrivate::handle(QSystemSemaphore::AccessMode mode)
-{
-#if defined(Q_OS_DARWIN)
- if (qt_apple_isSandboxed()) {
- errorString = QSystemSemaphore::tr("%1: System V semaphores are not available " \
- "for sandboxed applications. Please build Qt with -feature-ipc_posix")
- .arg(QLatin1String("QSystemSemaphore::handle:"));
- error = QSystemSemaphore::PermissionDenied;
- return -1;
- }
-#endif
-
- if (key.isEmpty()){
- errorString = QSystemSemaphore::tr("%1: key is empty")
- .arg(QLatin1String("QSystemSemaphore::handle:"));
- error = QSystemSemaphore::KeyError;
- return -1;
- }
-
- // ftok requires that an actual file exists somewhere
- if (-1 != unix_key)
- return unix_key;
-
- // Create the file needed for ftok
- int built = QSharedMemoryPrivate::createUnixKeyFile(fileName);
- if (-1 == built) {
- errorString = QSystemSemaphore::tr("%1: unable to make key")
- .arg(QLatin1String("QSystemSemaphore::handle:"));
- error = QSystemSemaphore::KeyError;
- return -1;
- }
- createdFile = (1 == built);
-
-#if !defined(QT_NO_SHAREDMEMORY) && !defined(QT_POSIX_IPC) && !defined(Q_OS_ANDROID)
- // Get the unix key for the created file
- unix_key = ftok(QFile::encodeName(fileName).constData(), 'Q');
-#endif
- if (-1 == unix_key) {
- errorString = QSystemSemaphore::tr("%1: ftok failed")
- .arg(QLatin1String("QSystemSemaphore::handle:"));
- error = QSystemSemaphore::KeyError;
- return -1;
- }
-
- // Get semaphore
- semaphore = semget(unix_key, 1, 0600 | IPC_CREAT | IPC_EXCL);
- if (-1 == semaphore) {
- if (errno == EEXIST)
- semaphore = semget(unix_key, 1, 0600 | IPC_CREAT);
- if (-1 == semaphore) {
- setErrorString(QLatin1String("QSystemSemaphore::handle"));
- cleanHandle();
- return -1;
- }
- } else {
- createdSemaphore = true;
- // Force cleanup of file, it is possible that it can be left over from a crash
- createdFile = true;
- }
-
- if (mode == QSystemSemaphore::Create) {
- createdSemaphore = true;
- createdFile = true;
- }
-
- // Created semaphore so initialize its value.
- if (createdSemaphore && initialValue >= 0) {
- qt_semun init_op;
- init_op.val = initialValue;
- if (-1 == semctl(semaphore, 0, SETVAL, init_op)) {
- setErrorString(QLatin1String("QSystemSemaphore::handle"));
- cleanHandle();
- return -1;
- }
- }
-
- return unix_key;
-}
-
-/*!
- \internal
-
- Cleanup the unix_key
- */
-void QSystemSemaphorePrivate::cleanHandle()
-{
- unix_key = -1;
-
- // remove the file if we made it
- if (createdFile) {
- QFile::remove(fileName);
- createdFile = false;
- }
-
- if (createdSemaphore) {
- if (-1 != semaphore) {
- if (-1 == semctl(semaphore, 0, IPC_RMID, 0)) {
- setErrorString(QLatin1String("QSystemSemaphore::cleanHandle"));
-#if defined QSYSTEMSEMAPHORE_DEBUG
- qDebug("QSystemSemaphore::cleanHandle semctl failed.");
-#endif
- }
- semaphore = -1;
- }
- createdSemaphore = false;
- }
-}
-
-/*!
- \internal
- */
-bool QSystemSemaphorePrivate::modifySemaphore(int count)
-{
- if (-1 == handle())
- return false;
-
- struct sembuf operation;
- operation.sem_num = 0;
- operation.sem_op = count;
- operation.sem_flg = SEM_UNDO;
-
- int res;
- EINTR_LOOP(res, semop(semaphore, &operation, 1));
- if (-1 == res) {
- // If the semaphore was removed be nice and create it and then modifySemaphore again
- if (errno == EINVAL || errno == EIDRM) {
- semaphore = -1;
- cleanHandle();
- handle();
- return modifySemaphore(count);
- }
- setErrorString(QLatin1String("QSystemSemaphore::modifySemaphore"));
-#if defined QSYSTEMSEMAPHORE_DEBUG
- qDebug("QSystemSemaphore::modify failed %d %d %d %d %d",
- count, int(semctl(semaphore, 0, GETVAL)), int(errno), int(EIDRM), int(EINVAL);
-#endif
- return false;
- }
-
- clearError();
- return true;
-}
-
-
-QT_END_NAMESPACE
-
-#endif // QT_NO_SYSTEMSEMAPHORE
-
-#endif // QT_POSIX_IPC
diff --git a/src/corelib/kernel/qsystemsemaphore_unix.cpp b/src/corelib/kernel/qsystemsemaphore_unix.cpp
deleted file mode 100644
index dad2249270..0000000000
--- a/src/corelib/kernel/qsystemsemaphore_unix.cpp
+++ /dev/null
@@ -1,109 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtCore module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#include "qsystemsemaphore.h"
-#include "qsystemsemaphore_p.h"
-
-#include <qdebug.h>
-#include <qcoreapplication.h>
-
-#ifndef QT_NO_SYSTEMSEMAPHORE
-
-#include <sys/types.h>
-#ifndef QT_POSIX_IPC
-#include <sys/ipc.h>
-#include <sys/sem.h>
-#endif
-#include <fcntl.h>
-#include <errno.h>
-
-#include "private/qcore_unix_p.h"
-
-QT_BEGIN_NAMESPACE
-
-QSystemSemaphorePrivate::QSystemSemaphorePrivate() :
-#ifndef QT_POSIX_IPC
- unix_key(-1), semaphore(-1), createdFile(false),
-#else
- semaphore(SEM_FAILED),
-#endif // QT_POSIX_IPC
- createdSemaphore(false), error(QSystemSemaphore::NoError)
-{
-}
-
-void QSystemSemaphorePrivate::setErrorString(const QString &function)
-{
- // EINVAL is handled in functions so they can give better error strings
- switch (errno) {
- case EPERM:
- case EACCES:
- errorString = QSystemSemaphore::tr("%1: permission denied").arg(function);
- error = QSystemSemaphore::PermissionDenied;
- break;
- case EEXIST:
- errorString = QSystemSemaphore::tr("%1: already exists").arg(function);
- error = QSystemSemaphore::AlreadyExists;
- break;
- case ENOENT:
- errorString = QSystemSemaphore::tr("%1: does not exist").arg(function);
- error = QSystemSemaphore::NotFound;
- break;
- case ERANGE:
- case ENOSPC:
- errorString = QSystemSemaphore::tr("%1: out of resources").arg(function);
- error = QSystemSemaphore::OutOfResources;
- break;
-#if defined(QT_POSIX_IPC)
- case ENAMETOOLONG:
- errorString = QSystemSemaphore::tr("%1: key too long").arg(function);
- error = QSystemSemaphore::KeyError;
- break;
-#endif
- default:
- errorString = QSystemSemaphore::tr("%1: unknown error %2").arg(function).arg(errno);
- error = QSystemSemaphore::UnknownError;
-#if defined QSYSTEMSEMAPHORE_DEBUG
- qDebug() << errorString << "key" << key << "errno" << errno << EINVAL;
-#endif
- }
-}
-
-QT_END_NAMESPACE
-
-#endif // QT_NO_SYSTEMSEMAPHORE
diff --git a/src/corelib/kernel/qsystemsemaphore_win.cpp b/src/corelib/kernel/qsystemsemaphore_win.cpp
deleted file mode 100644
index d19476db4a..0000000000
--- a/src/corelib/kernel/qsystemsemaphore_win.cpp
+++ /dev/null
@@ -1,136 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtCore module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#include "qsystemsemaphore.h"
-#include "qsystemsemaphore_p.h"
-#include "qcoreapplication.h"
-#include <qdebug.h>
-#include <qt_windows.h>
-
-QT_BEGIN_NAMESPACE
-
-#ifndef QT_NO_SYSTEMSEMAPHORE
-
-QSystemSemaphorePrivate::QSystemSemaphorePrivate() :
- semaphore(0), error(QSystemSemaphore::NoError)
-{
-}
-
-void QSystemSemaphorePrivate::setErrorString(const QString &function)
-{
- BOOL windowsError = GetLastError();
- if (windowsError == 0)
- return;
-
- switch (windowsError) {
- case ERROR_NO_SYSTEM_RESOURCES:
- case ERROR_NOT_ENOUGH_MEMORY:
- error = QSystemSemaphore::OutOfResources;
- errorString = QCoreApplication::translate("QSystemSemaphore", "%1: out of resources").arg(function);
- break;
- case ERROR_ACCESS_DENIED:
- error = QSystemSemaphore::PermissionDenied;
- errorString = QCoreApplication::translate("QSystemSemaphore", "%1: permission denied").arg(function);
- break;
- default:
- errorString = QCoreApplication::translate("QSystemSemaphore", "%1: unknown error %2").arg(function).arg(windowsError);
- error = QSystemSemaphore::UnknownError;
-#if defined QSYSTEMSEMAPHORE_DEBUG
- qDebug() << errorString << "key" << key;
-#endif
- }
-}
-
-HANDLE QSystemSemaphorePrivate::handle(QSystemSemaphore::AccessMode)
-{
- // don't allow making handles on empty keys
- if (key.isEmpty())
- return 0;
-
- // Create it if it doesn't already exists.
- if (semaphore == 0) {
- semaphore = CreateSemaphore(0, initialValue, MAXLONG,
- reinterpret_cast<const wchar_t*>(fileName.utf16()));
- if (semaphore == NULL)
- setErrorString(QLatin1String("QSystemSemaphore::handle"));
- }
-
- return semaphore;
-}
-
-void QSystemSemaphorePrivate::cleanHandle()
-{
- if (semaphore && !CloseHandle(semaphore)) {
-#if defined QSYSTEMSEMAPHORE_DEBUG
- qDebug("QSystemSemaphorePrivate::CloseHandle: sem failed");
-#endif
- }
- semaphore = 0;
-}
-
-bool QSystemSemaphorePrivate::modifySemaphore(int count)
-{
- if (0 == handle())
- return false;
-
- if (count > 0) {
- if (0 == ReleaseSemaphore(semaphore, count, 0)) {
- setErrorString(QLatin1String("QSystemSemaphore::modifySemaphore"));
-#if defined QSYSTEMSEMAPHORE_DEBUG
- qDebug("QSystemSemaphore::modifySemaphore ReleaseSemaphore failed");
-#endif
- return false;
- }
- } else {
- if (WAIT_OBJECT_0 != WaitForSingleObjectEx(semaphore, INFINITE, FALSE)) {
- setErrorString(QLatin1String("QSystemSemaphore::modifySemaphore"));
-#if defined QSYSTEMSEMAPHORE_DEBUG
- qDebug("QSystemSemaphore::modifySemaphore WaitForSingleObject failed");
-#endif
- return false;
- }
- }
-
- clearError();
- return true;
-}
-
-#endif //QT_NO_SYSTEMSEMAPHORE
-
-QT_END_NAMESPACE
diff --git a/src/corelib/kernel/qt_attribution.json b/src/corelib/kernel/qt_attribution.json
index 6d8f4f2abc..86ca3a2664 100644
--- a/src/corelib/kernel/qt_attribution.json
+++ b/src/corelib/kernel/qt_attribution.json
@@ -3,9 +3,9 @@
"Name": "QEventDispatcher on macOS",
"QDocModule": "qtcore",
"QtUsage": "Used in Qt Core on macOS.",
- "Path": "qeventdispatcher_cf_p.h",
+ "Files": "qeventdispatcher_cf_p.h",
- "Description": "Treat as final version; no upstream known",
+ "Comment": "Treat as final version; no upstream known",
"Description": "Implementation of QAbstractEventDispatcher for macOS.",
"License": "BSD 3-clause \"New\" or \"Revised\" License",
"LicenseId": "BSD-3-Clause",
diff --git a/src/corelib/kernel/qtestsupport_core.cpp b/src/corelib/kernel/qtestsupport_core.cpp
index f5b6ed18cd..2ac44bb13d 100644
--- a/src/corelib/kernel/qtestsupport_core.cpp
+++ b/src/corelib/kernel/qtestsupport_core.cpp
@@ -1,99 +1,103 @@
-/****************************************************************************
-**
-** Copyright (C) 2018 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtCore module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// 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
#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:
@@ -105,25 +109,34 @@ 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 due to a compiler bug in
- // GCC 6 we can't do that.
+ // Ideally this method would be implemented in terms of qWaitFor(), with a
+ // predicate that always returns false, but qWaitFor() uses the 1-arg overload
+ // of processEvents(), which doesn't handle events posted in this round of event
+ // processing, which, together with the 10ms qSleep() after every processEvents(),
+ // lead to a 10x slow-down in some webengine tests.
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 04e1c50e1c..27265903af 100644
--- a/src/corelib/kernel/qtestsupport_core.h
+++ b/src/corelib/kernel/qtestsupport_core.h
@@ -1,69 +1,32 @@
-/****************************************************************************
-**
-** Copyright (C) 2018 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtCore module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2018 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 QTESTSUPPORT_CORE_H
#define QTESTSUPPORT_CORE_H
#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
@@ -78,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 ddd1213f07..cc46c1433b 100644
--- a/src/corelib/kernel/qtimer.cpp
+++ b/src/corelib/kernel/qtimer.cpp
@@ -1,70 +1,23 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Copyright (C) 2016 Intel Corporation.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtCore module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// 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 "qtimer.h"
+#include "qtimer_p.h"
+#include "qsingleshottimer_p.h"
+
#include "qabstracteventdispatcher.h"
#include "qcoreapplication.h"
-#include "qobject_p.h"
-#include "qthread.h"
#include "qcoreapplication_p.h"
+#include "qdeadlinetimer.h"
+#include "qmetaobject_p.h"
+#include "qobject_p.h"
#include "qproperty_p.h"
+#include "qthread.h"
-QT_BEGIN_NAMESPACE
-
-static constexpr int INV_TIMER = -1; // invalid timer id
+using namespace std::chrono_literals;
-class QTimerPrivate : public QObjectPrivate
-{
- Q_DECLARE_PUBLIC(QTimer)
-public:
- void setInterval(int msec) { q_func()->setInterval(msec); }
- bool isActiveActualCalculation() const { return id >= 0; }
-
- int id = INV_TIMER;
- Q_OBJECT_COMPAT_PROPERTY_WITH_ARGS(QTimerPrivate, int, inter, &QTimerPrivate::setInterval, 0)
- Q_OBJECT_BINDABLE_PROPERTY_WITH_ARGS(QTimerPrivate, bool, single, false)
- bool nulltimer = false;
- Q_OBJECT_BINDABLE_PROPERTY_WITH_ARGS(QTimerPrivate, Qt::TimerType, type, Qt::CoarseTimer)
- Q_OBJECT_COMPUTED_PROPERTY(QTimerPrivate, bool, isActiveData,
- &QTimerPrivate::isActiveActualCalculation)
-};
+QT_BEGIN_NAMESPACE
/*!
\class QTimer
@@ -121,6 +74,13 @@ public:
more and more platforms, we expect that zero-millisecond
QTimer objects will gradually be replaced by \l{QThread}s.
+ \note Since Qt 6.7 this class is superseded by \l{QChronoTimer}.
+ The maximum interval QTimer supports is limited by the number of
+ milliseconds that would fit in an \c int (which is around 24 days);
+ whereas QChronoTimer stores its interval as \c std::chrono::nanoseconds
+ (which raises that limit to around 292 million years), that is, there is
+ less chance of integer overflow with QChronoTimer.
+
\section1 Accuracy and Timer Resolution
The accuracy of timers depends on the underlying operating system
@@ -157,7 +117,7 @@ public:
used; Qt tries to work around these limitations.
\sa QBasicTimer, QTimerEvent, QObject::timerEvent(), Timers,
- {Analog Clock Example}, {Wiggly Example}
+ {Analog Clock}
*/
/*!
@@ -165,8 +125,9 @@ public:
*/
QTimer::QTimer(QObject *parent)
- : QObject(*new QTimerPrivate, parent)
+ : QObject(*new QTimerPrivate(this), parent)
{
+ Q_ASSERT(d_func()->isQTimer);
}
@@ -176,7 +137,7 @@ QTimer::QTimer(QObject *parent)
QTimer::~QTimer()
{
- if (d_func()->id != INV_TIMER) // stop running timer
+ if (d_func()->isActive()) // stop running timer
stop();
}
@@ -221,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()
@@ -237,11 +210,14 @@ int QTimer::timerId() const
void QTimer::start()
{
Q_D(QTimer);
- if (d->id != INV_TIMER) // stop running timer
+ if (d->isActive()) // stop running timer
stop();
- d->nulltimer = (!d->inter && d->single);
- d->id = QObject::startTimer(d->inter, d->type);
- d->isActiveData.notify();
+
+ const auto newId = Qt::TimerId{QObject::startTimer(d->inter * 1ms, d->type)};
+ if (newId > Qt::TimerId::Invalid) {
+ d->id = newId;
+ d->isActiveData.notify();
+ }
}
/*!
@@ -251,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();
@@ -277,9 +267,9 @@ void QTimer::start(int msec)
void QTimer::stop()
{
Q_D(QTimer);
- if (d->id != INV_TIMER) {
+ if (d->isActive()) {
QObject::killTimer(d->id);
- d->id = INV_TIMER;
+ d->id = Qt::TimerId::Invalid;
d->isActiveData.notify();
}
}
@@ -291,84 +281,13 @@ void QTimer::stop()
void QTimer::timerEvent(QTimerEvent *e)
{
Q_D(QTimer);
- if (e->timerId() == d->id) {
+ if (Qt::TimerId{e->timerId()} == d->id) {
if (d->single)
stop();
emit timeout(QPrivateSignal());
}
}
-class QSingleShotTimer : public QObject
-{
- Q_OBJECT
- int timerId;
- bool hasValidReceiver;
- QPointer<const QObject> receiver;
- QtPrivate::QSlotObjectBase *slotObj;
-public:
- ~QSingleShotTimer();
- QSingleShotTimer(int msec, Qt::TimerType timerType, const QObject *r, const char * m);
- QSingleShotTimer(int msec, Qt::TimerType timerType, const QObject *r, QtPrivate::QSlotObjectBase *slotObj);
-
-Q_SIGNALS:
- void timeout();
-protected:
- void timerEvent(QTimerEvent *) override;
-};
-
-QSingleShotTimer::QSingleShotTimer(int msec, Qt::TimerType timerType, const QObject *r, const char *member)
- : QObject(QAbstractEventDispatcher::instance()), hasValidReceiver(true), slotObj(nullptr)
-{
- timerId = startTimer(msec, timerType);
- connect(this, SIGNAL(timeout()), r, member);
-}
-
-QSingleShotTimer::QSingleShotTimer(int msec, Qt::TimerType timerType, const QObject *r, QtPrivate::QSlotObjectBase *slotObj)
- : QObject(QAbstractEventDispatcher::instance()), hasValidReceiver(r), receiver(r), slotObj(slotObj)
-{
- timerId = startTimer(msec, timerType);
- if (r && thread() != r->thread()) {
- // Avoid leaking the QSingleShotTimer instance in case the application exits before the timer fires
- connect(QCoreApplication::instance(), &QCoreApplication::aboutToQuit, this, &QObject::deleteLater);
- setParent(nullptr);
- moveToThread(r->thread());
- }
-}
-
-QSingleShotTimer::~QSingleShotTimer()
-{
- if (timerId > 0)
- killTimer(timerId);
- if (slotObj)
- slotObj->destroyIfLastRef();
-}
-
-void QSingleShotTimer::timerEvent(QTimerEvent *)
-{
- // need to kill the timer _before_ we emit timeout() in case the
- // slot connected to timeout calls processEvents()
- if (timerId > 0)
- killTimer(timerId);
- timerId = -1;
-
- if (slotObj) {
- // If the receiver was destroyed, skip this part
- if (Q_LIKELY(!receiver.isNull() || !hasValidReceiver)) {
- // We allocate only the return type - we previously checked the function had
- // no arguments.
- void *args[1] = { nullptr };
- slotObj->call(const_cast<QObject*>(receiver.data()), args);
- }
- } else {
- emit timeout();
- }
-
- // we would like to use delete later here, but it feels like a
- // waste to post a new event to handle this event, so we just unset the flag
- // and explicitly delete...
- qDeleteInEventHandler(this);
-}
-
/*!
\internal
@@ -378,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.
@@ -404,8 +322,10 @@ 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();
@@ -416,7 +336,9 @@ void QTimer::singleShotImpl(int msec, Qt::TimerType timerType,
}
/*!
+ \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
@@ -435,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
@@ -457,147 +374,54 @@ void QTimer::singleShot(int msec, const QObject *receiver, const char *member)
\sa start()
*/
-void QTimer::singleShot(int msec, Qt::TimerType timerType, const QObject *receiver, const char *member)
+
+void QTimer::singleShot(std::chrono::milliseconds msec, Qt::TimerType timerType,
+ const QObject *receiver, const char *member)
{
- if (Q_UNLIKELY(msec < 0)) {
+ if (Q_UNLIKELY(msec < 0ms)) {
qWarning("QTimer::singleShot: Timers cannot have negative timeouts");
return;
}
if (receiver && member) {
- if (msec == 0) {
+ if (msec == 0ms) {
// special code shortpath for 0-timers
const char* bracketPosition = strchr(member, '(');
if (!bracketPosition || !(member[0] >= '0' && member[0] <= '2')) {
qWarning("QTimer::singleShot: Invalid slot specification");
return;
}
- QByteArray methodName(member+1, bracketPosition - 1 - member); // extract method name
- QMetaObject::invokeMethod(const_cast<QObject *>(receiver), methodName.constData(), Qt::QueuedConnection);
+ const auto methodName = QByteArrayView(member + 1, // extract method name
+ bracketPosition - 1 - member).trimmed();
+ QMetaObject::invokeMethod(const_cast<QObject *>(receiver), methodName.toByteArray().constData(),
+ Qt::QueuedConnection);
return;
}
(void) new QSingleShotTimer(msec, timerType, receiver, member);
}
}
-/*! \fn template<typename PointerToMemberFunction> void QTimer::singleShot(int msec, const QObject *receiver, PointerToMemberFunction method)
-
- \since 5.4
-
- \overload
- \reentrant
- This static function calls a member function of a QObject after a given time interval.
-
- It is very convenient to use this function because you do not need
- to bother with a \l{QObject::timerEvent()}{timerEvent} or
- create a local QTimer object.
-
- The \a receiver is the receiving object and the \a method is the member function. The
- time interval is \a msec milliseconds.
-
- If \a receiver is destroyed before the interval occurs, the method will not be called.
- The function will be run in the thread of \a receiver. The receiver's thread must have
- a running Qt event loop.
-
- \sa start()
-*/
-
-/*! \fn template<typename PointerToMemberFunction> void QTimer::singleShot(int msec, Qt::TimerType timerType, const QObject *receiver, PointerToMemberFunction method)
-
- \since 5.4
-
- \overload
- \reentrant
- This static function calls a member function of a QObject after a given time interval.
-
- It is very convenient to use this function because you do not need
- to bother with a \l{QObject::timerEvent()}{timerEvent} or
- create a local QTimer object.
-
- The \a receiver is the receiving object and the \a method is the member function. The
- time interval is \a msec milliseconds. The \a timerType affects the
- accuracy of the timer.
-
- If \a receiver is destroyed before the interval occurs, the method will not be called.
- The function will be run in the thread of \a receiver. The receiver's thread must have
- a running Qt event loop.
-
- \sa start()
-*/
-
-/*! \fn template<typename Functor> void QTimer::singleShot(int msec, Functor functor)
-
+/*! \fn template<typename Duration, typename Functor> void QTimer::singleShot(Duration msec, const QObject *context, Functor &&functor)
+ \fn template<typename Duration, typename Functor> void QTimer::singleShot(Duration msec, Qt::TimerType timerType, const QObject *context, Functor &&functor)
+ \fn template<typename Duration, typename Functor> void QTimer::singleShot(Duration msec, Functor &&functor)
+ \fn template<typename Duration, typename Functor> void QTimer::singleShot(Duration msec, Qt::TimerType timerType, Functor &&functor)
\since 5.4
- \overload
\reentrant
- This static function calls \a functor after a given time interval.
+ This static function calls \a functor after \a msec milliseconds.
It is very convenient to use this function because you do not need
to bother with a \l{QObject::timerEvent()}{timerEvent} or
create a local QTimer object.
- The time interval is \a msec milliseconds.
+ If \a context is specified, then the \a functor will be called only if the
+ \a context object has not been destroyed before the interval occurs. The functor
+ will then be run the thread of \a context. The context's thread must have a
+ running Qt event loop.
- \sa start()
-*/
+ If \a functor is a member
+ function of \a context, then the function will be called on the object.
-/*! \fn template<typename Functor> void QTimer::singleShot(int msec, Qt::TimerType timerType, Functor functor)
-
- \since 5.4
-
- \overload
- \reentrant
- This static function calls \a functor after a given time interval.
-
- It is very convenient to use this function because you do not need
- to bother with a \l{QObject::timerEvent()}{timerEvent} or
- create a local QTimer object.
-
- The time interval is \a msec milliseconds. The \a timerType affects the
- accuracy of the timer.
-
- \sa start()
-*/
-
-/*! \fn template<typename Functor> void QTimer::singleShot(int msec, const QObject *context, Functor functor)
-
- \since 5.4
-
- \overload
- \reentrant
- This static function calls \a functor after a given time interval.
-
- It is very convenient to use this function because you do not need
- to bother with a \l{QObject::timerEvent()}{timerEvent} or
- create a local QTimer object.
-
- The time interval is \a msec milliseconds.
-
- If \a context is destroyed before the interval occurs, the method will not be called.
- The function will be run in the thread of \a context. The context's thread must have
- a running Qt event loop.
-
- \sa start()
-*/
-
-/*! \fn template<typename Functor> void QTimer::singleShot(int msec, Qt::TimerType timerType, const QObject *context, Functor functor)
-
- \since 5.4
-
- \overload
- \reentrant
- This static function calls \a functor after a given time interval.
-
- It is very convenient to use this function because you do not need
- to bother with a \l{QObject::timerEvent()}{timerEvent} or
- create a local QTimer object.
-
- The time interval is \a msec milliseconds. The \a timerType affects the
- accuracy of the timer.
-
- If \a context is destroyed before the interval occurs, the method will not be called.
- The function will be run in the thread of \a context. The context's thread must have
- a running Qt event loop.
+ The \a msec parameter can be an \c int or a \c std::chrono::milliseconds value.
\sa start()
*/
@@ -640,43 +464,35 @@ void QTimer::singleShot(int msec, Qt::TimerType timerType, const QObject *receiv
*/
/*!
- \fn template <typename Functor> QMetaObject::Connection QTimer::callOnTimeout(Functor slot, Qt::ConnectionType connectionType = Qt::AutoConnection)
+ \fn template <typename Functor> QMetaObject::Connection QTimer::callOnTimeout(Functor &&slot)
\since 5.12
- \overload
- Creates a connection of type \a connectionType from the timeout() signal
- to \a slot, and returns a handle to the connection.
+ Creates a connection from the timer's timeout() signal to \a slot.
+ Returns a handle to the connection.
- This method is provided for convenience.
- It's equivalent to calling \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()
*/
/*!
- \fn template <typename Functor> QMetaObject::Connection QTimer::callOnTimeout(const QObject *context, Functor slot, Qt::ConnectionType connectionType = Qt::AutoConnection)
+ \fn template <typename Functor> QMetaObject::Connection QTimer::callOnTimeout(const QObject *context, Functor &&slot, Qt::ConnectionType connectionType = Qt::AutoConnection)
\since 5.12
\overload callOnTimeout()
Creates a connection from the timeout() signal to \a slot to be placed in a specific
event loop of \a context, and returns a handle to the connection.
- This method is provided for convenience. It's equivalent to calling
- \c {QObject::connect(timer, &QTimer::timeout, context, slot, connectionType)}.
-
- \sa QObject::connect(), timeout()
-*/
-
-/*!
- \fn template <typename MemberFunction> QMetaObject::Connection QTimer::callOnTimeout(const QObject *receiver, MemberFunction *slot, Qt::ConnectionType connectionType = Qt::AutoConnection)
- \since 5.12
- \overload callOnTimeout()
-
- Creates a connection from the timeout() signal to the \a slot in the \a receiver object. Returns
- a handle to the connection.
-
- This method is provided for convenience. It's equivalent to calling
- \c {QObject::connect(timer, &QTimer::timeout, receiver, slot, connectionType)}.
+ This method is provided for convenience. It's equivalent to calling:
+ \code
+ QObject::connect(timer, &QTimer::timeout, context, slot, connectionType);
+ \endcode
\sa QObject::connect(), timeout()
*/
@@ -691,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
*/
/*!
@@ -756,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 != INV_TIMER) { // create new timer
+ // This could be narrowing as the interval is stored in an `int` QProperty,
+ // and the type can't be changed in Qt6.
+ const int msec = interval.count();
+ d->inter.removeBindingUnlessInWrapper();
+ const bool intervalChanged = msec != d->inter.valueBypassingBindings();
+ d->inter.setValueBypassingBindings(msec);
+ if (d->isActive()) { // create new timer
QObject::killTimer(d->id); // restart timer
- d->id = QObject::startTimer(msec, d->type);
- // No need to call markDirty() for d->isActiveData here,
- // as timer state actually does not change
+ const auto newId = Qt::TimerId{QObject::startTimer(msec * 1ms, 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->inter.notify();
@@ -793,8 +631,10 @@ QBindable<int> QTimer::bindableInterval()
int QTimer::remainingTime() const
{
Q_D(const QTimer);
- if (d->id != 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;
@@ -825,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 2b262f6311..854d9072f2 100644
--- a/src/corelib/kernel/qtimer.h
+++ b/src/corelib/kernel/qtimer.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtCore module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// 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 QTIMER_H
#define QTIMER_H
@@ -47,9 +11,7 @@
#include <QtCore/qbasictimer.h> // conceptual inheritance
#include <QtCore/qobject.h>
-#if __has_include(<chrono>)
-# include <chrono>
-#endif
+#include <chrono>
QT_BEGIN_NAMESPACE
@@ -69,6 +31,7 @@ public:
bool isActive() const;
QBindable<bool> bindableActive();
int timerId() const;
+ Qt::TimerId id() const;
void setInterval(int msec);
int interval() const;
@@ -84,85 +47,56 @@ public:
bool isSingleShot() const;
QBindable<bool> bindableSingleShot();
+ QT_CORE_INLINE_SINCE(6, 8)
static void singleShot(int msec, const QObject *receiver, const char *member);
+
+ QT_CORE_INLINE_SINCE(6, 8)
static void singleShot(int msec, Qt::TimerType timerType, const QObject *receiver, const char *member);
-#ifdef Q_CLANG_QDOC
- template<typename PointerToMemberFunction>
- static void singleShot(int msec, const QObject *receiver, PointerToMemberFunction method);
- template<typename PointerToMemberFunction>
- static void singleShot(int msec, Qt::TimerType timerType, const QObject *receiver, PointerToMemberFunction method);
- template<typename Functor>
- static void singleShot(int msec, Functor functor);
- template<typename Functor>
- static void singleShot(int msec, Qt::TimerType timerType, Functor functor);
- template<typename Functor, int>
- static void singleShot(int msec, const QObject *context, Functor functor);
- template<typename Functor, int>
- static void singleShot(int msec, Qt::TimerType timerType, const QObject *context, Functor functor);
- template <typename Functor>
- QMetaObject::Connection callOnTimeout(Functor slot, Qt::ConnectionType connectionType = Qt::AutoConnection);
- template <typename Functor>
- QMetaObject::Connection callOnTimeout(const QObject *context, Functor slot, Qt::ConnectionType connectionType = Qt::AutoConnection);
- template <typename MemberFunction>
- QMetaObject::Connection callOnTimeout(const QObject *receiver, MemberFunction *slot, Qt::ConnectionType connectionType = Qt::AutoConnection);
+ // singleShot with context
+#ifdef Q_QDOC
+ template <typename Duration, typename Functor>
+ static inline void singleShot(Duration interval, const QObject *receiver, Functor &&slot);
+ template <typename Duration, typename Functor>
+ static inline void singleShot(Duration interval, Qt::TimerType timerType,
+ const QObject *receiver, Functor &&slot);
#else
- // singleShot to a QObject slot
- template <typename Duration, typename Func1>
- static inline void singleShot(Duration interval, const typename QtPrivate::FunctionPointer<Func1>::Object *receiver, Func1 slot)
+ template <typename Duration, typename Functor>
+ static inline void singleShot(Duration interval,
+ const typename QtPrivate::ContextTypeForFunctor<Functor>::ContextType *receiver,
+ Functor &&slot)
{
- singleShot(interval, defaultTypeFor(interval), receiver, slot);
+ singleShot(interval, defaultTypeFor(interval), receiver, std::forward<Functor>(slot));
}
- template <typename Duration, typename Func1>
- static inline void singleShot(Duration interval, Qt::TimerType timerType, const typename QtPrivate::FunctionPointer<Func1>::Object *receiver,
- Func1 slot)
+ template <typename Duration, typename Functor>
+ static inline void singleShot(Duration interval, Qt::TimerType timerType,
+ const typename QtPrivate::ContextTypeForFunctor<Functor>::ContextType *receiver,
+ Functor &&slot)
{
- typedef QtPrivate::FunctionPointer<Func1> SlotType;
-
- //compilation error if the slot has arguments.
- static_assert(int(SlotType::ArgumentCount) == 0,
- "The slot must not have any arguments.");
-
+ using Prototype = void(*)();
singleShotImpl(interval, timerType, receiver,
- new QtPrivate::QSlotObject<Func1, typename SlotType::Arguments, void>(slot));
- }
- // singleShot to a functor or function pointer (without context)
- template <typename Duration, typename Func1>
- static inline typename std::enable_if<!QtPrivate::FunctionPointer<Func1>::IsPointerToMemberFunction &&
- !std::is_same<const char*, Func1>::value, void>::type
- singleShot(Duration interval, Func1 slot)
- {
- singleShot(interval, defaultTypeFor(interval), nullptr, std::move(slot));
- }
- template <typename Duration, typename Func1>
- static inline typename std::enable_if<!QtPrivate::FunctionPointer<Func1>::IsPointerToMemberFunction &&
- !std::is_same<const char*, Func1>::value, void>::type
- singleShot(Duration interval, Qt::TimerType timerType, Func1 slot)
- {
- singleShot(interval, timerType, nullptr, std::move(slot));
+ QtPrivate::makeCallableObject<Prototype>(std::forward<Functor>(slot)));
}
- // singleShot to a functor or function pointer (with context)
- template <typename Duration, typename Func1>
- static inline typename std::enable_if<!QtPrivate::FunctionPointer<Func1>::IsPointerToMemberFunction &&
- !std::is_same<const char*, Func1>::value, void>::type
- singleShot(Duration interval, const QObject *context, Func1 slot)
+#endif
+
+ // singleShot without context
+ template <typename Duration, typename Functor>
+ static inline void singleShot(Duration interval, Functor &&slot)
{
- singleShot(interval, defaultTypeFor(interval), context, std::move(slot));
+ singleShot(interval, defaultTypeFor(interval), nullptr, std::forward<Functor>(slot));
}
- template <typename Duration, typename Func1>
- static inline typename std::enable_if<!QtPrivate::FunctionPointer<Func1>::IsPointerToMemberFunction &&
- !std::is_same<const char*, Func1>::value, void>::type
- singleShot(Duration interval, Qt::TimerType timerType, const QObject *context, Func1 slot)
+ template <typename Duration, typename Functor>
+ static inline void singleShot(Duration interval, Qt::TimerType timerType, Functor &&slot)
{
- //compilation error if the slot has arguments.
- typedef QtPrivate::FunctionPointer<Func1> SlotType;
- static_assert(int(SlotType::ArgumentCount) <= 0, "The slot must not have any arguments.");
-
- singleShotImpl(interval, timerType, context,
- new QtPrivate::QFunctorSlotObject<Func1, 0,
- typename QtPrivate::List_Left<void, 0>::Value, void>(std::move(slot)));
+ singleShot(interval, timerType, nullptr, std::forward<Functor>(slot));
}
+#ifdef Q_QDOC
+ template <typename Functor>
+ QMetaObject::Connection callOnTimeout(Functor &&slot);
+ template <typename Functor>
+ QMetaObject::Connection callOnTimeout(const QObject *context, Functor &&slot, Qt::ConnectionType connectionType = Qt::AutoConnection);
+#else
template <typename ... Args>
QMetaObject::Connection callOnTimeout(Args && ...args)
{
@@ -181,11 +115,7 @@ Q_SIGNALS:
void timeout(QPrivateSignal);
public:
-#if __has_include(<chrono>) || defined(Q_QDOC)
- void setInterval(std::chrono::milliseconds value)
- {
- setInterval(int(value.count()));
- }
+ void setInterval(std::chrono::milliseconds value);
std::chrono::milliseconds intervalAsDuration() const
{
@@ -199,19 +129,12 @@ public:
static void singleShot(std::chrono::milliseconds value, const QObject *receiver, const char *member)
{
- singleShot(int(value.count()), receiver, member);
- }
-
- static void singleShot(std::chrono::milliseconds value, Qt::TimerType timerType, const QObject *receiver, const char *member)
- {
- singleShot(int(value.count()), timerType, receiver, member);
+ singleShot(value, defaultTypeFor(value), receiver, member);
}
+ static void singleShot(std::chrono::milliseconds interval, Qt::TimerType timerType,
+ const QObject *receiver, const char *member);
- void start(std::chrono::milliseconds value)
- {
- start(int(value.count()));
- }
-#endif
+ void start(std::chrono::milliseconds value);
protected:
void timerEvent(QTimerEvent *) override;
@@ -224,23 +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);
-#if __has_include(<chrono>)
- 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);
- }
-#endif
+ 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
new file mode 100644
index 0000000000..9347f6c241
--- /dev/null
+++ b/src/corelib/kernel/qtimer_p.h
@@ -0,0 +1,73 @@
+// 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 QTIMER_P_H
+#define QTIMER_P_H
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of the Qt translation tools. This header file may change from version
+// to version without notice, or even be removed.
+//
+// We mean it.
+//
+#include "qtimer.h"
+#include "qchronotimer.h"
+
+#include "qobject_p.h"
+#include "qproperty_p.h"
+#include "qttypetraits.h"
+
+QT_BEGIN_NAMESPACE
+
+class QTimerPrivate : public QObjectPrivate
+{
+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 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);
+ }
+
+ 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::isActive)
+
+ QObject *q;
+ const bool isQTimer = false; // true if q is a QTimer*
+};
+
+QT_END_NAMESPACE
+#endif // QTIMER_P_H
diff --git a/src/corelib/kernel/qtimerinfo_unix.cpp b/src/corelib/kernel/qtimerinfo_unix.cpp
index db4d4b1188..b83f0194c2 100644
--- a/src/corelib/kernel/qtimerinfo_unix.cpp
+++ b/src/corelib/kernel/qtimerinfo_unix.cpp
@@ -1,42 +1,6 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Copyright (C) 2016 Intel Corporation.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtCore module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// Copyright (C) 2016 Intel Corporation.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include <qelapsedtimer.h>
#include <qcoreapplication.h>
@@ -46,13 +10,12 @@
#include "private/qobject_p.h"
#include "private/qabstracteventdispatcher_p.h"
-#ifdef QTIMERINFO_DEBUG
-# include <QDebug>
-# include <QThread>
-#endif
-
#include <sys/times.h>
+using namespace std::chrono;
+// Implied by "using namespace std::chrono", but be explicit about it, for grep-ability
+using namespace std::chrono_literals;
+
QT_BEGIN_NAMESPACE
Q_CORE_EXPORT bool qt_disable_lowpriority_timers=false;
@@ -62,168 +25,75 @@ Q_CORE_EXPORT bool qt_disable_lowpriority_timers=false;
* timerBitVec array is used for keeping track of timer identifiers.
*/
-QTimerInfoList::QTimerInfoList()
-{
-#if (_POSIX_MONOTONIC_CLOCK-0 <= 0) && !defined(Q_OS_MAC)
- if (!QElapsedTimer::isMonotonic()) {
- // not using monotonic timers, initialize the timeChanged() machinery
- previousTime = qt_gettime();
-
- tms unused;
- previousTicks = times(&unused);
-
- ticksPerSecond = sysconf(_SC_CLK_TCK);
- msPerTick = 1000/ticksPerSecond;
- } else {
- // detected monotonic timers
- previousTime.tv_sec = previousTime.tv_nsec = 0;
- previousTicks = 0;
- ticksPerSecond = 0;
- msPerTick = 0;
- }
-#endif
-
- firstTimerInfo = nullptr;
-}
-
-timespec QTimerInfoList::updateCurrentTime()
-{
- return (currentTime = qt_gettime());
-}
-
-#if ((_POSIX_MONOTONIC_CLOCK-0 <= 0) && !defined(Q_OS_MAC) && !defined(Q_OS_INTEGRITY)) || defined(QT_BOOTSTRAPPED)
+QTimerInfoList::QTimerInfoList() = default;
-timespec qAbsTimespec(const timespec &t)
+steady_clock::time_point QTimerInfoList::updateCurrentTime() const
{
- timespec tmp = t;
- if (tmp.tv_sec < 0) {
- tmp.tv_sec = -tmp.tv_sec - 1;
- tmp.tv_nsec -= 1000000000;
- }
- if (tmp.tv_sec == 0 && tmp.tv_nsec < 0) {
- tmp.tv_nsec = -tmp.tv_nsec;
- }
- return normalizedTimespec(tmp);
+ currentTime = steady_clock::now();
+ return currentTime;
}
-/*
- Returns \c true if the real time clock has changed by more than 10%
- relative to the processor time since the last time this function was
- called. This presumably means that the system time has been changed.
-
- If /a delta is nonzero, delta is set to our best guess at how much the system clock was changed.
-*/
-bool QTimerInfoList::timeChanged(timespec *delta)
-{
- struct tms unused;
- clock_t currentTicks = times(&unused);
-
- clock_t elapsedTicks = currentTicks - previousTicks;
- timespec elapsedTime = currentTime - previousTime;
-
- timespec elapsedTimeTicks;
- elapsedTimeTicks.tv_sec = elapsedTicks / ticksPerSecond;
- elapsedTimeTicks.tv_nsec = (((elapsedTicks * 1000) / ticksPerSecond) % 1000) * 1000 * 1000;
-
- timespec dummy;
- if (!delta)
- delta = &dummy;
- *delta = elapsedTime - elapsedTimeTicks;
-
- previousTicks = currentTicks;
- previousTime = currentTime;
-
- // If tick drift is more than 10% off compared to realtime, we assume that the clock has
- // been set. Of course, we have to allow for the tick granularity as well.
- timespec tickGranularity;
- tickGranularity.tv_sec = 0;
- tickGranularity.tv_nsec = msPerTick * 1000 * 1000;
- return elapsedTimeTicks < ((qAbsTimespec(*delta) - tickGranularity) * 10);
-}
+/*! \internal
+ Updates the currentTime member to the current time, and returns \c true if
+ the first timer's timeout is in the future (after currentTime).
-/*
- repair broken timer
+ The list is sorted by timeout, thus it's enough to check the first timer only.
*/
-void QTimerInfoList::timerRepair(const timespec &diff)
-{
- // repair all timers
- for (int i = 0; i < size(); ++i) {
- QTimerInfo *t = at(i);
- t->timeout = t->timeout + diff;
- }
-}
-
-void QTimerInfoList::repairTimersIfNeeded()
-{
- if (QElapsedTimer::isMonotonic())
- return;
- timespec delta;
- if (timeChanged(&delta))
- timerRepair(delta);
-}
-
-#else // !(_POSIX_MONOTONIC_CLOCK-0 <= 0) && !defined(QT_BOOTSTRAPPED)
-
-void QTimerInfoList::repairTimersIfNeeded()
+bool QTimerInfoList::hasPendingTimers()
{
+ if (timers.isEmpty())
+ return false;
+ return updateCurrentTime() < timers.at(0)->timeout;
}
-#endif
+static bool byTimeout(const QTimerInfo *a, const QTimerInfo *b)
+{ return a->timeout < b->timeout; };
/*
insert timer info into list
*/
void QTimerInfoList::timerInsert(QTimerInfo *ti)
{
- int index = size();
- while (index--) {
- const QTimerInfo * const t = at(index);
- if (!(ti->timeout < t->timeout))
- break;
- }
- insert(index+1, ti);
-}
-
-inline timespec &operator+=(timespec &t1, int ms)
-{
- t1.tv_sec += ms / 1000;
- t1.tv_nsec += ms % 1000 * 1000 * 1000;
- return normalizedTimespec(t1);
-}
-
-inline timespec operator+(const timespec &t1, int ms)
-{
- timespec t2 = t1;
- return t2 += ms;
+ timers.insert(std::upper_bound(timers.cbegin(), timers.cend(), ti, byTimeout),
+ ti);
}
-static 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);
- val.tv_nsec += 1000 * 1000 - ns;
- return normalizedTimespec(val);
+ return ceil<milliseconds>(val);
}
-#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)
+static_assert(roundToMillisecond(0ns) == 0ms);
+static_assert(roundToMillisecond(1ns) == 1ms);
+static_assert(roundToMillisecond(999'999ns) == 1ms);
+static_assert(roundToMillisecond(1'000'000ns) == 1ms);
+static_assert(roundToMillisecond(999'000'000ns) == 999ms);
+static_assert(roundToMillisecond(999'000'001ns) == 1000ms);
+static_assert(roundToMillisecond(999'999'999ns) == 1000ms);
+static_assert(roundToMillisecond(1s) == 1s);
+
+static constexpr seconds roundToSecs(nanoseconds interval)
{
- QDebugStateSaver saver(s);
- s << (t == Qt::PreciseTimer ? "P" :
- t == Qt::CoarseTimer ? "C" : "VC");
- return s;
+ // The very coarse timer is based on full second precision, so we want to
+ // round the interval to the closest second, rounding 500ms up to 1s.
+ //
+ // std::chrono::round() wouldn't work with all multiples of 500 because for the
+ // middle point it would round to even:
+ // value round() wanted
+ // 500 0 1
+ // 1500 2 2
+ // 2500 2 3
+
+ auto secs = duration_cast<seconds>(interval);
+ const nanoseconds frac = interval - secs;
+ if (frac >= 500ms)
+ ++secs;
+ return secs;
}
-#endif
-static void calculateCoarseTimerTimeout(QTimerInfo *t, timespec currentTime)
+static void calculateCoarseTimerTimeout(QTimerInfo *t, steady_clock::time_point now)
{
// The coarse timer works like this:
// - interval under 40 ms: round to even
@@ -241,214 +111,186 @@ static void calculateCoarseTimerTimeout(QTimerInfo *t, timespec currentTime)
//
// The objective is to make most timers wake up at the same time, thereby reducing CPU wakeups.
- uint interval = uint(t->interval);
- uint msec = uint(t->timeout.tv_nsec) / 1000 / 1000;
- Q_ASSERT(interval >= 20);
+ Q_ASSERT(t->interval >= 20ms);
+
+ const auto timeoutInSecs = time_point_cast<seconds>(t->timeout);
+
+ auto recalculate = [&](const milliseconds frac) {
+ t->timeout = timeoutInSecs + frac;
+ if (t->timeout < now)
+ t->timeout += t->interval;
+ };
// Calculate how much we can round and still keep within 5% error
- uint absMaxRounding = interval / 20;
+ milliseconds interval = roundToMillisecond(t->interval);
+ const milliseconds absMaxRounding = interval / 20;
+
+ auto fracMsec = duration_cast<milliseconds>(t->timeout - timeoutInSecs);
- if (interval < 100 && interval != 25 && interval != 50 && interval != 75) {
+ if (interval < 100ms && interval != 25ms && interval != 50ms && interval != 75ms) {
+ auto fracCount = fracMsec.count();
// special mode for timers of less than 100 ms
- if (interval < 50) {
+ if (interval < 50ms) {
// round to even
// round towards multiples of 50 ms
- bool roundUp = (msec % 50) >= 25;
- msec >>= 1;
- msec |= uint(roundUp);
- msec <<= 1;
+ bool roundUp = (fracCount % 50) >= 25;
+ fracCount >>= 1;
+ fracCount |= roundUp;
+ fracCount <<= 1;
} else {
// round to multiple of 4
// round towards multiples of 100 ms
- bool roundUp = (msec % 100) >= 50;
- msec >>= 2;
- msec |= uint(roundUp);
- msec <<= 2;
- }
- } else {
- uint min = qMax<int>(0, msec - absMaxRounding);
- uint max = qMin(1000u, msec + absMaxRounding);
-
- // find the boundary that we want, according to the rules above
- // extra rules:
- // 1) whatever the interval, we'll take any round-to-the-second timeout
- if (min == 0) {
- msec = 0;
- goto recalculate;
- } else if (max == 1000) {
- msec = 1000;
- goto recalculate;
+ bool roundUp = (fracCount % 100) >= 50;
+ fracCount >>= 2;
+ fracCount |= roundUp;
+ fracCount <<= 2;
}
+ fracMsec = milliseconds{fracCount};
+ recalculate(fracMsec);
+ return;
+ }
- uint wantedBoundaryMultiple;
-
- // 2) if the interval is a multiple of 500 ms and > 5000 ms, we'll always round
- // towards a round-to-the-second
- // 3) if the interval is a multiple of 500 ms, we'll round towards the nearest
- // multiple of 500 ms
- if ((interval % 500) == 0) {
- if (interval >= 5000) {
- msec = msec >= 500 ? max : min;
- goto recalculate;
- } else {
- wantedBoundaryMultiple = 500;
- }
- } else if ((interval % 50) == 0) {
- // 4) same for multiples of 250, 200, 100, 50
- uint mult50 = interval / 50;
- if ((mult50 % 4) == 0) {
- // multiple of 200
- wantedBoundaryMultiple = 200;
- } else if ((mult50 % 2) == 0) {
- // multiple of 100
- wantedBoundaryMultiple = 100;
- } else if ((mult50 % 5) == 0) {
- // multiple of 250
- wantedBoundaryMultiple = 250;
- } else {
- // multiple of 50
- wantedBoundaryMultiple = 50;
- }
- } else {
- wantedBoundaryMultiple = 25;
- }
+ milliseconds min = std::max(0ms, fracMsec - absMaxRounding);
+ milliseconds max = std::min(1000ms, fracMsec + absMaxRounding);
- uint base = msec / wantedBoundaryMultiple * wantedBoundaryMultiple;
- uint middlepoint = base + wantedBoundaryMultiple / 2;
- if (msec < middlepoint)
- msec = qMax(base, min);
- else
- msec = qMin(base + wantedBoundaryMultiple, max);
+ // find the boundary that we want, according to the rules above
+ // extra rules:
+ // 1) whatever the interval, we'll take any round-to-the-second timeout
+ if (min == 0ms) {
+ fracMsec = 0ms;
+ recalculate(fracMsec);
+ return;
+ } else if (max == 1000ms) {
+ fracMsec = 1000ms;
+ recalculate(fracMsec);
+ return;
}
-recalculate:
- if (msec == 1000u) {
- ++t->timeout.tv_sec;
- t->timeout.tv_nsec = 0;
- } else {
- t->timeout.tv_nsec = msec * 1000 * 1000;
+ milliseconds wantedBoundaryMultiple{25};
+
+ // 2) if the interval is a multiple of 500 ms and > 5000 ms, we'll always round
+ // towards a round-to-the-second
+ // 3) if the interval is a multiple of 500 ms, we'll round towards the nearest
+ // multiple of 500 ms
+ if ((interval % 500) == 0ms) {
+ if (interval >= 5s) {
+ fracMsec = fracMsec >= 500ms ? max : min;
+ recalculate(fracMsec);
+ return;
+ } else {
+ wantedBoundaryMultiple = 500ms;
+ }
+ } else if ((interval % 50) == 0ms) {
+ // 4) same for multiples of 250, 200, 100, 50
+ milliseconds mult50 = interval / 50;
+ if ((mult50 % 4) == 0ms) {
+ // multiple of 200
+ wantedBoundaryMultiple = 200ms;
+ } else if ((mult50 % 2) == 0ms) {
+ // multiple of 100
+ wantedBoundaryMultiple = 100ms;
+ } else if ((mult50 % 5) == 0ms) {
+ // multiple of 250
+ wantedBoundaryMultiple = 250ms;
+ } else {
+ // multiple of 50
+ wantedBoundaryMultiple = 50ms;
+ }
}
- if (t->timeout < currentTime)
- t->timeout += interval;
+ milliseconds base = (fracMsec / wantedBoundaryMultiple) * wantedBoundaryMultiple;
+ milliseconds middlepoint = base + wantedBoundaryMultiple / 2;
+ if (fracMsec < middlepoint)
+ fracMsec = qMax(base, min);
+ else
+ fracMsec = qMin(base + wantedBoundaryMultiple, max);
+
+ recalculate(fracMsec);
}
-static void calculateNextTimeout(QTimerInfo *t, timespec currentTime)
+static void calculateNextTimeout(QTimerInfo *t, steady_clock::time_point now)
{
switch (t->timerType) {
case Qt::PreciseTimer:
case Qt::CoarseTimer:
t->timeout += t->interval;
- if (t->timeout < currentTime) {
- t->timeout = currentTime;
+ if (t->timeout < now) {
+ t->timeout = now;
t->timeout += t->interval;
}
-#ifdef QTIMERINFO_DEBUG
- t->expected += t->interval;
- if (t->expected < currentTime) {
- t->expected = currentTime;
- t->expected += t->interval;
- }
-#endif
if (t->timerType == Qt::CoarseTimer)
- calculateCoarseTimerTimeout(t, currentTime);
+ calculateCoarseTimerTimeout(t, now);
return;
case Qt::VeryCoarseTimer:
- // we don't need to take care of the microsecond component of t->interval
- t->timeout.tv_sec += t->interval;
- if (t->timeout.tv_sec <= currentTime.tv_sec)
- t->timeout.tv_sec = currentTime.tv_sec + t->interval;
-#ifdef QTIMERINFO_DEBUG
- t->expected.tv_sec += t->interval;
- if (t->expected.tv_sec <= currentTime.tv_sec)
- t->expected.tv_sec = currentTime.tv_sec + t->interval;
-#endif
- return;
+ // t->interval already rounded to full seconds in registerTimer()
+ t->timeout += t->interval;
+ if (t->timeout <= now)
+ t->timeout = time_point_cast<seconds>(now + t->interval);
+ break;
}
-
-#ifdef QTIMERINFO_DEBUG
- if (t->timerType != Qt::PreciseTimer)
- qDebug() << "timer" << t->timerType << Qt::hex << t->id << Qt::dec << "interval" << t->interval
- << "originally expected at" << t->expected << "will fire at" << t->timeout
- << "or" << (t->timeout - t->expected) << "s late";
-#endif
}
/*
- Returns the time to wait for the next timer, or null if no timers
- are waiting.
-*/
-bool QTimerInfoList::timerWait(timespec &tm)
+ Returns the time to wait for the first timer that has not been activated yet,
+ otherwise returns std::nullopt.
+ */
+std::optional<QTimerInfoList::Duration> QTimerInfoList::timerWait()
{
- timespec currentTime = updateCurrentTime();
- repairTimersIfNeeded();
+ steady_clock::time_point now = updateCurrentTime();
+ auto isWaiting = [](QTimerInfo *tinfo) { return !tinfo->activateRef; };
// Find first waiting timer not already active
- QTimerInfo *t = nullptr;
- for (QTimerInfoList::const_iterator it = constBegin(); it != constEnd(); ++it) {
- if (!(*it)->activateRef) {
- t = *it;
- break;
- }
- }
-
- if (!t)
- return false;
-
- if (currentTime < t->timeout) {
- // time to wait
- tm = roundToMillisecond(t->timeout - currentTime);
- } else {
- // no time to wait
- tm.tv_sec = 0;
- tm.tv_nsec = 0;
- }
-
- return true;
+ auto it = std::find_if(timers.cbegin(), timers.cend(), isWaiting);
+ if (it == timers.cend())
+ return std::nullopt;
+
+ Duration timeToWait = (*it)->timeout - now;
+ if (timeToWait > 0ns)
+ return roundToMillisecond(timeToWait);
+ return 0ms;
}
/*
- Returns the timer's remaining time in milliseconds with the given timerId, or
- null if there is nothing left. If the timer id is not found in the list, the
- returned value will be -1. If the timer is overdue, the returned value will be 0.
+ 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 \c{Duration::min()}.
+ If the timer is overdue, the returned value will be 0.
*/
-int QTimerInfoList::timerRemainingTime(int timerId)
+QTimerInfoList::Duration QTimerInfoList::remainingDuration(Qt::TimerId timerId) const
{
- timespec currentTime = updateCurrentTime();
- repairTimersIfNeeded();
- timespec tm = {0, 0};
-
- for (int i = 0; i < count(); ++i) {
- QTimerInfo *t = at(i);
- if (t->id == timerId) {
- if (currentTime < t->timeout) {
- // time to wait
- tm = roundToMillisecond(t->timeout - currentTime);
- return tm.tv_sec*1000 + tm.tv_nsec/1000/1000;
- } else {
- return 0;
- }
- }
- }
+ const steady_clock::time_point now = updateCurrentTime();
+ auto it = findTimerById(timerId);
+ if (it == timers.cend()) {
#ifndef QT_NO_DEBUG
- qWarning("QTimerInfoList::timerRemainingTime: timer id %i not found", timerId);
+ qWarning("QTimerInfoList::timerRemainingTime: timer id %i not found", int(timerId));
#endif
+ return Duration::min();
+ }
- return -1;
+ const QTimerInfo *t = *it;
+ if (now < t->timeout) // time to wait
+ return t->timeout - now;
+ return 0ms;
}
-void QTimerInfoList::registerTimer(int timerId, qint64 interval, Qt::TimerType timerType, QObject *object)
+void QTimerInfoList::registerTimer(Qt::TimerId timerId, QTimerInfoList::Duration interval,
+ Qt::TimerType timerType, QObject *object)
{
- QTimerInfo *t = new QTimerInfo;
- t->id = timerId;
- t->interval = interval;
- t->timerType = timerType;
- t->obj = object;
- t->activateRef = nullptr;
+ // correct the timer type first
+ if (timerType == Qt::CoarseTimer) {
+ // this timer has up to 5% coarseness
+ // so our boundaries are 20 ms and 20 s
+ // below 20 ms, 5% inaccuracy is below 1 ms, so we convert to high precision
+ // above 20 s, 5% inaccuracy is above 1 s, so we convert to VeryCoarseTimer
+ if (interval >= 20s)
+ timerType = Qt::VeryCoarseTimer;
+ else if (interval <= 20ms)
+ timerType = Qt::PreciseTimer;
+ }
- timespec expected = updateCurrentTime() + interval;
+ QTimerInfo *t = new QTimerInfo(timerId, interval, timerType, object);
+ QTimerInfo::TimePoint expected = updateCurrentTime() + interval;
switch (timerType) {
case Qt::PreciseTimer:
@@ -458,102 +300,69 @@ void QTimerInfoList::registerTimer(int timerId, qint64 interval, Qt::TimerType t
break;
case Qt::CoarseTimer:
- // this timer has up to 5% coarseness
- // so our boundaries are 20 ms and 20 s
- // below 20 ms, 5% inaccuracy is below 1 ms, so we convert to high precision
- // above 20 s, 5% inaccuracy is above 1 s, so we convert to VeryCoarseTimer
- if (interval >= 20000) {
- t->timerType = Qt::VeryCoarseTimer;
- } else {
- t->timeout = expected;
- if (interval <= 20) {
- t->timerType = Qt::PreciseTimer;
- // no adjustment is necessary
- } else if (interval <= 20000) {
- calculateCoarseTimerTimeout(t, currentTime);
- }
- break;
- }
- Q_FALLTHROUGH();
+ t->timeout = expected;
+ t->interval = roundToMillisecond(interval);
+ calculateCoarseTimerTimeout(t, currentTime);
+ break;
+
case Qt::VeryCoarseTimer:
- // the very coarse timer is based on full second precision,
- // so we keep the interval in seconds (round to closest second)
- t->interval /= 500;
- t->interval += 1;
- t->interval >>= 1;
- t->timeout.tv_sec = currentTime.tv_sec + t->interval;
- t->timeout.tv_nsec = 0;
-
- // if we're past the half-second mark, increase the timeout again
- if (currentTime.tv_nsec > 500*1000*1000)
- ++t->timeout.tv_sec;
+ t->interval = roundToSecs(t->interval);
+ const auto currentTimeInSecs = floor<seconds>(currentTime);
+ t->timeout = currentTimeInSecs + t->interval;
+ // If we're past the half-second mark, increase the timeout again
+ if (currentTime - currentTimeInSecs > 500ms)
+ t->timeout += 1s;
}
timerInsert(t);
-
-#ifdef QTIMERINFO_DEBUG
- t->expected = expected;
- t->cumulativeError = 0;
- t->count = 0;
- if (t->timerType != Qt::PreciseTimer)
- qDebug() << "timer" << t->timerType << Qt::hex <<t->id << Qt::dec << "interval" << t->interval << "expected at"
- << t->expected << "will fire first at" << t->timeout;
-#endif
}
-bool QTimerInfoList::unregisterTimer(int timerId)
+bool QTimerInfoList::unregisterTimer(Qt::TimerId timerId)
{
+ auto it = findTimerById(timerId);
+ if (it == timers.cend())
+ return false; // id not found
+
// set timer inactive
- for (int i = 0; i < count(); ++i) {
- QTimerInfo *t = at(i);
- if (t->id == timerId) {
- // found it
- removeAt(i);
- if (t == firstTimerInfo)
- firstTimerInfo = nullptr;
- if (t->activateRef)
- *(t->activateRef) = nullptr;
- delete t;
- return true;
- }
- }
- // id not found
- return false;
+ QTimerInfo *t = *it;
+ if (t == firstTimerInfo)
+ firstTimerInfo = nullptr;
+ if (t->activateRef)
+ *(t->activateRef) = nullptr;
+ delete t;
+ timers.erase(it);
+ return true;
}
bool QTimerInfoList::unregisterTimers(QObject *object)
{
- if (isEmpty())
+ if (timers.isEmpty())
return false;
- for (int i = 0; i < count(); ++i) {
- QTimerInfo *t = at(i);
- if (t->obj == object) {
- // object found
- removeAt(i);
- if (t == firstTimerInfo)
- firstTimerInfo = nullptr;
- if (t->activateRef)
- *(t->activateRef) = nullptr;
- delete t;
- // move back one so that we don't skip the new current item
- --i;
- }
- }
- return true;
+
+ auto associatedWith = [this](QObject *o) {
+ return [this, o](auto &t) {
+ if (t->obj == o) {
+ if (t == firstTimerInfo)
+ firstTimerInfo = nullptr;
+ if (t->activateRef)
+ *(t->activateRef) = nullptr;
+ delete t;
+ return true;
+ }
+ return false;
+ };
+ };
+
+ qsizetype count = timers.removeIf(associatedWith(object));
+ return count > 0;
}
-QList<QAbstractEventDispatcher::TimerInfo> QTimerInfoList::registeredTimers(QObject *object) const
+auto QTimerInfoList::registeredTimers(QObject *object) const -> QList<TimerInfo>
{
- QList<QAbstractEventDispatcher::TimerInfo> list;
- for (int i = 0; i < count(); ++i) {
- const QTimerInfo * const t = at(i);
- if (t->obj == object) {
- list << QAbstractEventDispatcher::TimerInfo(t->id,
- (t->timerType == Qt::VeryCoarseTimer
- ? t->interval * 1000
- : t->interval),
- t->timerType);
- }
+ QList<TimerInfo> list;
+ for (const auto &t : timers) {
+ if (t->obj == object)
+ list.emplaceBack(TimerInfo{t->interval, t->id, t->timerType});
}
return list;
}
@@ -563,31 +372,27 @@ QList<QAbstractEventDispatcher::TimerInfo> QTimerInfoList::registeredTimers(QObj
*/
int QTimerInfoList::activateTimers()
{
- if (qt_disable_lowpriority_timers || isEmpty())
+ if (qt_disable_lowpriority_timers || timers.isEmpty())
return 0; // nothing to do
- int n_act = 0, maxCount = 0;
firstTimerInfo = nullptr;
- timespec currentTime = updateCurrentTime();
- // qDebug() << "Thread" << QThread::currentThreadId() << "woken up at" << currentTime;
- repairTimersIfNeeded();
-
-
+ const steady_clock::time_point now = updateCurrentTime();
+ // qDebug() << "Thread" << QThread::currentThreadId() << "woken up at" << now;
// Find out how many timer have expired
- for (QTimerInfoList::const_iterator it = constBegin(); it != constEnd(); ++it) {
- if (currentTime < (*it)->timeout)
- break;
- maxCount++;
- }
+ auto stillActive = [&now](const QTimerInfo *t) { return now < t->timeout; };
+ // Find first one still active (list is sorted by timeout)
+ auto it = std::find_if(timers.cbegin(), timers.cend(), stillActive);
+ auto maxCount = it - timers.cbegin();
+ int n_act = 0;
//fire the timers.
while (maxCount--) {
- if (isEmpty())
+ if (timers.isEmpty())
break;
- QTimerInfo *currentTimerInfo = constFirst();
- if (currentTime < currentTimerInfo->timeout)
+ QTimerInfo *currentTimerInfo = timers.constFirst();
+ if (now < currentTimerInfo->timeout)
break; // no timer has expired
if (!firstTimerInfo) {
@@ -600,42 +405,24 @@ int QTimerInfoList::activateTimers()
firstTimerInfo = currentTimerInfo;
}
- // remove from list
- removeFirst();
-
-#ifdef QTIMERINFO_DEBUG
- float diff;
- if (currentTime < currentTimerInfo->expected) {
- // early
- timeval early = currentTimerInfo->expected - currentTime;
- diff = -(early.tv_sec + early.tv_usec / 1000000.0);
- } else {
- timeval late = currentTime - currentTimerInfo->expected;
- diff = late.tv_sec + late.tv_usec / 1000000.0;
- }
- currentTimerInfo->cumulativeError += diff;
- ++currentTimerInfo->count;
- if (currentTimerInfo->timerType != Qt::PreciseTimer)
- qDebug() << "timer" << currentTimerInfo->timerType << Qt::hex << currentTimerInfo->id << Qt::dec << "interval"
- << currentTimerInfo->interval << "firing at" << currentTime
- << "(orig" << currentTimerInfo->expected << "scheduled at" << currentTimerInfo->timeout
- << ") off by" << diff << "activation" << currentTimerInfo->count
- << "avg error" << (currentTimerInfo->cumulativeError / currentTimerInfo->count);
-#endif
-
// determine next timeout time
- calculateNextTimeout(currentTimerInfo, currentTime);
+ calculateNextTimeout(currentTimerInfo, now);
+ if (timers.size() > 1) {
+ // Find where "currentTimerInfo" should be in the list so as
+ // to keep the list ordered by timeout
+ auto afterCurrentIt = timers.begin() + 1;
+ auto iter = std::upper_bound(afterCurrentIt, timers.end(), currentTimerInfo, byTimeout);
+ currentTimerInfo = *std::rotate(timers.begin(), afterCurrentIt, iter);
+ }
- // reinsert timer
- timerInsert(currentTimerInfo);
- if (currentTimerInfo->interval > 0)
+ if (currentTimerInfo->interval > 0ms)
n_act++;
// Send event, but don't allow it to recurse:
if (!currentTimerInfo->activateRef) {
currentTimerInfo->activateRef = &currentTimerInfo;
- QTimerEvent e(currentTimerInfo->id);
+ QTimerEvent e(qToUnderlying(currentTimerInfo->id));
QCoreApplication::sendEvent(currentTimerInfo->obj, &e);
// Storing currentTimerInfo's address in its activateRef allows the
diff --git a/src/corelib/kernel/qtimerinfo_unix_p.h b/src/corelib/kernel/qtimerinfo_unix_p.h
index 1a1a101dda..293e9c4d4e 100644
--- a/src/corelib/kernel/qtimerinfo_unix_p.h
+++ b/src/corelib/kernel/qtimerinfo_unix_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtCore module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// 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 QTIMERINFO_UNIX_P_H
#define QTIMERINFO_UNIX_P_H
@@ -53,65 +17,76 @@
#include <QtCore/private/qglobal_p.h>
-// #define QTIMERINFO_DEBUG
-
#include "qabstracteventdispatcher.h"
-#include <sys/time.h> // struct timeval
+#include <sys/time.h> // struct timespec
+#include <chrono>
QT_BEGIN_NAMESPACE
// internal timer info
-struct QTimerInfo {
- int id; // - timer identifier
- qint64 interval; // - timer interval in milliseconds
+struct QTimerInfo
+{
+ using Duration = QAbstractEventDispatcher::Duration;
+ using TimePoint = std::chrono::time_point<std::chrono::steady_clock, Duration>;
+ QTimerInfo(Qt::TimerId timerId, Duration interval, Qt::TimerType type, QObject *obj)
+ : interval(interval), id(timerId), timerType(type), obj(obj)
+ {
+ }
+
+ TimePoint timeout = {}; // - when to actually fire
+ Duration interval = Duration{-1}; // - timer interval
+ Qt::TimerId id = Qt::TimerId::Invalid; // - timer identifier
Qt::TimerType timerType; // - timer type
- timespec timeout; // - when to actually fire
- QObject *obj; // - object to receive event
- QTimerInfo **activateRef; // - ref from activateTimers
-
-#ifdef QTIMERINFO_DEBUG
- timeval expected; // when timer is expected to fire
- float cumulativeError;
- uint count;
-#endif
+ QObject *obj = nullptr; // - object to receive event
+ QTimerInfo **activateRef = nullptr; // - ref from activateTimers
};
-class Q_CORE_EXPORT QTimerInfoList : public QList<QTimerInfo*>
+class Q_CORE_EXPORT QTimerInfoList
{
-#if ((_POSIX_MONOTONIC_CLOCK-0 <= 0) && !defined(Q_OS_MAC)) || defined(QT_BOOTSTRAPPED)
- timespec previousTime;
- clock_t previousTicks;
- int ticksPerSecond;
- int msPerTick;
-
- bool timeChanged(timespec *delta);
- void timerRepair(const timespec &);
-#endif
-
- // state variables used by activateTimers()
- QTimerInfo *firstTimerInfo;
-
public:
+ using Duration = QAbstractEventDispatcher::Duration;
+ using TimerInfo = QAbstractEventDispatcher::TimerInfoV2;
QTimerInfoList();
- timespec currentTime;
- timespec updateCurrentTime();
-
- // must call updateCurrentTime() first!
- void repairTimersIfNeeded();
+ mutable std::chrono::steady_clock::time_point currentTime;
- bool timerWait(timespec &);
+ std::optional<Duration> timerWait();
void timerInsert(QTimerInfo *);
- int timerRemainingTime(int timerId);
+ Duration remainingDuration(Qt::TimerId timerId) const;
- void registerTimer(int timerId, qint64 interval, Qt::TimerType timerType, QObject *object);
- bool unregisterTimer(int timerId);
+ void registerTimer(Qt::TimerId timerId, Duration interval,
+ Qt::TimerType timerType, QObject *object);
+ bool unregisterTimer(Qt::TimerId timerId);
bool unregisterTimers(QObject *object);
- QList<QAbstractEventDispatcher::TimerInfo> registeredTimers(QObject *object) const;
+ QList<TimerInfo> registeredTimers(QObject *object) const;
int activateTimers();
+ bool hasPendingTimers();
+
+ void clearTimers()
+ {
+ qDeleteAll(timers);
+ timers.clear();
+ }
+
+ bool isEmpty() const { return timers.empty(); }
+
+ qsizetype size() const { return timers.size(); }
+
+ auto findTimerById(Qt::TimerId timerId) const
+ {
+ auto matchesId = [timerId](const auto &t) { return t->id == timerId; };
+ return std::find_if(timers.cbegin(), timers.cend(), matchesId);
+ }
+
+private:
+ std::chrono::steady_clock::time_point updateCurrentTime() const;
+
+ // state variables used by activateTimers()
+ QTimerInfo *firstTimerInfo = nullptr;
+ QList<QTimerInfo *> timers;
};
QT_END_NAMESPACE
diff --git a/src/corelib/kernel/qtmetamacros.h b/src/corelib/kernel/qtmetamacros.h
index 96b1ada57e..ae68abcfee 100644
--- a/src/corelib/kernel/qtmetamacros.h
+++ b/src/corelib/kernel/qtmetamacros.h
@@ -1,42 +1,6 @@
-/****************************************************************************
-**
-** Copyright (C) 2019 The Qt Company Ltd.
-** Copyright (C) 2019 Olivier Goffart <ogoffart@woboq.com>
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtCore module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2019 The Qt Company Ltd.
+// Copyright (C) 2019 Olivier Goffart <ogoffart@woboq.com>
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QTMETAMACROS_H
#define QTMETAMACROS_H
@@ -125,26 +89,27 @@ QT_BEGIN_NAMESPACE
# define QT_TR_FUNCTIONS
#endif
-#ifdef Q_CLANG_QDOC
+#ifdef Q_QDOC
#define QT_TR_FUNCTIONS
#endif
-#if defined(Q_CC_INTEL)
-// Cannot redefine the visibility of a method in an exported class
-# define Q_DECL_HIDDEN_STATIC_METACALL
-#else
-# define Q_DECL_HIDDEN_STATIC_METACALL Q_DECL_HIDDEN
-#endif
+#define Q_DECL_HIDDEN_STATIC_METACALL Q_DECL_HIDDEN
-#if defined(Q_CC_CLANG) && Q_CC_CLANG >= 306
-# define Q_OBJECT_NO_OVERRIDE_WARNING QT_WARNING_DISABLE_CLANG("-Winconsistent-missing-override")
-#elif defined(Q_CC_GNU) && !defined(Q_CC_INTEL) && Q_CC_GNU >= 501
+#if defined(Q_CC_CLANG)
+# if Q_CC_CLANG >= 1100
+# define Q_OBJECT_NO_OVERRIDE_WARNING QT_WARNING_DISABLE_CLANG("-Winconsistent-missing-override") QT_WARNING_DISABLE_CLANG("-Wsuggest-override")
+# elif Q_CC_CLANG >= 306
+# define Q_OBJECT_NO_OVERRIDE_WARNING QT_WARNING_DISABLE_CLANG("-Winconsistent-missing-override")
+# 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
-#if defined(Q_CC_GNU) && !defined(Q_CC_INTEL) && Q_CC_GNU >= 600
+#if defined(Q_CC_GNU) && Q_CC_GNU >= 600
# define Q_OBJECT_NO_ATTRIBUTES_WARNING QT_WARNING_DISABLE_GCC("-Wattributes")
#else
# define Q_OBJECT_NO_ATTRIBUTES_WARNING
@@ -164,17 +129,17 @@ private: \
Q_OBJECT_NO_ATTRIBUTES_WARNING \
Q_DECL_HIDDEN_STATIC_METACALL static void qt_static_metacall(QObject *, QMetaObject::Call, int, void **); \
QT_WARNING_POP \
- struct QPrivateSignal {}; \
+ struct QPrivateSignal { explicit QPrivateSignal() = default; }; \
QT_ANNOTATE_CLASS(qt_qobject, "")
/* qmake ignore Q_OBJECT */
#define Q_OBJECT_FAKE Q_OBJECT QT_ANNOTATE_CLASS(qt_fake, "")
#ifndef QT_NO_META_MACROS
-/* qmake ignore Q_GADGET */
-#define Q_GADGET \
+/* qmake ignore Q_GADGET_EXPORT */
+#define Q_GADGET_EXPORT(...) \
public: \
- static const QMetaObject staticMetaObject; \
+ static __VA_ARGS__ const QMetaObject staticMetaObject; \
void qt_check_for_QGADGET_macro(); \
typedef void QtGadgetHelper; \
private: \
@@ -185,7 +150,10 @@ private: \
QT_ANNOTATE_CLASS(qt_qgadget, "") \
/*end*/
-/* qmake ignore Q_NAMESPACE_EXPORT */
+/* qmake ignore Q_GADGET */
+#define Q_GADGET Q_GADGET_EXPORT()
+
+ /* qmake ignore Q_NAMESPACE_EXPORT */
#define Q_NAMESPACE_EXPORT(...) \
extern __VA_ARGS__ const QMetaObject staticMetaObject; \
QT_ANNOTATE_CLASS(qt_qnamespace, "") \
diff --git a/src/corelib/kernel/qtmochelpers.h b/src/corelib/kernel/qtmochelpers.h
new file mode 100644
index 0000000000..9d75177645
--- /dev/null
+++ b/src/corelib/kernel/qtmochelpers.h
@@ -0,0 +1,86 @@
+// Copyright (C) 2022 Intel Corporation.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QTMOCHELPERS_H
+#define QTMOCHELPERS_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists to be used by the code that
+// moc generates. This file will not change quickly, but it over the long term,
+// it will likely change or even be removed.
+//
+// We mean it.
+//
+
+#include <QtCore/qglobal.h>
+
+#include <algorithm> // std::min
+#include <limits>
+
+#if 0
+#pragma qt_no_master_include
+#endif
+
+QT_BEGIN_NAMESPACE
+namespace QtMocHelpers {
+// The maximum Size of a string literal is 2 GB on 32-bit and 4 GB on 64-bit
+// (but the compiler is likely to give up before you get anywhere near that much)
+static constexpr size_t MaxStringSize =
+ (std::min)(size_t((std::numeric_limits<uint>::max)()),
+ size_t((std::numeric_limits<qsizetype>::max)()));
+
+template <uint... Nx> constexpr size_t stringDataSizeHelper(std::integer_sequence<uint, Nx...>)
+{
+ // same as:
+ // return (0 + ... + Nx);
+ // but not using the fold expression to avoid exceeding compiler limits
+ size_t total = 0;
+ uint sizes[] = { Nx... };
+ for (uint n : sizes)
+ total += n;
+ return total;
+}
+
+template <int Count, size_t StringSize> struct StringData
+{
+ static_assert(StringSize <= MaxStringSize, "Meta Object data is too big");
+ uint offsetsAndSizes[Count] = {};
+ char stringdata0[StringSize] = {};
+ constexpr StringData() = default;
+};
+
+template <uint... Nx> constexpr auto stringData(const char (&...strings)[Nx])
+{
+ constexpr size_t StringSize = stringDataSizeHelper<Nx...>({});
+ constexpr size_t Count = 2 * sizeof...(Nx);
+
+ StringData<Count, StringSize> result;
+ const char *inputs[] = { strings... };
+ uint sizes[] = { Nx... };
+
+ uint offset = 0;
+ char *output = result.stringdata0;
+ for (size_t i = 0; i < sizeof...(Nx); ++i) {
+ // copy the input string, including the terminating null
+ uint len = sizes[i];
+ for (uint j = 0; j < len; ++j)
+ output[offset + j] = inputs[i][j];
+ result.offsetsAndSizes[2 * i] = offset + sizeof(result.offsetsAndSizes);
+ result.offsetsAndSizes[2 * i + 1] = len - 1;
+ offset += len;
+ }
+
+ return result;
+}
+
+# define QT_MOC_HAS_STRINGDATA 1
+
+} // namespace QtMocHelpers
+QT_END_NAMESPACE
+
+QT_USE_NAMESPACE
+
+#endif // QTMOCHELPERS_H
diff --git a/src/corelib/kernel/qtranslator.cpp b/src/corelib/kernel/qtranslator.cpp
index 1d0c613c17..ec92404a15 100644
--- a/src/corelib/kernel/qtranslator.cpp
+++ b/src/corelib/kernel/qtranslator.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtCore module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qplatformdefs.h"
@@ -58,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
@@ -75,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
@@ -485,8 +452,8 @@ bool QTranslator::load(const QString & filename, const QString & directory,
QString prefix;
if (QFileInfo(filename).isRelative()) {
prefix = directory;
- if (prefix.length() && !prefix.endsWith(QLatin1Char('/')))
- prefix += QLatin1Char('/');
+ if (prefix.size() && !prefix.endsWith(u'/'))
+ prefix += u'/';
}
const QString suffixOrDotQM = suffix.isNull() ? dotQmLiteral() : suffix;
@@ -508,7 +475,7 @@ bool QTranslator::load(const QString & filename, const QString & directory,
break;
int rightmost = 0;
- for (int i = 0; i < (int)delims.length(); i++) {
+ for (int i = 0; i < (int)delims.size(); i++) {
int k = fname.lastIndexOf(delims[i]);
if (k > rightmost)
rightmost = k;
@@ -530,7 +497,7 @@ bool QTranslatorPrivate::do_load(const QString &realname, const QString &directo
QTranslatorPrivate *d = this;
bool ok = false;
- if (realname.startsWith(QLatin1Char(':'))) {
+ if (realname.startsWith(u':')) {
// If the translation is in a non-compressed resource file, the data is already in
// memory, so no need to use QFile to copy it again.
Q_ASSERT(!d->resource);
@@ -645,8 +612,8 @@ static QString find_translation(const QLocale & locale,
QString path;
if (QFileInfo(filename).isRelative()) {
path = directory;
- if (!path.isEmpty() && !path.endsWith(QLatin1Char('/')))
- path += QLatin1Char('/');
+ if (!path.isEmpty() && !path.endsWith(u'/'))
+ path += u'/';
}
const QString suffixOrDotQM = suffix.isNull() ? dotQmLiteral() : suffix;
@@ -656,19 +623,22 @@ static QString find_translation(const QLocale & locale,
// see http://www.unicode.org/reports/tr35/#LanguageMatching for inspiration
- QStringList languages = locale.uiLanguages();
-#if defined(Q_OS_UNIX)
+ // For each language_country returned by locale.uiLanguages(), add
+ // also a lowercase version to the list. Since these languages are
+ // used to create file names, this is important on case-sensitive
+ // file systems, where otherwise a file called something like
+ // "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(QLocale::TagSeparator::Underscore);
for (int i = languages.size()-1; i >= 0; --i) {
QString lang = languages.at(i);
QString lowerLang = lang.toLower();
if (lang != lowerLang)
languages.insert(i + 1, lowerLang);
}
-#endif
-
- for (QString localeName : qAsConst(languages)) {
- localeName.replace(QLatin1Char('-'), QLatin1Char('_'));
+ for (QString localeName : std::as_const(languages)) {
// try the complete locale name first and progressively truncate from
// the end until a matching language tag is found (with or without suffix)
for (;;) {
@@ -682,7 +652,7 @@ static QString find_translation(const QLocale & locale,
realname.truncate(realNameBaseSize);
- int rightmost = localeName.lastIndexOf(QLatin1Char('_'));
+ int rightmost = localeName.lastIndexOf(u'_');
if (rightmost <= 0)
break; // no truncations anymore, break
localeName.truncate(rightmost);
@@ -945,7 +915,7 @@ end:
if (!tn)
return QString();
QString str(tn_length / 2, Qt::Uninitialized);
- qFromBigEndian<ushort>(tn, str.length(), str.data());
+ qFromBigEndian<char16_t>(tn, str.size(), str.data());
return str;
}
diff --git a/src/corelib/kernel/qtranslator.h b/src/corelib/kernel/qtranslator.h
index 1296186134..95d99e8ca8 100644
--- a/src/corelib/kernel/qtranslator.h
+++ b/src/corelib/kernel/qtranslator.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2020 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtCore module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2020 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 QTRANSLATOR_H
#define QTRANSLATOR_H
diff --git a/src/corelib/kernel/qtranslator_p.h b/src/corelib/kernel/qtranslator_p.h
index e148637ac6..8b4b8b8c48 100644
--- a/src/corelib/kernel/qtranslator_p.h
+++ b/src/corelib/kernel/qtranslator_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtCore module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// 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 QTRANSLATOR_P_H
#define QTRANSLATOR_P_H
diff --git a/src/corelib/kernel/qvariant.cpp b/src/corelib/kernel/qvariant.cpp
index b34da6ca18..92a44c462b 100644
--- a/src/corelib/kernel/qvariant.cpp
+++ b/src/corelib/kernel/qvariant.cpp
@@ -1,50 +1,15 @@
-/****************************************************************************
-**
-** Copyright (C) 2020 The Qt Company Ltd.
-** Copyright (C) 2018 Intel Corporation.
-** Copyright (C) 2015 Olivier Goffart <ogoffart@woboq.com>
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtCore module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#include "qvariant.h"
+// Copyright (C) 2022 The Qt Company Ltd.
+// Copyright (C) 2021 Intel Corporation.
+// Copyright (C) 2015 Olivier Goffart <ogoffart@woboq.com>
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "qvariant_p.h"
#include "qbitarray.h"
#include "qbytearray.h"
#include "qdatastream.h"
#include "qdebug.h"
#include "qmap.h"
+#include "qhash.h"
#include "qdatetime.h"
#if QT_CONFIG(easingcurve)
#include "qeasingcurve.h"
@@ -71,7 +36,6 @@
#include "qjsondocument.h"
#include "qbytearraylist.h"
#endif
-#include "private/qvariant_p.h"
#include "private/qlocale_p.h"
#include "qmetatype_p.h"
#include <qmetaobject.h>
@@ -83,32 +47,43 @@
#include "qline.h"
#endif
+#include <memory>
+
#include <cmath>
#include <float.h>
#include <cstring>
QT_BEGIN_NAMESPACE
+using namespace Qt::StringLiterals;
+
namespace { // anonymous used to hide QVariant handlers
-/*!
- \internal
- */
+static qlonglong qMetaTypeNumberBySize(const QVariant::Private *d)
+{
+ switch (d->typeInterface()->size) {
+ case 1:
+ return d->get<signed char>();
+ case 2:
+ return d->get<short>();
+ case 4:
+ return d->get<int>();
+ case 8:
+ return d->get<qlonglong>();
+ }
+ Q_UNREACHABLE_RETURN(0);
+}
+
static qlonglong qMetaTypeNumber(const QVariant::Private *d)
{
- switch (d->typeId()) {
+ switch (d->typeInterface()->typeId) {
case QMetaType::Int:
- return d->get<int>();
case QMetaType::LongLong:
- return d->get<qlonglong>();
case QMetaType::Char:
- return qlonglong(d->get<char>());
case QMetaType::SChar:
- return qlonglong(d->get<signed char>());
case QMetaType::Short:
- return qlonglong(d->get<short>());
case QMetaType::Long:
- return qlonglong(d->get<long>());
+ return qMetaTypeNumberBySize(d);
case QMetaType::Float:
return qRound64(d->get<float>());
case QMetaType::Double:
@@ -120,54 +95,46 @@ static qlonglong qMetaTypeNumber(const QVariant::Private *d)
return d->get<QCborValue>().toInteger();
#endif
}
- Q_ASSERT(false);
- return 0;
+ Q_UNREACHABLE_RETURN(0);
}
static qulonglong qMetaTypeUNumber(const QVariant::Private *d)
{
- switch (d->typeId()) {
- case QMetaType::UInt:
- return d->get<unsigned int>();
- case QMetaType::ULongLong:
- return d->get<qulonglong>();
- case QMetaType::UChar:
+ switch (d->typeInterface()->size) {
+ case 1:
return d->get<unsigned char>();
- case QMetaType::UShort:
+ case 2:
return d->get<unsigned short>();
- case QMetaType::ULong:
- return d->get<unsigned long>();
+ case 4:
+ return d->get<unsigned int>();
+ case 8:
+ return d->get<qulonglong>();
}
- Q_ASSERT(false);
- return 0;
+ Q_UNREACHABLE_RETURN(0);
}
-static qlonglong qConvertToNumber(const QVariant::Private *d, bool *ok, bool allowStringToBool = false)
+static std::optional<qlonglong> qConvertToNumber(const QVariant::Private *d, bool allowStringToBool = false)
{
- *ok = true;
-
- switch (uint(d->typeId())) {
+ bool ok;
+ switch (d->typeInterface()->typeId) {
case QMetaType::QString: {
const QString &s = d->get<QString>();
- qlonglong l = s.toLongLong(ok);
- if (*ok)
+ if (qlonglong l = s.toLongLong(&ok); ok)
return l;
if (allowStringToBool) {
- if (s == QLatin1String("false") || s == QLatin1String("0")) {
- *ok = true;
+ if (s == "false"_L1 || s == "0"_L1)
return 0;
- }
- if (s == QLatin1String("true") || s == QLatin1String("1")) {
- *ok = true;
+ if (s == "true"_L1 || s == "1"_L1)
return 1;
- }
}
- return 0;
+ return std::nullopt;
}
case QMetaType::QChar:
return d->get<QChar>().unicode();
case QMetaType::QByteArray:
- return d->get<QByteArray>().toLongLong(ok);
+ if (qlonglong l = d->get<QByteArray>().toLongLong(&ok); ok)
+ return l;
+ return std::nullopt;
case QMetaType::Bool:
return qlonglong(d->get<bool>());
#ifndef QT_BOOTSTRAPPED
@@ -192,47 +159,42 @@ static qlonglong qConvertToNumber(const QVariant::Private *d, bool *ok, bool all
case QMetaType::ULongLong:
case QMetaType::UInt:
case QMetaType::UChar:
+ case QMetaType::Char16:
+ case QMetaType::Char32:
case QMetaType::UShort:
case QMetaType::ULong:
-
return qlonglong(qMetaTypeUNumber(d));
}
- QMetaType typeInfo = d->type();
- if (typeInfo.flags() & QMetaType::IsEnumeration
- || d->typeId() == QMetaType::QCborSimpleType) {
- switch (typeInfo.sizeOf()) {
- case 1:
- return d->get<signed char>();
- case 2:
- return d->get<short>();
- case 4:
- return d->get<int>();
- case 8:
- return d->get<qlonglong>();
- }
- }
+ if (d->typeInterface()->flags & QMetaType::IsEnumeration
+ || d->typeInterface()->typeId == QMetaType::QCborSimpleType)
+ return qMetaTypeNumberBySize(d);
- *ok = false;
- return Q_INT64_C(0);
+ return std::nullopt;
}
-static qreal qConvertToRealNumber(const QVariant::Private *d, bool *ok)
+static std::optional<double> qConvertToRealNumber(const QVariant::Private *d)
{
- *ok = true;
- switch (uint(d->typeId())) {
+ bool ok;
+ switch (d->typeInterface()->typeId) {
case QMetaType::QString:
- return d->get<QString>().toDouble(ok);
+ if (double r = d->get<QString>().toDouble(&ok); ok)
+ 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 double(d->get<qfloat16>());
case QMetaType::ULongLong:
case QMetaType::UInt:
case QMetaType::UChar:
+ case QMetaType::Char16:
+ 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();
@@ -241,56 +203,116 @@ static qreal qConvertToRealNumber(const QVariant::Private *d, bool *ok)
#endif
default:
// includes enum conversion as well as invalid types
- return qreal(qConvertToNumber(d, ok));
+ if (std::optional<qlonglong> l = qConvertToNumber(d))
+ return double(*l);
+ return std::nullopt;
}
}
-// the type of d has already been set, but other field are not set
-static void customConstruct(QVariant::Private *d, const void *copy)
+static bool isValidMetaTypeForVariant(const QtPrivate::QMetaTypeInterface *iface, const void *copy)
{
- QtPrivate::QMetaTypeInterface *iface = d->typeInterface();
- if (!(iface && iface->size)) {
- *d = QVariant::Private();
- return;
+ using namespace QtMetaTypePrivate;
+ if (!iface || iface->size == 0)
+ return false;
+
+ Q_ASSERT(!isInterfaceFor<void>(iface)); // only void should have size 0
+ if (!isCopyConstructible(iface) || !isDestructible(iface)) {
+ // all meta types must be copyable (because QVariant is) and
+ // destructible (because QVariant owns it)
+ qWarning("QVariant: Provided metatype for '%s' does not support destruction and "
+ "copy construction", iface->name);
+ return false;
+ }
+ if (!copy && !isDefaultConstructible(iface)) {
+ // non-default-constructible types are acceptable, but not if you're
+ // asking us to construct from nothing
+ qWarning("QVariant: Cannot create type '%s' without a default constructor", iface->name);
+ return false;
}
+ 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,
+ std::conditional_t<moveOption == ForceMove, void *, const void *> copy)
+{
+ using namespace QtMetaTypePrivate;
+ Q_ASSERT(iface);
+ Q_ASSERT(iface->size);
+ Q_ASSERT(!isInterfaceFor<void>(iface));
+ 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.
+ // ### Qt 7: remove nullptr_t special casing
+ d->is_null = !copy QT6_ONLY(|| isInterfaceFor<std::nullptr_t>(iface));
+
if (QVariant::Private::canUseInternalSpace(iface)) {
- // QVariant requires type to be copy and default constructible
- Q_ASSERT(iface->copyCtr);
- Q_ASSERT(iface->defaultCtr);
- if (copy)
- iface->copyCtr(iface, &d->data, copy);
- else
- iface->defaultCtr(iface, &d->data);
d->is_shared = false;
- } else {
- d->data.shared = QVariant::PrivateShared::create(iface);
- if (copy)
- iface->copyCtr(iface, d->data.shared->data(), copy);
+ if (!copy && !iface->defaultCtr)
+ 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
- iface->defaultCtr(iface, d->data.shared->data());
+ construct(iface, d->data.data, copy);
+ } else {
+ d->data.shared = customConstructShared(iface->size, iface->alignment, [=](void *where) {
+ if constexpr (moveOption == ForceMove && nullability == NonNull)
+ moveConstruct(iface, where, copy);
+ else
+ construct(iface, where, copy);
+ });
d->is_shared = true;
}
- // 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.
- d->is_null = !copy || QMetaType(iface) == QMetaType::fromType<std::nullptr_t>();
}
static void customClear(QVariant::Private *d)
{
- auto iface = reinterpret_cast<QtPrivate::QMetaTypeInterface *>(d->packedType << 2);
+ const QtPrivate::QMetaTypeInterface *iface = d->typeInterface();
if (!iface)
return;
if (!d->is_shared) {
- if (iface->dtor)
- iface->dtor(iface, &d->data);
+ QtMetaTypePrivate::destruct(iface, d->data.data);
} else {
- if (iface->dtor)
- iface->dtor(iface, d->data.shared->data());
+ QtMetaTypePrivate::destruct(iface, d->data.shared->data());
QVariant::PrivateShared::free(d->data.shared);
}
}
+static QVariant::Private clonePrivate(const QVariant::Private &other)
+{
+ QVariant::Private d = other;
+ if (d.is_shared) {
+ d.data.shared->ref.ref();
+ } else if (const QtPrivate::QMetaTypeInterface *iface = d.typeInterface()) {
+ Q_ASSERT(d.canUseInternalSpace(iface));
+
+ // if not trivially copyable, ask to copy
+ if (iface->copyCtr)
+ QtMetaTypePrivate::copyConstruct(iface, d.data.data, other.data.data);
+ }
+ return d;
+}
} // anonymous used to hide QVariant handlers
@@ -302,6 +324,7 @@ static void customClear(QVariant::Private *d)
\ingroup objectmodel
\ingroup shared
+ \compares equality
Because C++ forbids unions from including types that have
non-default constructors or destructors, most interesting Qt
@@ -399,7 +422,7 @@ static void customClear(QVariant::Private *d)
\value EasingCurve a QEasingCurve
\value Uuid a QUuid
\value ModelIndex a QModelIndex
- \value PersistentModelIndex a QPersistentModelIndex (since 5.5)
+ \value [since 5.5] PersistentModelIndex a QPersistentModelIndex
\value Font a QFont
\value Hash a QVariantHash
\value Icon a QIcon
@@ -494,24 +517,18 @@ void QVariant::create(int type, const void *copy)
*/
void QVariant::create(QMetaType type, const void *copy)
{
- d = Private(type);
- customConstruct(&d, 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);
}
@@ -523,32 +540,121 @@ QVariant::~QVariant()
*/
QVariant::QVariant(const QVariant &p)
- : d(p.d)
+ : d(clonePrivate(p.d))
{
- if (d.is_shared) {
- d.data.shared->ref.ref();
- return;
+}
+
+/*!
+ \fn template <typename T, typename... Args, QVariant::if_constructible<T, Args...> = true> QVariant::QVariant(std::in_place_type_t<T>, Args&&... args) noexcept(is_noexcept_constructible<q20::remove_cvref_t<T>, Args...>::value)
+
+ \since 6.6
+ Constructs a new variant containing a value of type \c T. The contained
+ value is is initialized with the arguments
+ \c{std::forward<Args>(args)...}.
+
+ This overload only participates in overload resolution if \c T can be
+ constructed from \a args.
+
+ This constructor is provided for STL/std::any compatibility.
+
+ \overload
+ */
+
+/*!
+
+ \fn template <typename T, typename U, typename... Args, QVariant::if_constructible<T, std::initializer_list<U> &, Args...> = true> explicit QVariant::QVariant(std::in_place_type_t<T>, std::initializer_list<U> il, Args&&... args) noexcept(is_noexcept_constructible<q20::remove_cvref_t<T>, std::initializer_list<U> &, Args... >::value)
+
+ \since 6.6
+ \overload
+ This overload exists to support types with constructors taking an
+ \c initializer_list. It behaves otherwise equivalent to the
+ non-initializer list \c{in_place_type_t} overload.
+*/
+
+
+/*!
+ \fn template <typename T, typename... Args, QVariant::if_constructible<T, Args...> = true> QVariant::emplace(Args&&... args)
+
+ \since 6.6
+ Replaces the object currently held in \c{*this} with an object of
+ type \c{T}, constructed from \a{args}\c{...}. If \c{*this} was non-null,
+ the previously held object is destroyed first.
+ If possible, this method will reuse memory allocated by the QVariant.
+ Returns a reference to the newly-created object.
+ */
+
+/*!
+ \fn template <typename T, typename U, typename... Args, QVariant::if_constructible<T, std::initializer_list<U> &, Args...> = true> QVariant::emplace(std::initializer_list<U> list, Args&&... args)
+
+ \since 6.6
+ \overload
+ This overload exists to support types with constructors taking an
+ \c initializer_list. It behaves otherwise equivalent to the
+ non-initializer list overload.
+*/
+
+QVariant::QVariant(std::in_place_t, QMetaType type) : d(type.iface())
+{
+ // we query the metatype instead of detecting it at compile time
+ // so that we can change relocatability of internal types
+ if (!Private::canUseInternalSpace(type.iface())) {
+ d.data.shared = PrivateShared::create(type.sizeOf(), type.alignOf());
+ d.is_shared = true;
}
- QtPrivate::QMetaTypeInterface *iface = d.typeInterface();
- auto other = p.constData();
- if (iface) {
- if (other)
- iface->copyCtr(iface, &d.data, other);
- else
- iface->defaultCtr(iface, &d.data);
+}
+
+/*!
+ \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)
+ \fn QVariant::QVariant(const QString &val) noexcept
Constructs a new variant with a string value, \a val.
*/
/*!
- \fn QVariant::QVariant(QLatin1String val)
+ \fn QVariant::QVariant(QLatin1StringView val)
- Constructs a new variant with a string value, \a val.
+ Constructs a new variant with a QString value from the Latin-1
+ string viewed by \a val.
*/
/*!
@@ -567,37 +673,37 @@ QVariant::QVariant(const QVariant &p)
*/
/*!
- \fn QVariant::QVariant(const QStringList &val)
+ \fn QVariant::QVariant(const QStringList &val) noexcept
Constructs a new variant with a string list value, \a val.
*/
/*!
- \fn QVariant::QVariant(const QMap<QString, QVariant> &val)
+ \fn QVariant::QVariant(const QMap<QString, QVariant> &val) noexcept
Constructs a new variant with a map of \l {QVariant}s, \a val.
*/
/*!
- \fn QVariant::QVariant(const QHash<QString, QVariant> &val)
+ \fn QVariant::QVariant(const QHash<QString, QVariant> &val) noexcept
Constructs a new variant with a hash of \l {QVariant}s, \a val.
*/
/*!
- \fn QVariant::QVariant(QDate val)
+ \fn QVariant::QVariant(QDate val) noexcept
Constructs a new variant with a date value, \a val.
*/
/*!
- \fn QVariant::QVariant(QTime val)
+ \fn QVariant::QVariant(QTime val) noexcept
Constructs a new variant with a time value, \a val.
*/
/*!
- \fn QVariant::QVariant(const QDateTime &val)
+ \fn QVariant::QVariant(const QDateTime &val) noexcept
Constructs a new variant with a date/time value, \a val.
*/
@@ -611,14 +717,14 @@ QVariant::QVariant(const QVariant &p)
/*!
\since 5.0
- \fn QVariant::QVariant(const QUuid &val)
+ \fn QVariant::QVariant(QUuid val) noexcept
Constructs a new variant with an uuid value, \a val.
*/
/*!
\since 5.0
- \fn QVariant::QVariant(const QModelIndex &val)
+ \fn QVariant::QVariant(const QModelIndex &val) noexcept
Constructs a new variant with a QModelIndex value, \a val.
*/
@@ -659,135 +765,135 @@ QVariant::QVariant(const QVariant &p)
*/
/*!
- \fn QVariant::QVariant(const QByteArray &val)
+ \fn QVariant::QVariant(const QByteArray &val) noexcept
Constructs a new variant with a bytearray value, \a val.
*/
/*!
- \fn QVariant::QVariant(const QBitArray &val)
+ \fn QVariant::QVariant(const QBitArray &val) noexcept
Constructs a new variant with a bitarray value, \a val.
*/
/*!
- \fn QVariant::QVariant(const QPoint &val)
+ \fn QVariant::QVariant(QPoint val) noexcept
Constructs a new variant with a point value of \a val.
*/
/*!
- \fn QVariant::QVariant(const QPointF &val)
+ \fn QVariant::QVariant(QPointF val) noexcept
Constructs a new variant with a point value of \a val.
*/
/*!
- \fn QVariant::QVariant(const QRectF &val)
+ \fn QVariant::QVariant(QRectF val)
Constructs a new variant with a rect value of \a val.
*/
/*!
- \fn QVariant::QVariant(const QLineF &val)
+ \fn QVariant::QVariant(QLineF val) noexcept
Constructs a new variant with a line value of \a val.
*/
/*!
- \fn QVariant::QVariant(const QLine &val)
+ \fn QVariant::QVariant(QLine val) noexcept
Constructs a new variant with a line value of \a val.
*/
/*!
- \fn QVariant::QVariant(const QRect &val)
+ \fn QVariant::QVariant(QRect val) noexcept
Constructs a new variant with a rect value of \a val.
*/
/*!
- \fn QVariant::QVariant(const QSize &val)
+ \fn QVariant::QVariant(QSize val) noexcept
Constructs a new variant with a size value of \a val.
*/
/*!
- \fn QVariant::QVariant(const QSizeF &val)
+ \fn QVariant::QVariant(QSizeF val) noexcept
Constructs a new variant with a size value of \a val.
*/
/*!
- \fn QVariant::QVariant(const QUrl &val)
+ \fn QVariant::QVariant(const QUrl &val) noexcept
Constructs a new variant with a url value of \a val.
*/
/*!
- \fn QVariant::QVariant(int val)
+ \fn QVariant::QVariant(int val) noexcept
Constructs a new variant with an integer value, \a val.
*/
/*!
- \fn QVariant::QVariant(uint val)
+ \fn QVariant::QVariant(uint val) noexcept
Constructs a new variant with an unsigned integer value, \a val.
*/
/*!
- \fn QVariant::QVariant(qlonglong val)
+ \fn QVariant::QVariant(qlonglong val) noexcept
Constructs a new variant with a long long integer value, \a val.
*/
/*!
- \fn QVariant::QVariant(qulonglong val)
+ \fn QVariant::QVariant(qulonglong val) noexcept
Constructs a new variant with an unsigned long long integer value, \a val.
*/
/*!
- \fn QVariant::QVariant(bool val)
+ \fn QVariant::QVariant(bool val) noexcept
Constructs a new variant with a boolean value, \a val.
*/
/*!
- \fn QVariant::QVariant(double val)
+ \fn QVariant::QVariant(double val) noexcept
Constructs a new variant with a floating point value, \a val.
*/
/*!
- \fn QVariant::QVariant(float val)
+ \fn QVariant::QVariant(float val) noexcept
Constructs a new variant with a floating point value, \a val.
\since 4.6
*/
/*!
- \fn QVariant::QVariant(const QList<QVariant> &val)
+ \fn QVariant::QVariant(const QList<QVariant> &val) noexcept
Constructs a new variant with a list value, \a val.
*/
/*!
- \fn QVariant::QVariant(QChar c)
+ \fn QVariant::QVariant(QChar c) noexcept
Constructs a new variant with a char value, \a c.
*/
/*!
- \fn QVariant::QVariant(const QLocale &l)
+ \fn QVariant::QVariant(const QLocale &l) noexcept
Constructs a new variant with a locale value, \a l.
*/
/*!
- \fn QVariant::QVariant(const QRegularExpression &re)
+ \fn QVariant::QVariant(const QRegularExpression &re) noexcept
\since 5.0
@@ -805,149 +911,96 @@ QVariant::QVariant(const QVariant &p)
*/
/*!
- Constructs variant of type \a type, and initializes with
- \a copy if \a copy is not \nullptr.
+ Constructs a variant of type \a type, and initializes it with
+ a copy of \c{*copy} if \a copy is not \nullptr (in which case, \a copy
+ must point to an object of type \a type).
- Note that you have to pass the address of the variable you want stored.
+ Note that you have to pass the address of the object you want stored.
Usually, you never have to use this constructor, use QVariant::fromValue()
instead to construct variants from the pointer types represented by
\c QMetaType::VoidStar, and \c QMetaType::QObjectStar.
- \sa QVariant::fromValue(), QMetaType::Type
+ 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::fromMetaType, QVariant::fromValue(), QMetaType::Type
*/
-QVariant::QVariant(QMetaType type, const void *copy) : d(type)
-{
- customConstruct(&d, copy);
-}
-
-QVariant::QVariant(int val)
- : d(QMetaType::fromType<int>())
-{ d.set(val); }
-QVariant::QVariant(uint val)
- : d(QMetaType::fromType<uint>())
-{ d.set(val); }
-QVariant::QVariant(qlonglong val)
- : d(QMetaType::fromType<qlonglong>())
-{ d.set(val); }
-QVariant::QVariant(qulonglong val)
- : d(QMetaType::fromType<qulonglong>())
-{ d.set(val); }
-QVariant::QVariant(bool val)
- : d(QMetaType::fromType<bool>())
-{ d.set(val); }
-QVariant::QVariant(double val)
- : d(QMetaType::fromType<double>())
-{ d.set(val); }
-QVariant::QVariant(float val)
- : d(QMetaType::fromType<float>())
-{ d.set(val); }
-
-QVariant::QVariant(const QByteArray &val)
- : d(QMetaType::fromType<QByteArray>())
-{ v_construct<QByteArray>(&d, val); }
-QVariant::QVariant(const QBitArray &val)
- : d(QMetaType::fromType<QBitArray>())
-{ v_construct<QBitArray>(&d, val); }
-QVariant::QVariant(const QString &val)
- : d(QMetaType::fromType<QString>())
-{ v_construct<QString>(&d, val); }
-QVariant::QVariant(QChar val)
- : d(QMetaType::fromType<QChar>())
-{ v_construct<QChar>(&d, val); }
-QVariant::QVariant(QLatin1String val)
- : d(QMetaType::fromType<QString>())
-{ v_construct<QString>(&d, val); }
-QVariant::QVariant(const QStringList &val)
- : d(QMetaType::fromType<QStringList>())
-{ v_construct<QStringList>(&d, val); }
-
-QVariant::QVariant(QDate val)
- : d(QMetaType::fromType<QDate>())
-{ v_construct<QDate>(&d, val); }
-QVariant::QVariant(QTime val)
- : d(QMetaType::fromType<QTime>())
-{ v_construct<QTime>(&d, val); }
-QVariant::QVariant(const QDateTime &val)
- : d(QMetaType::fromType<QDateTime>())
-{ v_construct<QDateTime>(&d, val); }
+QVariant::QVariant(QMetaType type, const void *copy)
+ : d()
+{
+ *this = fromMetaType(type, copy);
+}
+
+QVariant::QVariant(int val) noexcept : d(std::piecewise_construct_t{}, val) {}
+QVariant::QVariant(uint val) noexcept : d(std::piecewise_construct_t{}, val) {}
+QVariant::QVariant(qlonglong val) noexcept : d(std::piecewise_construct_t{}, val) {}
+QVariant::QVariant(qulonglong val) noexcept : d(std::piecewise_construct_t{}, val) {}
+QVariant::QVariant(bool val) noexcept : d(std::piecewise_construct_t{}, val) {}
+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) {}
+
+QVariant::QVariant(QDate val) noexcept : d(std::piecewise_construct_t{}, val) {}
+QVariant::QVariant(QTime val) noexcept : d(std::piecewise_construct_t{}, val) {}
+QVariant::QVariant(const QDateTime &val) noexcept : d(std::piecewise_construct_t{}, val) {}
+
+QVariant::QVariant(const QList<QVariant> &list) noexcept : d(std::piecewise_construct_t{}, list) {}
+QVariant::QVariant(const QMap<QString, QVariant> &map) noexcept : d(std::piecewise_construct_t{}, map) {}
+QVariant::QVariant(const QHash<QString, QVariant> &hash) noexcept : d(std::piecewise_construct_t{}, hash) {}
+
+QVariant::QVariant(QLatin1StringView val) : QVariant(QString(val)) {}
+
#if QT_CONFIG(easingcurve)
-QVariant::QVariant(const QEasingCurve &val)
- : d(QMetaType::fromType<QEasingCurve>())
-{ v_construct<QEasingCurve>(&d, val); }
+QVariant::QVariant(const QEasingCurve &val) : d(std::piecewise_construct_t{}, val) {}
#endif
-QVariant::QVariant(const QList<QVariant> &list)
- : d(QMetaType::fromType<QList<QVariant>>())
-{ v_construct<QVariantList>(&d, list); }
-QVariant::QVariant(const QMap<QString, QVariant> &map)
- : d(QMetaType::fromType<QMap<QString, QVariant>>())
-{ v_construct<QVariantMap>(&d, map); }
-QVariant::QVariant(const QHash<QString, QVariant> &hash)
- : d(QMetaType::fromType<QHash<QString, QVariant>>())
-{ v_construct<QVariantHash>(&d, hash); }
#ifndef QT_NO_GEOM_VARIANT
-QVariant::QVariant(const QPoint &pt)
- : d(QMetaType::fromType<QPoint>())
-{ v_construct<QPoint>(&d, pt); }
-QVariant::QVariant(const QPointF &pt)
- : d(QMetaType::fromType<QPointF>())
-{ v_construct<QPointF>(&d, pt); }
-QVariant::QVariant(const QRectF &r)
- : d(QMetaType::fromType<QRectF>())
-{ v_construct<QRectF>(&d, r); }
-QVariant::QVariant(const QLineF &l)
- : d(QMetaType::fromType<QLineF>())
-{ v_construct<QLineF>(&d, l); }
-QVariant::QVariant(const QLine &l)
- : d(QMetaType::fromType<QLine>())
-{ v_construct<QLine>(&d, l); }
-QVariant::QVariant(const QRect &r)
- : d(QMetaType::fromType<QRect>())
-{ v_construct<QRect>(&d, r); }
-QVariant::QVariant(const QSize &s)
- : d(QMetaType::fromType<QSize>())
-{ v_construct<QSize>(&d, s); }
-QVariant::QVariant(const QSizeF &s)
- : d(QMetaType::fromType<QSizeF>())
-{ v_construct<QSizeF>(&d, s); }
+QVariant::QVariant(QPoint pt) noexcept
+ : d(std::piecewise_construct_t{}, pt) {}
+QVariant::QVariant(QPointF pt) noexcept(Private::FitsInInternalSize<sizeof(qreal) * 2>)
+ : d(std::piecewise_construct_t{}, pt) {}
+QVariant::QVariant(QRect r) noexcept(Private::FitsInInternalSize<sizeof(int) * 4>)
+ : d(std::piecewise_construct_t{}, r) {}
+QVariant::QVariant(QRectF r) noexcept(Private::FitsInInternalSize<sizeof(qreal) * 4>)
+ : d(std::piecewise_construct_t{}, r) {}
+QVariant::QVariant(QLine l) noexcept(Private::FitsInInternalSize<sizeof(int) * 4>)
+ : d(std::piecewise_construct_t{}, l) {}
+QVariant::QVariant(QLineF l) noexcept(Private::FitsInInternalSize<sizeof(qreal) * 4>)
+ : d(std::piecewise_construct_t{}, l) {}
+QVariant::QVariant(QSize s) noexcept
+ : d(std::piecewise_construct_t{}, s) {}
+QVariant::QVariant(QSizeF s) noexcept(Private::FitsInInternalSize<sizeof(qreal) * 2>)
+ : d(std::piecewise_construct_t{}, s) {}
#endif
#ifndef QT_BOOTSTRAPPED
-QVariant::QVariant(const QUrl &u)
- : d(QMetaType::fromType<QUrl>())
-{ v_construct<QUrl>(&d, u); }
+QVariant::QVariant(const QUrl &u) noexcept : d(std::piecewise_construct_t{}, u) {}
#endif
-QVariant::QVariant(const QLocale &l)
- : d(QMetaType::fromType<QLocale>())
-{ v_construct<QLocale>(&d, l); }
+QVariant::QVariant(const QLocale &l) noexcept : d(std::piecewise_construct_t{}, l) {}
#if QT_CONFIG(regularexpression)
-QVariant::QVariant(const QRegularExpression &re)
- : d(QMetaType::fromType<QRegularExpression>())
-{ v_construct<QRegularExpression>(&d, re); }
+QVariant::QVariant(const QRegularExpression &re) noexcept : d(std::piecewise_construct_t{}, re) {}
#endif // QT_CONFIG(regularexpression)
-QVariant::QVariant(const QUuid &uuid)
- : d(QMetaType::fromType<QUuid>())
-{ v_construct<QUuid>(&d, uuid); }
+QVariant::QVariant(QUuid uuid) noexcept(Private::FitsInInternalSize<16>) : d(std::piecewise_construct_t{}, uuid) {}
#ifndef QT_BOOTSTRAPPED
-QVariant::QVariant(const QJsonValue &jsonValue)
- : d(QMetaType::fromType<QJsonValue>())
-{ v_construct<QJsonValue>(&d, jsonValue); }
-QVariant::QVariant(const QJsonObject &jsonObject)
- : d(QMetaType::fromType<QJsonObject>())
-{ v_construct<QJsonObject>(&d, jsonObject); }
-QVariant::QVariant(const QJsonArray &jsonArray)
- : d(QMetaType::fromType<QJsonArray>())
-{ v_construct<QJsonArray>(&d, jsonArray); }
-QVariant::QVariant(const QJsonDocument &jsonDocument)
- : d(QMetaType::fromType<QJsonDocument>())
-{ v_construct<QJsonDocument>(&d, jsonDocument); }
+QVariant::QVariant(const QJsonValue &jsonValue) noexcept(Private::FitsInInternalSize<sizeof(CborValueStandIn)>)
+ : d(std::piecewise_construct_t{}, jsonValue)
+{ static_assert(sizeof(CborValueStandIn) == sizeof(QJsonValue)); }
+QVariant::QVariant(const QJsonObject &jsonObject) noexcept : d(std::piecewise_construct_t{}, jsonObject) {}
+QVariant::QVariant(const QJsonArray &jsonArray) noexcept : d(std::piecewise_construct_t{}, jsonArray) {}
+QVariant::QVariant(const QJsonDocument &jsonDocument) : d(std::piecewise_construct_t{}, jsonDocument) {}
#endif // QT_BOOTSTRAPPED
#if QT_CONFIG(itemmodel)
-QVariant::QVariant(const QModelIndex &modelIndex)
- : d(QMetaType::fromType<QModelIndex>())
-{ v_construct<QModelIndex>(&d, modelIndex); }
-QVariant::QVariant(const QPersistentModelIndex &modelIndex)
- : d(QMetaType::fromType<QPersistentModelIndex>())
-{ v_construct<QPersistentModelIndex>(&d, modelIndex); }
+QVariant::QVariant(const QModelIndex &modelIndex) noexcept(Private::FitsInInternalSize<8 + 2 * sizeof(quintptr)>)
+ : d(std::piecewise_construct_t{}, modelIndex) {}
+QVariant::QVariant(const QPersistentModelIndex &modelIndex) : d(std::piecewise_construct_t{}, modelIndex) {}
#endif
/*! \fn QVariant::Type QVariant::type() const
@@ -1014,21 +1067,7 @@ QVariant &QVariant::operator=(const QVariant &variant)
return *this;
clear();
- if (variant.d.is_shared) {
- variant.d.data.shared->ref.ref();
- d = variant.d;
- } else {
- d = variant.d;
- QtPrivate::QMetaTypeInterface *iface = d.typeInterface();
- const void *other = variant.constData();
- if (iface) {
- if (other)
- iface->copyCtr(iface, &d, other);
- else
- iface->defaultCtr(iface, &d);
- }
- }
-
+ d = clonePrivate(variant.d);
return *this;
}
@@ -1051,8 +1090,10 @@ void QVariant::detach()
if (!d.is_shared || d.data.shared->ref.loadRelaxed() == 1)
return;
- Private dd(d.type());
- customConstruct(&dd, constData());
+ Q_ASSERT(isValidMetaTypeForVariant(d.typeInterface(), constData()));
+ Private dd(d.typeInterface());
+ // 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;
@@ -1081,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 = {};
}
@@ -1098,7 +1139,7 @@ void QVariant::clear()
/*!
\fn QVariant::Type QVariant::nameToType(const char *name)
- \deprecated [6.0] Use \c QMetaType.fromName(name).id() instead
+ \deprecated [6.0] Use \c QMetaType::fromName(name).id() instead
Converts the string representation of the storage type given in \a
name, to its enum representation.
@@ -1243,7 +1284,7 @@ void QVariant::load(QDataStream &s)
void *data = const_cast<void *>(constData());
if (!d.type().load(s, data)) {
s.setStatus(QDataStream::ReadCorruptData);
- qWarning("QVariant::load: unable to load type %d.", d.typeId());
+ qWarning("QVariant::load: unable to load type %d.", d.type().id());
}
}
@@ -1255,7 +1296,7 @@ void QVariant::load(QDataStream &s)
*/
void QVariant::save(QDataStream &s) const
{
- quint32 typeId = d.typeId();
+ quint32 typeId = d.type().id();
bool saveAsUserType = false;
if (typeId >= QMetaType::User) {
typeId = QMetaType::User;
@@ -1314,13 +1355,17 @@ void QVariant::save(QDataStream &s) const
}
}
const char *typeName = nullptr;
- if (saveAsUserType)
- typeName = d.type().name();
+ if (saveAsUserType) {
+ if (s.version() < QDataStream::Qt_6_0)
+ typeName = QtMetaTypePrivate::typedefNameForType(d.type().d_ptr);
+ if (!typeName)
+ typeName = d.type().name();
+ }
s << typeId;
if (s.version() >= QDataStream::Qt_4_2)
s << qint8(d.is_null);
if (typeName)
- s << d.type().name();
+ s << typeName;
if (!isValid()) {
if (s.version() < QDataStream::Qt_5_0)
@@ -1330,16 +1375,28 @@ void QVariant::save(QDataStream &s) const
if (!d.type().save(s, constData())) {
qWarning("QVariant::save: unable to save type '%s' (type id: %d).\n",
- d.type().name(), d.typeId());
+ d.type().name(), d.type().id());
Q_ASSERT_X(false, "QVariant::save", "Invalid type to save");
}
}
/*!
\since 4.4
+ \relates QVariant
Reads a variant \a p from the stream \a s.
+ \note If the stream contains types that aren't the built-in ones (see \l
+ QMetaType::Type), those types must be registered using qRegisterMetaType()
+ or QMetaType::registerType() before the variant can be properly loaded. If
+ an unregistered type is found, QVariant will set the corrupt flag in the
+ stream, stop processing and print a warning. For example, for QList<int>
+ it would print the following:
+
+ \quotation
+ QVariant::load: unknown user type with name QList<int>
+ \endquotation
+
\sa{Serializing Qt Data Types}{Format of the QDataStream operators}
*/
QDataStream &operator>>(QDataStream &s, QVariant &p)
@@ -1350,6 +1407,7 @@ QDataStream &operator>>(QDataStream &s, QVariant &p)
/*!
Writes a variant \a p to the stream \a s.
+ \relates QVariant
\sa{Serializing Qt Data Types}{Format of the QDataStream operators}
*/
@@ -1360,12 +1418,14 @@ QDataStream &operator<<(QDataStream &s, const QVariant &p)
}
/*! \fn QDataStream& operator>>(QDataStream &s, QVariant::Type &p)
+ \relates QVariant
\deprecated [6.0] Stream QMetaType::Type instead.
Reads a variant type \a p in enum representation from the stream \a s.
*/
/*! \fn QDataStream& operator<<(QDataStream &s, const QVariant::Type p)
+ \relates QVariant
\deprecated [6.0] Stream QMetaType::Type instead.
Writes a variant type \a p to the stream \a s.
@@ -1415,8 +1475,13 @@ QString QVariant::toString() const
}
/*!
- Returns the variant as a QMap<QString, QVariant> if the variant
- has type() \l QMetaType::QVariantMap; otherwise returns an empty map.
+ Returns the variant as a QVariantMap if the variant has type() \l
+ QMetaType::QVariantMap. If it doesn't, QVariant will attempt to
+ convert the type to a map and then return it. This will succeed for
+ any type that has registered a converter to QVariantMap or which was
+ declared as a associative container using
+ \l{Q_DECLARE_ASSOCIATIVE_CONTAINER_METATYPE}. If none of those
+ conditions are true, this function will return an empty map.
\sa canConvert(), convert()
*/
@@ -1781,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.
@@ -1791,16 +1857,17 @@ QBitArray QVariant::toBitArray() const
{
return qvariant_cast<QBitArray>(*this);
}
+#endif // QT_BOOTSTRAPPED
template <typename T>
-inline T qNumVariantToHelper(const QVariant::Private &d, bool *ok, const T& val)
+inline T qNumVariantToHelper(const QVariant::Private &d, bool *ok)
{
QMetaType t = QMetaType::fromType<T>();
if (ok)
*ok = true;
if (d.type() == t)
- return val;
+ return d.get<T>();
T ret = 0;
bool success = QMetaType::convert(d.type(), d.storage(), t, &ret);
@@ -1828,7 +1895,7 @@ inline T qNumVariantToHelper(const QVariant::Private &d, bool *ok, const T& val)
*/
int QVariant::toInt(bool *ok) const
{
- return qNumVariantToHelper<int>(d, ok, d.get<int>());
+ return qNumVariantToHelper<int>(d, ok);
}
/*!
@@ -1850,7 +1917,7 @@ int QVariant::toInt(bool *ok) const
*/
uint QVariant::toUInt(bool *ok) const
{
- return qNumVariantToHelper<uint>(d, ok, d.get<unsigned int>());
+ return qNumVariantToHelper<uint>(d, ok);
}
/*!
@@ -1867,7 +1934,7 @@ uint QVariant::toUInt(bool *ok) const
*/
qlonglong QVariant::toLongLong(bool *ok) const
{
- return qNumVariantToHelper<qlonglong>(d, ok, d.get<qlonglong>());
+ return qNumVariantToHelper<qlonglong>(d, ok);
}
/*!
@@ -1884,7 +1951,7 @@ qlonglong QVariant::toLongLong(bool *ok) const
*/
qulonglong QVariant::toULongLong(bool *ok) const
{
- return qNumVariantToHelper<qulonglong>(d, ok, d.get<qulonglong>());
+ return qNumVariantToHelper<qulonglong>(d, ok);
}
/*!
@@ -1924,7 +1991,7 @@ bool QVariant::toBool() const
*/
double QVariant::toDouble(bool *ok) const
{
- return qNumVariantToHelper<double>(d, ok, d.get<double>());
+ return qNumVariantToHelper<double>(d, ok);
}
/*!
@@ -1943,7 +2010,7 @@ double QVariant::toDouble(bool *ok) const
*/
float QVariant::toFloat(bool *ok) const
{
- return qNumVariantToHelper<float>(d, ok, d.get<float>());
+ return qNumVariantToHelper<float>(d, ok);
}
/*!
@@ -1962,13 +2029,17 @@ float QVariant::toFloat(bool *ok) const
*/
qreal QVariant::toReal(bool *ok) const
{
- return qNumVariantToHelper<qreal>(d, ok, d.get<qreal>());
+ return qNumVariantToHelper<qreal>(d, ok);
}
/*!
- Returns the variant as a QVariantList if the variant has userType()
- \l QMetaType::QVariantList or \l QMetaType::QStringList; otherwise returns
- an empty list.
+ Returns the variant as a QVariantList if the variant has userType() \l
+ QMetaType::QVariantList. If it doesn't, QVariant will attempt to convert
+ the type to a list and then return it. This will succeed for any type that
+ has registered a converter to QVariantList or which was declared as a
+ sequential container using \l{Q_DECLARE_SEQUENTIAL_CONTAINER_METATYPE}. If
+ none of those conditions are true, this function will return an empty
+ list.
\sa canConvert(), convert()
*/
@@ -2055,7 +2126,7 @@ bool QVariant::convert(QMetaType targetType)
return false;
// Fail if the value is not initialized or was forced null by a previous failed convert.
- if (oldValue.d.is_null && oldValue.d.typeId() != QMetaType::Nullptr)
+ if (oldValue.d.is_null && oldValue.d.type().id() != QMetaType::Nullptr)
return false;
bool ok = QMetaType::convert(oldValue.d.type(), oldValue.constData(), targetType, data());
@@ -2082,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.
@@ -2108,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.
@@ -2137,6 +2208,8 @@ static bool qIsNumericType(uint tp)
Q_UINT64_C(1) << QMetaType::Double |
Q_UINT64_C(1) << QMetaType::Float |
Q_UINT64_C(1) << QMetaType::Char |
+ Q_UINT64_C(1) << QMetaType::Char16 |
+ Q_UINT64_C(1) << QMetaType::Char32 |
Q_UINT64_C(1) << QMetaType::SChar |
Q_UINT64_C(1) << QMetaType::UChar |
Q_UINT64_C(1) << QMetaType::Short |
@@ -2152,33 +2225,52 @@ static bool qIsNumericType(uint tp)
static bool qIsFloatingPoint(uint tp)
{
- return tp == QMetaType::Double || tp == QMetaType::Float;
+ return tp == QMetaType::Double || tp == QMetaType::Float || tp == QMetaType::Float16;
}
-static int normalizeLowerRanks(uint tp)
+static bool canBeNumericallyCompared(const QtPrivate::QMetaTypeInterface *iface1,
+ const QtPrivate::QMetaTypeInterface *iface2)
{
- static const qulonglong numericTypeBits =
- Q_UINT64_C(1) << QMetaType::Bool |
- Q_UINT64_C(1) << QMetaType::Char |
- Q_UINT64_C(1) << QMetaType::SChar |
- Q_UINT64_C(1) << QMetaType::UChar |
- Q_UINT64_C(1) << QMetaType::Short |
- Q_UINT64_C(1) << QMetaType::UShort;
- return numericTypeBits & (Q_UINT64_C(1) << tp) ? uint(QMetaType::Int) : tp;
-}
+ if (!iface1 || !iface2)
+ return false;
-static int normalizeLong(uint tp)
-{
- const uint IntType = sizeof(long) == sizeof(int) ? QMetaType::Int : QMetaType::LongLong;
- const uint UIntType = sizeof(ulong) == sizeof(uint) ? QMetaType::UInt : QMetaType::ULongLong;
- return tp == QMetaType::Long ? IntType :
- tp == QMetaType::ULong ? UIntType : tp;
+ // We don't need QMetaType::id() here because the type Id is always stored
+ // directly for all built-in types.
+ bool isNumeric1 = qIsNumericType(iface1->typeId);
+ bool isNumeric2 = qIsNumericType(iface2->typeId);
+
+ // if they're both numeric (or QString), then they can be compared
+ if (isNumeric1 && isNumeric2)
+ return true;
+
+ bool isEnum1 = iface1->flags & QMetaType::IsEnumeration;
+ bool isEnum2 = iface2->flags & QMetaType::IsEnumeration;
+
+ // if both are enums, we can only compare if they are the same enum
+ // (the language does allow comparing two different enum types, but that's
+ // usually considered poor coding and produces a warning)
+ if (isEnum1 && isEnum2)
+ return QMetaType(iface1) == QMetaType(iface2);
+
+ // if one is an enum and the other is a numeric, we can compare too
+ if (isEnum1 && isNumeric2)
+ return true;
+ if (isNumeric1 && isEnum2)
+ return true;
+
+ // we need at least one enum and one numeric...
+ return false;
}
-static int numericTypePromotion(uint t1, uint t2)
+static int numericTypePromotion(const QtPrivate::QMetaTypeInterface *iface1,
+ const QtPrivate::QMetaTypeInterface *iface2)
{
- Q_ASSERT(qIsNumericType(t1));
- Q_ASSERT(qIsNumericType(t2));
+ Q_ASSERT(canBeNumericallyCompared(iface1, iface2));
+
+ // We don't need QMetaType::id() here because the type Id is always stored
+ // directly for the types we're comparing against below.
+ uint t1 = iface1->typeId;
+ uint t2 = iface2->typeId;
if ((t1 == QMetaType::Bool && t2 == QMetaType::QString) ||
(t2 == QMetaType::Bool && t1 == QMetaType::QString))
@@ -2202,144 +2294,86 @@ static int numericTypePromotion(uint t1, uint t2)
if (qIsFloatingPoint(t1) || qIsFloatingPoint(t2))
return QMetaType::QReal;
+ auto isUnsigned = [](uint tp) {
+ // only types for which sizeof(T) >= sizeof(int); lesser ones promote to int
+ return tp == QMetaType::ULongLong || tp == QMetaType::ULong ||
+ tp == QMetaType::UInt || tp == QMetaType::Char32;
+ };
+ bool isUnsigned1 = isUnsigned(t1);
+ bool isUnsigned2 = isUnsigned(t2);
+
// integral rules:
- // for all platforms we support, int can always hold the values of lower-ranked types
- t1 = normalizeLowerRanks(t1);
- t2 = normalizeLowerRanks(t2);
-
- // normalize long / ulong: in all platforms we run, they're either the same as int or as long long
- t1 = normalizeLong(t1);
- t2 = normalizeLong(t2);
-
- // implement the other rules
- // the four possibilities are Int, UInt, LongLong and ULongLong
- // if any of the two is ULongLong, then it wins (highest rank, unsigned)
- // otherwise, if one of the two is LongLong, then the other is either LongLong too or lower-ranked
- // otherwise, if one of the two is UInt, then the other is either UInt too or Int
- if (t1 == QMetaType::ULongLong || t2 == QMetaType::ULongLong)
+ // 1) if either type is a 64-bit unsigned, compare as 64-bit unsigned
+ if (isUnsigned1 && iface1->size > sizeof(int))
return QMetaType::ULongLong;
- if (t1 == QMetaType::LongLong || t2 == QMetaType::LongLong)
+ if (isUnsigned2 && iface2->size > sizeof(int))
+ return QMetaType::ULongLong;
+
+ // 2) if either type is 64-bit, compare as 64-bit signed
+ if (iface1->size > sizeof(int) || iface2->size > sizeof(int))
return QMetaType::LongLong;
- if (t1 == QMetaType::UInt || t2 == QMetaType::UInt)
+
+ // 3) if either type is 32-bit unsigned, compare as 32-bit unsigned
+ if (isUnsigned1 || isUnsigned2)
return QMetaType::UInt;
+
+ // 4) otherwise, just do int promotion
return QMetaType::Int;
}
-static bool integralEquals(uint promotedType, const QVariant::Private *d1, const QVariant::Private *d2)
+template <typename Numeric> static QPartialOrdering spaceShip(Numeric lhs, Numeric rhs)
{
- // use toLongLong to retrieve the data, it gets us all the bits
- bool ok;
- qlonglong l1 = qConvertToNumber(d1, &ok, promotedType == QMetaType::Bool);
- if (!ok)
- return false;
-
- qlonglong l2 = qConvertToNumber(d2, &ok, promotedType == QMetaType::Bool);
- if (!ok)
- return false;
-
- if (promotedType == QMetaType::Bool)
- return bool(l1) == bool(l2);
- if (promotedType == QMetaType::Int)
- return int(l1) == int(l2);
- if (promotedType == QMetaType::UInt)
- return uint(l1) == uint(l2);
- if (promotedType == QMetaType::LongLong)
- return l1 == l2;
- if (promotedType == QMetaType::ULongLong)
- return qulonglong(l1) == qulonglong(l2);
-
- Q_UNREACHABLE();
- return 0;
-}
+ if (lhs == rhs)
+ return QPartialOrdering::Equivalent;
+ if constexpr (std::numeric_limits<Numeric>::has_quiet_NaN) {
+ if (std::isnan(lhs) || std::isnan(rhs))
+ return QPartialOrdering::Unordered;
+ }
-namespace {
-template<typename Numeric>
-int spaceShip(Numeric lhs, Numeric rhs)
-{
bool smaller;
if constexpr (std::is_same_v<Numeric, QObject *>)
smaller = std::less<QObject *>()(lhs, rhs); // can't use less all the time because of bool
else
smaller = lhs < rhs;
- if (smaller)
- return -1;
- else if (lhs == rhs)
- return 0;
- else
- return 1;
-}
+ return smaller ? QPartialOrdering::Less : QPartialOrdering::Greater;
}
-static std::optional<int> integralCompare(uint promotedType, const QVariant::Private *d1, const QVariant::Private *d2)
+static QPartialOrdering integralCompare(uint promotedType, const QVariant::Private *d1, const QVariant::Private *d2)
{
// use toLongLong to retrieve the data, it gets us all the bits
- bool ok;
- qlonglong l1 = qConvertToNumber(d1, &ok, promotedType == QMetaType::Bool);
- if (!ok)
- return std::nullopt;
-
- qlonglong l2 = qConvertToNumber(d2, &ok, promotedType == QMetaType::Bool);
- if (!ok)
- return std::nullopt;
-
- if (promotedType == QMetaType::Bool)
- return spaceShip<bool>(l1, l2);
- if (promotedType == QMetaType::Int)
- return spaceShip<int>(l1, l2);
+ std::optional<qlonglong> l1 = qConvertToNumber(d1, promotedType == QMetaType::Bool);
+ std::optional<qlonglong> l2 = qConvertToNumber(d2, promotedType == QMetaType::Bool);
+ if (!l1 || !l2)
+ return QPartialOrdering::Unordered;
if (promotedType == QMetaType::UInt)
- return spaceShip<uint>(l1, l2);
+ return spaceShip<uint>(*l1, *l2);
if (promotedType == QMetaType::LongLong)
- return spaceShip<qlonglong>(l1, l2);
+ return spaceShip<qlonglong>(*l1, *l2);
if (promotedType == QMetaType::ULongLong)
- return spaceShip<qulonglong>(l1, l2);
+ return spaceShip<qulonglong>(*l1, *l2);
- Q_UNREACHABLE();
- return 0;
+ return spaceShip<int>(*l1, *l2);
}
-static std::optional<int> numericCompare(const QVariant::Private *d1, const QVariant::Private *d2)
+static QPartialOrdering numericCompare(const QVariant::Private *d1, const QVariant::Private *d2)
{
- uint promotedType = numericTypePromotion(d1->typeId(), d2->typeId());
+ uint promotedType = numericTypePromotion(d1->typeInterface(), d2->typeInterface());
if (promotedType != QMetaType::QReal)
return integralCompare(promotedType, d1, d2);
- // qreal comparisons
- bool ok;
- qreal r1 = qConvertToRealNumber(d1, &ok);
- if (!ok)
- return std::nullopt;
- qreal r2 = qConvertToRealNumber(d2, &ok);
- if (!ok)
- return std::nullopt;
- if (r1 == r2)
- return 0;
-
- if (std::isnan(r1) || std::isnan(r2))
- return std::nullopt;
- return spaceShip<qreal>(r1, r2);
-}
-
-static bool numericEquals(const QVariant::Private *d1, const QVariant::Private *d2)
-{
- uint promotedType = numericTypePromotion(d1->typeId(), d2->typeId());
- if (promotedType != QMetaType::QReal)
- return integralEquals(promotedType, d1, d2);
- // qreal comparisons
- bool ok;
- qreal r1 = qConvertToRealNumber(d1, &ok);
- if (!ok)
- return false;
- qreal r2 = qConvertToRealNumber(d2, &ok);
- if (!ok)
- return false;
- if (r1 == r2)
- return true;
+ // 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 false;
+ return spaceShip(*r1, *r2);
}
#ifndef QT_BOOTSTRAPPED
-static bool canConvertMetaObject(QMetaType fromType, QMetaType toType)
+static bool qvCanConvertMetaObject(QMetaType fromType, QMetaType toType)
{
if ((fromType.flags() & QMetaType::PointerToQObject)
&& (toType.flags() & QMetaType::PointerToQObject)) {
@@ -2350,13 +2384,7 @@ static bool canConvertMetaObject(QMetaType fromType, QMetaType toType)
return false;
}
-static bool pointerEquals(const QVariant::Private *d1, const QVariant::Private *d2)
-{
- // simply check whether both types point to the same data
- return d1->get<QObject *>() == d2->get<QObject *>();
-}
-
-static int pointerCompare(const QVariant::Private *d1, const QVariant::Private *d2)
+static QPartialOrdering pointerCompare(const QVariant::Private *d1, const QVariant::Private *d2)
{
return spaceShip<QObject *>(d1->get<QObject *>(), d2->get<QObject *>());
}
@@ -2371,12 +2399,12 @@ bool QVariant::equals(const QVariant &v) const
if (metatype != v.metaType()) {
// try numeric comparisons, with C++ type promotion rules (no conversion)
- if (qIsNumericType(metatype.id()) && qIsNumericType(v.d.typeId()))
- return numericEquals(&d, &v.d);
+ if (canBeNumericallyCompared(metatype.iface(), v.d.type().iface()))
+ return numericCompare(&d, &v.d) == QPartialOrdering::Equivalent;
#ifndef QT_BOOTSTRAPPED
// if both types are related pointers to QObjects, check if they point to the same object
- if (canConvertMetaObject(metatype, v.metaType()))
- return pointerEquals(&d, &v.d);
+ if (qvCanConvertMetaObject(metatype, v.metaType()))
+ return pointerCompare(&d, &v.d) == QPartialOrdering::Equivalent;
#endif
return false;
}
@@ -2388,18 +2416,6 @@ bool QVariant::equals(const QVariant &v) const
return metatype.equals(d.storage(), v.d.storage());
}
-static QPartialOrdering convertOptionalToPartialOrdering(const std::optional<int> &opt)
-{
- if (!opt)
- return QPartialOrdering::Unordered;
- else if (*opt < 0)
- return QPartialOrdering::Less;
- else if (*opt == 0)
- return QPartialOrdering::Equivalent;
- else
- return QPartialOrdering::Greater;
-}
-
/*!
Compares the objects at \a lhs and \a rhs for ordering.
@@ -2427,11 +2443,11 @@ QPartialOrdering QVariant::compare(const QVariant &lhs, const QVariant &rhs)
QMetaType t = lhs.d.type();
if (t != rhs.d.type()) {
// try numeric comparisons, with C++ type promotion rules (no conversion)
- if (qIsNumericType(lhs.d.typeId()) && qIsNumericType(rhs.d.typeId()))
- return convertOptionalToPartialOrdering(numericCompare(&lhs.d, &rhs.d));
+ if (canBeNumericallyCompared(lhs.d.type().iface(), rhs.d.type().iface()))
+ return numericCompare(&lhs.d, &rhs.d);
#ifndef QT_BOOTSTRAPPED
- if (canConvertMetaObject(lhs.metaType(), rhs.metaType()))
- return convertOptionalToPartialOrdering(pointerCompare(&lhs.d, &rhs.d));
+ if (qvCanConvertMetaObject(lhs.metaType(), rhs.metaType()))
+ return pointerCompare(&lhs.d, &rhs.d);
#endif
return QPartialOrdering::Unordered;
}
@@ -2445,7 +2461,7 @@ QPartialOrdering QVariant::compare(const QVariant &lhs, const QVariant &rhs)
Returns a pointer to the contained object as a generic void* that cannot be
written to.
- \sa QMetaType
+ \sa get_if(), QMetaType
*/
/*!
@@ -2455,7 +2471,7 @@ QPartialOrdering QVariant::compare(const QVariant &lhs, const QVariant &rhs)
This function detaches the QVariant. When called on a \l{isNull}{null-QVariant},
the QVariant will not be null after the call.
- \sa QMetaType
+ \sa get_if(), QMetaType
*/
void *QVariant::data()
{
@@ -2466,6 +2482,42 @@ void *QVariant::data()
}
/*!
+ \since 6.6
+ \fn template <typename T> const T* QVariant::get_if(const QVariant *v)
+ \fn template <typename T> T* QVariant::get_if(QVariant *v)
+
+ If \a v contains an object of type \c T, returns a pointer to the contained
+ object, otherwise returns \nullptr.
+
+ The overload taking a mutable \a v detaches \a v: When called on a
+ \l{isNull()}{null} \a v with matching type \c T, \a v will not be null
+ after the call.
+
+ These functions are provided for compatibility with \c{std::variant}.
+
+ \sa data()
+*/
+
+/*!
+ \since 6.6
+ \fn template <typename T> T &QVariant::get(QVariant &v)
+ \fn template <typename T> const T &QVariant::get(const QVariant &v)
+ \fn template <typename T> T &&QVariant::get(QVariant &&v)
+ \fn template <typename T> const T &&QVariant::get(const QVariant &&v)
+
+ If \a v contains an object of type \c T, returns a reference to the contained
+ object, otherwise the call has undefined behavior.
+
+ The overloads taking a mutable \a v detach \a v: When called on a
+ \l{isNull()}{null} \a v with matching type \c T, \a v will not be null
+ after the call.
+
+ These functions are provided for compatibility with \c{std::variant}.
+
+ \sa get_if(), data()
+*/
+
+/*!
Returns \c true if this is a null variant, false otherwise.
A variant is considered null if it contains no initialized value or a null pointer.
@@ -2489,7 +2541,7 @@ bool QVariant::isNull() const
QDebug QVariant::qdebugHelper(QDebug dbg) const
{
QDebugStateSaver saver(dbg);
- const uint typeId = d.typeId();
+ const uint typeId = d.type().id();
dbg.nospace() << "QVariant(";
if (typeId != QMetaType::UnknownType) {
dbg << d.type().name() << ", ";
@@ -2503,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
@@ -2522,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
@@ -2535,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.
@@ -2587,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.
@@ -2603,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.
@@ -2620,12 +2688,15 @@ QT_WARNING_POP
\snippet code/src_corelib_kernel_qvariant.cpp 7
- \note If you are working with custom types, you should use
- the Q_DECLARE_METATYPE() macro to register your custom type.
-
\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
@@ -2640,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
@@ -2650,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 a61b91cb92..d567bcbb7c 100644
--- a/src/corelib/kernel/qvariant.h
+++ b/src/corelib/kernel/qvariant.h
@@ -1,99 +1,144 @@
-/****************************************************************************
-**
-** Copyright (C) 2020 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtCore module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2020 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QVARIANT_H
#define QVARIANT_H
#include <QtCore/qatomic.h>
-#include <QtCore/qbytearray.h>
-#include <QtCore/qlist.h>
+#include <QtCore/qcompare.h>
+#include <QtCore/qcontainerfwd.h>
#include <QtCore/qmetatype.h>
-#include <QtCore/qmap.h>
-#include <QtCore/qhash.h>
-#include <QtCore/qstring.h>
-#include <QtCore/qstringlist.h>
-#include <QtCore/qobject.h>
#ifndef QT_NO_DEBUG_STREAM
#include <QtCore/qdebug.h>
#endif
-#ifndef QT_BOOTSTRAPPED
-#include <QtCore/qbytearraylist.h>
-#endif
+
#include <memory>
-#include <type_traits>
+#include <QtCore/q20type_traits.h>
+#include <QtCore/q23utility.h>
#include <variant>
+#if !defined(QT_LEAN_HEADERS) || QT_LEAN_HEADERS < 1
+# include <QtCore/qlist.h>
+# include <QtCore/qstringlist.h>
+# include <QtCore/qbytearraylist.h>
+# include <QtCore/qhash.h>
+# include <QtCore/qmap.h>
+# include <QtCore/qobject.h>
+#endif
+
QT_BEGIN_NAMESPACE
+QT_ENABLE_P0846_SEMANTICS_FOR(get_if)
+QT_ENABLE_P0846_SEMANTICS_FOR(get)
class QBitArray;
class QDataStream;
class QDate;
class QDateTime;
-#if QT_CONFIG(easingcurve)
class QEasingCurve;
-#endif
class QLine;
class QLineF;
class QLocale;
-class QTransform;
-class QTime;
+class QModelIndex;
+class QPersistentModelIndex;
class QPoint;
class QPointF;
-class QSize;
-class QSizeF;
class QRect;
class QRectF;
-#if QT_CONFIG(regularexpression)
class QRegularExpression;
-#endif // QT_CONFIG(regularexpression)
+class QSize;
+class QSizeF;
class QTextFormat;
class QTextLength;
+class QTime;
+class QTransform;
class QUrl;
class QVariant;
template<typename T>
inline T qvariant_cast(const QVariant &);
+namespace QtPrivate {
+template<> constexpr inline bool qIsRelocatable<QVariant> = true;
+}
class Q_CORE_EXPORT QVariant
{
- public:
+ template <typename T, typename... Args>
+ using if_constructible = std::enable_if_t<
+ std::conjunction_v<
+ std::is_copy_constructible<q20::remove_cvref_t<T>>,
+ std::is_destructible<q20::remove_cvref_t<T>>,
+ std::is_constructible<q20::remove_cvref_t<T>, Args...>
+ >,
+ bool>;
+
+ template <typename T>
+ using if_rvalue = std::enable_if_t<!std::is_reference_v<T>, bool>;
+
+ struct CborValueStandIn { qint64 n; void *c; int t; };
+public:
+ struct PrivateShared
+ {
+ 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);
+
+ alignas(8) QAtomicInt ref;
+ int offset;
+
+ 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 *);
+ template <size_t S> static constexpr bool FitsInInternalSize = S <= MaxInternalSize;
+ template<typename T> static constexpr bool CanUseInternalSpace =
+ (QTypeInfo<T>::isRelocatable && FitsInInternalSize<sizeof(T)> && alignof(T) <= alignof(double));
+ static constexpr bool canUseInternalSpace(const QtPrivate::QMetaTypeInterface *type)
+ {
+ Q_ASSERT(type);
+ return QMetaType::TypeFlags(type->flags) & QMetaType::RelocatableType &&
+ size_t(type->size) <= MaxInternalSize && size_t(type->alignment) <= alignof(double);
+ }
+
+ union
+ {
+ uchar data[MaxInternalSize] = {};
+ PrivateShared *shared;
+ double _forAlignment; // we want an 8byte alignment on 32bit systems as well
+ } data;
+ quintptr is_shared : 1;
+ quintptr is_null : 1;
+ quintptr packedType : sizeof(QMetaType) * 8 - 2;
+
+ constexpr Private() noexcept : is_shared(false), is_null(true), packedType(0) {}
+ explicit Private(const QtPrivate::QMetaTypeInterface *iface) noexcept;
+ template <typename T> explicit Private(std::piecewise_construct_t, const T &t);
+
+ const void *storage() const
+ { return is_shared ? data.shared->data() : &data.data; }
+
+ // determine internal storage at compile time
+ template<typename T> const T &get() const
+ { return *static_cast<const T *>(CanUseInternalSpace<T> ? &data.data : data.shared->data()); }
+
+ inline const QtPrivate::QMetaTypeInterface *typeInterface() const
+ {
+ return reinterpret_cast<const QtPrivate::QMetaTypeInterface *>(packedType << 2);
+ }
+
+ inline QMetaType type() const
+ {
+ return QMetaType(typeInterface());
+ }
+ };
+
#if QT_DEPRECATED_SINCE(6, 0)
enum QT_DEPRECATED_VERSION_X_6_0("Use QMetaType::Type instead.") Type
{
@@ -175,32 +220,104 @@ class Q_CORE_EXPORT QVariant
explicit QVariant(QMetaType type, const void *copy = nullptr);
QVariant(const QVariant &other);
- QVariant(int i);
- QVariant(uint ui);
- QVariant(qlonglong ll);
- QVariant(qulonglong ull);
- QVariant(bool b);
- QVariant(double d);
- QVariant(float f);
+private:
+ template <typename T, typename ...Args>
+ using is_noexcept_constructible = std::conjunction<
+ std::bool_constant<Private::CanUseInternalSpace<T>>,
+ std::is_nothrow_constructible<T, Args...>
+ >;
+
+public:
+ template <typename T, typename... Args,
+ if_constructible<T, Args...> = true>
+ explicit QVariant(std::in_place_type_t<T>, Args&&... args)
+ noexcept(is_noexcept_constructible<q20::remove_cvref_t<T>, Args...>::value)
+ : QVariant(std::in_place, QMetaType::fromType<q20::remove_cvref_t<T>>() )
+ {
+ void *data = const_cast<void *>(constData());
+ new (data) T(std::forward<Args>(args)...);
+ }
+
+ template <typename T, typename U, typename... Args,
+ if_constructible<T, std::initializer_list<U> &, Args...> = true>
+ explicit QVariant(std::in_place_type_t<T>, std::initializer_list<U> il, Args&&... args)
+ noexcept(is_noexcept_constructible<q20::remove_cvref_t<T>,
+ std::initializer_list<U> &,
+ Args...
+ >::value)
+ : QVariant(std::in_place, QMetaType::fromType<q20::remove_cvref_t<T>>())
+ {
+ char *data = static_cast<char *>(const_cast<void *>(constData()));
+ new (data) T(il, std::forward<Args>(args)...);
+ }
+
+ // primitives
+ QVariant(int i) noexcept;
+ QVariant(uint ui) noexcept;
+ QVariant(qlonglong ll) noexcept;
+ QVariant(qulonglong ull) noexcept;
+ QVariant(bool b) noexcept;
+ QVariant(double d) noexcept;
+ QVariant(float f) noexcept;
+
+ // trivial, trivially-copyable or COW
+ 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;
+ QVariant(const QJsonArray &jsonArray) noexcept;
+ QVariant(const QJsonObject &jsonObject) noexcept;
+ QVariant(const QList<QVariant> &list) noexcept;
+ QVariant(const QLocale &locale) noexcept;
+ QVariant(const QMap<QString, QVariant> &map) noexcept;
+ QVariant(const QRegularExpression &re) noexcept;
+ QVariant(const QString &string) noexcept;
+ QVariant(const QStringList &stringlist) noexcept;
+ QVariant(const QUrl &url) noexcept;
+
+ // conditionally noexcept trivial or trivially-copyable
+ // (most of these are noexcept on 64-bit)
+ QVariant(const QJsonValue &jsonValue) noexcept(Private::FitsInInternalSize<sizeof(CborValueStandIn)>);
+ QVariant(const QModelIndex &modelIndex) noexcept(Private::FitsInInternalSize<8 + 2 * sizeof(quintptr)>);
+ QVariant(QUuid uuid) noexcept(Private::FitsInInternalSize<16>);
+#ifndef QT_NO_GEOM_VARIANT
+ QVariant(QSize size) noexcept;
+ QVariant(QSizeF size) noexcept(Private::FitsInInternalSize<sizeof(qreal) * 2>);
+ QVariant(QPoint pt) noexcept;
+ QVariant(QPointF pt) noexcept(Private::FitsInInternalSize<sizeof(qreal) * 2>);
+ QVariant(QLine line) noexcept(Private::FitsInInternalSize<sizeof(int) * 4>);
+ QVariant(QLineF line) noexcept(Private::FitsInInternalSize<sizeof(qreal) * 4>);
+ QVariant(QRect rect) noexcept(Private::FitsInInternalSize<sizeof(int) * 4>);
+ QVariant(QRectF rect) noexcept(Private::FitsInInternalSize<sizeof(qreal) * 4>);
+#endif
+
+ // not noexcept
+ QVariant(const QEasingCurve &easing) noexcept(false);
+ QVariant(const QJsonDocument &jsonDocument) noexcept(false);
+ QVariant(const QPersistentModelIndex &modelIndex) noexcept(false);
+
#ifndef QT_NO_CAST_FROM_ASCII
- QT_ASCII_CAST_WARN QVariant(const char *str)
+ QT_ASCII_CAST_WARN QVariant(const char *str) noexcept(false)
: QVariant(QString::fromUtf8(str))
{}
#endif
+ QVariant(QLatin1StringView string) noexcept(false); // converts to QString
- QVariant(const QByteArray &bytearray);
- QVariant(const QBitArray &bitarray);
- QVariant(const QString &string);
- QVariant(QLatin1String string);
- QVariant(const QStringList &stringlist);
- QVariant(QChar qchar);
- QVariant(QDate date);
- QVariant(QTime time);
- QVariant(const QDateTime &datetime);
- QVariant(const QList<QVariant> &list);
- QVariant(const QMap<QString, QVariant> &map);
- QVariant(const QHash<QString, QVariant> &hash);
-#ifndef QT_NO_GEOM_VARIANT
+#if !defined(Q_CC_GHS)
+ // GHS has an ICE with this code; use the simplified version below
+ template <typename T,
+ std::enable_if_t<std::disjunction_v<std::is_pointer<T>, std::is_member_pointer<T>>, bool> = false>
+ QVariant(T) = delete;
+#else
+ QVariant(const volatile void *) = delete;
+#endif
+
+#if QT_CORE_REMOVED_SINCE(6, 5)
QVariant(const QSize &size);
QVariant(const QSizeF &size);
QVariant(const QPoint &pt);
@@ -209,25 +326,7 @@ class Q_CORE_EXPORT QVariant
QVariant(const QLineF &line);
QVariant(const QRect &rect);
QVariant(const QRectF &rect);
-#endif
- QVariant(const QLocale &locale);
-#if QT_CONFIG(regularexpression)
- QVariant(const QRegularExpression &re);
-#endif // QT_CONFIG(regularexpression)
-#if QT_CONFIG(easingcurve)
- QVariant(const QEasingCurve &easing);
-#endif
QVariant(const QUuid &uuid);
-#ifndef QT_BOOTSTRAPPED
- QVariant(const QUrl &url);
- QVariant(const QJsonValue &jsonValue);
- QVariant(const QJsonObject &jsonObject);
- QVariant(const QJsonArray &jsonArray);
- QVariant(const QJsonDocument &jsonDocument);
-#endif // QT_BOOTSTRAPPED
-#if QT_CONFIG(itemmodel)
- QVariant(const QModelIndex &modelIndex);
- QVariant(const QPersistentModelIndex &modelIndex);
#endif
QVariant& operator=(const QVariant &other);
@@ -235,7 +334,7 @@ class Q_CORE_EXPORT QVariant
{ other.d = Private(); }
QT_MOVE_ASSIGNMENT_OPERATOR_IMPL_VIA_MOVE_AND_SWAP(QVariant)
- inline void swap(QVariant &other) noexcept { qSwap(d, other.d); }
+ inline void swap(QVariant &other) noexcept { std::swap(d, other.d); }
int userType() const { return typeId(); }
int typeId() const { return metaType().id(); }
@@ -276,7 +375,9 @@ class Q_CORE_EXPORT QVariant
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;
@@ -331,7 +432,7 @@ class Q_CORE_EXPORT QVariant
QT_DEPRECATED_VERSION_X_6_0("Use typeId() or metaType().")
Type type() const
{
- int type = d.typeId();
+ int type = d.type().id();
return type >= QMetaType::User ? UserType : static_cast<Type>(type);
}
QT_DEPRECATED_VERSION_6_0
@@ -351,6 +452,43 @@ class Q_CORE_EXPORT QVariant
{ 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)
{
@@ -375,7 +513,7 @@ class Q_CORE_EXPORT QVariant
}
template<typename T>
- inline T value() const
+ inline T value() const &
{ return qvariant_cast<T>(*this); }
template<typename T>
@@ -387,24 +525,74 @@ class Q_CORE_EXPORT QVariant
}
template<typename T>
-#ifndef Q_CLANG_QDOC
- static inline auto fromValue(const T &value) ->
- std::enable_if_t<std::is_copy_constructible_v<T>, QVariant>
+ 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>)
+ -> std::enable_if_t<std::is_copy_constructible_v<T> && std::is_destructible_v<T>, QVariant>
#else
static inline QVariant fromValue(const T &value)
#endif
{
+ 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>()); }
@@ -413,113 +601,24 @@ class Q_CORE_EXPORT QVariant
bool canView() const
{ return canView(QMetaType::fromType<T>()); }
-public:
- struct PrivateShared
- {
- private:
- inline PrivateShared() : ref(1) { }
- public:
- static PrivateShared *create(const QtPrivate::QMetaTypeInterface *type)
- {
- Q_ASSERT(type);
- size_t size = type->size;
- size_t align = type->alignment;
-
- size += sizeof(PrivateShared);
- if (align > sizeof(PrivateShared)) {
- // The alignment is larger than the alignment we can guarantee for the pointer
- // directly following PrivateShared, so we need to allocate some additional
- // memory to be able to fit the object into the available memory with suitable
- // alignment.
- size += align - sizeof(PrivateShared);
- }
- void *data = operator new(size);
- auto *ps = new (data) QVariant::PrivateShared();
- ps->offset = int(((quintptr(ps) + sizeof(PrivateShared) + align - 1) & ~(align - 1)) - quintptr(ps));
- return ps;
- }
- static void free(PrivateShared *p)
- {
- p->~PrivateShared();
- operator delete(p);
- }
-
- alignas(8) QAtomicInt ref;
- int offset;
-
- const void *data() const
- { return reinterpret_cast<const unsigned char *>(this) + offset; }
- void *data()
- { return reinterpret_cast<unsigned char *>(this) + offset; }
- };
- struct Private
- {
- static constexpr size_t MaxInternalSize = 3*sizeof(void *);
- template<typename T>
- static constexpr bool CanUseInternalSpace = (QTypeInfo<T>::isRelocatable && sizeof(T) <= MaxInternalSize && alignof(T) <= alignof(double));
- static constexpr bool canUseInternalSpace(QtPrivate::QMetaTypeInterface *type)
- {
- Q_ASSERT(type);
- return QMetaType::TypeFlags(type->flags) & QMetaType::RelocatableType &&
- size_t(type->size) <= MaxInternalSize && size_t(type->alignment) <= alignof(double);
- }
-
- union
- {
- uchar data[MaxInternalSize] = {};
- PrivateShared *shared;
- double _forAlignment; // we want an 8byte alignment on 32bit systems as well
- } data;
- quintptr is_shared : 1;
- quintptr is_null : 1;
- quintptr packedType : sizeof(QMetaType) * 8 - 2;
-
- Private() noexcept : is_shared(false), is_null(true), packedType(0) {}
- explicit Private(QMetaType type) noexcept : is_shared(false), is_null(false)
- {
- quintptr mt = quintptr(type.d_ptr);
- Q_ASSERT((mt & 0x3) == 0);
- packedType = mt >> 2;
- }
- explicit Private(int type) noexcept : Private(QMetaType(type)) {}
-
- const void *storage() const
- { return is_shared ? data.shared->data() : &data.data; }
-
- const void *internalStorage() const
- { Q_ASSERT(is_shared); return &data.data; }
-
- // determine internal storage at compile time
- template<typename T>
- const T &get() const
- { return *static_cast<const T *>(CanUseInternalSpace<T> ? &data.data : data.shared->data()); }
- template<typename T>
- void set(const T &t)
- { *static_cast<T *>(CanUseInternalSpace<T> ? &data.data : data.shared->data()) = t; }
-
- inline QMetaType type() const
- {
- return QMetaType(reinterpret_cast<QtPrivate::QMetaTypeInterface *>(packedType << 2));
- }
-
- inline QtPrivate::QMetaTypeInterface * typeInterface() const
- {
- return reinterpret_cast<QtPrivate::QMetaTypeInterface *>(packedType << 2);
- }
-
- inline int typeId() const
- {
- return type().id();
- }
- };
- 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> {
@@ -527,8 +626,48 @@ private:
}
QDebug qdebugHelper(QDebug) const;
#endif
+
+ template <typename T>
+ friend T *get_if(QVariant *v) noexcept
+ {
+ // data() will detach from is_null, returning non-nullptr
+ if (!v || v->d.type() != QMetaType::fromType<T>())
+ return nullptr;
+ return static_cast<T*>(v->data());
+ }
+ template <typename T>
+ friend const T *get_if(const QVariant *v) noexcept
+ {
+ // (const) data() will not detach from is_null, return nullptr
+ if (!v || v->d.is_null || v->d.type() != QMetaType::fromType<T>())
+ return nullptr;
+ return static_cast<const T*>(v->data());
+ }
+
+#define Q_MK_GET(cvref) \
+ template <typename T> \
+ friend T cvref get(QVariant cvref v) \
+ { \
+ if constexpr (std::is_const_v<T cvref>) \
+ Q_ASSERT(!v.d.is_null); \
+ Q_ASSERT(v.d.type() == QMetaType::fromType<q20::remove_cvref_t<T>>()); \
+ return static_cast<T cvref>(*get_if<T>(&v)); \
+ } \
+ /* end */
+ Q_MK_GET(&)
+ Q_MK_GET(const &)
+ Q_MK_GET(&&)
+ Q_MK_GET(const &&)
+#undef Q_MK_GET
+
+ static QVariant moveConstruct(QMetaType type, void *data);
+ static QVariant copyConstruct(QMetaType type, const void *data);
+
template<typename T>
friend inline T qvariant_cast(const QVariant &);
+ template<typename T>
+ friend inline T qvariant_cast(QVariant &&);
+
protected:
Private d;
void create(int type, const void *copy);
@@ -548,6 +687,11 @@ private:
// int variant, so delete this constructor:
QVariant(QMetaType::Type) = delete;
+ // used to setup the QVariant internals for the "real" inplace ctor
+ QVariant(std::in_place_t, QMetaType type);
+ // helper for emplace
+ void *prepareForEmplace(QMetaType type);
+
// These constructors don't create QVariants of the type associated
// with the enum, as expected, but they would create a QVariant of
// type int with the value of the enum value.
@@ -567,18 +711,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 &)
-{
- return QVariant();
-}
-
inline bool QVariant::isValid() const
{
return d.type().isValid();
@@ -613,7 +745,8 @@ QT_WARNING_POP
inline bool QVariant::isDetached() const
{ return !d.is_shared || d.data.shared->ref.loadRelaxed() == 1; }
-Q_DECLARE_SHARED(QVariant)
+inline void swap(QVariant &value1, QVariant &value2) noexcept
+{ value1.swap(value2); }
#ifndef QT_MOC
@@ -634,14 +767,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 ef6042d745..d2a7390938 100644
--- a/src/corelib/kernel/qvariant_p.h
+++ b/src/corelib/kernel/qvariant_p.h
@@ -1,42 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Copyright (C) 2016 Intel Corporation.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtCore module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QVARIANT_P_H
#define QVARIANT_P_H
@@ -52,22 +15,92 @@
// We mean it.
//
-#include <QtCore/qglobal.h>
-#include <QtCore/qvariant.h>
-#include <QtCore/private/qmetatype_p.h>
+#include "qvariant.h"
QT_BEGIN_NAMESPACE
-template <class T>
-inline void v_construct(QVariant::Private *x, const T &t)
+inline auto customConstructSharedImpl(size_t size, size_t align)
{
- if constexpr (QVariant::Private::CanUseInternalSpace<T>) {
- new (&x->data) T(t);
- x->is_shared = false;
+ struct Deleter {
+ void operator()(QVariant::PrivateShared *p) const
+ { QVariant::PrivateShared::free(p); }
+ };
+
+ // this is exception-safe
+ std::unique_ptr<QVariant::PrivateShared, Deleter> ptr;
+ ptr.reset(QVariant::PrivateShared::create(size, align));
+ return ptr;
+}
+
+template <typename F> static QVariant::PrivateShared *
+customConstructShared(size_t size, size_t align, F &&construct)
+{
+ auto ptr = customConstructSharedImpl(size, align);
+ construct(ptr->data());
+ return ptr.release();
+}
+
+inline int QVariant::PrivateShared::computeOffset(PrivateShared *ps, size_t align)
+{
+ return int(((quintptr(ps) + sizeof(PrivateShared) + align - 1) & ~(align - 1)) - quintptr(ps));
+}
+
+inline size_t QVariant::PrivateShared::computeAllocationSize(size_t size, size_t align)
+{
+ size += sizeof(PrivateShared);
+ if (align > sizeof(PrivateShared)) {
+ // The alignment is larger than the alignment we can guarantee for the pointer
+ // directly following PrivateShared, so we need to allocate some additional
+ // memory to be able to fit the object into the available memory with suitable
+ // alignment.
+ size += align - sizeof(PrivateShared);
+ }
+ return size;
+}
+
+inline QVariant::PrivateShared *QVariant::PrivateShared::create(size_t size, size_t align)
+{
+ size = computeAllocationSize(size, align);
+ void *data = operator new(size);
+ auto *ps = new (data) QVariant::PrivateShared();
+ ps->offset = computeOffset(ps, align);
+ return ps;
+}
+
+inline void QVariant::PrivateShared::free(PrivateShared *p)
+{
+ p->~PrivateShared();
+ operator delete(p);
+}
+
+inline QVariant::Private::Private(const QtPrivate::QMetaTypeInterface *iface) noexcept
+ : is_shared(false), is_null(false), packedType(quintptr(iface) >> 2)
+{
+ Q_ASSERT((quintptr(iface) & 0x3) == 0);
+}
+
+template <typename T> inline
+QVariant::Private::Private(std::piecewise_construct_t, const T &t)
+ : is_shared(!CanUseInternalSpace<T>), is_null(std::is_same_v<T, std::nullptr_t>)
+{
+ // confirm noexceptness
+ static constexpr bool isNothrowQVariantConstructible = noexcept(QVariant(t));
+ static constexpr bool isNothrowCopyConstructible = std::is_nothrow_copy_constructible_v<T>;
+ static constexpr bool isNothrowCopyAssignable = std::is_nothrow_copy_assignable_v<T>;
+
+ const QtPrivate::QMetaTypeInterface *iface = QtPrivate::qMetaTypeInterfaceForType<T>();
+ Q_ASSERT((quintptr(iface) & 0x3) == 0);
+ packedType = quintptr(iface) >> 2;
+
+ if constexpr (CanUseInternalSpace<T>) {
+ static_assert(isNothrowQVariantConstructible == isNothrowCopyConstructible);
+ static_assert(isNothrowQVariantConstructible == isNothrowCopyAssignable);
+ new (data.data) T(t);
} else {
- x->data.shared = QVariant::PrivateShared::create(QtPrivate::qMetaTypeInterfaceForType<T>());
- new (x->data.shared->data()) T(t);
- x->is_shared = true;
+ static_assert(!isNothrowQVariantConstructible); // we allocate memory, even if T doesn't
+ data.shared = customConstructShared(sizeof(T), alignof(T), [=](void *where) {
+ new (where) T(t);
+ });
}
}
diff --git a/src/corelib/kernel/qvarianthash.h b/src/corelib/kernel/qvarianthash.h
new file mode 100644
index 0000000000..a36efc6e9a
--- /dev/null
+++ b/src/corelib/kernel/qvarianthash.h
@@ -0,0 +1,20 @@
+// 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 QVARIANTHASH_H
+#define QVARIANTHASH_H
+
+#if 0
+#pragma qt_class(QVariantHash)
+#endif
+
+#include <QtCore/qglobal.h>
+
+#include <QtCore/QHash>
+#include <QtCore/QVariant>
+#include <QtCore/QString>
+
+QT_BEGIN_NAMESPACE
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/corelib/kernel/qvariantlist.h b/src/corelib/kernel/qvariantlist.h
new file mode 100644
index 0000000000..5a567854e8
--- /dev/null
+++ b/src/corelib/kernel/qvariantlist.h
@@ -0,0 +1,19 @@
+// 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 QVARIANTLIST_H
+#define QVARIANTLIST_H
+
+#if 0
+#pragma qt_class(QVariantList)
+#endif
+
+#include <QtCore/qglobal.h>
+
+#include <QtCore/QList>
+#include <QtCore/QVariant>
+
+QT_BEGIN_NAMESPACE
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/corelib/kernel/qvariantmap.h b/src/corelib/kernel/qvariantmap.h
new file mode 100644
index 0000000000..22cb975b5c
--- /dev/null
+++ b/src/corelib/kernel/qvariantmap.h
@@ -0,0 +1,20 @@
+// 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 QVARIANTMAP_H
+#define QVARIANTMAP_H
+
+#if 0
+#pragma qt_class(QVariantMap)
+#endif
+
+#include <QtCore/qglobal.h>
+
+#include <QtCore/QMap>
+#include <QtCore/QVariant>
+#include <QtCore/QString>
+
+QT_BEGIN_NAMESPACE
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/corelib/kernel/qwineventnotifier.cpp b/src/corelib/kernel/qwineventnotifier.cpp
index 073b8f61ab..afbf4227dc 100644
--- a/src/corelib/kernel/qwineventnotifier.cpp
+++ b/src/corelib/kernel/qwineventnotifier.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtCore module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// 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 "qwineventnotifier_p.h"
@@ -284,3 +248,5 @@ void QWinEventNotifierPrivate::waitCallback(PTP_CALLBACK_INSTANCE instance, PVOI
}
QT_END_NAMESPACE
+
+#include "moc_qwineventnotifier.cpp"
diff --git a/src/corelib/kernel/qwineventnotifier.h b/src/corelib/kernel/qwineventnotifier.h
index 71a1d29057..bd0ece1f5b 100644
--- a/src/corelib/kernel/qwineventnotifier.h
+++ b/src/corelib/kernel/qwineventnotifier.h
@@ -1,48 +1,12 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtCore module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// 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 QWINEVENTNOTIFIER_H
#define QWINEVENTNOTIFIER_H
#include "QtCore/qobject.h"
-#if defined(Q_OS_WIN) || defined(Q_CLANG_QDOC)
+#if defined(Q_OS_WIN) || defined(Q_QDOC)
QT_BEGIN_NAMESPACE
diff --git a/src/corelib/kernel/qwineventnotifier_p.h b/src/corelib/kernel/qwineventnotifier_p.h
index 4e402ec826..3f78f55e61 100644
--- a/src/corelib/kernel/qwineventnotifier_p.h
+++ b/src/corelib/kernel/qwineventnotifier_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2017 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtCore module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2017 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 QWINEVENTNOTIFIER_P_H
#define QWINEVENTNOTIFIER_P_H
diff --git a/src/corelib/kernel/qwinregistry.cpp b/src/corelib/kernel/qwinregistry.cpp
index ac3b2ec370..d237316577 100644
--- a/src/corelib/kernel/qwinregistry.cpp
+++ b/src/corelib/kernel/qwinregistry.cpp
@@ -1,52 +1,14 @@
-/****************************************************************************
-**
-** Copyright (C) 2019 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtCore module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2019 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 "qwinregistry_p.h"
-
#include <QtCore/qvarlengtharray.h>
-
-#include <algorithm>
+#include <QtCore/qendian.h>
+#include <QtCore/qlist.h>
QT_BEGIN_NAMESPACE
-QWinRegistryKey::QWinRegistryKey() :
- m_key(nullptr)
+QWinRegistryKey::QWinRegistryKey()
{
}
@@ -55,8 +17,8 @@ QWinRegistryKey::QWinRegistryKey() :
QWinRegistryKey::QWinRegistryKey(HKEY parentHandle, QStringView subKey,
REGSAM permissions, REGSAM access)
{
- if (RegOpenKeyEx(parentHandle, reinterpret_cast<const wchar_t *>(subKey.utf16()),
- 0, permissions | access, &m_key) != ERROR_SUCCESS) {
+ if (RegOpenKeyExW(parentHandle, reinterpret_cast<const wchar_t *>(subKey.utf16()),
+ 0, permissions | access, &m_key) != ERROR_SUCCESS) {
m_key = nullptr;
}
}
@@ -74,45 +36,99 @@ void QWinRegistryKey::close()
}
}
-QString QWinRegistryKey::stringValue(QStringView subKey) const
+QVariant QWinRegistryKey::value(QStringView subKey) const
{
- QString result;
+ // NOTE: Empty value name is allowed in Windows registry, it means the default
+ // or unnamed value of a key, you can read/write/delete such value normally.
+
if (!isValid())
- return result;
- DWORD type;
- DWORD size;
- auto subKeyC = reinterpret_cast<const wchar_t *>(subKey.utf16());
- if (RegQueryValueEx(m_key, subKeyC, nullptr, &type, nullptr, &size) != ERROR_SUCCESS
- || (type != REG_SZ && type != REG_EXPAND_SZ) || size <= 2) {
- return result;
+ return {};
+
+ // Use nullptr when we need to access the default value.
+ const auto subKeyC = subKey.isEmpty() ? nullptr : reinterpret_cast<const wchar_t *>(subKey.utf16());
+
+ // Get the size and type of the value.
+ DWORD dataType = REG_NONE;
+ DWORD dataSize = 0;
+ LONG ret = RegQueryValueExW(m_key, subKeyC, nullptr, &dataType, nullptr, &dataSize);
+ if (ret != ERROR_SUCCESS)
+ return {};
+
+ // Workaround for rare cases where the trailing '\0' is missing.
+ if (dataType == REG_SZ || dataType == REG_EXPAND_SZ)
+ dataSize += 2;
+ else if (dataType == REG_MULTI_SZ)
+ dataSize += 4;
+
+ // Get the value.
+ QVarLengthArray<unsigned char> data(dataSize);
+ std::fill(data.data(), data.data() + dataSize, 0u);
+
+ ret = RegQueryValueExW(m_key, subKeyC, nullptr, nullptr, data.data(), &dataSize);
+ if (ret != ERROR_SUCCESS)
+ return {};
+
+ switch (dataType) {
+ case REG_SZ:
+ case REG_EXPAND_SZ: {
+ if (dataSize > 0) {
+ return QString::fromWCharArray(
+ reinterpret_cast<const wchar_t *>(data.constData()));
+ }
+ return QString();
+ }
+
+ case REG_MULTI_SZ: {
+ if (dataSize > 0) {
+ QStringList list = {};
+ int i = 0;
+ while (true) {
+ const QString str = QString::fromWCharArray(
+ reinterpret_cast<const wchar_t *>(data.constData()) + i);
+ i += str.length() + 1;
+ if (str.isEmpty())
+ break;
+ list.append(str);
+ }
+ return list;
+ }
+ return QStringList();
+ }
+
+ case REG_NONE: // No specific type, treat as binary data.
+ case REG_BINARY: {
+ if (dataSize > 0) {
+ return QString::fromWCharArray(
+ reinterpret_cast<const wchar_t *>(data.constData()), data.size() / 2);
+ }
+ return QString();
+ }
+
+ case REG_DWORD: // Same as REG_DWORD_LITTLE_ENDIAN
+ return qFromLittleEndian<quint32>(data.constData());
+
+ case REG_DWORD_BIG_ENDIAN:
+ return qFromBigEndian<quint32>(data.constData());
+
+ case REG_QWORD: // Same as REG_QWORD_LITTLE_ENDIAN
+ return qFromLittleEndian<quint64>(data.constData());
+
+ default:
+ break;
}
- // Reserve more for rare cases where trailing '\0' are missing in registry.
- // Rely on 0-termination since strings of size 256 padded with 0 have been
- // observed (QTBUG-84455).
- size += 2;
- QVarLengthArray<unsigned char> buffer(static_cast<int>(size));
- std::fill(buffer.data(), buffer.data() + size, 0u);
- if (RegQueryValueEx(m_key, subKeyC, nullptr, &type, buffer.data(), &size) == ERROR_SUCCESS)
- result = QString::fromWCharArray(reinterpret_cast<const wchar_t *>(buffer.constData()));
- return result;
+
+ return {};
}
-QPair<DWORD, bool> QWinRegistryKey::dwordValue(QStringView subKey) const
+QString QWinRegistryKey::stringValue(QStringView subKey) const
{
- if (!isValid())
- return qMakePair(0, false);
- DWORD type;
- auto subKeyC = reinterpret_cast<const wchar_t *>(subKey.utf16());
- if (RegQueryValueEx(m_key, subKeyC, nullptr, &type, nullptr, nullptr) != ERROR_SUCCESS
- || type != REG_DWORD) {
- return qMakePair(0, false);
- }
- DWORD value = 0;
- DWORD size = sizeof(value);
- const bool ok =
- RegQueryValueEx(m_key, subKeyC, nullptr, nullptr,
- reinterpret_cast<unsigned char *>(&value), &size) == ERROR_SUCCESS;
- return qMakePair(value, ok);
+ return value<QString>(subKey).value_or(QString());
+}
+
+std::pair<DWORD, bool> QWinRegistryKey::dwordValue(QStringView subKey) const
+{
+ const std::optional<DWORD> val = value<DWORD>(subKey);
+ 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 a456292835..20b2d10dd7 100644
--- a/src/corelib/kernel/qwinregistry_p.h
+++ b/src/corelib/kernel/qwinregistry_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2019 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtCore module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2019 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 QWINREGISTRY_H
#define QWINREGISTRY_H
@@ -55,33 +19,49 @@
#include <QtCore/qstring.h>
#include <QtCore/qstringview.h>
#include <QtCore/qt_windows.h>
+#include <QtCore/qvariant.h>
QT_BEGIN_NAMESPACE
class Q_CORE_EXPORT QWinRegistryKey
{
-public:
Q_DISABLE_COPY(QWinRegistryKey)
+public:
QWinRegistryKey();
explicit QWinRegistryKey(HKEY parentHandle, QStringView subKey,
REGSAM permissions = KEY_READ, REGSAM access = 0);
~QWinRegistryKey();
QWinRegistryKey(QWinRegistryKey &&other) noexcept
- : m_key(qExchange(other.m_key, nullptr)) {}
+ : m_key(std::exchange(other.m_key, nullptr)) {}
QT_MOVE_ASSIGNMENT_OPERATOR_IMPL_VIA_MOVE_AND_SWAP(QWinRegistryKey)
void swap(QWinRegistryKey &other) noexcept { qSwap(m_key, other.m_key); }
- bool isValid() const { return m_key != nullptr; }
- operator HKEY() const { return m_key; }
+ [[nodiscard]] bool isValid() const { return m_key != nullptr; }
+
+ [[nodiscard]] HKEY handle() const { return m_key; }
+
+ operator HKEY() const { return handle(); }
+
void close();
+ [[nodiscard]] QVariant value(QStringView subKey) const;
+ template<typename T>
+ [[nodiscard]] std::optional<T> value(QStringView subKey) const
+ {
+ const QVariant var = value(subKey);
+ if (var.isValid())
+ return qvariant_cast<T>(var);
+ return std::nullopt;
+ }
+
+ // ### 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;
+ HKEY m_key = nullptr;
};
QT_END_NAMESPACE