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.cpp571
1 files changed, 430 insertions, 141 deletions
diff --git a/src/corelib/kernel/qcoreapplication.cpp b/src/corelib/kernel/qcoreapplication.cpp
index 43ff18fa5c..939965cff7 100644
--- a/src/corelib/kernel/qcoreapplication.cpp
+++ b/src/corelib/kernel/qcoreapplication.cpp
@@ -8,6 +8,7 @@
#ifndef QT_NO_QOBJECT
#include "qabstracteventdispatcher.h"
#include "qcoreevent.h"
+#include "qcoreevent_p.h"
#include "qeventloop.h"
#endif
#include "qmetaobject.h"
@@ -31,10 +32,11 @@
#include <private/qthread_p.h>
#if QT_CONFIG(thread)
#include <qthreadpool.h>
+#include <private/qthreadpool_p.h>
#endif
#endif
-#include <qelapsedtimer.h>
#include <qlibraryinfo.h>
+#include <qpointer.h>
#include <qvarlengtharray.h>
#include <private/qfactoryloader_p.h>
#include <private/qfunctions_p.h>
@@ -42,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)
@@ -51,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"
@@ -62,7 +70,7 @@
#include <QtCore/qjniobject.h>
#endif
-#ifdef Q_OS_MAC
+#ifdef Q_OS_DARWIN
# include "qcore_mac_p.h"
#endif
@@ -99,21 +107,33 @@
#include <algorithm>
#include <memory>
+#include <string>
QT_BEGIN_NAMESPACE
+#ifndef QT_NO_QOBJECT
+Q_LOGGING_CATEGORY(lcDeleteLater, "qt.core.qobject.deletelater")
+#endif
+
using namespace Qt::StringLiterals;
-#if defined(Q_OS_WIN) || defined(Q_OS_MAC)
+Q_TRACE_PREFIX(qtcore,
+ "#include <qcoreevent.h>"
+);
+Q_TRACE_METADATA(qtcore, "ENUM { AUTO, RANGE User ... MaxUser } QEvent::Type;");
+Q_TRACE_POINT(qtcore, QCoreApplication_postEvent_entry, QObject *receiver, QEvent *event, QEvent::Type type);
+Q_TRACE_POINT(qtcore, QCoreApplication_postEvent_exit);
+Q_TRACE_POINT(qtcore, QCoreApplication_postEvent_event_compressed, QObject *receiver, QEvent *event);
+Q_TRACE_POINT(qtcore, QCoreApplication_postEvent_event_posted, QObject *receiver, QEvent *event, QEvent::Type type);
+Q_TRACE_POINT(qtcore, QCoreApplication_sendEvent, QObject *receiver, QEvent *event, QEvent::Type type);
+Q_TRACE_POINT(qtcore, QCoreApplication_sendSpontaneousEvent, QObject *receiver, QEvent *event, QEvent::Type type);
+Q_TRACE_POINT(qtcore, QCoreApplication_notify_entry, QObject *receiver, QEvent *event, QEvent::Type type);
+Q_TRACE_POINT(qtcore, QCoreApplication_notify_exit, bool consumed, bool filtered);
+
+#if defined(Q_OS_WIN) || defined(Q_OS_DARWIN)
extern QString qAppFileName();
#endif
-#if QT_VERSION >= QT_VERSION_CHECK(7, 0, 0)
-# error "Bump QCoreApplicatoinPrivate::app_compile_version to QT_VERSION_CHECK(7, 0, 0)"
-#endif
-// We don't know exactly, but it's at least 6.0.0:
-Q_CONSTINIT int QCoreApplicationPrivate::app_compile_version = QT_VERSION_CHECK(6, 0, 0);
-
Q_CONSTINIT bool QCoreApplicationPrivate::setuidAllowed = false;
#if !defined(Q_OS_WIN)
@@ -221,7 +241,11 @@ void QCoreApplicationPrivate::processCommandLineArguments()
// Support for introspection
-extern "C" void Q_DECL_EXPORT_OVERRIDABLE qt_startup_hook()
+extern "C" void
+#ifdef QT_SHARED
+Q_DECL_EXPORT_OVERRIDABLE
+#endif
+qt_startup_hook()
{
}
@@ -281,15 +305,15 @@ static void qt_call_pre_routines()
if (!preRList.exists())
return;
- QVFuncList list;
- {
+ const QStartUpFuncList list = [] {
const auto locker = qt_scoped_lock(globalRoutinesMutex);
// Unlike qt_call_post_routines, we don't empty the list, because
// Q_COREAPP_STARTUP_FUNCTION is a macro, so the user expects
// the function to be executed every time QCoreApplication is created.
- list = *preRList;
- }
- for (QtCleanUpFunction f : std::as_const(list))
+ return *preRList;
+ }();
+
+ for (QtStartUpFunction f : list)
f();
}
@@ -308,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();
}
}
@@ -399,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(),
@@ -419,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;
@@ -454,6 +477,8 @@ QCoreApplicationPrivate::~QCoreApplicationPrivate()
#endif
#if defined(Q_OS_WIN)
delete [] origArgv;
+ if (consoleAllocated)
+ FreeConsole();
#endif
QCoreApplicationPrivate::clearApplicationFilePath();
}
@@ -552,26 +577,86 @@ QString qAppName()
return QCoreApplication::instance()->d_func()->appName();
}
+void QCoreApplicationPrivate::initConsole()
+{
+#ifdef Q_OS_WINDOWS
+ const QString env = qEnvironmentVariable("QT_WIN_DEBUG_CONSOLE");
+ if (env.isEmpty())
+ return;
+ if (env.compare(u"new"_s, Qt::CaseInsensitive) == 0) {
+ if (AllocConsole() == FALSE)
+ return;
+ consoleAllocated = true;
+ } else if (env.compare(u"attach"_s, Qt::CaseInsensitive) == 0) {
+ // If the calling process is already attached to a console,
+ // the error code returned is ERROR_ACCESS_DENIED.
+ if (!::AttachConsole(ATTACH_PARENT_PROCESS) && ::GetLastError() != ERROR_ACCESS_DENIED)
+ return;
+ } else {
+ // Unknown input, don't make any decision for the user.
+ return;
+ }
+ // The std{in,out,err} handles are read-only, so we need to pass in dummies.
+ FILE *in = nullptr;
+ FILE *out = nullptr;
+ FILE *err = nullptr;
+ freopen_s(&in, "CONIN$", "r", stdin);
+ freopen_s(&out, "CONOUT$", "w", stdout);
+ freopen_s(&err, "CONOUT$", "w", stderr);
+ // However, things wouldn't work if the runtime did not preserve the pointers.
+ Q_ASSERT(in == stdin);
+ Q_ASSERT(out == stdout);
+ Q_ASSERT(err == stderr);
+#endif
+}
+
void QCoreApplicationPrivate::initLocale()
{
-#if defined(Q_OS_UNIX) && !defined(QT_BOOTSTRAPPED)
+#if defined(QT_BOOTSTRAPPED)
+ // Don't try to control bootstrap library locale or encoding.
+#elif defined(Q_OS_UNIX)
Q_CONSTINIT static bool qt_locale_initialized = false;
if (qt_locale_initialized)
return;
qt_locale_initialized = true;
-#ifdef Q_OS_INTEGRITY
+ // By default the portable "C"/POSIX locale is selected and active.
+ // Apply the locale from the environment, via setlocale(), which will
+ // read LC_ALL, LC_<category>, and LANG, in order (for each category).
+ setlocale(LC_ALL, "");
+
+ // Next, let's ensure that LC_CTYPE is UTF-8, since QStringConverter's
+ // QLocal8Bit hard-codes this, and we need to be consistent.
+# if defined(Q_OS_INTEGRITY)
setlocale(LC_CTYPE, "UTF-8");
-#else
- // Android's Bionic didn't get nl_langinfo until NDK 15 (Android 8.0),
- // which is too new for Qt, so we just assume it's always UTF-8.
- auto nl_langinfo = [](int) { return "UTF-8"; };
-
- const char *locale = setlocale(LC_ALL, "");
- const char *codec = nl_langinfo(CODESET);
- if (Q_UNLIKELY(strcmp(codec, "UTF-8") != 0 && strcmp(codec, "utf8") != 0)) {
- QByteArray oldLocale = locale;
- QByteArray newLocale = setlocale(LC_CTYPE, nullptr);
+# elif defined(Q_OS_QNX)
+ // QNX has no nl_langinfo, so we can't check.
+ // FIXME: Shouldn't we still setlocale("UTF-8")?
+# elif defined(Q_OS_ANDROID) && __ANDROID_API__ < __ANDROID_API_O__
+ // Android 6 still lacks nl_langinfo(), so we can't check.
+ // FIXME: Shouldn't we still setlocale("UTF-8")?
+# elif defined(Q_OS_VXWORKS)
+ // VxWorks has no nl_langinfo, so we can't check.
+# else
+ // std::string's SSO usually saves this the need to allocate:
+ const std::string oldEncoding = nl_langinfo(CODESET);
+ if (!Q_LIKELY(qstricmp(oldEncoding.data(), "UTF-8") == 0
+ || qstricmp(oldEncoding.data(), "utf8") == 0)) {
+ const QByteArray oldLocale = setlocale(LC_ALL, nullptr);
+ QByteArray newLocale;
+ bool warnOnOverride = true;
+# if defined(Q_OS_DARWIN)
+ // Don't warn unless the char encoding has been changed from the
+ // default "C" encoding, or the user touched any of the locale
+ // environment variables to force the "C" char encoding.
+ warnOnOverride = qstrcmp(setlocale(LC_CTYPE, nullptr), "C") != 0
+ || getenv("LC_ALL") || getenv("LC_CTYPE") || getenv("LANG");
+
+ // No need to try language or region specific CTYPEs, as they
+ // all point back to the same generic UTF-8 CTYPE.
+ newLocale = setlocale(LC_CTYPE, "UTF-8");
+# else
+ newLocale = setlocale(LC_CTYPE, nullptr);
if (qsizetype dot = newLocale.indexOf('.'); dot != -1)
newLocale.truncate(dot); // remove encoding, if any
if (qsizetype at = newLocale.indexOf('@'); at != -1)
@@ -579,23 +664,30 @@ void QCoreApplicationPrivate::initLocale()
newLocale += ".UTF-8";
newLocale = setlocale(LC_CTYPE, newLocale);
- // if locale doesn't exist, try some fallbacks
-# ifdef Q_OS_DARWIN
- if (newLocale.isEmpty())
- newLocale = setlocale(LC_CTYPE, "UTF-8");
-# endif
+ // If that locale doesn't exist, try some fallbacks:
if (newLocale.isEmpty())
newLocale = setlocale(LC_CTYPE, "C.UTF-8");
if (newLocale.isEmpty())
newLocale = setlocale(LC_CTYPE, "C.utf8");
-
- qWarning("Detected system locale encoding (%s, locale \"%s\") is not UTF-8.\n"
- "Qt shall use a UTF-8 locale (\"%s\") instead. If this causes problems,\n"
- "reconfigure your locale. See the locale(1) manual for more information.",
- codec, oldLocale.constData(), newLocale.constData());
+# endif
+
+ if (newLocale.isEmpty()) {
+ // Failed to set a UTF-8 locale.
+ qWarning("Detected locale \"%s\" with character encoding \"%s\", which is not UTF-8.\n"
+ "Qt depends on a UTF-8 locale, but has failed to switch to one.\n"
+ "If this causes problems, reconfigure your locale. See the locale(1) manual\n"
+ "for more information.", oldLocale.constData(), oldEncoding.data());
+ } else if (warnOnOverride) {
+ // Let the user know we over-rode their configuration.
+ qWarning("Detected locale \"%s\" with character encoding \"%s\", which is not UTF-8.\n"
+ "Qt depends on a UTF-8 locale, and has switched to \"%s\" instead.\n"
+ "If this causes problems, reconfigure your locale. See the locale(1) manual\n"
+ "for more information.",
+ oldLocale.constData(), oldEncoding.data(), newLocale.constData());
+ }
}
-#endif
-#endif
+# endif // Platform choice
+#endif // Unix
}
@@ -671,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}
*/
/*!
@@ -714,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;
@@ -737,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);
@@ -747,6 +840,8 @@ void QCoreApplicationPrivate::init()
Q_Q(QCoreApplication);
+ initConsole();
+
initLocale();
Q_ASSERT_X(!QCoreApplication::self, "QCoreApplication", "there should be only one application object");
@@ -790,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 (qsizetype 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) {
@@ -860,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
}
@@ -869,6 +966,10 @@ QCoreApplication::~QCoreApplication()
globalThreadPool->waitForDone();
delete globalThreadPool;
}
+ if (guiThreadPool) {
+ guiThreadPool->waitForDone();
+ delete guiThreadPool;
+ }
#endif
#ifndef QT_NO_QOBJECT
@@ -902,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)
{
@@ -922,7 +1026,6 @@ bool QCoreApplication::isSetuidAllowed()
return QCoreApplicationPrivate::setuidAllowed;
}
-
/*!
Sets the attribute \a attribute if \a on is true;
otherwise clears the attribute.
@@ -935,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
@@ -1010,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
@@ -1030,6 +1137,11 @@ bool QCoreApplication::notifyInternal2(QObject *receiver, QEvent *event)
QScopedScopeLevelCounter scopeLevelCounter(threadData);
if (!selfRequired)
return doNotify(receiver, event);
+
+#if QT_VERSION >= QT_VERSION_CHECK(7, 0, 0)
+ if (threadData->thread.loadRelaxed() != QCoreApplicationPrivate::mainThread())
+ return false;
+#endif
return self->notify(receiver, event);
}
@@ -1088,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.
@@ -1106,6 +1218,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;
@@ -1132,7 +1249,7 @@ 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
@@ -1153,7 +1270,9 @@ bool QCoreApplicationPrivate::sendThroughApplicationEventFilters(QObject *receiv
bool QCoreApplicationPrivate::sendThroughObjectEventFilters(QObject *receiver, QEvent *event)
{
- if (receiver != QCoreApplication::instance() && receiver->d_func()->extraData) {
+ if ((receiver->d_func()->threadData.loadRelaxed()->thread.loadAcquire() != mainThread()
+ || receiver != QCoreApplication::instance())
+ && receiver->d_func()->extraData) {
for (qsizetype i = 0; i < receiver->d_func()->extraData->eventFilters.size(); ++i) {
QObject *obj = receiver->d_func()->extraData->eventFilters.at(i);
if (!obj)
@@ -1184,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;
@@ -1252,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)
{
@@ -1263,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
@@ -1284,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
@@ -1294,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;
}
}
@@ -1315,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
@@ -1370,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);
}
@@ -1411,7 +1546,12 @@ 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 (qsizetype i = 0; i < data->eventLoops.size(); ++i) {
QEventLoop *eventLoop = data->eventLoops.at(i);
@@ -1551,31 +1691,6 @@ void QCoreApplication::postEvent(QObject *receiver, QEvent *event, int priority)
return;
}
- if (event->type() == QEvent::DeferredDelete)
- receiver->d_ptr->deleteLaterCalled = true;
-
- if (event->type() == QEvent::DeferredDelete && data == QThreadData::current()) {
- // remember the current running eventloop for DeferredDelete
- // events posted in the receiver's thread.
-
- // Events sent by non-Qt event handlers (such as glib) may not
- // have the scopeLevel set correctly. The scope level makes sure that
- // code like this:
- // foo->deleteLater();
- // qApp->processEvents(); // without passing QEvent::DeferredDelete
- // will not cause "foo" to be deleted before returning to the event loop.
-
- // If the scope level is 0 while loopLevel != 0, we are called from a
- // non-conformant code path, and our best guess is that the scope level
- // should be 1. (Loop level 0 is special: it means that no event loops
- // are running.)
- int loopLevel = data->loopLevel;
- int scopeLevel = data->scopeLevel;
- if (scopeLevel == 0 && loopLevel != 0)
- scopeLevel = 1;
- static_cast<QDeferredDeleteEvent *>(event)->level = loopLevel + scopeLevel;
- }
-
// delete the event on exceptions to protect against memory leaks till the event is
// properly owned in the postEventList
std::unique_ptr<QEvent> eventDeleter(event);
@@ -1602,33 +1717,26 @@ bool QCoreApplication::compressEvent(QEvent *event, QObject *receiver, QPostEven
Q_ASSERT(receiver);
Q_ASSERT(postedEvents);
-#ifdef Q_OS_WIN
+ int receiverPostedEvents = receiver->d_func()->postedEvents.loadRelaxed();
// compress posted timers to this object.
- if (event->type() == QEvent::Timer && receiver->d_func()->postedEvents > 0) {
- int timerId = ((QTimerEvent *) event)->timerId();
- for (const QPostEvent &e : std::as_const(*postedEvents)) {
- if (e.receiver == receiver && e.event && e.event->type() == QEvent::Timer
- && ((QTimerEvent *) e.event)->timerId() == timerId) {
+ if (event->type() == QEvent::Timer && receiverPostedEvents > 0) {
+ int timerId = static_cast<QTimerEvent *>(event)->timerId();
+ auto sameReceiver = [receiver](const QPostEvent &e) { return e.receiver == receiver; };
+ auto it = std::find_if(postedEvents->cbegin(), postedEvents->cend(), sameReceiver);
+ while (receiverPostedEvents > 0 && it != postedEvents->cend()) {
+ if (it->event && it->event->type() == QEvent::Timer
+ && static_cast<QTimerEvent *>(it->event)->timerId() == timerId) {
delete event;
return true;
}
- }
- return false;
- }
-#endif
- if (event->type() == QEvent::DeferredDelete) {
- if (receiver->d_ptr->deleteLaterCalled) {
- // there was a previous DeferredDelete event, so we can drop the new one
- delete event;
- return true;
+ if (--receiverPostedEvents)
+ it = std::find_if(it + 1, postedEvents->cend(), sameReceiver);
}
- // deleteLaterCalled is set to true in postedEvents when queueing the very first
- // deferred deletion event.
return false;
}
- if (event->type() == QEvent::Quit && receiver->d_func()->postedEvents > 0) {
+ if (event->type() == QEvent::Quit && receiverPostedEvents > 0) {
for (const QPostEvent &cur : std::as_const(*postedEvents)) {
if (cur.receiver != receiver
|| cur.event == nullptr
@@ -1762,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
@@ -1786,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;
}
}
@@ -2048,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()
*/
@@ -2071,14 +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.
\note QCoreApplication does \e not take ownership of \a translationFile.
- \sa removeTranslator(), translate(), QTranslator::load(), {Dynamic Translation}
+ \sa removeTranslator(), translate(), QTranslator::load(),
+ {Writing Source Code for Translation#Prepare for Dynamic Language Changes}{Prepare for Dynamic Language Changes}
*/
bool QCoreApplication::installTranslator(QTranslator *translationFile)
@@ -2145,12 +2285,12 @@ static void replacePercentN(QString *result, int n)
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) == u'L') {
++len;
- if (percentPos + len == result->length())
+ if (percentPos + len == result->size())
break;
fmt = "%L1"_L1;
} else {
@@ -2160,7 +2300,7 @@ static void replacePercentN(QString *result, int n)
fmt = fmt.arg(n);
++len;
result->replace(percentPos, len, fmt);
- len = fmt.length();
+ len = fmt.size();
}
}
}
@@ -2196,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)
@@ -2354,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]);
}
}
@@ -2501,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
@@ -2541,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
@@ -2577,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()
*/
@@ -2664,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)
@@ -2694,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}
*/