summaryrefslogtreecommitdiffstats
path: root/src/corelib/kernel/qcoreapplication.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/corelib/kernel/qcoreapplication.cpp')
-rw-r--r--src/corelib/kernel/qcoreapplication.cpp826
1 files changed, 560 insertions, 266 deletions
diff --git a/src/corelib/kernel/qcoreapplication.cpp b/src/corelib/kernel/qcoreapplication.cpp
index 812b4afcc4..939965cff7 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,9 +8,11 @@
#ifndef QT_NO_QOBJECT
#include "qabstracteventdispatcher.h"
#include "qcoreevent.h"
+#include "qcoreevent_p.h"
#include "qeventloop.h"
#endif
#include "qmetaobject.h"
+#include <private/qproperty_p.h>
#include "qcorecmdlineargs_p.h"
#include <qdatastream.h>
#include <qdebug.h>
@@ -66,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>
@@ -77,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)
@@ -86,18 +57,20 @@
# 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"
#endif
#endif // QT_NO_QOBJECT
-#if defined(Q_OS_ANDROID) && !defined(Q_OS_ANDROID_EMBEDDED)
+#if defined(Q_OS_ANDROID)
#include <QtCore/qjniobject.h>
#endif
-#ifdef Q_OS_MAC
+#ifdef Q_OS_DARWIN
# include "qcore_mac_p.h"
#endif
@@ -133,19 +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 >= 0x070000
-# error "Bump QCoreApplicatoinPrivate::app_compile_version to 0x070000"
+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
-int QCoreApplicationPrivate::app_compile_version = 0x060000; //we don't know exactly, but it's at least 6.0.0
-bool QCoreApplicationPrivate::setuidAllowed = false;
+Q_CONSTINIT bool QCoreApplicationPrivate::setuidAllowed = false;
#if !defined(Q_OS_WIN)
#ifdef Q_OS_DARWIN
@@ -176,10 +165,10 @@ QString QCoreApplicationPrivate::appName() const
QString QCoreApplicationPrivate::appVersion() const
{
QString applicationVersion;
-#ifndef QT_BOOTSTRAPPED
-# ifdef Q_OS_DARWIN
+#ifdef QT_BOOTSTRAPPED
+#elif defined(Q_OS_DARWIN)
applicationVersion = infoDictionaryStringProperty(QStringLiteral("CFBundleVersion"));
-# elif defined(Q_OS_ANDROID) && !defined(Q_OS_ANDROID_EMBEDDED)
+#elif defined(Q_OS_ANDROID)
QJniObject context(QNativeInterface::QAndroidApplication::context());
if (context.isValid()) {
QJniObject pm = context.callObjectMethod(
@@ -197,13 +186,12 @@ QString QCoreApplicationPrivate::appVersion() const
}
}
}
-# endif
#endif
return applicationVersion;
}
-#endif
+#endif // !Q_OS_WIN
-QString *QCoreApplicationPrivate::cachedApplicationFilePath = nullptr;
+Q_CONSTINIT QString *QCoreApplicationPrivate::cachedApplicationFilePath = nullptr;
bool QCoreApplicationPrivate::checkInstance(const char *function)
{
@@ -253,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()
{
}
@@ -261,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
@@ -313,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()
@@ -340,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();
}
}
@@ -349,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);
@@ -390,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
};
@@ -399,7 +391,7 @@ struct QCoreApplicationData {
Q_GLOBAL_STATIC(QCoreApplicationData, coreappdata)
#ifndef QT_NO_QOBJECT
-static bool quitLockRefEnabled = true;
+Q_CONSTINIT static bool quitLockEnabled = true;
#endif
#if defined(Q_OS_WIN)
@@ -431,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(),
@@ -451,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;
@@ -486,6 +477,8 @@ QCoreApplicationPrivate::~QCoreApplicationPrivate()
#endif
#if defined(Q_OS_WIN)
delete [] origArgv;
+ if (consoleAllocated)
+ FreeConsole();
#endif
QCoreApplicationPrivate::clearApplicationFilePath();
}
@@ -504,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;
@@ -532,7 +524,7 @@ void QCoreApplicationPrivate::eventDispatcherReady()
{
}
-QBasicAtomicPointer<QThread> QCoreApplicationPrivate::theMainThread = Q_BASIC_ATOMIC_INITIALIZER(nullptr);
+Q_CONSTINIT QBasicAtomicPointer<QThread> QCoreApplicationPrivate::theMainThread = Q_BASIC_ATOMIC_INITIALIZER(nullptr);
QThread *QCoreApplicationPrivate::mainThread()
{
Q_ASSERT(theMainThread.loadRelaxed() != nullptr);
@@ -567,11 +559,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);
@@ -585,50 +577,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
}
@@ -704,7 +763,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}
*/
/*!
@@ -747,13 +807,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;
@@ -770,7 +830,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);
@@ -780,6 +840,8 @@ void QCoreApplicationPrivate::init()
Q_Q(QCoreApplication);
+ initConsole();
+
initLocale();
Q_ASSERT_X(!QCoreApplication::self, "QCoreApplication", "there should be only one application object");
@@ -787,7 +849,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
@@ -810,8 +876,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
@@ -819,7 +885,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) {
@@ -861,6 +927,7 @@ void QCoreApplicationPrivate::init()
qt_call_pre_routines();
qt_startup_hook();
#ifndef QT_BOOTSTRAPPED
+ QtPrivate::initBindingStatusThreadId();
if (Q_UNLIKELY(qtHookData[QHooks::Startup]))
reinterpret_cast<QHooks::StartupCallback>(qtHookData[QHooks::Startup])();
#endif
@@ -888,8 +955,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
}
@@ -897,6 +966,10 @@ QCoreApplication::~QCoreApplication()
globalThreadPool->waitForDone();
delete globalThreadPool;
}
+ if (guiThreadPool) {
+ guiThreadPool->waitForDone();
+ delete guiThreadPool;
+ }
#endif
#ifndef QT_NO_QOBJECT
@@ -930,7 +1003,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)
{
@@ -950,7 +1026,6 @@ bool QCoreApplication::isSetuidAllowed()
return QCoreApplicationPrivate::setuidAllowed;
}
-
/*!
Sets the attribute \a attribute if \a on is true;
otherwise clears the attribute.
@@ -963,6 +1038,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
@@ -1018,14 +1097,14 @@ bool QCoreApplication::testAttribute(Qt::ApplicationAttribute attribute)
bool QCoreApplication::isQuitLockEnabled()
{
- return quitLockRefEnabled;
+ return quitLockEnabled;
}
static bool doNotify(QObject *, QEvent *);
void QCoreApplication::setQuitLockEnabled(bool enabled)
{
- quitLockRefEnabled = enabled;
+ quitLockEnabled = enabled;
}
/*!
@@ -1038,7 +1117,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
@@ -1054,10 +1133,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);
}
@@ -1116,7 +1200,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.
@@ -1131,6 +1215,14 @@ bool QCoreApplication::forwardEvent(QObject *receiver, QEvent *event, QEvent *or
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;
@@ -1139,6 +1231,9 @@ bool QCoreApplication::notify(QObject *receiver, QEvent *event)
static bool doNotify(QObject *receiver, QEvent *event)
{
+ Q_ASSERT(event);
+
+ // ### Qt 7: turn into an assert
if (receiver == nullptr) { // serious error
qWarning("QCoreApplication::notify: Unexpected null receiver");
return true;
@@ -1154,15 +1249,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;
}
@@ -1175,12 +1270,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;
}
@@ -1206,8 +1303,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;
@@ -1274,7 +1371,8 @@ bool QCoreApplication::closingDown()
\threadsafe
- \sa exec(), QTimer, QEventLoop::processEvents(), sendPostedEvents()
+ \sa exec(), QTimer, QChronoTimer, QEventLoop::processEvents(),
+ sendPostedEvents()
*/
void QCoreApplication::processEvents(QEventLoop::ProcessEventsFlags flags)
{
@@ -1285,12 +1383,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
@@ -1306,9 +1421,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
@@ -1316,10 +1431,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;
}
}
@@ -1337,10 +1451,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
@@ -1359,7 +1473,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;
@@ -1373,7 +1487,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)
@@ -1392,9 +1506,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);
}
@@ -1433,9 +1546,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);
}
@@ -1462,10 +1580,12 @@ void QCoreApplication::exit(int returnCode)
*/
bool QCoreApplication::sendEvent(QObject *receiver, QEvent *event)
{
+ Q_ASSERT_X(receiver, "QCoreApplication::sendEvent", "Unexpected null receiver");
+ Q_ASSERT_X(event, "QCoreApplication::sendEvent", "Unexpected null event");
+
Q_TRACE(QCoreApplication_sendEvent, receiver, event, event->type());
- if (event)
- event->m_spont = false;
+ event->m_spont = false;
return notifyInternal2(receiver, event);
}
@@ -1474,10 +1594,12 @@ bool QCoreApplication::sendEvent(QObject *receiver, QEvent *event)
*/
bool QCoreApplication::sendSpontaneousEvent(QObject *receiver, QEvent *event)
{
+ Q_ASSERT_X(receiver, "QCoreApplication::sendSpontaneousEvent", "Unexpected null receiver");
+ Q_ASSERT_X(event, "QCoreApplication::sendSpontaneousEvent", "Unexpected null event");
+
Q_TRACE(QCoreApplication_sendSpontaneousEvent, receiver, event, event->type());
- if (event)
- event->m_spont = true;
+ event->m_spont = true;
return notifyInternal2(receiver, event);
}
@@ -1542,8 +1664,11 @@ QCoreApplicationPrivate::QPostEventListLocker QCoreApplicationPrivate::lockThrea
*/
void QCoreApplication::postEvent(QObject *receiver, QEvent *event, int priority)
{
+ Q_ASSERT_X(event, "QCoreApplication::postEvent", "Unexpected null event");
+
Q_TRACE_SCOPE(QCoreApplication_postEvent, receiver, event, event->type());
+ // ### Qt 7: turn into an assert
if (receiver == nullptr) {
qWarning("QCoreApplication::postEvent: Unexpected null receiver");
delete event;
@@ -1566,37 +1691,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;
@@ -1613,40 +1713,31 @@ void QCoreApplication::postEvent(QObject *receiver, QEvent *event, int priority)
*/
bool QCoreApplication::compressEvent(QEvent *event, QObject *receiver, QPostEventList *postedEvents)
{
-#ifdef Q_OS_WIN
Q_ASSERT(event);
Q_ASSERT(receiver);
Q_ASSERT(postedEvents);
+ 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())
@@ -1695,7 +1786,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;
@@ -1719,8 +1810,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
@@ -1779,14 +1870,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
@@ -1803,6 +1917,8 @@ void QCoreApplicationPrivate::sendPostedEvents(QObject *receiver, int event_type
data->postEventList.addEvent(pe_copy);
}
continue;
+ } else {
+ qCDebug(lcDeleteLater) << "Sending deferred delete to" << pe.receiver;
}
}
@@ -1869,10 +1985,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)
@@ -1930,8 +2046,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",
@@ -1967,14 +2082,34 @@ void QCoreApplicationPrivate::ref()
void QCoreApplicationPrivate::deref()
{
- if (!quitLockRef.deref())
- maybeQuit();
+ quitLockRef.deref();
+
+ if (quitLockEnabled && canQuitAutomatically())
+ quitAutomatically();
}
-void QCoreApplicationPrivate::maybeQuit()
+bool QCoreApplicationPrivate::canQuitAutomatically()
{
- if (quitLockRef.loadRelaxed() == 0 && in_exec && quitLockRefEnabled && shouldQuit())
- QCoreApplication::postEvent(QCoreApplication::instance(), new QEvent(QEvent::Quit));
+ if (!in_exec)
+ return false;
+
+ if (quitLockEnabled && quitLockRef.loadRelaxed())
+ return false;
+
+ return true;
+}
+
+void QCoreApplicationPrivate::quitAutomatically()
+{
+ Q_Q(QCoreApplication);
+
+ // Explicit requests by the user to quit() is plumbed via the platform
+ // if possible, and delivers the quit event synchronously. For automatic
+ // quits we implicitly support cancelling the quit by showing another
+ // window, which currently relies on removing any posted quit events
+ // from the event queue. As a result, we can't use the normal quit()
+ // code path, and need to post manually.
+ QCoreApplication::postEvent(q, new QEvent(QEvent::Quit));
}
/*!
@@ -2016,6 +2151,9 @@ void QCoreApplication::quit()
if (!self)
return;
+ if (!self->d_func()->in_exec)
+ return;
+
self->d_func()->quit();
}
@@ -2043,6 +2181,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()
*/
@@ -2066,12 +2210,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)
@@ -2134,26 +2281,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();
}
}
}
@@ -2189,7 +2336,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)
@@ -2245,7 +2393,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;
}
@@ -2300,7 +2448,7 @@ QString QCoreApplication::applicationDirPath()
#if !defined(Q_OS_WIN) && !defined(Q_OS_DARWIN) // qcoreapplication_win.cpp or qcoreapplication_mac.cpp
static QString qAppFileName()
{
-# if defined(Q_OS_ANDROID) && !defined(Q_OS_ANDROID_EMBEDDED)
+# if defined(Q_OS_ANDROID)
// the actual process on Android is the Java VM, so this doesn't help us
return QString();
# elif defined(Q_OS_LINUX)
@@ -2347,10 +2495,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]);
}
}
@@ -2361,13 +2509,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.
@@ -2430,7 +2578,7 @@ qint64 QCoreApplication::applicationPid()
encoding problems might occur.
Otherwise, the arguments() are constructed from the return value of
- \l{http://msdn2.microsoft.com/en-us/library/ms683156(VS.85).aspx}{GetCommandLine()}.
+ \l{https://docs.microsoft.com/en-us/windows/win32/api/processenv/nf-processenv-getcommandlinea}{GetCommandLine()}.
As a result of this, the string given by arguments().at(0) might not be
the program name on Windows, depending on how the application was started.
@@ -2494,7 +2642,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
@@ -2534,7 +2682,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
@@ -2570,11 +2718,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()
*/
@@ -2657,6 +2809,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)
@@ -2687,11 +2988,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}
*/
@@ -2726,9 +3022,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
@@ -2802,10 +3095,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.
@@ -2823,14 +3117,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;
@@ -2862,14 +3156,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;