summaryrefslogtreecommitdiffstats
path: root/src/corelib
diff options
context:
space:
mode:
Diffstat (limited to 'src/corelib')
-rw-r--r--src/corelib/.prev_configure.cmake145
-rw-r--r--src/corelib/CMakeLists.txt198
-rw-r--r--src/corelib/Qt6AndroidMacros.cmake11
-rw-r--r--src/corelib/Qt6CTestMacros.cmake115
-rw-r--r--src/corelib/Qt6CoreConfigExtras.cmake.in9
-rw-r--r--src/corelib/Qt6CoreMacros.cmake194
-rw-r--r--src/corelib/Qt6WasmMacros.cmake31
-rw-r--r--src/corelib/animation/qabstractanimation.cpp2
-rw-r--r--src/corelib/animation/qabstractanimation_p.h39
-rw-r--r--src/corelib/animation/qpauseanimation.cpp20
-rw-r--r--src/corelib/animation/qpauseanimation.h3
-rw-r--r--src/corelib/animation/qsequentialanimationgroup.cpp4
-rw-r--r--src/corelib/configure.cmake143
-rw-r--r--src/corelib/configure.json83
-rw-r--r--src/corelib/doc/snippets/cmake-macros/examples.cmake2
-rw-r--r--src/corelib/doc/snippets/code/doc_src_qplugin.pro4
-rw-r--r--src/corelib/doc/snippets/code/src_corelib_global_qglobal.cpp6
-rw-r--r--src/corelib/doc/snippets/code/src_corelib_kernel_qabstractnativeeventfilter.pro5
-rw-r--r--src/corelib/doc/snippets/code/src_corelib_kernel_qdeadlinetimer.cpp2
-rw-r--r--src/corelib/doc/snippets/code/src_corelib_kernel_qproperty.cpp53
-rw-r--r--src/corelib/doc/snippets/code/src_corelib_thread_qfuture.cpp11
-rw-r--r--src/corelib/doc/snippets/code/src_corelib_thread_qmutex.cpp2
-rw-r--r--src/corelib/doc/snippets/jni/src_qjniobject.cpp126
-rw-r--r--src/corelib/doc/snippets/resource-system/application.pro3
-rw-r--r--src/corelib/doc/src/cmake-macros.qdoc20
-rw-r--r--src/corelib/doc/src/external-resources.qdoc27
-rw-r--r--src/corelib/doc/src/objectmodel/bindableproperties.qdoc223
-rw-r--r--src/corelib/doc/src/qt6-changes.qdoc27
-rw-r--r--src/corelib/global/qcompilerdetection.h17
-rw-r--r--src/corelib/global/qconfig-bootstrapped.h12
-rw-r--r--src/corelib/global/qconfig.cpp.in34
-rw-r--r--src/corelib/global/qfloat16.h59
-rw-r--r--src/corelib/global/qfloat16_f16c.c11
-rw-r--r--src/corelib/global/qglobal.cpp19
-rw-r--r--src/corelib/global/qglobal.h50
-rw-r--r--src/corelib/global/qglobalstatic.h43
-rw-r--r--src/corelib/global/qlibraryinfo.cpp361
-rw-r--r--src/corelib/global/qlibraryinfo.h24
-rw-r--r--src/corelib/global/qlibraryinfo_p.h78
-rw-r--r--src/corelib/global/qnamespace.h1
-rw-r--r--src/corelib/global/qnamespace.qdoc42
-rw-r--r--src/corelib/global/qnumeric.h7
-rw-r--r--src/corelib/global/qoperatingsystemversion.cpp20
-rw-r--r--src/corelib/global/qoperatingsystemversion.h4
-rw-r--r--src/corelib/global/qrandom.cpp31
-rw-r--r--src/corelib/global/qrandom_p.h4
-rw-r--r--src/corelib/global/qsimd_p.h16
-rw-r--r--src/corelib/global/qsysinfo.h10
-rw-r--r--src/corelib/global/qt_pch.h4
-rw-r--r--src/corelib/global/qtypeinfo.h18
-rw-r--r--src/corelib/io/qfilesystemengine_unix.cpp2
-rw-r--r--src/corelib/io/qfilesystemengine_win.cpp4
-rw-r--r--src/corelib/io/qiodevice.cpp12
-rw-r--r--src/corelib/io/qlockfile.cpp48
-rw-r--r--src/corelib/io/qlockfile.h15
-rw-r--r--src/corelib/io/qprocess.cpp51
-rw-r--r--src/corelib/io/qprocess_p.h4
-rw-r--r--src/corelib/io/qprocess_unix.cpp383
-rw-r--r--src/corelib/io/qprocess_win.cpp155
-rw-r--r--src/corelib/io/qsettings.cpp6
-rw-r--r--src/corelib/io/qsettings.h2
-rw-r--r--src/corelib/io/qstandardpaths_android.cpp66
-rw-r--r--src/corelib/io/qstandardpaths_mac.mm4
-rw-r--r--src/corelib/io/qurl.h4
-rw-r--r--src/corelib/io/qwindowspipereader.cpp476
-rw-r--r--src/corelib/io/qwindowspipereader_p.h53
-rw-r--r--src/corelib/io/qwindowspipewriter.cpp348
-rw-r--r--src/corelib/io/qwindowspipewriter_p.h96
-rw-r--r--src/corelib/itemmodels/qconcatenatetablesproxymodel.cpp7
-rw-r--r--src/corelib/itemmodels/qsortfilterproxymodel.cpp17
-rw-r--r--src/corelib/itemmodels/qsortfilterproxymodel.h10
-rw-r--r--src/corelib/kernel/qcore_mac.mm15
-rw-r--r--src/corelib/kernel/qcoreapplication.cpp30
-rw-r--r--src/corelib/kernel/qcoreapplication.h7
-rw-r--r--src/corelib/kernel/qcoreapplication_p.h2
-rw-r--r--src/corelib/kernel/qcoreapplication_platform.h66
-rw-r--r--src/corelib/kernel/qeventdispatcher_glib.cpp10
-rw-r--r--src/corelib/kernel/qeventdispatcher_win.cpp42
-rw-r--r--src/corelib/kernel/qeventdispatcher_win_p.h3
-rw-r--r--src/corelib/kernel/qfunctions_winrt_p.h6
-rw-r--r--src/corelib/kernel/qjni.cpp4
-rw-r--r--src/corelib/kernel/qjni_p.h1
-rw-r--r--src/corelib/kernel/qjnienvironment.cpp303
-rw-r--r--src/corelib/kernel/qjnienvironment.h85
-rw-r--r--src/corelib/kernel/qjnihelpers.cpp74
-rw-r--r--src/corelib/kernel/qjnihelpers_p.h1
-rw-r--r--src/corelib/kernel/qjniobject.cpp1892
-rw-r--r--src/corelib/kernel/qjniobject.h213
-rw-r--r--src/corelib/kernel/qjnionload.cpp5
-rw-r--r--src/corelib/kernel/qmath.h68
-rw-r--r--src/corelib/kernel/qmath.qdoc56
-rw-r--r--src/corelib/kernel/qmetaobject.cpp27
-rw-r--r--src/corelib/kernel/qmetaobject_p.h3
-rw-r--r--src/corelib/kernel/qmetaobjectbuilder.cpp277
-rw-r--r--src/corelib/kernel/qmetaobjectbuilder_p.h7
-rw-r--r--src/corelib/kernel/qmetatype.cpp50
-rw-r--r--src/corelib/kernel/qmetatype.h108
-rw-r--r--src/corelib/kernel/qobject.cpp4
-rw-r--r--src/corelib/kernel/qobject_p.h1
-rw-r--r--src/corelib/kernel/qobjectdefs.h2
-rw-r--r--src/corelib/kernel/qproperty.cpp385
-rw-r--r--src/corelib/kernel/qproperty.h34
-rw-r--r--src/corelib/kernel/qproperty_p.h103
-rw-r--r--src/corelib/kernel/qpropertyprivate.h8
-rw-r--r--src/corelib/kernel/qsocketnotifier.h2
-rw-r--r--src/corelib/kernel/qsystemsemaphore.h3
-rw-r--r--src/corelib/kernel/qsystemsemaphore_p.h1
-rw-r--r--src/corelib/kernel/qsystemsemaphore_posix.cpp2
-rw-r--r--src/corelib/kernel/qsystemsemaphore_systemv.cpp27
-rw-r--r--src/corelib/kernel/qsystemsemaphore_unix.cpp10
-rw-r--r--src/corelib/kernel/qtimer.cpp50
-rw-r--r--src/corelib/kernel/qtimer.h12
-rw-r--r--src/corelib/kernel/qtimerinfo_unix.cpp7
-rw-r--r--src/corelib/kernel/qvariant.cpp149
-rw-r--r--src/corelib/kernel/qvariant.h21
-rw-r--r--src/corelib/kernel/qvariant_p.h2
-rw-r--r--src/corelib/kernel/qwineventnotifier.cpp70
-rw-r--r--src/corelib/kernel/qwineventnotifier_p.h15
-rw-r--r--src/corelib/mimetypes/mime/generate.pl4
-rw-r--r--src/corelib/mimetypes/qmimedatabase.cpp2
-rw-r--r--src/corelib/platform/android/qandroidnativeinterface.cpp85
-rw-r--r--src/corelib/plugin/qlibrary_unix.cpp5
-rw-r--r--src/corelib/serialization/qdatastream.cpp1
-rw-r--r--src/corelib/serialization/qdatastream.h5
-rw-r--r--src/corelib/serialization/qjsoncbor.cpp2
-rw-r--r--src/corelib/serialization/qjsonobject.cpp1
-rw-r--r--src/corelib/serialization/qjsonobject.h2
-rw-r--r--src/corelib/serialization/qjsonvalue.h4
-rw-r--r--src/corelib/text/qbytearray.cpp25
-rw-r--r--src/corelib/text/qbytearray.h9
-rw-r--r--src/corelib/text/qlocale.cpp150
-rw-r--r--src/corelib/text/qlocale.h3
-rw-r--r--src/corelib/text/qlocale.qdoc20
-rw-r--r--src/corelib/text/qlocale_mac.mm49
-rw-r--r--src/corelib/text/qlocale_p.h7
-rw-r--r--src/corelib/text/qlocale_unix.cpp2
-rw-r--r--src/corelib/text/qlocale_win.cpp10
-rw-r--r--src/corelib/text/qregularexpression.cpp73
-rw-r--r--src/corelib/text/qregularexpression.h8
-rw-r--r--src/corelib/text/qstring.cpp46
-rw-r--r--src/corelib/text/qstring.h9
-rw-r--r--src/corelib/text/qstringconverter.cpp4
-rw-r--r--src/corelib/text/qunicodetools.cpp21
-rw-r--r--src/corelib/text/qutf8stringview.h3
-rw-r--r--src/corelib/thread/qfutex_p.h76
-rw-r--r--src/corelib/thread/qfuture.h1
-rw-r--r--src/corelib/thread/qfuture.qdoc17
-rw-r--r--src/corelib/thread/qfuture_impl.h14
-rw-r--r--src/corelib/thread/qfutureinterface.cpp9
-rw-r--r--src/corelib/thread/qfutureinterface.h31
-rw-r--r--src/corelib/thread/qfutureinterface_p.h35
-rw-r--r--src/corelib/thread/qgenericatomic.h6
-rw-r--r--src/corelib/thread/qmutex.cpp131
-rw-r--r--src/corelib/thread/qmutex_linux.cpp179
-rw-r--r--src/corelib/thread/qmutex_p.h5
-rw-r--r--src/corelib/thread/qsemaphore.cpp34
-rw-r--r--src/corelib/thread/qthread.h25
-rw-r--r--src/corelib/thread/qthreadpool.cpp38
-rw-r--r--src/corelib/thread/qthreadpool.h4
-rw-r--r--src/corelib/thread/qthreadpool_p.h1
-rw-r--r--src/corelib/time/qcalendar.cpp453
-rw-r--r--src/corelib/time/qcalendar.h1
-rw-r--r--src/corelib/time/qcalendarbackend_p.h5
-rw-r--r--src/corelib/time/qdatetime.cpp227
-rw-r--r--src/corelib/time/qdatetimeparser.cpp39
-rw-r--r--src/corelib/time/qgregoriancalendar.cpp12
-rw-r--r--src/corelib/time/qgregoriancalendar_p.h5
-rw-r--r--src/corelib/time/qislamiccivilcalendar.cpp18
-rw-r--r--src/corelib/time/qislamiccivilcalendar_p.h3
-rw-r--r--src/corelib/time/qjalalicalendar.cpp10
-rw-r--r--src/corelib/time/qjalalicalendar_p.h1
-rw-r--r--src/corelib/time/qjuliancalendar.cpp12
-rw-r--r--src/corelib/time/qjuliancalendar_p.h3
-rw-r--r--src/corelib/time/qmilankoviccalendar.cpp12
-rw-r--r--src/corelib/time/qmilankoviccalendar_p.h3
-rw-r--r--src/corelib/time/qtimezoneprivate.cpp61
-rw-r--r--src/corelib/time/qtimezoneprivate_android.cpp69
-rw-r--r--src/corelib/time/qtimezoneprivate_data_p.h445
-rw-r--r--src/corelib/time/qtimezoneprivate_p.h10
-rw-r--r--src/corelib/time/qtimezoneprivate_tz.cpp118
-rw-r--r--src/corelib/tools/qarraydata.cpp23
-rw-r--r--src/corelib/tools/qarraydata.h2
-rw-r--r--src/corelib/tools/qarraydataops.h1
-rw-r--r--src/corelib/tools/qcommandlineparser.cpp7
-rw-r--r--src/corelib/tools/qcontainerfwd.h3
-rw-r--r--src/corelib/tools/qcontiguouscache.h4
-rw-r--r--src/corelib/tools/qhash.cpp136
-rw-r--r--src/corelib/tools/qhash.h4
-rw-r--r--src/corelib/tools/qhashfunctions.h1
-rw-r--r--src/corelib/tools/qline.cpp30
-rw-r--r--src/corelib/tools/qline.h2
-rw-r--r--src/corelib/tools/qlist.h38
-rw-r--r--src/corelib/tools/qmargins.cpp63
-rw-r--r--src/corelib/tools/qmargins.h9
-rw-r--r--src/corelib/tools/qpoint.cpp20
-rw-r--r--src/corelib/tools/qpoint.h12
-rw-r--r--src/corelib/tools/qrect.cpp51
-rw-r--r--src/corelib/tools/qrect.h15
-rw-r--r--src/corelib/tools/qringbuffer_p.h2
-rw-r--r--src/corelib/tools/qscopedpointer.cpp10
-rw-r--r--src/corelib/tools/qscopedpointer.h12
-rw-r--r--src/corelib/tools/qscopedvaluerollback.cpp2
-rw-r--r--src/corelib/tools/qsharedpointer_impl.h3
-rw-r--r--src/corelib/tools/qsize.cpp20
-rw-r--r--src/corelib/tools/qsize.h46
205 files changed, 8075 insertions, 3503 deletions
diff --git a/src/corelib/.prev_configure.cmake b/src/corelib/.prev_configure.cmake
index 1218f552cb..893acda953 100644
--- a/src/corelib/.prev_configure.cmake
+++ b/src/corelib/.prev_configure.cmake
@@ -14,6 +14,9 @@ set_property(CACHE INPUT_libb2 PROPERTY STRINGS undefined no qt system)
#### Libraries
+if((UNIX) OR QT_FIND_ALL_PACKAGES_ALWAYS)
+ qt_find_package(WrapBacktrace PROVIDED_TARGETS WrapBacktrace::WrapBacktrace MODULE_NAME core QMAKE_LIB backtrace)
+endif()
qt_find_package(WrapDoubleConversion PROVIDED_TARGETS WrapDoubleConversion::WrapDoubleConversion MODULE_NAME core QMAKE_LIB doubleconversion)
qt_find_package(GLIB2 PROVIDED_TARGETS GLIB2::GLIB2 MODULE_NAME core QMAKE_LIB glib)
qt_find_package(ICU COMPONENTS i18n uc data PROVIDED_TARGETS ICU::i18n ICU::uc ICU::data MODULE_NAME core QMAKE_LIB icu)
@@ -22,7 +25,7 @@ if(QT_FEATURE_dlopen)
endif()
qt_find_package(Libsystemd PROVIDED_TARGETS PkgConfig::Libsystemd MODULE_NAME core QMAKE_LIB journald)
qt_find_package(WrapAtomic PROVIDED_TARGETS WrapAtomic::WrapAtomic MODULE_NAME core QMAKE_LIB libatomic)
-qt_find_package(Libb2 PROVIDED_TARGETS PkgConfig::Libb2 MODULE_NAME core QMAKE_LIB libb2)
+qt_find_package(Libb2 PROVIDED_TARGETS Libb2::Libb2 MODULE_NAME core QMAKE_LIB libb2)
qt_find_package(WrapRt PROVIDED_TARGETS WrapRt::WrapRt MODULE_NAME core QMAKE_LIB librt)
qt_find_package(LTTngUST PROVIDED_TARGETS LTTng::UST MODULE_NAME core QMAKE_LIB lttng-ust)
qt_add_qmake_lib_dependency(lttng-ust libdl)
@@ -40,8 +43,7 @@ qt_find_package(Slog2 PROVIDED_TARGETS Slog2::Slog2 MODULE_NAME core QMAKE_LIB s
qt_config_compile_test(atomicfptr
LABEL "working std::atomic for function pointers"
CODE
-"
-#include <atomic>
+"#include <atomic>
typedef void (*fptr)(int);
typedef std::atomic<fptr> atomicfptr;
void testfunction(int) { }
@@ -55,9 +57,9 @@ void test(volatile atomicfptr &a)
}
a.store(&testfunction, std::memory_order_release);
}
-int main(int argc, char **argv)
+
+int main(void)
{
- (void)argc; (void)argv;
/* BEGIN TEST: */
atomicfptr fptr(testfunction);
test(fptr);
@@ -72,13 +74,11 @@ qt_config_compile_test(clock_monotonic
LIBRARIES
WrapRt::WrapRt
CODE
-"
-#include <unistd.h>
+"#include <unistd.h>
#include <time.h>
-int main(int argc, char **argv)
+int main(void)
{
- (void)argc; (void)argv;
/* BEGIN TEST: */
#if defined(_POSIX_MONOTONIC_CLOCK) && (_POSIX_MONOTONIC_CLOCK-0 >= 0)
timespec ts;
@@ -101,9 +101,8 @@ qt_config_compile_test(cloexec
#include <fcntl.h>
#include <unistd.h>
-int main(int argc, char **argv)
+int main(void)
{
- (void)argc; (void)argv;
/* BEGIN TEST: */
int pipes[2];
(void) pipe2(pipes, O_CLOEXEC | O_NONBLOCK);
@@ -123,12 +122,10 @@ int pipes[2];
qt_config_compile_test(cxx11_future
LABEL "C++11 <future>"
CODE
-"
-#include <future>
+"#include <future>
-int main(int argc, char **argv)
+int main(void)
{
- (void)argc; (void)argv;
/* BEGIN TEST: */
std::future<int> f = std::async([]() { return 42; });
(void)f.get();
@@ -142,12 +139,10 @@ std::future<int> f = std::async([]() { return 42; });
qt_config_compile_test(cxx11_random
LABEL "C++11 <random>"
CODE
-"
-#include <random>
+"#include <random>
-int main(int argc, char **argv)
+int main(void)
{
- (void)argc; (void)argv;
/* BEGIN TEST: */
std::mt19937 mt(0);
/* END TEST: */
@@ -159,12 +154,10 @@ std::mt19937 mt(0);
qt_config_compile_test(cxx17_filesystem
LABEL "C++17 <filesystem>"
CODE
-"
-#include <filesystem>
+"#include <filesystem>
-int main(int argc, char **argv)
+int main(void)
{
- (void)argc; (void)argv;
/* BEGIN TEST: */
std::filesystem::copy(
std::filesystem::path(\"./file\"),
@@ -179,12 +172,10 @@ std::filesystem::copy(
qt_config_compile_test(eventfd
LABEL "eventfd"
CODE
-"
-#include <sys/eventfd.h>
+"#include <sys/eventfd.h>
-int main(int argc, char **argv)
+int main(void)
{
- (void)argc; (void)argv;
/* BEGIN TEST: */
eventfd_t value;
int fd = eventfd(0, EFD_CLOEXEC);
@@ -199,12 +190,10 @@ eventfd_write(fd, value);
qt_config_compile_test(futimens
LABEL "futimens()"
CODE
-"
-#include <sys/stat.h>
+"#include <sys/stat.h>
-int main(int argc, char **argv)
+int main(void)
{
- (void)argc; (void)argv;
/* BEGIN TEST: */
futimens(-1, 0);
/* END TEST: */
@@ -217,12 +206,10 @@ futimens(-1, 0);
qt_config_compile_test(futimes
LABEL "futimes()"
CODE
-"
-#include <sys/time.h>
+"#include <sys/time.h>
-int main(int argc, char **argv)
+int main(void)
{
- (void)argc; (void)argv;
/* BEGIN TEST: */
futimes(-1, 0);
/* END TEST: */
@@ -234,12 +221,10 @@ futimes(-1, 0);
qt_config_compile_test(getauxval
LABEL "getauxval()"
CODE
-"
-#include <sys/auxv.h>
+"#include <sys/auxv.h>
-int main(int argc, char **argv)
+int main(void)
{
- (void)argc; (void)argv;
/* BEGIN TEST: */
(void) getauxval(AT_NULL);
/* END TEST: */
@@ -251,12 +236,10 @@ int main(int argc, char **argv)
qt_config_compile_test(getentropy
LABEL "getentropy()"
CODE
-"
-#include <unistd.h>
+"#include <unistd.h>
-int main(int argc, char **argv)
+int main(void)
{
- (void)argc; (void)argv;
/* BEGIN TEST: */
char buf[32];
(void) getentropy(buf, sizeof(buf));
@@ -269,12 +252,10 @@ char buf[32];
qt_config_compile_test(glibc
LABEL "GNU libc"
CODE
-"
-#include <stdlib.h>
+"#include <stdlib.h>
-int main(int argc, char **argv)
+int main(void)
{
- (void)argc; (void)argv;
/* BEGIN TEST: */
return __GLIBC__;
/* END TEST: */
@@ -286,12 +267,10 @@ return __GLIBC__;
qt_config_compile_test(inotify
LABEL "inotify"
CODE
-"
-#include <sys/inotify.h>
+"#include <sys/inotify.h>
-int main(int argc, char **argv)
+int main(void)
{
- (void)argc; (void)argv;
/* BEGIN TEST: */
inotify_init();
inotify_add_watch(0, \"foobar\", IN_ACCESS);
@@ -305,16 +284,14 @@ inotify_rm_watch(0, 1);
qt_config_compile_test(ipc_sysv
LABEL "SysV IPC"
CODE
-"
-#include <sys/types.h>
+"#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <sys/shm.h>
#include <fcntl.h>
-int main(int argc, char **argv)
+int main(void)
{
- (void)argc; (void)argv;
/* BEGIN TEST: */
key_t unix_key = ftok(\"test\", 'Q');
semctl(semget(unix_key, 1, 0666 | IPC_CREAT | IPC_EXCL), 0, IPC_RMID, 0);
@@ -334,15 +311,13 @@ qt_config_compile_test(ipc_posix
LIBRARIES
"${ipc_posix_TEST_LIBRARIES}"
CODE
-"
-#include <sys/types.h>
+"#include <sys/types.h>
#include <sys/mman.h>
#include <semaphore.h>
#include <fcntl.h>
-int main(int argc, char **argv)
+int main(void)
{
- (void)argc; (void)argv;
/* BEGIN TEST: */
sem_close(sem_open(\"test\", O_CREAT | O_EXCL, 0666, 0));
shm_open(\"test\", O_RDWR | O_CREAT | O_EXCL, 0666);
@@ -360,9 +335,8 @@ qt_config_compile_test(linkat
#include <fcntl.h>
#include <unistd.h>
-int main(int argc, char **argv)
+int main(void)
{
- (void)argc; (void)argv;
/* BEGIN TEST: */
linkat(AT_FDCWD, \"foo\", AT_FDCWD, \"bar\", AT_SYMLINK_FOLLOW);
/* END TEST: */
@@ -374,13 +348,11 @@ linkat(AT_FDCWD, \"foo\", AT_FDCWD, \"bar\", AT_SYMLINK_FOLLOW);
qt_config_compile_test(ppoll
LABEL "ppoll()"
CODE
-"
-#include <signal.h>
+"#include <signal.h>
#include <poll.h>
-int main(int argc, char **argv)
+int main(void)
{
- (void)argc; (void)argv;
/* BEGIN TEST: */
struct pollfd pfd;
struct timespec ts;
@@ -395,14 +367,12 @@ ppoll(&pfd, 1, &ts, &sig);
qt_config_compile_test(pollts
LABEL "pollts()"
CODE
-"
-#include <poll.h>
+"#include <poll.h>
#include <signal.h>
#include <time.h>
-int main(int argc, char **argv)
+int main(void)
{
- (void)argc; (void)argv;
/* BEGIN TEST: */
struct pollfd pfd;
struct timespec ts;
@@ -417,12 +387,10 @@ pollts(&pfd, 1, &ts, &sig);
qt_config_compile_test(poll
LABEL "poll()"
CODE
-"
-#include <poll.h>
+"#include <poll.h>
-int main(int argc, char **argv)
+int main(void)
{
- (void)argc; (void)argv;
/* BEGIN TEST: */
struct pollfd pfd;
poll(&pfd, 1, 0);
@@ -439,9 +407,8 @@ qt_config_compile_test(renameat2
#include <fcntl.h>
#include <stdio.h>
-int main(int argc, char **argv)
+int main(void)
{
- (void)argc; (void)argv;
/* BEGIN TEST: */
renameat2(AT_FDCWD, argv[1], AT_FDCWD, argv[2], RENAME_NOREPLACE | RENAME_WHITEOUT);
/* END TEST: */
@@ -459,9 +426,8 @@ qt_config_compile_test(statx
#include <unistd.h>
#include <fcntl.h>
-int main(int argc, char **argv)
+int main(void)
{
- (void)argc; (void)argv;
/* BEGIN TEST: */
struct statx statxbuf;
unsigned int mask = STATX_BASIC_STATS;
@@ -475,12 +441,10 @@ return statx(AT_FDCWD, \"\", AT_STATX_SYNC_AS_STAT, mask, &statxbuf);
qt_config_compile_test(syslog
LABEL "syslog"
CODE
-"
-#include <syslog.h>
+"#include <syslog.h>
-int main(int argc, char **argv)
+int main(void)
{
- (void)argc; (void)argv;
/* BEGIN TEST: */
openlog(\"qt\", 0, LOG_USER);
syslog(LOG_INFO, \"configure\");
@@ -494,9 +458,7 @@ closelog();
qt_config_compile_test(xlocalescanprint
LABEL "xlocale.h (or equivalents)"
CODE
-"
-
-#define QT_BEGIN_NAMESPACE
+"#define QT_BEGIN_NAMESPACE
#define QT_END_NAMESPACE
#ifdef _MSVC_VER
@@ -506,9 +468,9 @@ qt_config_compile_test(xlocalescanprint
#define QT_NO_DOUBLECONVERSION
#include QDSP_P_H
-int main(int argc, char **argv)
+
+int main(void)
{
- (void)argc; (void)argv;
/* BEGIN TEST: */
#ifdef _MSVC_VER
_locale_t invalidLocale = NULL;
@@ -709,6 +671,10 @@ qt_feature("regularexpression" PUBLIC
CONDITION QT_FEATURE_system_pcre2 OR QT_FEATURE_pcre2
)
qt_feature_definition("regularexpression" "QT_NO_REGULAREXPRESSION" NEGATE VALUE "1")
+qt_feature("backtrace" PRIVATE
+ LABEL "backtrace"
+ CONDITION UNIX AND QT_FEATURE_regularexpression AND WrapBacktrace_FOUND
+)
qt_feature("sharedmemory" PUBLIC
SECTION "Kernel"
LABEL "QSharedMemory"
@@ -823,7 +789,7 @@ qt_feature("sortfilterproxymodel" PUBLIC
SECTION "ItemViews"
LABEL "QSortFilterProxyModel"
PURPOSE "Supports sorting and filtering of data passed between another model and a view."
- CONDITION QT_FEATURE_proxymodel
+ CONDITION QT_FEATURE_proxymodel AND QT_FEATURE_regularexpression
)
qt_feature_definition("sortfilterproxymodel" "QT_NO_SORTFILTERPROXYMODEL" NEGATE VALUE "1")
qt_feature("identityproxymodel" PUBLIC
@@ -933,10 +899,6 @@ qt_feature("forkfd_pidfd" PRIVATE
LABEL "CLONE_PIDFD support in forkfd"
CONDITION LINUX
)
-qt_feature("win32_system_libs"
- LABEL "Windows System Libraries"
- CONDITION WIN32 AND libs.advapi32 AND libs.gdi32 AND libs.kernel32 AND libs.netapi32 AND libs.ole32 AND libs.shell32 AND libs.uuid AND libs.user32 AND libs.winmm AND libs.ws2_32 OR FIXME
-)
qt_feature("cborstreamreader" PUBLIC
SECTION "Utilities"
LABEL "CBOR stream reading"
@@ -948,6 +910,7 @@ qt_feature("cborstreamwriter" PUBLIC
PURPOSE "Provides support for writing the CBOR binary format."
)
qt_configure_add_summary_section(NAME "Qt Core")
+qt_configure_add_summary_entry(ARGS "backtrace")
qt_configure_add_summary_entry(ARGS "doubleconversion")
qt_configure_add_summary_entry(ARGS "system-doubleconversion")
qt_configure_add_summary_entry(ARGS "glib")
diff --git a/src/corelib/CMakeLists.txt b/src/corelib/CMakeLists.txt
index 355ba42ee6..3b4fdd1b46 100644
--- a/src/corelib/CMakeLists.txt
+++ b/src/corelib/CMakeLists.txt
@@ -20,6 +20,10 @@ if(ANDROID)
set(corelib_extra_cmake_files
"${CMAKE_CURRENT_SOURCE_DIR}/${QT_CMAKE_EXPORT_NAMESPACE}AndroidMacros.cmake")
endif()
+if(WASM)
+ set(corelib_extra_cmake_files
+ "${CMAKE_CURRENT_SOURCE_DIR}/${QT_CMAKE_EXPORT_NAMESPACE}WasmMacros.cmake")
+endif()
# special case end
#####################################################################
@@ -27,7 +31,6 @@ endif()
#####################################################################
qt_internal_add_module(Core
- GENERATE_METATYPES
QMAKE_MODULE_CONFIG moc resources
EXCEPTIONS
SOURCES
@@ -41,10 +44,10 @@ qt_internal_add_module(Core
global/qglobal.cpp global/qglobal.h
global/qglobalstatic.h
global/qhooks.cpp global/qhooks_p.h
- global/qlibraryinfo.cpp global/qlibraryinfo.h
+ global/qlibraryinfo.cpp global/qlibraryinfo.h global/qlibraryinfo_p.h
global/qlogging.cpp global/qlogging.h
global/qmalloc.cpp
- # global/qnamespace.h # special case
+ global/qnamespace.h # this header is specified on purpose so AUTOMOC processes it
global/qnumeric.cpp global/qnumeric.h global/qnumeric_p.h
global/qoperatingsystemversion.cpp global/qoperatingsystemversion.h global/qoperatingsystemversion_p.h
global/qprocessordetection.h
@@ -94,6 +97,7 @@ qt_internal_add_module(Core
kernel/qassociativeiterable.cpp kernel/qassociativeiterable.h
kernel/qbasictimer.cpp kernel/qbasictimer.h
kernel/qcoreapplication.cpp kernel/qcoreapplication.h kernel/qcoreapplication_p.h
+ kernel/qcoreapplication_platform.h
kernel/qcorecmdlineargs_p.h
kernel/qcoreevent.cpp kernel/qcoreevent.h
kernel/qcoreglobaldata.cpp kernel/qcoreglobaldata_p.h
@@ -109,7 +113,7 @@ qt_internal_add_module(Core
kernel/qmetaobjectbuilder.cpp kernel/qmetaobjectbuilder_p.h
kernel/qmetatype.cpp kernel/qmetatype.h kernel/qmetatype_p.h
kernel/qmimedata.cpp kernel/qmimedata.h
- # kernel/qobject.cpp kernel/qobject.h kernel/qobject_p.h # special case
+ kernel/qobject.cpp kernel/qobject.h kernel/qobject_p.h
kernel/qobject_impl.h
kernel/qobjectcleanuphandler.cpp kernel/qobjectcleanuphandler.h
kernel/qobjectdefs.h
@@ -244,6 +248,7 @@ qt_internal_add_module(Core
QT_NO_USING_NAMESPACE
INCLUDE_DIRECTORIES
"${CMAKE_CURRENT_BINARY_DIR}/global" # special case
+ "${CMAKE_CURRENT_BINARY_DIR}/kernel" # for moc_qobject.cpp to be found by qobject.cpp
# ../3rdparty/md4 # special case remove
# ../3rdparty/md5 # special case remove
# ../3rdparty/sha3 # special case remove
@@ -263,6 +268,8 @@ qt_internal_add_module(Core
# special case end
)
+qt_update_ignore_pch_source(Core kernel/qmetatype.cpp )
+
# special case begin
add_dependencies(Core qmodule_pri)
add_dependencies(Core ${QT_CMAKE_EXPORT_NAMESPACE}::moc)
@@ -270,12 +277,14 @@ add_dependencies(Core ${QT_CMAKE_EXPORT_NAMESPACE}::rcc)
add_dependencies(CorePrivate ${QT_CMAKE_EXPORT_NAMESPACE}::moc)
add_dependencies(CorePrivate ${QT_CMAKE_EXPORT_NAMESPACE}::rcc)
-if (QT_NAMESPACE STREQUAL "")
-else()
+if (NOT QT_NAMESPACE STREQUAL "")
target_compile_definitions(Core PUBLIC "QT_NAMESPACE=${QT_NAMESPACE}")
endif()
-qt_generate_qconfig_cpp()
+qt_generate_qconfig_cpp(global/qconfig.cpp.in global/qconfig.cpp)
+
+set_target_properties(Core PROPERTIES INTERFACE_QT_COORD_TYPE "${QT_COORD_TYPE}")
+set_property(TARGET Core APPEND PROPERTY COMPATIBLE_INTERFACE_STRING QT_COORD_TYPE)
# Handle qtConfig(thread): CONFIG += thread like in qt.prf.
# Aka if the feature is enabled, publically link against the threading library.
@@ -284,49 +293,31 @@ if(QT_FEATURE_thread)
target_link_libraries(Platform INTERFACE Threads::Threads)
endif()
-# Handle QObject: Automoc does not work for this as it would
-# require to spill internals into users:
-qt_internal_add_module(Core_qobject STATIC
- NO_SYNC_QT
- NO_CONFIG_HEADER_FILE
- NO_MODULE_HEADERS
- INTERNAL_MODULE
- SKIP_DEPENDS_INCLUDE
-)
-set_target_properties(Core_qobject PROPERTIES AUTOMOC OFF)
-qt_manual_moc(qobject_moc_files OUTPUT_MOC_JSON_FILES core_qobject_metatypes_json_list kernel/qobject.h global/qnamespace.h)
+# Skip AUTOMOC processing of qobject.cpp and its headers.
+# We do this on purpose, because qobject.cpp contains a bunch of Q_GADGET, Q_NAMESPACE, etc
+# keywords and AUTOMOC gets confused about wanting to compile a qobject.moc file as well.
+# Instead use manual moc.
+set_source_files_properties(kernel/qobject.cpp kernel/qobject.h kernel/qobject_p.h
+ PROPERTIES SKIP_AUTOMOC TRUE)
+
+qt_manual_moc(qobject_moc_files
+ OUTPUT_MOC_JSON_FILES core_qobject_metatypes_json_list
+ kernel/qobject.h)
+# The moc file is included directly by qobject.cpp
set_source_files_properties(${qobject_moc_files} PROPERTIES HEADER_FILE_ONLY ON)
-target_sources(Core_qobject PRIVATE
- global/qnamespace.h
- kernel/qobject.cpp kernel/qproperty.cpp kernel/qproperty.h kernel/qproperty_p.h kernel/qobject.h kernel/qobject_p.h ${qobject_moc_files})
-set_target_properties(Core_qobject PROPERTIES
- COMPILE_OPTIONS $<TARGET_PROPERTY:Core,COMPILE_OPTIONS>
- COMPILE_DEFINITIONS $<TARGET_PROPERTY:Core,COMPILE_DEFINITIONS>
- INCLUDE_DIRECTORIES $<TARGET_PROPERTY:Core,INCLUDE_DIRECTORIES>
-)
-target_include_directories(Core_qobject PRIVATE "${CMAKE_CURRENT_BINARY_DIR}/kernel") # for moc_qobject.cpp
-target_link_libraries(Core_qobject PRIVATE Qt::Platform Qt::GlobalConfig)
-qt_internal_extend_target(Core LIBRARIES Qt::Core_qobject)
-add_dependencies(Core_qobject ${QT_CMAKE_EXPORT_NAMESPACE}::moc)
-
-set(core_qobject_metatypes_json_args)
-if (NOT QT_WILL_INSTALL)
- set(core_qobject_metatypes_json_args INSTALL_DIR "${QT_BUILD_DIR}/${INSTALL_LIBDIR}/metatypes")
-endif()
-qt6_extract_metatypes(Core_qobject
- MANUAL_MOC_JSON_FILES ${core_qobject_metatypes_json_list}
- ${core_qobject_metatypes_json_args}
-)
+set(core_metatype_args MANUAL_MOC_JSON_FILES ${core_qobject_metatypes_json_list})
-# Core_qobject is never exported so we need to duplicate the metatypes file
-# interface on Core
-get_target_property(core_qobject_metatypes_file_genex_build Core_qobject QT_MODULE_META_TYPES_FILE_GENEX_BUILD)
-get_target_property(core_qobject_metatypes_file_genex_install Core_qobject QT_MODULE_META_TYPES_FILE_GENEX_INSTALL)
-target_sources(Core INTERFACE
- ${core_qobject_metatypes_file_genex_build}
- ${core_qobject_metatypes_file_genex_install}
-)
+set(metatypes_install_dir ${INSTALL_LIBDIR}/metatypes)
+if (NOT QT_WILL_INSTALL)
+ list(APPEND core_metatype_args
+ COPY_OVER_INSTALL INSTALL_DIR "${QT_BUILD_DIR}/${metatypes_install_dir}")
+else()
+ list(APPEND core_metatype_args INSTALL_DIR "${metatypes_install_dir}")
+endif()
+# Use qt6_extract_metatypes instead of GENERATE_METATYPES so that we can manually pass the
+# additional json files.
+qt6_extract_metatypes(Core ${core_metatype_args})
set_property(TARGET Core APPEND PROPERTY
PUBLIC_HEADER "${CMAKE_CURRENT_BINARY_DIR}/global/qconfig.h")
@@ -439,8 +430,13 @@ qt_internal_extend_target(Core CONDITION MSVC AND (TEST_architecture_arch STREQU
"/BASE:0x67000000"
)
-#### Keys ignored in scope 6:.:.:corelib.pro:FREEBSD OR OPENBSD:
-# QMAKE_LFLAGS_NOUNDEF = <EMPTY>
+# QtCore can't be compiled with -Wl,-no-undefined because it uses the
+# "environ" variable and FreeBSD does not include a weak symbol for it
+# in libc.
+qt_internal_extend_target(Core CONDITION FREEBSD
+ LINK_OPTIONS
+ "LINKER:--warn-unresolved-symbols"
+)
qt_internal_extend_target(Core CONDITION QT_FEATURE_animation
SOURCES
@@ -453,6 +449,18 @@ qt_internal_extend_target(Core CONDITION QT_FEATURE_animation
animation/qvariantanimation.cpp animation/qvariantanimation.h animation/qvariantanimation_p.h
)
+# This needs to be done before one below adds kernel32 because the symbols we use
+# from synchronization also appears in kernel32 in the version of MinGW we use in CI.
+# However, when picking the symbols from libkernel32.a it will try to load the symbols
+# from the wrong DLL at runtime and crash!
+qt_internal_extend_target(Core CONDITION QT_FEATURE_thread AND WIN32
+ SOURCES
+ thread/qmutex_win.cpp
+ thread/qwaitcondition_win.cpp
+ LIBRARIES
+ synchronization
+)
+
qt_internal_extend_target(Core CONDITION WIN32
SOURCES
global/qoperatingsystemversion_win.cpp global/qoperatingsystemversion_win_p.h
@@ -529,6 +537,12 @@ qt_internal_extend_target(Core CONDITION INTEGRITY
--pending_instantiations=128
)
+# Workaround for QTBUG-90353
+# Remove if MinGW (our only supported gcc version < 9) is no longer supported
+qt_internal_extend_target(Core CONDITION GCC AND (CMAKE_CXX_COMPILER_VERSION VERSION_LESS "9.0.0")
+ PUBLIC_COMPILE_OPTIONS -Wno-error=invalid-offsetof
+)
+
#### Keys ignored in scope 14:.:.:corelib.pro:pathIsAbsolute(_ss_CMAKE_HOST_DATA_DIR):
# CMAKE_HOST_DATA_DIR = "$$[QT_HOST_DATA/src]/"
# CMAKE_HOST_DATA_DIR_IS_ABSOLUTE = "True"
@@ -596,29 +610,16 @@ qt_internal_extend_target(Core CONDITION QT_FEATURE_journald
PkgConfig::Libsystemd
)
-# special case begin
-add_library(Core_versiontagging OBJECT
- global/qversiontagging.cpp global/qversiontagging.h
-)
-
-set_target_properties(Core_versiontagging PROPERTIES
- COMPILE_OPTIONS $<TARGET_PROPERTY:Core,COMPILE_OPTIONS>
- COMPILE_DEFINITIONS $<TARGET_PROPERTY:Core,COMPILE_DEFINITIONS>
- INCLUDE_DIRECTORIES $<TARGET_PROPERTY:Core,INCLUDE_DIRECTORIES>
-)
+set(core_version_tagging_files global/qversiontagging.cpp global/qversiontagging.h)
+target_sources(Core PRIVATE ${core_version_tagging_files})
# Disable LTO, as the symbols disappear somehow under GCC
# (https://gcc.gnu.org/bugzilla/show_bug.cgi?id=48200)
if(GCC AND FEATURE_ltcg)
- target_compile_options(Core_versiontagging PRIVATE "-fno-lto")
+ set_source_files_properties(${core_version_tagging_files}
+ PROPERTIES COMPILE_OPTIONS "-fno-lto")
endif()
-qt_internal_extend_target(Core
- SOURCES
- $<TARGET_OBJECTS:Core_versiontagging>
-)
-# special case end
-
qt_internal_extend_target(Core CONDITION UNIX
SOURCES
io/qfilesystemengine_unix.cpp
@@ -650,12 +651,6 @@ qt_internal_extend_target(Core CONDITION QT_FEATURE_thread
thread/qthreadstorage.cpp
)
-qt_internal_extend_target(Core CONDITION QT_FEATURE_thread AND WIN32
- SOURCES
- thread/qmutex_win.cpp
- thread/qwaitcondition_win.cpp
-)
-
qt_internal_extend_target(Core CONDITION QT_FEATURE_thread AND UNIX
SOURCES
thread/qwaitcondition_unix.cpp
@@ -666,11 +661,6 @@ qt_internal_extend_target(Core CONDITION APPLE AND QT_FEATURE_thread
thread/qmutex_mac.cpp
)
-qt_internal_extend_target(Core CONDITION LINUX AND QT_FEATURE_thread
- SOURCES
- thread/qmutex_linux.cpp
-)
-
qt_internal_extend_target(Core CONDITION QT_FEATURE_thread AND UNIX AND NOT APPLE AND NOT LINUX
SOURCES
thread/qmutex_unix.cpp
@@ -731,6 +721,11 @@ qt_internal_extend_target(Core CONDITION QT_FEATURE_commandlineparser
tools/qcommandlineparser.cpp tools/qcommandlineparser.h
)
+qt_internal_extend_target(Core CONDITION QT_FEATURE_backtrace
+ LIBRARIES
+ WrapBacktrace::WrapBacktrace
+)
+
qt_internal_extend_target(Core CONDITION QT_FEATURE_system_doubleconversion
LIBRARIES
WrapDoubleConversion::WrapDoubleConversion
@@ -738,20 +733,29 @@ qt_internal_extend_target(Core CONDITION QT_FEATURE_system_doubleconversion
qt_internal_extend_target(Core CONDITION QT_FEATURE_doubleconversion AND NOT QT_FEATURE_system_doubleconversion
SOURCES
- ../3rdparty/double-conversion/bignum.cc ../3rdparty/double-conversion/bignum.h
- ../3rdparty/double-conversion/bignum-dtoa.cc ../3rdparty/double-conversion/bignum-dtoa.h
- ../3rdparty/double-conversion/cached-powers.cc ../3rdparty/double-conversion/cached-powers.h
- ../3rdparty/double-conversion/diy-fp.cc ../3rdparty/double-conversion/diy-fp.h
- ../3rdparty/double-conversion/double-conversion.cc
- ../3rdparty/double-conversion/fast-dtoa.cc ../3rdparty/double-conversion/fast-dtoa.h
- ../3rdparty/double-conversion/fixed-dtoa.cc ../3rdparty/double-conversion/fixed-dtoa.h
- ../3rdparty/double-conversion/ieee.h
- ../3rdparty/double-conversion/include/double-conversion/double-conversion.h
- ../3rdparty/double-conversion/include/double-conversion/utils.h
- ../3rdparty/double-conversion/strtod.cc ../3rdparty/double-conversion/strtod.h
+ ../3rdparty/double-conversion/double-conversion/bignum.cc
+ ../3rdparty/double-conversion/double-conversion/bignum-dtoa.cc
+ ../3rdparty/double-conversion/double-conversion/bignum-dtoa.h
+ ../3rdparty/double-conversion/double-conversion/bignum.h
+ ../3rdparty/double-conversion/double-conversion/cached-powers.cc
+ ../3rdparty/double-conversion/double-conversion/cached-powers.h
+ ../3rdparty/double-conversion/double-conversion/diy-fp.h
+ ../3rdparty/double-conversion/double-conversion/double-conversion.h
+ ../3rdparty/double-conversion/double-conversion/double-to-string.cc
+ ../3rdparty/double-conversion/double-conversion/double-to-string.h
+ ../3rdparty/double-conversion/double-conversion/fast-dtoa.cc
+ ../3rdparty/double-conversion/double-conversion/fast-dtoa.h
+ ../3rdparty/double-conversion/double-conversion/fixed-dtoa.cc
+ ../3rdparty/double-conversion/double-conversion/fixed-dtoa.h
+ ../3rdparty/double-conversion/double-conversion/ieee.h
+ ../3rdparty/double-conversion/double-conversion/string-to-double.cc
+ ../3rdparty/double-conversion/double-conversion/string-to-double.h
+ ../3rdparty/double-conversion/double-conversion/strtod.cc
+ ../3rdparty/double-conversion/double-conversion/strtod.h
+ ../3rdparty/double-conversion/double-conversion/utils.h
INCLUDE_DIRECTORIES
- ../3rdparty/double-conversion/..
- ../3rdparty/double-conversion/include
+ ../3rdparty/double-conversion/double-conversion
+ ../3rdparty/double-conversion
)
qt_internal_extend_target(Core CONDITION QT_FEATURE_system_libb2
@@ -973,8 +977,11 @@ qt_internal_extend_target(Core CONDITION ANDROID AND NOT ANDROID_EMBEDDED
io/qstandardpaths_android.cpp
io/qstorageinfo_unix.cpp
kernel/qjni.cpp kernel/qjni_p.h
+ kernel/qjnienvironment.cpp kernel/qjnienvironment.h
+ kernel/qjniobject.cpp kernel/qjniobject.h
kernel/qjnihelpers.cpp kernel/qjnihelpers_p.h
kernel/qjnionload.cpp
+ platform/android/qandroidnativeinterface.cpp
)
qt_internal_extend_target(Core CONDITION HAIKU AND (ANDROID_EMBEDDED OR NOT ANDROID)
@@ -1150,10 +1157,10 @@ qt_internal_extend_target(Core CONDITION QT_FEATURE_mimetype
# edited qrc files.
if(QT_FEATURE_mimetype AND QT_FEATURE_mimetype_database)
include(${CMAKE_CURRENT_SOURCE_DIR}/mimetypes/mimetypes_resources.cmake)
- corelib_add_mimetypes_resources(Core)
# Generate qmimeprovider_database.cpp
- set(qmimeprovider_db_output "${CMAKE_CURRENT_BINARY_DIR}/.rcc/qmimeprovider_database.cpp")
+ set(qmimeprovider_db_output_dir "${CMAKE_CURRENT_BINARY_DIR}/.rcc")
+ set(qmimeprovider_db_output "${qmimeprovider_db_output_dir}/qmimeprovider_database.cpp")
if(CMAKE_VERSION VERSION_LESS 3.18 OR QT_AVOID_CMAKE_ARCHIVING_API)
set(command_args "")
set(mime_dir "${CMAKE_CURRENT_SOURCE_DIR}/mimetypes/mime")
@@ -1162,6 +1169,7 @@ if(QT_FEATURE_mimetype AND QT_FEATURE_mimetype_database)
list(APPEND command_args "${mime_dir}/generate.bat")
list(APPEND command_depends "${mime_dir}/generate.bat" "${mime_dir}/hexdump.ps1" )
else()
+ file(MAKE_DIRECTORY ${qmimeprovider_db_output_dir})
list(APPEND command_args perl "${mime_dir}/generate.pl" )
endif()
@@ -1281,7 +1289,6 @@ else()
endif()
set_source_files_properties(
- thread/qmutex_linux.cpp
thread/qmutex_mac.cpp
thread/qmutex_unix.cpp
thread/qmutex_win.cpp
@@ -1328,11 +1335,6 @@ endif()
# special case end
qt_internal_create_tracepoints(Core qtcore.tracepoints)
-# special case begin
-if(TARGET Core_tracepoints_header)
- add_dependencies(Core_qobject Core_tracepoints_header)
-endif()
-# special case end
qt_internal_add_docs(Core
doc/qtcore.qdocconf
)
diff --git a/src/corelib/Qt6AndroidMacros.cmake b/src/corelib/Qt6AndroidMacros.cmake
index f4732e4579..c520242d06 100644
--- a/src/corelib/Qt6AndroidMacros.cmake
+++ b/src/corelib/Qt6AndroidMacros.cmake
@@ -45,7 +45,7 @@ function(qt6_android_generate_deployment_settings target)
string(APPEND file_contents
" \"description\": \"This file is generated by cmake to be read by androiddeployqt and should not be modified by hand.\",\n")
- # Host Qt Android install path
+ # Host Qt Android install path
if (NOT QT_BUILDING_QT OR QT_STANDALONE_TEST_PATH)
set(qt_path "${QT6_INSTALL_PREFIX}")
set(android_plugin_dir_path "${qt_path}/${QT6_INSTALL_PLUGINS}/platforms")
@@ -62,12 +62,9 @@ function(qt6_android_generate_deployment_settings target)
list(GET plugin_dir_files 0 android_platform_plugin_path)
message(STATUS "Found android platform plugin at: ${android_platform_plugin_path}")
endif()
- set(qt_android_install_dir "${qt_path}")
- else()
- # Building from source, use the same install prefix.
- set(qt_android_install_dir "${CMAKE_INSTALL_PREFIX}")
endif()
+ set(qt_android_install_dir "${QT6_INSTALL_PREFIX}")
file(TO_CMAKE_PATH "${qt_android_install_dir}" qt_android_install_dir_native)
string(APPEND file_contents
" \"qt\": \"${qt_android_install_dir_native}\",\n")
@@ -194,7 +191,7 @@ function(qt6_android_generate_deployment_settings target)
" \"qml-importscanner-binary\" : \"${qml_importscanner_binary_path_native}\",\n")
# Override rcc binary path
- set(rcc_binary_path "${QT_HOST_PATH}/${QT6_HOST_INFO_BINDIR}/rcc")
+ set(rcc_binary_path "${QT_HOST_PATH}/${QT6_HOST_INFO_LIBEXECDIR}/rcc")
if (WIN32)
string(APPEND rcc_binary_path ".exe")
endif()
@@ -281,7 +278,7 @@ function(qt6_android_add_apk_target target)
COMMAND ${CMAKE_COMMAND}
-E copy $<TARGET_FILE:${target}>
"${apk_dir}/libs/${CMAKE_ANDROID_ARCH_ABI}/$<TARGET_FILE_NAME:${target}>"
- COMMENT "Copying ${target} binarty to apk folder"
+ COMMENT "Copying ${target} binary to apk folder"
)
add_custom_target(${target}_make_apk
diff --git a/src/corelib/Qt6CTestMacros.cmake b/src/corelib/Qt6CTestMacros.cmake
index 50cc40939a..4e1abe904c 100644
--- a/src/corelib/Qt6CTestMacros.cmake
+++ b/src/corelib/Qt6CTestMacros.cmake
@@ -113,31 +113,104 @@ function(_qt_internal_set_up_test_run_environment testname)
endfunction()
+# Checks if the test project can be built successfully. Arguments:
+#
+# SIMULATE_IN_SOURCE: If the option is specified, the function copies sources of the tests to the
+# CMAKE_CURRENT_BINARY_DIR directory, creates internal build directory in the
+# copied sources and uses this directory to build and test the project.
+# This makes possible to have relative paths to the source files in the
+# generated ninja rules.
+#
+# BINARY: Path to the test artifact that will be executed after the build is complete. If a
+# relative path is specified, it will be counted from the build directory.
+#
+# TESTNAME: a custom test name to use instead of the one derived from the source directory name
+#
+# BUILD_OPTIONS: a list of -D style CMake definitions to pass to ctest's --build-options (which
+# are ultimately passed to the CMake invocation of the test project)
macro(_qt_internal_test_expect_pass _dir)
- cmake_parse_arguments(_ARGS "" "BINARY" "" ${ARGN})
- string(REPLACE "(" "_" testname "${_dir}")
- string(REPLACE ")" "_" testname "${testname}")
-
- set(__expect_pass__prefixes "${CMAKE_PREFIX_PATH}")
- string(REPLACE ";" "\;" __expect_pass__prefixes "${__expect_pass__prefixes}")
-
- set(ctest_command_args
- --build-and-test
- "${CMAKE_CURRENT_SOURCE_DIR}/${_dir}"
- "${CMAKE_CURRENT_BINARY_DIR}/${_dir}"
- --build-config "${CMAKE_BUILD_TYPE}"
- --build-generator "${CMAKE_GENERATOR}"
- --build-makeprogram "${CMAKE_MAKE_PROGRAM}"
- --build-project "${_dir}"
- --build-options "-DCMAKE_PREFIX_PATH=${__expect_pass__prefixes}" ${BUILD_OPTIONS_LIST}
- --test-command ${_ARGS_BINARY})
- add_test(${testname} ${CMAKE_CTEST_COMMAND} ${ctest_command_args})
- if(_ARGS_BINARY)
- _qt_internal_set_up_test_run_environment("${testname}")
+ cmake_parse_arguments(_ARGS "SIMULATE_IN_SOURCE" "BINARY;TESTNAME" "BUILD_OPTIONS" ${ARGN})
+ if(_ARGS_TESTNAME)
+ set(testname "${_ARGS_TESTNAME}")
+ else()
+ string(REPLACE "(" "_" testname "${_dir}")
+ string(REPLACE ")" "_" testname "${testname}")
+ string(REPLACE "/" "_" testname "${testname}")
endif()
+
+ set(__expect_pass_prefixes "${CMAKE_PREFIX_PATH}")
+ string(REPLACE ";" "\;" __expect_pass_prefixes "${__expect_pass_prefixes}")
+
+ set(__expect_pass_build_dir "${CMAKE_CURRENT_BINARY_DIR}/${_dir}")
+ set(__expect_pass_source_dir "${CMAKE_CURRENT_SOURCE_DIR}/${_dir}")
+ if(_ARGS_SIMULATE_IN_SOURCE)
+ set(__expect_pass_in_source_build_dir "${CMAKE_CURRENT_BINARY_DIR}/in_source")
+ set(__expect_pass_build_dir "${__expect_pass_in_source_build_dir}/${_dir}/build")
+ set(__expect_pass_source_dir "${__expect_pass_in_source_build_dir}/${_dir}")
+
+ unset(__expect_pass_in_source_build_dir)
+ endif()
+
+ if(_ARGS_BINARY AND NOT IS_ABSOLUTE "${_ARGS_BINARY}")
+ set(_ARGS_BINARY "${__expect_pass_build_dir}/${_ARGS_BINARY}")
+ endif()
+
+ if(_ARGS_SIMULATE_IN_SOURCE)
+ add_test(NAME ${testname}_cleanup
+ COMMAND ${CMAKE_COMMAND} -E remove_directory "${__expect_pass_source_dir}"
+ )
+ set_tests_properties(${testname}_cleanup PROPERTIES
+ FIXTURES_SETUP "${testname}SIMULATE_IN_SOURCE_FIXTURE"
+ )
+ add_test(${testname}_copy_sources ${CMAKE_COMMAND} -E copy_directory
+ "${CMAKE_CURRENT_SOURCE_DIR}/${_dir}" "${__expect_pass_source_dir}"
+ )
+ set_tests_properties(${testname}_copy_sources PROPERTIES
+ FIXTURES_SETUP "${testname}SIMULATE_IN_SOURCE_FIXTURE"
+ DEPENDS ${testname}_cleanup
+ )
+ endif()
+
+ set(ctest_command_args
+ --build-and-test
+ "${__expect_pass_source_dir}"
+ "${__expect_pass_build_dir}"
+ --build-config "${CMAKE_BUILD_TYPE}"
+ --build-generator "${CMAKE_GENERATOR}"
+ --build-makeprogram "${CMAKE_MAKE_PROGRAM}"
+ --build-project "${_dir}"
+ --build-options "-DCMAKE_PREFIX_PATH=${__expect_pass_prefixes}" ${BUILD_OPTIONS_LIST}
+ ${_ARGS_BUILD_OPTIONS}
+ --test-command ${_ARGS_BINARY}
+ )
+ add_test(${testname} ${CMAKE_CTEST_COMMAND} ${ctest_command_args})
+ if(_ARGS_SIMULATE_IN_SOURCE)
+ set_tests_properties(${testname} PROPERTIES
+ FIXTURES_REQUIRED "${testname}SIMULATE_IN_SOURCE_FIXTURE")
+ endif()
+
+ if(_ARGS_BINARY)
+ _qt_internal_set_up_test_run_environment("${testname}")
+ endif()
+
+ unset(__expect_pass_source_dir)
+ unset(__expect_pass_build_dir)
+ unset(__expect_pass_prefixes)
+endmacro()
+
+# Checks if the build of the test project fails.
+# This test passes if the test project fails either at the
+# configuring or build steps.
+# Arguments: See _qt_internal_test_expect_pass
+macro(_qt_internal_test_expect_fail)
+ _qt_internal_test_expect_pass(${ARGV})
+ set_tests_properties(${testname} PROPERTIES WILL_FAIL TRUE)
endmacro()
-macro(_qt_internal_test_expect_fail _dir)
+# Checks if the build of the test project fails.
+# This test passes only if the test project fails at the build step,
+# but not at the configuring step.
+macro(_qt_internal_test_expect_build_fail _dir)
string(REPLACE "(" "_" testname "${_dir}")
string(REPLACE ")" "_" testname "${testname}")
file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/failbuild/${_dir}")
diff --git a/src/corelib/Qt6CoreConfigExtras.cmake.in b/src/corelib/Qt6CoreConfigExtras.cmake.in
index 5c4468fa10..25b3f5acd5 100644
--- a/src/corelib/Qt6CoreConfigExtras.cmake.in
+++ b/src/corelib/Qt6CoreConfigExtras.cmake.in
@@ -13,11 +13,6 @@ endif()
# set(_qt5_corelib_extra_includes)
if (NOT QT_NO_CREATE_TARGETS)
- set_property(TARGET @QT_CMAKE_EXPORT_NAMESPACE@::Core PROPERTY INTERFACE_QT_MAJOR_VERSION @PROJECT_VERSION_MAJOR@)
- set_property(TARGET @QT_CMAKE_EXPORT_NAMESPACE@::Core PROPERTY INTERFACE_QT_COORD_TYPE @QT_COORD_TYPE@)
- set_property(TARGET @QT_CMAKE_EXPORT_NAMESPACE@::Core APPEND PROPERTY
- COMPATIBLE_INTERFACE_STRING QT_MAJOR_VERSION QT_COORD_TYPE
- )
if (NOT "@QT_NAMESPACE@" STREQUAL "")
set_property(TARGET @QT_CMAKE_EXPORT_NAMESPACE@::Core APPEND PROPERTY INTERFACE_COMPILE_DEFINITIONS QT_NAMESPACE=@QT_NAMESPACE@)
endif()
@@ -52,3 +47,7 @@ set(_Qt6CTestMacros "${_Qt6CoreConfigDir}/Qt6CTestMacros.cmake")
if(ANDROID_PLATFORM)
include("${CMAKE_CURRENT_LIST_DIR}/@QT_CMAKE_EXPORT_NAMESPACE@AndroidMacros.cmake")
endif()
+
+if(EMSCRIPTEN)
+ include("${CMAKE_CURRENT_LIST_DIR}/@QT_CMAKE_EXPORT_NAMESPACE@WasmMacros.cmake")
+endif()
diff --git a/src/corelib/Qt6CoreMacros.cmake b/src/corelib/Qt6CoreMacros.cmake
index 2a178de16c..99738c4000 100644
--- a/src/corelib/Qt6CoreMacros.cmake
+++ b/src/corelib/Qt6CoreMacros.cmake
@@ -423,6 +423,43 @@ if(NOT QT_NO_CREATE_VERSIONLESS_FUNCTIONS)
endfunction()
endif()
+function(_qt_internal_apply_win_prefix_and_suffix target)
+ if(WIN32)
+ # Table of prefix / suffixes for MSVC libraries as qmake expects them to be created.
+ # static - Qt6EdidSupport.lib (platform support libraries / or static QtCore, etc)
+ # shared - Qt6Core.dll
+ # shared import library - Qt6Core.lib
+ # module aka Qt plugin - qwindows.dll
+ # module import library - qwindows.lib
+ #
+ # The CMake defaults are fine for us.
+
+ # Table of prefix / suffixes for MinGW libraries as qmake expects them to be created.
+ # static - libQt6EdidSupport.a (platform support libraries / or static QtCore, etc)
+ # shared - Qt6Core.dll
+ # shared import library - libQt6Core.a
+ # module aka Qt plugin - qwindows.dll
+ # module import library - libqwindows.a
+ #
+ # CMake for Windows-GNU platforms defaults the prefix to "lib".
+ # CMake for Windows-GNU platforms defaults the import suffix to ".dll.a".
+ # These CMake defaults are not ok for us.
+
+ # This should cover both MINGW with GCC and CLANG.
+ if(NOT MSVC)
+ set_property(TARGET "${target}" PROPERTY IMPORT_SUFFIX ".a")
+
+ get_target_property(target_type ${target} TYPE)
+ if(target_type STREQUAL "STATIC_LIBRARY")
+ set_property(TARGET "${target}" PROPERTY PREFIX "lib")
+ else()
+ set_property(TARGET "${target}" PROPERTY PREFIX "")
+ set_property(TARGET "${target}" PROPERTY IMPORT_PREFIX "lib")
+ endif()
+ endif()
+ endif()
+endfunction()
+
set(_Qt6_COMPONENT_PATH "${CMAKE_CURRENT_LIST_DIR}/..")
# This function is currently in Technical Preview.
@@ -477,10 +514,54 @@ endfunction()
# This function is currently in Technical Preview.
# It's signature and behavior might change.
function(qt6_finalize_executable target)
+
+ # We can't evaluate generator expressions at configure time, so we can't
+ # ask for any transitive properties or even the full library dependency
+ # chain. We can still look at the immediate dependencies though and query
+ # any that are not expressed as generator expressions. For any we can
+ # identify as a CMake target known to the current scope, we can check if
+ # that target has a finalizer to be called. This is expected to cover the
+ # vast majority of use cases, since projects will typically link directly
+ # to Qt::* targets. For the cases where this isn't so, the project will be
+ # responsible for calling any relevant functions themselves instead of
+ # relying on these automatic finalization calls.
+ set(finalizers)
+ get_target_property(immediate_deps ${target} LINK_LIBRARIES)
+ if(immediate_deps)
+ foreach(dep IN LISTS immediate_deps)
+ if(NOT TARGET ${dep})
+ continue()
+ endif()
+ get_target_property(dep_finalizers ${dep}
+ INTERFACE_QT_EXECUTABLE_FINALIZERS
+ )
+ if(dep_finalizers)
+ list(APPEND finalizers ${dep_finalizers})
+ endif()
+ endforeach()
+ list(REMOVE_DUPLICATES finalizers)
+ endif()
+ if(finalizers)
+ if(CMAKE_VERSION VERSION_LESS 3.18)
+ # cmake_language() not available
+ message(WARNING
+ "Skipping module-specific finalizers for target ${target} "
+ "(requires CMake 3.18 or later)"
+ )
+ else()
+ foreach(finalizer_func IN LISTS finalizers)
+ cmake_language(CALL ${finalizer_func} ${target})
+ endforeach()
+ endif()
+ endif()
+
if(ANDROID)
qt6_android_generate_deployment_settings("${target}")
qt6_android_add_apk_target("${target}")
endif()
+ if(EMSCRIPTEN)
+ qt_wasm_add_target_helpers("${target}")
+ endif()
endfunction()
if(NOT QT_NO_CREATE_VERSIONLESS_FUNCTIONS)
@@ -1107,6 +1188,16 @@ function(__qt_propagate_generated_resource target resource_name generated_source
set(resource_target "${target}_resources_${resource_count}")
add_library("${resource_target}" OBJECT "${generated_source_code}")
+ target_compile_definitions("${resource_target}" PRIVATE
+ "$<TARGET_PROPERTY:${QT_CMAKE_EXPORT_NAMESPACE}::Core,INTERFACE_COMPILE_DEFINITIONS>"
+ )
+ # Special handling is required for the Core library resources. The linking of the Core
+ # library to the resources adds a circular dependency. This leads to the wrong
+ # objects/library order in the linker command line, since the Core library target is resolved
+ # first.
+ if(NOT target STREQUAL "Core")
+ target_link_libraries(${resource_target} INTERFACE ${QT_CMAKE_EXPORT_NAMESPACE}::Core)
+ endif()
set_property(TARGET ${resource_target} APPEND PROPERTY _qt_resource_name ${resource_name})
# Save the path to the generated source file, relative to the the current build dir.
@@ -1122,9 +1213,23 @@ function(__qt_propagate_generated_resource target resource_name generated_source
# Use TARGET_NAME genex to map to the correct prefixed target name when it is exported
# via qt_install(EXPORT), so that the consumers of the target can find the object library
- # as well.
- target_link_libraries(${target} INTERFACE
- "$<TARGET_OBJECTS:$<TARGET_NAME:${resource_target}>>")
+ # as well. It's also necessary to link the object library target, since we want to pass
+ # the object library dependencies to the target.
+ if(NOT target STREQUAL "Core")
+ target_link_libraries(${target} INTERFACE
+ "$<TARGET_OBJECTS:$<TARGET_NAME:${resource_target}>>"
+ ${resource_target}
+ )
+ else()
+ # Since linking of the Core library to the own resources adds a circular dependency
+ # we need to add the Core library resources as the target sources but not link the
+ # resource objects to the Core library. This places the resource objects to the
+ # correct position in linker line, but doesn't affect correctness of the Core library
+ # exports.
+ target_sources(${target} INTERFACE
+ "$<TARGET_OBJECTS:$<TARGET_NAME:${resource_target}>>"
+ )
+ endif()
set(${output_generated_target} "${resource_target}" PARENT_SCOPE)
# No need to compile Q_IMPORT_PLUGIN-containing files for non-executables.
@@ -1342,17 +1447,65 @@ function(_qt_internal_process_resource target resourceName)
endif()
endfunction()
+macro(_qt_internal_get_add_plugin_keywords option_args single_args multi_args)
+ set(${option_args}
+ STATIC
+ SHARED
+ )
+ set(${single_args}
+ TYPE
+ CLASS_NAME
+ OUTPUT_NAME
+ )
+ set(${multi_args})
+endmacro()
+
# This function is currently in Technical Preview.
# It's signature and behavior might change.
function(qt6_add_plugin target)
- cmake_parse_arguments(arg
- "STATIC"
- "OUTPUT_NAME;CLASS_NAME;TYPE"
- ""
- ${ARGN}
- )
- if (arg_STATIC)
+ _qt_internal_get_add_plugin_keywords(opt_args single_args multi_args)
+
+ # TODO: Transitional use only, replaced by CLASS_NAME. Remove this once
+ # all other repos have been updated to use CLASS_NAME.
+ list(APPEND single_args CLASSNAME)
+
+ cmake_parse_arguments(PARSE_ARGV 1 arg "${opt_args}" "${single_args}" "${multi_args}")
+
+ # Handle the inconsistent CLASSNAME/CLASS_NAME keyword naming between commands
+ if(arg_CLASSNAME)
+ if(arg_CLASS_NAME AND NOT arg_CLASSNAME STREQUAL arg_CLASS_NAME)
+ message(FATAL_ERROR
+ "Both CLASSNAME and CLASS_NAME were given and were different. "
+ "Only one of the two should be used."
+ )
+ endif()
+ set(arg_CLASS_NAME "${arg_CLASSNAME}")
+ unset(arg_CLASSNAME)
+ endif()
+
+ if(arg_STATIC AND arg_SHARED)
+ message(FATAL_ERROR
+ "Both STATIC and SHARED options were given. Only one of the two should be used."
+ )
+ endif()
+
+ # If no explicit STATIC/SHARED option is set, default to the flavor of the Qt build.
+ if(QT6_IS_SHARED_LIBS_BUILD)
+ set(create_static_plugin FALSE)
+ else()
+ set(create_static_plugin TRUE)
+ endif()
+
+ # Explicit option takes priority over the computed default.
+ if(arg_STATIC)
+ set(create_static_plugin TRUE)
+ elseif(arg_SHARED)
+ set(create_static_plugin FALSE)
+ endif()
+
+ if (create_static_plugin)
add_library(${target} STATIC)
+ target_compile_definitions(${target} PRIVATE QT_STATICPLUGIN)
else()
add_library(${target} MODULE)
if(APPLE)
@@ -1360,10 +1513,7 @@ function(qt6_add_plugin target)
# but Qt plugins are actually suffixed with .dylib.
set_property(TARGET "${target}" PROPERTY SUFFIX ".dylib")
endif()
- if(WIN32)
- # CMake sets for Windows-GNU platforms the suffix "lib"
- set_property(TARGET "${target}" PROPERTY PREFIX "")
- endif()
+ _qt_internal_apply_win_prefix_and_suffix(${target})
endif()
set(output_name ${target})
@@ -1381,22 +1531,20 @@ function(qt6_add_plugin target)
endif()
# Derive the class name from the target name if it's not explicitly specified.
+ # Don't set it for qml plugins though.
set(plugin_class_name "")
- if (NOT arg_CLASS_NAME)
- set(plugin_class_name "${target}")
- else()
- set(plugin_class_name "${arg_CLASS_NAME}")
+ if (NOT "${arg_TYPE}" STREQUAL "qml_plugin")
+ if (NOT arg_CLASS_NAME)
+ set(plugin_class_name "${target}")
+ else()
+ set(plugin_class_name "${arg_CLASS_NAME}")
+ endif()
endif()
set_target_properties(${target} PROPERTIES QT_PLUGIN_CLASS_NAME "${plugin_class_name}")
- set(static_plugin_define "")
- if (arg_STATIC)
- set(static_plugin_define "QT_STATICPLUGIN")
- endif()
target_compile_definitions(${target} PRIVATE
QT_PLUGIN
QT_DEPRECATED_WARNINGS
- ${static_plugin_define}
)
endfunction()
diff --git a/src/corelib/Qt6WasmMacros.cmake b/src/corelib/Qt6WasmMacros.cmake
new file mode 100644
index 0000000000..e9c19160a3
--- /dev/null
+++ b/src/corelib/Qt6WasmMacros.cmake
@@ -0,0 +1,31 @@
+
+function(qt6_wasm_add_target_helpers target)
+ # copy in Qt HTML/JS launch files for apps
+ get_target_property(targetType "${target}" TYPE)
+ if("${targetType}" STREQUAL "EXECUTABLE")
+
+ set(APPNAME ${target})
+
+ if(QT6_INSTALL_PREFIX)
+ set(WASM_BUILD_DIR "${QT6_INSTALL_PREFIX}")
+ elseif(QT_BUILD_DIR)
+ set(WASM_BUILD_DIR "${QT_BUILD_DIR}")
+ endif()
+
+ configure_file("${WASM_BUILD_DIR}/plugins/platforms/wasm_shell.html"
+ "${target}.html")
+ configure_file("${WASM_BUILD_DIR}/plugins/platforms/qtloader.js"
+ qtloader.js COPYONLY)
+ configure_file("${WASM_BUILD_DIR}/plugins/platforms/qtlogo.svg"
+ qtlogo.svg COPYONLY)
+
+ endif()
+endfunction()
+
+if(NOT QT_NO_CREATE_VERSIONLESS_FUNCTIONS)
+ function(qt_wasm_add_target_helpers)
+ if(QT_DEFAULT_MAJOR_VERSION EQUAL 6)
+ qt6_wasm_add_target_helpers(${ARGV})
+ endif()
+ endfunction()
+endif()
diff --git a/src/corelib/animation/qabstractanimation.cpp b/src/corelib/animation/qabstractanimation.cpp
index 133bc5689d..d5d75e8ab3 100644
--- a/src/corelib/animation/qabstractanimation.cpp
+++ b/src/corelib/animation/qabstractanimation.cpp
@@ -911,7 +911,7 @@ void QDefaultAnimationDriver::stopTimer()
m_timer.stop();
}
-
+QAbstractAnimationPrivate::~QAbstractAnimationPrivate() { }
void QAbstractAnimationPrivate::setState(QAbstractAnimation::State newState)
{
diff --git a/src/corelib/animation/qabstractanimation_p.h b/src/corelib/animation/qabstractanimation_p.h
index e59c770280..9debb64424 100644
--- a/src/corelib/animation/qabstractanimation_p.h
+++ b/src/corelib/animation/qabstractanimation_p.h
@@ -67,43 +67,28 @@ class QAbstractAnimation;
class QAbstractAnimationPrivate : public QObjectPrivate
{
public:
- QAbstractAnimationPrivate()
- : state(QAbstractAnimation::Stopped),
- direction(QAbstractAnimation::Forward),
- totalCurrentTime(0),
- currentTime(0),
- loopCount(1),
- currentLoop(0),
- deleteWhenStopped(false),
- hasRegisteredTimer(false),
- isPause(false),
- isGroup(false),
- group(nullptr)
- {
- }
-
- virtual ~QAbstractAnimationPrivate() {}
+ virtual ~QAbstractAnimationPrivate();
static QAbstractAnimationPrivate *get(QAbstractAnimation *q)
{
return q->d_func();
}
- QAbstractAnimation::State state;
- QAbstractAnimation::Direction direction;
+ QAbstractAnimation::State state = QAbstractAnimation::Stopped;
+ QAbstractAnimation::Direction direction = QAbstractAnimation::Forward;
void setState(QAbstractAnimation::State state);
- int totalCurrentTime;
- int currentTime;
- int loopCount;
- int currentLoop;
+ int totalCurrentTime = 0;
+ int currentTime = 0;
+ int loopCount = 1;
+ int currentLoop = 0;
- bool deleteWhenStopped;
- bool hasRegisteredTimer;
- bool isPause;
- bool isGroup;
+ bool deleteWhenStopped = false;
+ bool hasRegisteredTimer = false;
+ bool isPause = false;
+ bool isGroup = false;
- QAnimationGroup *group;
+ QAnimationGroup *group = nullptr;
private:
Q_DECLARE_PUBLIC(QAbstractAnimation)
diff --git a/src/corelib/animation/qpauseanimation.cpp b/src/corelib/animation/qpauseanimation.cpp
index 5f8a8b3590..04d8cca273 100644
--- a/src/corelib/animation/qpauseanimation.cpp
+++ b/src/corelib/animation/qpauseanimation.cpp
@@ -63,18 +63,22 @@
#include "qpauseanimation.h"
#include "qabstractanimation_p.h"
+#include "private/qproperty_p.h"
QT_BEGIN_NAMESPACE
class QPauseAnimationPrivate : public QAbstractAnimationPrivate
{
+ Q_DECLARE_PUBLIC(QPauseAnimation)
public:
QPauseAnimationPrivate() : QAbstractAnimationPrivate(), duration(250)
{
isPause = true;
}
- int duration;
+ void setDuration(int msecs) { q_func()->setDuration(msecs); }
+ Q_OBJECT_COMPAT_PROPERTY(QPauseAnimationPrivate, int, duration,
+ &QPauseAnimationPrivate::setDuration)
};
/*!
@@ -125,7 +129,19 @@ void QPauseAnimation::setDuration(int msecs)
return;
}
Q_D(QPauseAnimation);
- d->duration = msecs;
+
+ if (msecs != d->duration) {
+ d->duration = msecs;
+ d->duration.notify();
+ } else {
+ d->duration.removeBindingUnlessInWrapper();
+ }
+}
+
+QBindable<int> QPauseAnimation::bindableDuration()
+{
+ Q_D(QPauseAnimation);
+ return &d->duration;
}
/*!
diff --git a/src/corelib/animation/qpauseanimation.h b/src/corelib/animation/qpauseanimation.h
index 6bdbeefdbd..1bbc52132d 100644
--- a/src/corelib/animation/qpauseanimation.h
+++ b/src/corelib/animation/qpauseanimation.h
@@ -51,7 +51,7 @@ class QPauseAnimationPrivate;
class Q_CORE_EXPORT QPauseAnimation : public QAbstractAnimation
{
Q_OBJECT
- Q_PROPERTY(int duration READ duration WRITE setDuration)
+ Q_PROPERTY(int duration READ duration WRITE setDuration BINDABLE bindableDuration)
public:
QPauseAnimation(QObject *parent = nullptr);
QPauseAnimation(int msecs, QObject *parent = nullptr);
@@ -59,6 +59,7 @@ public:
int duration() const override;
void setDuration(int msecs);
+ QBindable<int> bindableDuration();
protected:
bool event(QEvent *e) override;
diff --git a/src/corelib/animation/qsequentialanimationgroup.cpp b/src/corelib/animation/qsequentialanimationgroup.cpp
index 8b364384f4..1d0b799fef 100644
--- a/src/corelib/animation/qsequentialanimationgroup.cpp
+++ b/src/corelib/animation/qsequentialanimationgroup.cpp
@@ -503,8 +503,10 @@ void QSequentialAnimationGroupPrivate::_q_uncontrolledAnimationFinished()
*/
void QSequentialAnimationGroupPrivate::animationInsertedAt(int index)
{
- if (currentAnimation == nullptr)
+ if (currentAnimation == nullptr) {
setCurrentAnimation(0); // initialize the current animation
+ Q_ASSERT(currentAnimation);
+ }
if (currentAnimationIndex == index
&& currentAnimation->currentTime() == 0 && currentAnimation->currentLoop() == 0) {
diff --git a/src/corelib/configure.cmake b/src/corelib/configure.cmake
index 1c585f5b3c..e82ac8bf93 100644
--- a/src/corelib/configure.cmake
+++ b/src/corelib/configure.cmake
@@ -14,6 +14,9 @@ set_property(CACHE INPUT_libb2 PROPERTY STRINGS undefined no qt system)
#### Libraries
+if((UNIX) OR QT_FIND_ALL_PACKAGES_ALWAYS)
+ qt_find_package(WrapBacktrace PROVIDED_TARGETS WrapBacktrace::WrapBacktrace MODULE_NAME core QMAKE_LIB backtrace)
+endif()
qt_find_package(WrapDoubleConversion PROVIDED_TARGETS WrapDoubleConversion::WrapDoubleConversion MODULE_NAME core QMAKE_LIB doubleconversion)
qt_find_package(GLIB2 PROVIDED_TARGETS GLIB2::GLIB2 MODULE_NAME core QMAKE_LIB glib)
qt_find_package(ICU COMPONENTS i18n uc data PROVIDED_TARGETS ICU::i18n ICU::uc ICU::data MODULE_NAME core QMAKE_LIB icu)
@@ -40,8 +43,7 @@ qt_find_package(Slog2 PROVIDED_TARGETS Slog2::Slog2 MODULE_NAME core QMAKE_LIB s
qt_config_compile_test(atomicfptr
LABEL "working std::atomic for function pointers"
CODE
-"
-#include <atomic>
+"#include <atomic>
typedef void (*fptr)(int);
typedef std::atomic<fptr> atomicfptr;
void testfunction(int) { }
@@ -55,9 +57,9 @@ void test(volatile atomicfptr &a)
}
a.store(&testfunction, std::memory_order_release);
}
-int main(int argc, char **argv)
+
+int main(void)
{
- (void)argc; (void)argv;
/* BEGIN TEST: */
atomicfptr fptr(testfunction);
test(fptr);
@@ -72,13 +74,11 @@ qt_config_compile_test(clock_monotonic
LIBRARIES
WrapRt::WrapRt
CODE
-"
-#include <unistd.h>
+"#include <unistd.h>
#include <time.h>
-int main(int argc, char **argv)
+int main(void)
{
- (void)argc; (void)argv;
/* BEGIN TEST: */
#if defined(_POSIX_MONOTONIC_CLOCK) && (_POSIX_MONOTONIC_CLOCK-0 >= 0)
timespec ts;
@@ -101,9 +101,8 @@ qt_config_compile_test(cloexec
#include <fcntl.h>
#include <unistd.h>
-int main(int argc, char **argv)
+int main(void)
{
- (void)argc; (void)argv;
/* BEGIN TEST: */
int pipes[2];
(void) pipe2(pipes, O_CLOEXEC | O_NONBLOCK);
@@ -129,12 +128,10 @@ qt_config_compile_test(cxx11_future
LIBRARIES
"${cxx11_future_TEST_LIBRARIES}"
CODE
-"
-#include <future>
+"#include <future>
-int main(int argc, char **argv)
+int main(void)
{
- (void)argc; (void)argv;
/* BEGIN TEST: */
std::future<int> f = std::async([]() { return 42; });
(void)f.get();
@@ -148,12 +145,10 @@ std::future<int> f = std::async([]() { return 42; });
qt_config_compile_test(cxx11_random
LABEL "C++11 <random>"
CODE
-"
-#include <random>
+"#include <random>
-int main(int argc, char **argv)
+int main(void)
{
- (void)argc; (void)argv;
/* BEGIN TEST: */
std::mt19937 mt(0);
/* END TEST: */
@@ -165,12 +160,10 @@ std::mt19937 mt(0);
qt_config_compile_test(cxx17_filesystem
LABEL "C++17 <filesystem>"
CODE
-"
-#include <filesystem>
+"#include <filesystem>
-int main(int argc, char **argv)
+int main(void)
{
- (void)argc; (void)argv;
/* BEGIN TEST: */
std::filesystem::copy(
std::filesystem::path(\"./file\"),
@@ -185,12 +178,10 @@ std::filesystem::copy(
qt_config_compile_test(eventfd
LABEL "eventfd"
CODE
-"
-#include <sys/eventfd.h>
+"#include <sys/eventfd.h>
-int main(int argc, char **argv)
+int main(void)
{
- (void)argc; (void)argv;
/* BEGIN TEST: */
eventfd_t value;
int fd = eventfd(0, EFD_CLOEXEC);
@@ -205,12 +196,10 @@ eventfd_write(fd, value);
qt_config_compile_test(futimens
LABEL "futimens()"
CODE
-"
-#include <sys/stat.h>
+"#include <sys/stat.h>
-int main(int argc, char **argv)
+int main(void)
{
- (void)argc; (void)argv;
/* BEGIN TEST: */
futimens(-1, 0);
/* END TEST: */
@@ -223,12 +212,10 @@ futimens(-1, 0);
qt_config_compile_test(futimes
LABEL "futimes()"
CODE
-"
-#include <sys/time.h>
+"#include <sys/time.h>
-int main(int argc, char **argv)
+int main(void)
{
- (void)argc; (void)argv;
/* BEGIN TEST: */
futimes(-1, 0);
/* END TEST: */
@@ -240,12 +227,10 @@ futimes(-1, 0);
qt_config_compile_test(getauxval
LABEL "getauxval()"
CODE
-"
-#include <sys/auxv.h>
+"#include <sys/auxv.h>
-int main(int argc, char **argv)
+int main(void)
{
- (void)argc; (void)argv;
/* BEGIN TEST: */
(void) getauxval(AT_NULL);
/* END TEST: */
@@ -257,12 +242,10 @@ int main(int argc, char **argv)
qt_config_compile_test(getentropy
LABEL "getentropy()"
CODE
-"
-#include <unistd.h>
+"#include <unistd.h>
-int main(int argc, char **argv)
+int main(void)
{
- (void)argc; (void)argv;
/* BEGIN TEST: */
char buf[32];
(void) getentropy(buf, sizeof(buf));
@@ -275,12 +258,10 @@ char buf[32];
qt_config_compile_test(glibc
LABEL "GNU libc"
CODE
-"
-#include <stdlib.h>
+"#include <stdlib.h>
-int main(int argc, char **argv)
+int main(void)
{
- (void)argc; (void)argv;
/* BEGIN TEST: */
return __GLIBC__;
/* END TEST: */
@@ -292,12 +273,10 @@ return __GLIBC__;
qt_config_compile_test(inotify
LABEL "inotify"
CODE
-"
-#include <sys/inotify.h>
+"#include <sys/inotify.h>
-int main(int argc, char **argv)
+int main(void)
{
- (void)argc; (void)argv;
/* BEGIN TEST: */
inotify_init();
inotify_add_watch(0, \"foobar\", IN_ACCESS);
@@ -311,16 +290,14 @@ inotify_rm_watch(0, 1);
qt_config_compile_test(ipc_sysv
LABEL "SysV IPC"
CODE
-"
-#include <sys/types.h>
+"#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <sys/shm.h>
#include <fcntl.h>
-int main(int argc, char **argv)
+int main(void)
{
- (void)argc; (void)argv;
/* BEGIN TEST: */
key_t unix_key = ftok(\"test\", 'Q');
semctl(semget(unix_key, 1, 0666 | IPC_CREAT | IPC_EXCL), 0, IPC_RMID, 0);
@@ -340,15 +317,13 @@ qt_config_compile_test(ipc_posix
LIBRARIES
"${ipc_posix_TEST_LIBRARIES}"
CODE
-"
-#include <sys/types.h>
+"#include <sys/types.h>
#include <sys/mman.h>
#include <semaphore.h>
#include <fcntl.h>
-int main(int argc, char **argv)
+int main(void)
{
- (void)argc; (void)argv;
/* BEGIN TEST: */
sem_close(sem_open(\"test\", O_CREAT | O_EXCL, 0666, 0));
shm_open(\"test\", O_RDWR | O_CREAT | O_EXCL, 0666);
@@ -366,9 +341,8 @@ qt_config_compile_test(linkat
#include <fcntl.h>
#include <unistd.h>
-int main(int argc, char **argv)
+int main(void)
{
- (void)argc; (void)argv;
/* BEGIN TEST: */
linkat(AT_FDCWD, \"foo\", AT_FDCWD, \"bar\", AT_SYMLINK_FOLLOW);
/* END TEST: */
@@ -380,13 +354,11 @@ linkat(AT_FDCWD, \"foo\", AT_FDCWD, \"bar\", AT_SYMLINK_FOLLOW);
qt_config_compile_test(ppoll
LABEL "ppoll()"
CODE
-"
-#include <signal.h>
+"#include <signal.h>
#include <poll.h>
-int main(int argc, char **argv)
+int main(void)
{
- (void)argc; (void)argv;
/* BEGIN TEST: */
struct pollfd pfd;
struct timespec ts;
@@ -401,14 +373,12 @@ ppoll(&pfd, 1, &ts, &sig);
qt_config_compile_test(pollts
LABEL "pollts()"
CODE
-"
-#include <poll.h>
+"#include <poll.h>
#include <signal.h>
#include <time.h>
-int main(int argc, char **argv)
+int main(void)
{
- (void)argc; (void)argv;
/* BEGIN TEST: */
struct pollfd pfd;
struct timespec ts;
@@ -423,12 +393,10 @@ pollts(&pfd, 1, &ts, &sig);
qt_config_compile_test(poll
LABEL "poll()"
CODE
-"
-#include <poll.h>
+"#include <poll.h>
-int main(int argc, char **argv)
+int main(void)
{
- (void)argc; (void)argv;
/* BEGIN TEST: */
struct pollfd pfd;
poll(&pfd, 1, 0);
@@ -445,9 +413,8 @@ qt_config_compile_test(renameat2
#include <fcntl.h>
#include <stdio.h>
-int main(int argc, char **argv)
+int main(void)
{
- (void)argc; (void)argv;
/* BEGIN TEST: */
renameat2(AT_FDCWD, argv[1], AT_FDCWD, argv[2], RENAME_NOREPLACE | RENAME_WHITEOUT);
/* END TEST: */
@@ -465,9 +432,8 @@ qt_config_compile_test(statx
#include <unistd.h>
#include <fcntl.h>
-int main(int argc, char **argv)
+int main(void)
{
- (void)argc; (void)argv;
/* BEGIN TEST: */
struct statx statxbuf;
unsigned int mask = STATX_BASIC_STATS;
@@ -481,12 +447,10 @@ return statx(AT_FDCWD, \"\", AT_STATX_SYNC_AS_STAT, mask, &statxbuf);
qt_config_compile_test(syslog
LABEL "syslog"
CODE
-"
-#include <syslog.h>
+"#include <syslog.h>
-int main(int argc, char **argv)
+int main(void)
{
- (void)argc; (void)argv;
/* BEGIN TEST: */
openlog(\"qt\", 0, LOG_USER);
syslog(LOG_INFO, \"configure\");
@@ -500,9 +464,7 @@ closelog();
qt_config_compile_test(xlocalescanprint
LABEL "xlocale.h (or equivalents)"
CODE
-"
-
-#define QT_BEGIN_NAMESPACE
+"#define QT_BEGIN_NAMESPACE
#define QT_END_NAMESPACE
#ifdef _MSVC_VER
@@ -512,9 +474,9 @@ qt_config_compile_test(xlocalescanprint
#define QT_NO_DOUBLECONVERSION
#include QDSP_P_H
-int main(int argc, char **argv)
+
+int main(void)
{
- (void)argc; (void)argv;
/* BEGIN TEST: */
#ifdef _MSVC_VER
_locale_t invalidLocale = NULL;
@@ -715,6 +677,10 @@ qt_feature("regularexpression" PUBLIC
CONDITION QT_FEATURE_system_pcre2 OR QT_FEATURE_pcre2
)
qt_feature_definition("regularexpression" "QT_NO_REGULAREXPRESSION" NEGATE VALUE "1")
+qt_feature("backtrace" PRIVATE
+ LABEL "backtrace"
+ CONDITION UNIX AND QT_FEATURE_regularexpression AND WrapBacktrace_FOUND
+)
qt_feature("sharedmemory" PUBLIC
SECTION "Kernel"
LABEL "QSharedMemory"
@@ -829,7 +795,7 @@ qt_feature("sortfilterproxymodel" PUBLIC
SECTION "ItemViews"
LABEL "QSortFilterProxyModel"
PURPOSE "Supports sorting and filtering of data passed between another model and a view."
- CONDITION QT_FEATURE_proxymodel
+ CONDITION QT_FEATURE_proxymodel AND QT_FEATURE_regularexpression
)
qt_feature_definition("sortfilterproxymodel" "QT_NO_SORTFILTERPROXYMODEL" NEGATE VALUE "1")
qt_feature("identityproxymodel" PUBLIC
@@ -939,10 +905,6 @@ qt_feature("forkfd_pidfd" PRIVATE
LABEL "CLONE_PIDFD support in forkfd"
CONDITION LINUX
)
-qt_feature("win32_system_libs"
- LABEL "Windows System Libraries"
- CONDITION WIN32 AND libs.advapi32 AND libs.gdi32 AND libs.kernel32 AND libs.netapi32 AND libs.ole32 AND libs.shell32 AND libs.uuid AND libs.user32 AND libs.winmm AND libs.ws2_32 OR FIXME
-)
qt_feature("cborstreamreader" PUBLIC
SECTION "Utilities"
LABEL "CBOR stream reading"
@@ -954,6 +916,7 @@ qt_feature("cborstreamwriter" PUBLIC
PURPOSE "Provides support for writing the CBOR binary format."
)
qt_configure_add_summary_section(NAME "Qt Core")
+qt_configure_add_summary_entry(ARGS "backtrace")
qt_configure_add_summary_entry(ARGS "doubleconversion")
qt_configure_add_summary_entry(ARGS "system-doubleconversion")
qt_configure_add_summary_entry(ARGS "glib")
diff --git a/src/corelib/configure.json b/src/corelib/configure.json
index 9c8615cf74..4643531135 100644
--- a/src/corelib/configure.json
+++ b/src/corelib/configure.json
@@ -22,6 +22,17 @@
},
"libraries": {
+ "backtrace": {
+ "label": "backtrace",
+ "test": {
+ "main":
+ [ "void *buffer[100];",
+ "int nptrs = backtrace(buffer, 100);"
+ ]
+ },
+ "headers": "execinfo.h",
+ "sources": [ "-lexecinfo" ]
+ },
"doubleconversion": {
"label": "DoubleConversion",
"test": {
@@ -185,66 +196,6 @@
"sources": [
"-lslog2"
]
- },
- "advapi32": {
- "label": "advapi32",
- "sources": [
- "-ladvapi32"
- ]
- },
- "gdi32": {
- "label": "gdi32",
- "sources": [
- "-lgdi32"
- ]
- },
- "kernel32": {
- "label": "kernel32",
- "sources": [
- "-lkernel32"
- ]
- },
- "netapi32": {
- "label": "netapi32",
- "sources": [
- "-lnetapi32"
- ]
- },
- "ole32": {
- "label": "ole32",
- "sources": [
- "-lole32"
- ]
- },
- "shell32": {
- "label": "shell32",
- "sources": [
- "-lshell32"
- ]
- },
- "uuid": {
- "label": "uuid",
- "sources": [
- "-luuid"
- ]
- },
- "user32": {
- "label": "user32",
- "sources": [
- "-luser32"
- ]
- },
- "winmm": {
- "label": "winmm",
- "sources": [
- "-lwinmm"
- ]
- },
- "ws2_32": {
- "label": "ws2_32",
- "sources": [
- "-lws2_32"
- ]
}
},
@@ -778,6 +729,11 @@
"condition": "features.system-pcre2 || features.pcre2",
"output": [ "publicFeature", "feature" ]
},
+ "backtrace": {
+ "label": "backtrace",
+ "condition": "config.unix && features.regularexpression && libs.backtrace",
+ "output": [ "privateFeature" ]
+ },
"sharedmemory": {
"label": "QSharedMemory",
"purpose": "Provides access to a shared memory segment.",
@@ -898,7 +854,7 @@
"label": "QSortFilterProxyModel",
"purpose": "Supports sorting and filtering of data passed between another model and a view.",
"section": "ItemViews",
- "condition": "features.proxymodel",
+ "condition": "features.proxymodel && features.regularexpression",
"output": [ "publicFeature", "feature" ]
},
"identityproxymodel": {
@@ -1019,10 +975,6 @@
"condition": "config.linux",
"output": [ "privateFeature" ]
},
- "win32_system_libs": {
- "label": "Windows System Libraries",
- "condition": "config.win32 && libs.advapi32 && libs.gdi32 && libs.kernel32 && libs.netapi32 && libs.ole32 && libs.shell32 && libs.uuid && libs.user32 && libs.winmm && libs.ws2_32"
- },
"cborstreamreader": {
"label": "CBOR stream reading",
"purpose": "Provides support for reading the CBOR binary format.
@@ -1077,6 +1029,7 @@ Note that this is required for plugin loading. Qt GUI needs QPA plugins for basi
{
"section": "Qt Core",
"entries": [
+ "backtrace",
"doubleconversion",
"system-doubleconversion",
"glib",
diff --git a/src/corelib/doc/snippets/cmake-macros/examples.cmake b/src/corelib/doc/snippets/cmake-macros/examples.cmake
index 685b29db01..7515d7fdb2 100644
--- a/src/corelib/doc/snippets/cmake-macros/examples.cmake
+++ b/src/corelib/doc/snippets/cmake-macros/examples.cmake
@@ -81,7 +81,7 @@ qt_add_executable(simpleapp main.cpp)
#! [qt_add_executable_deferred]
qt_add_executable(complexapp MANUAL_FINALIZATION complex.cpp)
set_target_properties(complexapp PROPERTIES OUTPUT_NAME Complexify)
-qt_finalize_target(complexapp)
+qt_finalize_executable(complexapp)
#! [qt_add_executable_deferred]
#! [qt_android_deploy_basic]
diff --git a/src/corelib/doc/snippets/code/doc_src_qplugin.pro b/src/corelib/doc/snippets/code/doc_src_qplugin.pro
new file mode 100644
index 0000000000..52fb9e3163
--- /dev/null
+++ b/src/corelib/doc/snippets/code/doc_src_qplugin.pro
@@ -0,0 +1,4 @@
+#! [3]
+TEMPLATE = app
+QTPLUGIN += qjpeg qgif # image formats
+#! [3]
diff --git a/src/corelib/doc/snippets/code/src_corelib_global_qglobal.cpp b/src/corelib/doc/snippets/code/src_corelib_global_qglobal.cpp
index 2d060fbb47..ced30cf32a 100644
--- a/src/corelib/doc/snippets/code/src_corelib_global_qglobal.cpp
+++ b/src/corelib/doc/snippets/code/src_corelib_global_qglobal.cpp
@@ -339,8 +339,7 @@ void f(int c)
//! [27]
-qWarning() << "Brush:" << myQBrush << "Other value:"
-<< i;
+qWarning() << "Brush:" << myQBrush << "Other value:" << i;
//! [27]
@@ -355,8 +354,7 @@ void load(const QString &fileName)
//! [29]
-qCritical() << "Brush:" << myQBrush << "Other
-value:" << i;
+qCritical() << "Brush:" << myQBrush << "Other value:" << i;
//! [29]
diff --git a/src/corelib/doc/snippets/code/src_corelib_kernel_qabstractnativeeventfilter.pro b/src/corelib/doc/snippets/code/src_corelib_kernel_qabstractnativeeventfilter.pro
new file mode 100644
index 0000000000..8f447be514
--- /dev/null
+++ b/src/corelib/doc/snippets/code/src_corelib_kernel_qabstractnativeeventfilter.pro
@@ -0,0 +1,5 @@
+#! [0]
+HEADERS += mycocoaeventfilter.h
+OBJECTIVE_SOURCES += mycocoaeventfilter.mm
+LIBS += -framework AppKit
+#! [0]
diff --git a/src/corelib/doc/snippets/code/src_corelib_kernel_qdeadlinetimer.cpp b/src/corelib/doc/snippets/code/src_corelib_kernel_qdeadlinetimer.cpp
index 35c06f842e..5858b6a78d 100644
--- a/src/corelib/doc/snippets/code/src_corelib_kernel_qdeadlinetimer.cpp
+++ b/src/corelib/doc/snippets/code/src_corelib_kernel_qdeadlinetimer.cpp
@@ -53,7 +53,7 @@
{
QDeadlineTimer deadline(msecs);
do {
- if (readFromDevice(deadline.remainingTime())
+ if (readFromDevice(deadline.remainingTime()))
break;
waitForReadyRead(deadline);
} while (!deadline.hasExpired());
diff --git a/src/corelib/doc/snippets/code/src_corelib_kernel_qproperty.cpp b/src/corelib/doc/snippets/code/src_corelib_kernel_qproperty.cpp
index 4cb7c1c0ce..3d63abe590 100644
--- a/src/corelib/doc/snippets/code/src_corelib_kernel_qproperty.cpp
+++ b/src/corelib/doc/snippets/code/src_corelib_kernel_qproperty.cpp
@@ -49,6 +49,7 @@
****************************************************************************/
#include <QObject>
+#include <QDebug>
//! [0]
class MyClass : public QObject
@@ -107,3 +108,55 @@ private:
Q_OBJECT_BINDABLE_PROPERTY_WITH_ARGS(MyClass, CustomType xProp, CustomType(5, 10),
&MyClass::xChanged)
//! [2]
+
+void usage_QBindable() {
+ //! [3]
+ MyClass *myObject;
+ QBindable<int> bindableX = myObject->bindableX();
+ qDebug() << bindableX.hasBinding(); // prints false
+ QProperty<int> y {42};
+ bindableX.setBinding([&](){ return 2*y.value(); });
+ qDebug() << bindableX.hasBinding() << myObject->x(); // prints true 84
+ //! [3]
+}
+
+//! [4]
+#include <QObject>
+#include <QProperty>
+#include <QDebug>
+
+class Foo : public QObject
+{
+ Q_OBJECT
+ Q_PROPERTY(int myVal READ myVal WRITE setMyVal BINDABLE bindableMyVal)
+public:
+ int myVal() { return myValMember.value(); }
+ void setMyVal(int newvalue) { myValMember = newvalue; }
+ QBindable<int> bindableMyVal() { return &myValMember; }
+signals:
+ void myValChanged();
+
+private:
+ Q_OBJECT_BINDABLE_PROPERTY(Foo, int, myValMember, &Foo::myValChanged);
+};
+
+int main()
+{
+ bool debugout(true); // enable debug log
+ Foo myfoo;
+ QProperty<int> prop(42);
+ QObject::connect(&myfoo, &Foo::myValChanged, [&]() {
+ if (debugout)
+ qDebug() << myfoo.myVal();
+ });
+ myfoo.bindableMyVal().setBinding([&]() { return prop.value(); }); // prints "42"
+
+ prop = 5; // prints "5"
+ debugout = false;
+ prop = 6; // prints nothing
+ debugout = true;
+ prop = 7; // prints "7"
+}
+
+#include "main.moc"
+//! [4]
diff --git a/src/corelib/doc/snippets/code/src_corelib_thread_qfuture.cpp b/src/corelib/doc/snippets/code/src_corelib_thread_qfuture.cpp
index a5e1a7f6e4..a53ca13cc6 100644
--- a/src/corelib/doc/snippets/code/src_corelib_thread_qfuture.cpp
+++ b/src/corelib/doc/snippets/code/src_corelib_thread_qfuture.cpp
@@ -283,3 +283,14 @@ auto future = QtConcurrent::run([] {
// Update UI elements
});
//! [19]
+
+//! [20]
+QObject *context = ...;
+auto parentFuture = cachedResultsReady ? QtFuture::makeReadyFuture(results)
+ : QtConcurrent::run([] { /* compute results */});
+auto future = parentFuture.then(context, [] (Results results) {
+ // Runs in the context's thread
+}).then([] {
+ // May or may not run in the context's thread
+});
+//! [20]
diff --git a/src/corelib/doc/snippets/code/src_corelib_thread_qmutex.cpp b/src/corelib/doc/snippets/code/src_corelib_thread_qmutex.cpp
index 63897cabd4..020dc44eac 100644
--- a/src/corelib/doc/snippets/code/src_corelib_thread_qmutex.cpp
+++ b/src/corelib/doc/snippets/code/src_corelib_thread_qmutex.cpp
@@ -184,7 +184,7 @@ int complexFunction(int flag)
class SignalWaiter
{
private:
- QMutexLocker locker;
+ QMutexLocker<QMutex> locker;
public:
SignalWaiter(QMutex *mutex)
diff --git a/src/corelib/doc/snippets/jni/src_qjniobject.cpp b/src/corelib/doc/snippets/jni/src_qjniobject.cpp
new file mode 100644
index 0000000000..b729987d4f
--- /dev/null
+++ b/src/corelib/doc/snippets/jni/src_qjniobject.cpp
@@ -0,0 +1,126 @@
+/****************************************************************************
+ **
+ ** Copyright (C) 2021 The Qt Company Ltd.
+ ** Contact: http://www.qt.io/licensing/
+ **
+ ** This file is part of the documentation of the Qt Toolkit.
+ **
+ ** $QT_BEGIN_LICENSE:BSD$
+ ** 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.
+ **
+ ** BSD License Usage
+ ** Alternatively, you may use this file under the terms of the BSD license
+ ** as follows:
+ **
+ ** "Redistribution and use in source and binary forms, with or without
+ ** modification, are permitted provided that the following conditions are
+ ** met:
+ ** * Redistributions of source code must retain the above copyright
+ ** notice, this list of conditions and the following disclaimer.
+ ** * Redistributions in binary form must reproduce the above copyright
+ ** notice, this list of conditions and the following disclaimer in
+ ** the documentation and/or other materials provided with the
+ ** distribution.
+ ** * Neither the name of The Qt Company Ltd nor the names of its
+ ** contributors may be used to endorse or promote products derived
+ ** from this software without specific prior written permission.
+ **
+ **
+ ** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ ** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ ** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ ** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ ** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ ** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ ** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ ** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+ **
+ ** $QT_END_LICENSE$
+ **
+ ****************************************************************************/
+
+//! [Working with lists]
+QStringList getTrackTitles(const QJniObject &album) {
+ QStringList stringList;
+ QJniObject list = album.callObjectMethod("getTitles",
+ "()Ljava/util/List;");
+
+ if (list.isValid()) {
+ const int size = list.callMethod<jint>("size");
+ for (int i = 0; i < size; ++i) {
+ QJniObject title = list.callObjectMethod("get", "(I)Ljava/lang/Object;", i);
+ stringList.append(title.toString());
+ }
+ }
+ return stringList;
+}
+//! [Working with lists]
+
+//! [QJniObject scope]
+void functionScope()
+{
+ QString helloString("Hello");
+ jstring myJString = 0;
+ {
+ QJniObject string = QJniObject::fromString(helloString);
+ myJString = string.object<jstring>();
+ }
+
+ // Ops! myJString is no longer valid.
+}
+//! [QJniObject scope]
+
+//! [C++ native methods]
+static void fromJavaOne(JNIEnv *env, jobject thiz, jint x)
+{
+ Q_UNUSED(env);
+ Q_UNUSED(thiz);
+ qDebug() << x << "< 100";
+}
+
+static void fromJavaTwo(JNIEnv *env, jobject thiz, jint x)
+{
+ Q_UNUSED(env);
+ Q_UNUSED(thiz);
+ qDebug() << x << ">= 100";
+}
+
+void foo()
+{
+ // register the native methods first, ideally it better be done with the app start
+ JNINativeMethod methods[] {{"callNativeOne", "(I)V", reinterpret_cast<void *>(fromJavaOne)},
+ {"callNativeTwo", "(I)V", reinterpret_cast<void *>(fromJavaTwo)}};
+ QJniEnvironment env;
+ env.registerNativeMethods("my/java/project/FooJavaClass", methods, 2);
+
+ // Call the java method which will calls back to the C++ functions
+ QJniObject::callStaticMethod<void>("my/java/project/FooJavaClass", "foo", "(I)V", 10); // Output: 10 < 100
+ QJniObject::callStaticMethod<void>("my/java/project/FooJavaClass", "foo", "(I)V", 100); // Output: 100 >= 100
+}
+//! [C++ native methods]
+
+//! [Java native methods]
+class FooJavaClass
+{
+ public static void foo(int x)
+ {
+ if (x < 100)
+ callNativeOne(x);
+ else
+ callNativeTwo(x);
+ }
+
+private static native void callNativeOne(int x);
+private static native void callNativeTwo(int x);
+
+}
+//! [Java native methods]
diff --git a/src/corelib/doc/snippets/resource-system/application.pro b/src/corelib/doc/snippets/resource-system/application.pro
new file mode 100644
index 0000000000..ecf4ce1093
--- /dev/null
+++ b/src/corelib/doc/snippets/resource-system/application.pro
@@ -0,0 +1,3 @@
+#! [0]
+RESOURCES = application.qrc
+#! [0]
diff --git a/src/corelib/doc/src/cmake-macros.qdoc b/src/corelib/doc/src/cmake-macros.qdoc
index 46a81a690f..d14fc5badb 100644
--- a/src/corelib/doc/src/cmake-macros.qdoc
+++ b/src/corelib/doc/src/cmake-macros.qdoc
@@ -369,12 +369,12 @@ properties of the target. The finalization processing is implemented by the
\l{qt6_finalize_executable}{qt_finalize_executable()} command.
Finalization can occur either as part of this call or be deferred to sometime
-after this command returns. When using CMake 3.19 or later, finalization is
-automatically deferred to the end of the current directory scope. This gives the
-caller an opportunity to modify properties of the created target before it is
-finalized. When using CMake versions earlier than 3.19, automatic deferral isn't
-supported. In that case, finalization is performed immediately before this
-command returns.
+after this command returns (but it should still be in the same directory scope).
+When using CMake 3.19 or later, finalization is automatically deferred to the
+end of the current directory scope. This gives the caller an opportunity to
+modify properties of the created target before it is finalized. When using
+CMake versions earlier than 3.19, automatic deferral isn't supported. In that
+case, finalization is performed immediately before this command returns.
Regardless of the CMake version, the \c{MANUAL_FINALIZATION} keyword can be given to
indicate that you will explicitly call \l{qt6_finalize_executable}{qt_finalize_executable()}
@@ -424,8 +424,12 @@ qt6_finalize_executable(target)
After a target is created, further processing or \e{finalization} steps are
commonly needed. The steps to perform depend on the platform and on various
-properties of the target. This command implements the following, as appropriate
-for the platform and target provided:
+properties of the target. These steps are expected to be performed within the
+same directory scope as the one in which the \c{target} was created, so this
+command should also be called from that same directory scope.
+
+This command implements the following, as appropriate for the platform and
+target provided:
\list
\li When targeting Android, generate a deployment settings file for the target.
diff --git a/src/corelib/doc/src/external-resources.qdoc b/src/corelib/doc/src/external-resources.qdoc
index c3dc67a705..583e668be4 100644
--- a/src/corelib/doc/src/external-resources.qdoc
+++ b/src/corelib/doc/src/external-resources.qdoc
@@ -1,6 +1,6 @@
/****************************************************************************
**
-** Copyright (C) 2016 The Qt Company Ltd.
+** Copyright (C) 2021 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the documentation of the Qt Toolkit.
@@ -65,3 +65,28 @@
\externalpage https://marcmutz.wordpress.com/effective-qt/containers/
\title Understand the Qt Containers
*/
+
+/*!
+ \externalpage https://developer.android.com/training/articles/perf-jni#javavm-and-jnienv
+ \title JNI tips: JavaVM and JNIEnv
+*/
+
+/*!
+ \externalpage https://docs.oracle.com/javase/7/docs/technotes/guides/jni/spec/jniTOC.html
+ \title Java Native Interface Specification
+*/
+
+/*!
+ \externalpage https://docs.oracle.com/javase/7/docs/technotes/guides/jni/spec/functions.html
+ \title Oracle: JNI Functions
+*/
+
+/*!
+ \externalpage https://docs.oracle.com/javase/8/docs/technotes/guides/jni/spec/design.html#global_and_local_references
+ \title JNI Design Overview: Global and Local References
+*/
+
+/*!
+ \externalpage https://developer.android.com/training/articles/perf-jni#local-and-global-references
+ \title JNI tips: Local and global references
+*/
diff --git a/src/corelib/doc/src/objectmodel/bindableproperties.qdoc b/src/corelib/doc/src/objectmodel/bindableproperties.qdoc
new file mode 100644
index 0000000000..6e63481c14
--- /dev/null
+++ b/src/corelib/doc/src/objectmodel/bindableproperties.qdoc
@@ -0,0 +1,223 @@
+/****************************************************************************
+**
+** Copyright (C) 2021 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the documentation of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:FDL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Free Documentation License Usage
+** Alternatively, this file may be used under the terms of the GNU Free
+** Documentation License version 1.3 as published by the Free Software
+** Foundation and appearing in the file included in the packaging of
+** this file. Please review the following information to ensure
+** the GNU Free Documentation License version 1.3 requirements
+** will be met: https://www.gnu.org/licenses/fdl-1.3.html.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/*!
+ \page bindableproperties.html
+ \title Qt Bindable Properties
+ \brief Qt's bindable properties.
+
+ \ingroup qt-basic-concepts
+ \keyword Qt's Bindable Properties
+
+ Qt provides bindable properties. Bindable properties are properties
+ which either have a value or are specified using any C++ function,
+ typically a C++ lambda expression.
+ In case they are specified using a C++ function, they are
+ updated automatically whenever their dependencies change.
+
+ Bindable properties are implemented in the class QProperty, which
+ consists of the data object and a pointer to a management data structure, and
+ in class QObjectBindableProperty, which consists only of the data object and
+ uses the encapsulating QObject to store the pointer to the
+ management data structure.
+
+ \section1 Introductory Example
+
+ The binding expression computes the value by reading other QProperty values.
+ Behind the scenes this dependency is tracked. Whenever a change in any property's
+ dependency is detected, the binding expression is re-evaluated and the new
+ result is applied to the property. This happens lazily, by marking the binding
+ as dirty and evaluating it only when the property's value is requested. For example:
+
+ \code
+ QProperty<QString> firstname("John");
+ QProperty<QString> lastname("Smith");
+ QProperty<int> age(41);
+
+ QProperty<QString> fullname;
+ fullname.setBinding([&]() { return firstname.value() + " " + lastname.value() + " age: " + QString::number(age.value()); });
+
+ qDebug() << fullname.value(); // Prints "John Smith age: 41"
+
+ firstname = "Emma"; // Marks binding expression as dirty
+
+ qDebug() << fullname.value(); // Re-evaluates the binding expression and prints "Emma Smith age: 41"
+
+ // Birthday is coming up
+ age.setValue(age.value() + 1);
+
+ qDebug() << fullname.value(); // Re-evaluates the binding expression and prints "Emma Smith age: 42"
+ \endcode
+
+ When a new value is assigned to the \c firstname property, the binding
+ expression for \c fullname is marked as dirty. So when the last \c qDebug() statement
+ tries to read the name value of the \c fullname property, the expression is
+ evaluated again, \c firstname() will be called again and return the new value.
+
+ Since bindings are C++ functions, they may do anything that's possible
+ in C++. This includes calling other functions. If those functions access values
+ held by QProperty, they automatically become dependencies to the binding.
+
+ Binding expressions may use properties of any type, so in the above example the age
+ is an integer and folded into the string value using conversion to integer, but
+ the dependency is fully tracked.
+
+ \section1 Bindable Property Getters and Setters
+
+ When a class has a bindable property, either using QProperty
+ or QObjectBindableProperty, special care has to be taken when formulating
+ getters and setters for that property.
+
+ \section2 Bindable Property Getters
+
+ To ensure proper operation of the automatic dependency-tracking system,
+ every possible code path in a getter needs to read from the underlying
+ property object.
+ In addition, the property must not be written inside the getter.
+ Design patterns which recompute or refresh anything in the getter
+ are not compatible with bindable properties.
+
+ It is therefore recommended to only use trivial getters with bindable properties.
+
+ \section2 Bindable Property Setters
+
+ To ensure proper operation of the automatic dependency-tracking system,
+ every possible code path in a setter needs to write to the underlying
+ property object, even if the value did not change.
+
+ Any other code in a setter has a high propability of being incorrect.
+ Any code doing updates based on the new value is most likely a bug,
+ as this code won't be executed when the property is changed
+ through a binding.
+
+ It is therefore recommended to only use trivial setters with bindable properties.
+
+ \section1 Writing to a Bindable Property
+
+ Bindable properties inform their dependent properties about each change.
+ This might trigger change handlers, which in turn might call arbitrary code.
+ Thus, every write to a bindable property has to be inspected carefully.
+ The following problems might occur.
+
+ \section2 Writing Intermediate Values to Bindable Properties
+
+ Bindable properties must not be used as variables in algorithms. Each value written
+ would be communicated to dependent properties.
+ For example, in the following code, other properties that depend on
+ \b myProperty would be first informed about the change to \b 42, then about
+ the change to \b maxValue.
+
+ \badcode
+ myProperty = somecomputation(); // returning, say, 42
+ if (myProperty.value() > maxValue)
+ myProperty = maxValue;
+ \endcode
+
+ Instead, perform the computation in a separate variable. Correct usage is shown in the
+ following example.
+
+ \code
+ int newValue = someComputation();
+ if (newValue > maxValue)
+ newValue = maxValue;
+ myProperty = newValue; // only write to the property once
+ \endcode
+
+ \section2 Writing Bindable Properties in Transitional States
+
+ When a bindable property is a member of a class, each write to that property
+ might expose the current state to the outside. So bindable properties must
+ not be written in transient states, when class invariants are not met.
+
+ For example, in a class representing a circle which holds two members
+ \b radius and \b area consistent, a setter might look like this (where radius
+ is a bindable property):
+
+ \badcode
+ void setRadius(double newValue)
+ {
+ radius = newValue; // this might trigger change handlers
+ area = M_PI * radius * radius;
+ emit radiusChanged();
+ }
+ \endcode
+
+ Here, code triggered in change handlers might use the circle, while it has
+ the new radius, but still the old area.
+
+ \section1 Formulating a Property Binding
+
+ Any C++ expression evaluating to the correct type can be used as a binding
+ expression and be given to the setBinding() method. However, to formulate
+ a correct binding, some rules must be followed.
+
+ Dependency tracking only works on bindable properties. It's the developer's
+ responsibility to ensure that all properties used in the binding expression
+ are bindable properties. When non-bindable properties are used in a binding
+ expression, changes to those properties do not trigger updates to the bound
+ property. No warning or error is generated either at compile-time or at run-time.
+ The bound property will be updated only when bindable properties used in the
+ binding expression are changed.
+ Non-bindable properties might be used in a binding if it's possible
+ to ensure that markDirty is called on the property being bound on each
+ change of the non-bindable dependency.
+
+ The bound property might evaluate its binding several times during its lifetime.
+ The developer must make sure that all objects used in the binding expression
+ live longer than the binding.
+
+ The bindable property system is not thread-safe. Properties used in the binding
+ expression on one thread must not be read or modified by any other thread.
+ An object of a QObject-derived class which has a property with a binding must
+ not be moved to a different thread.
+ Also, an object of a QObject-derived class which has a property which is used
+ in a binding must not be moved to a different thread. In this context, it's
+ irrelevant whether it's used in a binding of a property in the same object
+ or in a binding of a property in another object.
+
+ The binding expression should not read from the property it's a binding for. Otherwise,
+ an evaluation loop exists.
+
+ The binding expression must not write to the property it's a binding for.
+
+ Functions used as bindings as well as all code which is called inside a binding
+ must not co_await. Doing so can confuse the property system's tracking of dependencies.
+
+ \section1 Tracking Bindable Properties
+
+ Sometimes the relationships between properties cannot be expressed using
+ bindings. Instead you may need to run custom code whenever the value of a property
+ changes and instead of assigning the value to another property, pass it to
+ other parts of your application. For example writing data into a network socket
+ or printing debug output. QProperty provides two mechanisms for tracking.
+
+ You can register for a callback function to be called whenever the value of
+ a property changes, by using onValueChanged(). If you want the callback to also
+ be called for the current value of the property, register your callback using
+ subscribe() instead.
+
+*/
diff --git a/src/corelib/doc/src/qt6-changes.qdoc b/src/corelib/doc/src/qt6-changes.qdoc
index d789f42fc5..7e048ead78 100644
--- a/src/corelib/doc/src/qt6-changes.qdoc
+++ b/src/corelib/doc/src/qt6-changes.qdoc
@@ -307,7 +307,7 @@
\section1 String related classes
- \section2 QStringView
+ \section2 The QStringView class
Starting with Qt6 it is generally recommended to use \l QStringView over
\c QStringRef. \l QStringView references a contiguous portion of a UTF-16
@@ -332,7 +332,7 @@
string += ...;
\endcode
- \section2 QStringRef
+ \section2 The QStringRef class
In Qt6 \l QStringRef got removed from Qt Core. To ease porting of existing
applications without touching the whole code-base, the \c QStringRef class
@@ -392,9 +392,18 @@
}
\endcode
+ \section1 QMutex and Related Classes
+
+ In Qt 6, QRecursiveMutex does not inherit from QMutex anymore. This change was
+ done to improve the performance of both QMutex and QRecursiveMutex.
+
+ Due to those changes, the QMutex::RecursionMode enum has been removed, and
+ QMutexLocker is now a templated class that can operate on both QMutex and
+ QRecursiveMutex.
+
\section1 QFuture and Related Classes
- \section2 QFuture
+ \section2 The QFuture class
To avoid unintended usage of QFuture, there were some changes to
QFuture API in Qt 6, which may introduce source compatibility breaks.
@@ -461,14 +470,14 @@
\endlist
- \section2 QPromise
+ \section2 The QPromise class
In Qt 6, the new QPromise class should be used instead of unofficial
QFutureInterface as a "setter" counterpart of QFuture.
\section1 IO Classes
- \section2 QProcess
+ \section2 The QProcess class
In Qt 6, the QProcess::start() overload that interprets a single command string
by splitting it into program name and arguments is renamed to QProcess::startCommand().
@@ -499,7 +508,7 @@
\section1 Meta-Type system
- \section2 QVariant
+ \section2 The QVariant class
\c QVariant has been rewritten to use \c QMetaType for all of its operations. This implies
behavior changes in a few methods:
@@ -520,7 +529,7 @@
\endlist
- \section2 QMetaType
+ \section2 The QMetaType class
In Qt 6, registration of comparators, and \cQDebug and \QDataStream streaming operators is
done automatically. Consequently, \c QMetaType::registerEqualsComparator(),
@@ -546,7 +555,7 @@
\section1 Regular expression classes
- \section2 QRegularExpression
+ \section2 The QRegularExpression class
In Qt6, all methods taking the \c QRegExp got removed from our code-base.
Therefore it is very likely that you will have to port your application or
@@ -777,7 +786,7 @@
{QRegularExpression::UseUnicodePropertiesOption}
pattern option.
- \section2 QRegExp
+ \section2 The QRegExp class
In Qt6 \l QRegExp got removed from Qt Core. If your application cannot be
ported right now, \c QRegExp still exists in Qt5Compat to keep these
diff --git a/src/corelib/global/qcompilerdetection.h b/src/corelib/global/qcompilerdetection.h
index d1ce56fd0a..cc48355ff0 100644
--- a/src/corelib/global/qcompilerdetection.h
+++ b/src/corelib/global/qcompilerdetection.h
@@ -378,7 +378,6 @@
# define Q_COMPILER_STATIC_ASSERT
# define Q_COMPILER_TEMPLATE_ALIAS
# define Q_COMPILER_THREAD_LOCAL
-# define Q_COMPILER_THREADSAFE_STATICS
# define Q_COMPILER_UDL
# define Q_COMPILER_UNICODE_STRINGS
# define Q_COMPILER_UNIFORM_INIT
@@ -573,6 +572,11 @@
* Q_COMPILER_RESTRICTED_VLA variable-length arrays, prior to __cpp_runtime_arrays
*/
+/*
+ * Now that we require C++17, we unconditionally expect threadsafe statics mandated since C++11
+ */
+#define Q_COMPILER_THREADSAFE_STATICS
+
#ifdef __cplusplus
# if __cplusplus < 201103L && !defined(Q_CC_MSVC)
# error Qt requires a C++11 compiler and yours does not seem to be that.
@@ -582,7 +586,6 @@
#if defined(Q_CC_INTEL) && !defined(Q_CC_MSVC)
# define Q_COMPILER_RESTRICTED_VLA
# define Q_COMPILER_VARIADIC_MACROS // C++11 feature supported as an extension in other modes, too
-# define Q_COMPILER_THREADSAFE_STATICS
# if __INTEL_COMPILER < 1200
# define Q_NO_TEMPLATE_FRIENDS
# endif
@@ -658,7 +661,6 @@
#if defined(Q_CC_CLANG) && !defined(Q_CC_INTEL) && !defined(Q_CC_MSVC)
/* General C++ features */
# define Q_COMPILER_RESTRICTED_VLA
-# define Q_COMPILER_THREADSAFE_STATICS
# if __has_feature(attribute_deprecated_with_message)
# define Q_DECL_DEPRECATED_X(text) __attribute__ ((__deprecated__(text)))
# endif
@@ -826,7 +828,6 @@
#if defined(Q_CC_GNU) && !defined(Q_CC_INTEL) && !defined(Q_CC_CLANG)
# define Q_COMPILER_RESTRICTED_VLA
-# define Q_COMPILER_THREADSAFE_STATICS
# if Q_CC_GNU >= 403
// GCC supports binary literals in C, C++98 and C++11 modes
# define Q_COMPILER_BINARY_LITERALS
@@ -980,7 +981,6 @@
# define Q_COMPILER_ATTRIBUTES
// Almost working, see https://connect.microsoft.com/VisualStudio/feedback/details/2011648
//# define Q_COMPILER_CONSTEXPR
-# define Q_COMPILER_THREADSAFE_STATICS
# define Q_COMPILER_UNIFORM_INIT
# endif
# if _MSC_VER >= 1910
@@ -1048,13 +1048,6 @@
# define __USE_CONSTEXPR 1
# define __USE_NOEXCEPT 1
# endif
-# if defined(Q_COMPILER_THREADSAFE_STATICS) && defined(Q_OS_MAC)
-// Apple's low-level implementation of the C++ support library
-// (libc++abi.dylib, shared between libstdc++ and libc++) has deadlocks. The
-// C++11 standard requires the deadlocks to be removed, so this will eventually
-// be fixed; for now, let's disable this.
-# undef Q_COMPILER_THREADSAFE_STATICS
-# endif
#endif
/*
diff --git a/src/corelib/global/qconfig-bootstrapped.h b/src/corelib/global/qconfig-bootstrapped.h
index b57511cb0a..a3db9231a9 100644
--- a/src/corelib/global/qconfig-bootstrapped.h
+++ b/src/corelib/global/qconfig-bootstrapped.h
@@ -66,10 +66,6 @@
#define QT_NO_USING_NAMESPACE
#define QT_NO_DEPRECATED
-#ifndef QT_BUILD_QMAKE
-#define QT_NO_REGEXP
-#endif
-
// Keep feature-test macros in alphabetic order by feature name:
#define QT_FEATURE_alloca 1
#define QT_FEATURE_alloca_h -1
@@ -139,15 +135,7 @@
#define QT_FEATURE_zstd -1
#endif
-#ifdef QT_BUILD_QMAKE
-#define QT_FEATURE_commandlineparser -1
-#define QT_NO_COMPRESS
-#define QT_JSON_READONLY
-#define QT_FEATURE_settings 1
-#define QT_NO_STANDARDPATHS
-#else
#define QT_FEATURE_commandlineparser 1
#define QT_FEATURE_settings -1
-#endif
#endif // QT_BOOTSTRAPPED
diff --git a/src/corelib/global/qconfig.cpp.in b/src/corelib/global/qconfig.cpp.in
index 664924abec..e6a85feffd 100644
--- a/src/corelib/global/qconfig.cpp.in
+++ b/src/corelib/global/qconfig.cpp.in
@@ -1,33 +1,25 @@
+/* This file is used to generate the Qt configuration info for the Core library.
+ * The 'qt_generate_qconfig_cpp' cmake routine
+ * contains variables that replace '@' entires in this file. It's important to
+ * align these values with the following:
+ *
+ * - QLibraryInfo::LibraryPath enum in qtbase/src/corelib/global/qlibraryinfo.h
+ * - qtConfEntries in qtbase/src/corelib/global/qlibraryinfo.cpp
+ *
+ * The reason for this is pointer mathematics in the QLibraryInfo implementation when iterating
+ * qt_configure_strs. Also qtConfEntries are strongly bound to QLibraryInfo::LibraryPath.
+ */
+
/* Installation date */
static const char qt_configure_installation [12+11] = "qt_instdate=2012-12-20";
/* Installation Info */
-static const char qt_configure_prefix_path_str [12+256] = "qt_prfxpath=@QT_BUILD_INTERNALS_RELOCATABLE_INSTALL_PREFIX@";
-#ifdef QT_BUILD_QMAKE
-static const char qt_configure_ext_prefix_path_str [12+256] = "qt_epfxpath=@QT_BUILD_INTERNALS_RELOCATABLE_INSTALL_PREFIX@";
-static const char qt_configure_host_prefix_path_str [12+256] = "qt_hpfxpath=@QT_BUILD_INTERNALS_RELOCATABLE_INSTALL_PREFIX@";
-#endif
+static const char qt_configure_prefix_path_str [12+256] = "qt_prfxpath=@QT_CONFIGURE_PREFIX_PATH_STR@";
static const short qt_configure_str_offsets[] = {
@QT_CONFIG_STR_OFFSETS_FIRST@
-#ifdef QT_BUILD_QMAKE
-@QT_CONFIG_STR_OFFSETS_SECOND@
-#endif
};
static const char qt_configure_strs[] =
@QT_CONFIG_STRS_FIRST@
-#ifdef QT_BUILD_QMAKE
-@QT_CONFIG_STRS_SECOND@
-#endif
;
#define QT_CONFIGURE_SETTINGS_PATH "@QT_SYS_CONF_DIR@"
#define QT_CONFIGURE_LIBLOCATION_TO_PREFIX_PATH "@QT_CONFIGURE_LIBLOCATION_TO_PREFIX_PATH@"
-#define QT_CONFIGURE_HOSTBINDIR_TO_EXTPREFIX_PATH "@QT_CONFIGURE_HOSTBINDIR_TO_EXTPREFIX_PATH@"
-#define QT_CONFIGURE_HOSTBINDIR_TO_HOSTPREFIX_PATH "@QT_CONFIGURE_HOSTBINDIR_TO_HOSTPREFIX_PATH@"
-#ifdef QT_BUILD_QMAKE
-# define QT_CONFIGURE_CROSSBUILD 0
-# define QT_CONFIGURE_SYSROOTIFY_PREFIX false
-#endif
#define QT_CONFIGURE_PREFIX_PATH qt_configure_prefix_path_str + 12
-#ifdef QT_BUILD_QMAKE
-# define QT_CONFIGURE_EXT_PREFIX_PATH qt_configure_ext_prefix_path_str + 12
-# define QT_CONFIGURE_HOST_PREFIX_PATH qt_configure_host_prefix_path_str + 12
-#endif
diff --git a/src/corelib/global/qfloat16.h b/src/corelib/global/qfloat16.h
index a25fac2886..387735863e 100644
--- a/src/corelib/global/qfloat16.h
+++ b/src/corelib/global/qfloat16.h
@@ -1,6 +1,6 @@
/****************************************************************************
**
-** Copyright (C) 2020 The Qt Company Ltd.
+** Copyright (C) 2021 The Qt Company Ltd.
** Copyright (C) 2016 by Southwest Research Institute (R)
** Contact: http://www.qt-project.org/legal
**
@@ -308,6 +308,63 @@ inline qfloat16::operator float() const noexcept
return qAbs(static_cast<float>(f)) <= 0.001f;
}
+/*
+ qHypot compatibility; see ../kernel/qmath.h
+*/
+namespace QtPrivate {
+template <typename R>
+struct QHypotType<R, qfloat16> { using type = decltype(std::hypot(R(1), 1.0f)); };
+template <typename R>
+struct QHypotType<qfloat16, R> { using type = decltype(std::hypot(1.0f, R(1))); };
+template <> struct QHypotType<qfloat16, qfloat16> { using type = qfloat16; };
+}
+// Avoid passing qfloat16 to std::hypot(), while ensuring return types
+// consistent with the above:
+template<typename F, typename ...Fs> auto qHypot(F first, Fs... rest);
+template <typename T, typename std::enable_if<!std::is_same<qfloat16, T>::value, int>::type = 0>
+auto qHypot(T x, qfloat16 y) { return qHypot(x, float(y)); }
+template <typename T, typename std::enable_if<!std::is_same<qfloat16, T>::value, int>::type = 0>
+auto qHypot(qfloat16 x, T y) { return qHypot(float(x), y); }
+template <> inline auto qHypot(qfloat16 x, qfloat16 y)
+{
+#if (defined(QT_COMPILER_SUPPORTS_F16C) && defined(__F16C__)) || defined (__ARM_FP16_FORMAT_IEEE)
+ return QtPrivate::QHypotHelper<qfloat16>(x).add(y).result();
+#else
+ return qfloat16(qHypot(float(x), float(y)));
+#endif
+}
+#if __cpp_lib_hypot >= 201603L // Expected to be true
+// If any are not qfloat16, convert each qfloat16 to float:
+/* (The following splits the some-but-not-all-qfloat16 cases up, using
+ (X|Y|Z)&~(X&Y&Z) = X ? ~(Y&Z) : Y|Z = X&~(Y&Z) | ~X&Y | ~X&~Y&Z,
+ into non-overlapping cases, to avoid ambiguity.) */
+template <typename Ty, typename Tz,
+ typename std::enable_if<
+ // Ty, Tz aren't both qfloat16:
+ !(std::is_same_v<qfloat16, Ty> && std::is_same_v<qfloat16, Tz>), int>::type = 0>
+auto qHypot(qfloat16 x, Ty y, Tz z) { return qHypot(float(x), y, z); }
+template <typename Tx, typename Tz,
+ typename std::enable_if<
+ // Tx isn't qfloat16:
+ !std::is_same_v<qfloat16, Tx>, int>::type = 0>
+auto qHypot(Tx x, qfloat16 y, Tz z) { return qHypot(x, float(y), z); }
+template <typename Tx, typename Ty,
+ typename std::enable_if<
+ // Neither Tx nor Ty is qfloat16:
+ !std::is_same_v<qfloat16, Tx> && !std::is_same_v<qfloat16, Ty>, int>::type = 0>
+auto qHypot(Tx x, Ty y, qfloat16 z) { return qHypot(x, y, float(z)); }
+// If all are qfloat16, stay with qfloat16 (albeit via float, if no native support):
+template <>
+inline auto qHypot(qfloat16 x, qfloat16 y, qfloat16 z)
+{
+#if (defined(QT_COMPILER_SUPPORTS_F16C) && defined(__F16C__)) || defined (__ARM_FP16_FORMAT_IEEE)
+ return QtPrivate::QHypotHelper<qfloat16>(x).add(y).add(z).result();
+#else
+ return qfloat16(qHypot(float(x), float(y), float(z)));
+#endif
+}
+#endif // 3-arg std::hypot() is available
+
QT_END_NAMESPACE
Q_DECLARE_METATYPE(qfloat16)
diff --git a/src/corelib/global/qfloat16_f16c.c b/src/corelib/global/qfloat16_f16c.c
index ba1e16f481..d60a021bdb 100644
--- a/src/corelib/global/qfloat16_f16c.c
+++ b/src/corelib/global/qfloat16_f16c.c
@@ -40,13 +40,8 @@
#include "private/qsimd_p.h"
// The x86 F16C instructions operate on AVX registers, so AVX support is
-// required. We don't need to check for __F16C__ because we this file wouldn't
-// have been compiled if the support was missing in the first place, and not
-// all compilers define it. Technically, we didn't need to check for __AVX__
-// either.
-#if !QT_COMPILER_SUPPORTS_HERE(AVX)
-# error "AVX support required"
-#endif
+// required.
+#if QT_COMPILER_SUPPORTS_HERE(AVX)
#ifdef __cplusplus
QT_BEGIN_NAMESPACE
@@ -89,3 +84,5 @@ void qFloatFromFloat16_fast(float *out, const quint16 *in, qsizetype len) Q_DECL
} // extern "C"
QT_END_NAMESPACE
#endif
+
+#endif // QT_COMPILER_SUPPORTS_HERE(AVX)
diff --git a/src/corelib/global/qglobal.cpp b/src/corelib/global/qglobal.cpp
index 139c8f3a30..342fecb300 100644
--- a/src/corelib/global/qglobal.cpp
+++ b/src/corelib/global/qglobal.cpp
@@ -1,6 +1,6 @@
/****************************************************************************
**
-** Copyright (C) 2020 The Qt Company Ltd.
+** Copyright (C) 2021 The Qt Company Ltd.
** Copyright (C) 2017 Intel Corporation.
** Contact: https://www.qt.io/licensing/
**
@@ -78,7 +78,7 @@
#endif
#if defined(Q_OS_ANDROID) && !defined(Q_OS_ANDROID_EMBEDDED)
-#include <private/qjni_p.h>
+#include <qjniobject.h>
#endif
#if defined(Q_OS_SOLARIS)
@@ -1903,12 +1903,6 @@ bool qSharedBuild() noexcept
\sa QT_DISABLE_DEPRECATED_BEFORE
*/
-#if defined(QT_BUILD_QMAKE)
-// needed to bootstrap qmake
-static const unsigned int qt_one = 1;
-const int QSysInfo::ByteOrder = ((*((unsigned char *) &qt_one) == 0) ? BigEndian : LittleEndian);
-#endif
-
#if defined(Q_OS_MAC)
QT_BEGIN_INCLUDE_NAMESPACE
@@ -1982,13 +1976,8 @@ Q_GLOBAL_STATIC(QWindowsSockInit, winsockInit)
static QString readVersionRegistryString(const wchar_t *subKey)
{
-#if !defined(QT_BUILD_QMAKE)
- return QWinRegistryKey(HKEY_LOCAL_MACHINE, LR"(SOFTWARE\Microsoft\Windows NT\CurrentVersion)")
+ return QWinRegistryKey(HKEY_LOCAL_MACHINE, LR"(SOFTWARE\Microsoft\Windows NT\CurrentVersion)")
.stringValue(subKey);
-#else
- Q_UNUSED(subKey);
- return QString();
-#endif
}
static inline QString windows10ReleaseId()
@@ -2296,7 +2285,7 @@ Oreo
// https://source.android.com/source/build-numbers.html
// https://developer.android.com/guide/topics/manifest/uses-sdk-element.html#ApiLevels
- const int sdk_int = QJNIObjectPrivate::getStaticField<jint>("android/os/Build$VERSION", "SDK_INT");
+ const int sdk_int = QJniObject::getStaticField<jint>("android/os/Build$VERSION", "SDK_INT");
return &versions_string[versions_indices[qBound(0, sdk_int, versions_count - 1)]];
}
#endif
diff --git a/src/corelib/global/qglobal.h b/src/corelib/global/qglobal.h
index 47ffe17034..aa9f26e982 100644
--- a/src/corelib/global/qglobal.h
+++ b/src/corelib/global/qglobal.h
@@ -86,6 +86,16 @@
#include <QtCore/qprocessordetection.h>
#include <QtCore/qcompilerdetection.h>
+// This could go to the very beginning of this file, but we're using compiler
+// detection, so it's here.
+#if defined(__cplusplus) && (__cplusplus < 201703L)
+# ifdef Q_CC_MSVC
+# error "Qt requires a C++17 compiler, and a suitable value for __cplusplus. On MSVC, you must pass the /Zc:__cplusplus option to the compiler."
+# else
+# error "Qt requires a C++17 compiler"
+# endif
+#endif // __cplusplus
+
#if defined (__ELF__)
# define Q_OF_ELF
#endif
@@ -374,6 +384,46 @@ typedef double qreal;
# define QT_DEPRECATED_VERSION_6_1
#endif
+#if QT_DEPRECATED_WARNINGS_SINCE >= QT_VERSION_CHECK(6, 2, 0)
+# define QT_DEPRECATED_VERSION_X_6_2(text) QT_DEPRECATED_X(text)
+# define QT_DEPRECATED_VERSION_6_2 QT_DEPRECATED
+#else
+# define QT_DEPRECATED_VERSION_X_6_2(text)
+# define QT_DEPRECATED_VERSION_6_2
+#endif
+
+#if QT_DEPRECATED_WARNINGS_SINCE >= QT_VERSION_CHECK(6, 3, 0)
+# define QT_DEPRECATED_VERSION_X_6_3(text) QT_DEPRECATED_X(text)
+# define QT_DEPRECATED_VERSION_6_3 QT_DEPRECATED
+#else
+# define QT_DEPRECATED_VERSION_X_6_3(text)
+# define QT_DEPRECATED_VERSION_6_3
+#endif
+
+#if QT_DEPRECATED_WARNINGS_SINCE >= QT_VERSION_CHECK(6, 4, 0)
+# define QT_DEPRECATED_VERSION_X_6_4(text) QT_DEPRECATED_X(text)
+# define QT_DEPRECATED_VERSION_6_4 QT_DEPRECATED
+#else
+# define QT_DEPRECATED_VERSION_X_6_4(text)
+# define QT_DEPRECATED_VERSION_6_4
+#endif
+
+#if QT_DEPRECATED_WARNINGS_SINCE >= QT_VERSION_CHECK(6, 5, 0)
+# define QT_DEPRECATED_VERSION_X_6_5(text) QT_DEPRECATED_X(text)
+# define QT_DEPRECATED_VERSION_6_5 QT_DEPRECATED
+#else
+# define QT_DEPRECATED_VERSION_X_6_5(text)
+# define QT_DEPRECATED_VERSION_6_5
+#endif
+
+#if QT_DEPRECATED_WARNINGS_SINCE >= QT_VERSION_CHECK(6, 6, 0)
+# define QT_DEPRECATED_VERSION_X_6_6(text) QT_DEPRECATED_X(text)
+# define QT_DEPRECATED_VERSION_6_6 QT_DEPRECATED
+#else
+# define QT_DEPRECATED_VERSION_X_6_6(text)
+# define QT_DEPRECATED_VERSION_6_6
+#endif
+
#define QT_DEPRECATED_VERSION_X_5(minor, text) QT_DEPRECATED_VERSION_X_5_##minor(text)
#define QT_DEPRECATED_VERSION_X(major, minor, text) QT_DEPRECATED_VERSION_X_##major##_##minor(text)
diff --git a/src/corelib/global/qglobalstatic.h b/src/corelib/global/qglobalstatic.h
index a0dfd462fd..034ef274fa 100644
--- a/src/corelib/global/qglobalstatic.h
+++ b/src/corelib/global/qglobalstatic.h
@@ -55,17 +55,6 @@ enum GuardValues {
};
}
-#if !QT_CONFIG(thread) || defined(Q_COMPILER_THREADSAFE_STATICS)
-// some compilers support thread-safe statics
-// The IA-64 C++ ABI requires this, so we know that all GCC versions since 3.4
-// support it. C++11 also requires this behavior.
-// Clang and Intel CC masquerade as GCC when compiling on Linux.
-//
-// Apple's libc++abi however uses a global lock for initializing local statics,
-// which will block other threads also trying to initialize a local static
-// until the constructor returns ...
-// We better avoid these kind of problems by using our own locked implementation.
-
#if defined(Q_OS_UNIX) && defined(Q_CC_INTEL)
// Work around Intel issue ID 6000058488:
// local statics inside an inline function inside an anonymous namespace are global
@@ -79,9 +68,11 @@ enum GuardValues {
Q_GLOBAL_STATIC_INTERNAL_DECORATION Type *innerFunction() \
{ \
struct HolderBase { \
+ HolderBase() = default; \
~HolderBase() noexcept \
{ if (guard.loadRelaxed() == QtGlobalStatic::Initialized) \
guard.storeRelaxed(QtGlobalStatic::Destroyed); } \
+ Q_DISABLE_COPY_MOVE(HolderBase) \
}; \
static struct Holder : public HolderBase { \
Type value; \
@@ -92,37 +83,7 @@ enum GuardValues {
} holder; \
return &holder.value; \
}
-#else
-// We don't know if this compiler supports thread-safe global statics
-// so use our own locked implementation
-QT_END_NAMESPACE
-#include <QtCore/qmutex.h>
-#include <mutex>
-QT_BEGIN_NAMESPACE
-
-#define Q_GLOBAL_STATIC_INTERNAL(ARGS) \
- Q_DECL_HIDDEN inline Type *innerFunction() \
- { \
- static Type *d; \
- static QBasicMutex mutex; \
- int x = guard.loadAcquire(); \
- if (Q_UNLIKELY(x >= QtGlobalStatic::Uninitialized)) { \
- const std::lock_guard<QBasicMutex> locker(mutex); \
- if (guard.loadRelaxed() == QtGlobalStatic::Uninitialized) { \
- d = new Type ARGS; \
- static struct Cleanup { \
- ~Cleanup() { \
- delete d; \
- guard.storeRelaxed(QtGlobalStatic::Destroyed); \
- } \
- } cleanup; \
- guard.storeRelease(QtGlobalStatic::Initialized); \
- } \
- } \
- return d; \
- }
-#endif
// this class must be POD, unless the compiler supports thread-safe statics
template <typename T, T *(&innerFunction)(), QBasicAtomicInt &guard>
diff --git a/src/corelib/global/qlibraryinfo.cpp b/src/corelib/global/qlibraryinfo.cpp
index bb1eafeccd..fccefa8008 100644
--- a/src/corelib/global/qlibraryinfo.cpp
+++ b/src/corelib/global/qlibraryinfo.cpp
@@ -45,20 +45,13 @@
#include "qsettings.h"
#endif
#include "qlibraryinfo.h"
+#include "qlibraryinfo_p.h"
#include "qscopedpointer.h"
-#ifdef QT_BUILD_QMAKE
-QT_BEGIN_NAMESPACE
-extern QString qmake_libraryInfoFile();
-QT_END_NAMESPACE
-#else
-# include "qcoreapplication.h"
-#endif
+#include "qcoreapplication.h"
-#ifndef QT_BUILD_QMAKE_BOOTSTRAP
-# include "private/qglobal_p.h"
-# include "qconfig.cpp"
-#endif
+#include "private/qglobal_p.h"
+#include "qconfig.cpp"
#ifdef Q_OS_DARWIN
# include "private/qcore_mac_p.h"
@@ -66,12 +59,12 @@ QT_END_NAMESPACE
#include "archdetect.cpp"
-#if !defined(QT_BUILD_QMAKE) && QT_CONFIG(relocatable) && QT_CONFIG(dlopen) && !QT_CONFIG(framework)
-# include <dlfcn.h>
+#if QT_CONFIG(relocatable) && QT_CONFIG(dlopen) && !QT_CONFIG(framework)
+# include <dlfcn.h>
#endif
-#if !defined(QT_BUILD_QMAKE) && QT_CONFIG(relocatable) && defined(Q_OS_WIN)
-# include <qt_windows.h>
+#if QT_CONFIG(relocatable) && defined(Q_OS_WIN)
+# include <qt_windows.h>
#endif
QT_BEGIN_NAMESPACE
@@ -80,113 +73,53 @@ extern void qDumpCPUFeatures(); // in qsimd.cpp
#if QT_CONFIG(settings)
+static QSettings *findConfiguration();
+
struct QLibrarySettings
{
QLibrarySettings();
void load();
+ QSettings *configuration();
QScopedPointer<QSettings> settings;
-#ifdef QT_BUILD_QMAKE
- bool haveDevicePaths;
- bool haveEffectiveSourcePaths;
- bool haveEffectivePaths;
bool havePaths;
-#else
bool reloadOnQAppAvailable;
-#endif
};
Q_GLOBAL_STATIC(QLibrarySettings, qt_library_settings)
-class QLibraryInfoPrivate
+QLibrarySettings::QLibrarySettings() : havePaths(false)
+ , reloadOnQAppAvailable(false)
{
-public:
- static QSettings *findConfiguration();
-#ifdef QT_BUILD_QMAKE
- static void reload()
- {
- if (qt_library_settings.exists())
- qt_library_settings->load();
- }
- static bool haveGroup(QLibraryInfo::PathGroup group)
- {
- QLibrarySettings *ls = qt_library_settings();
- return ls ? (group == QLibraryInfo::EffectiveSourcePaths
- ? ls->haveEffectiveSourcePaths
- : group == QLibraryInfo::EffectivePaths
- ? ls->haveEffectivePaths
- : group == QLibraryInfo::DevicePaths
- ? ls->haveDevicePaths
- : ls->havePaths) : false;
- }
-#endif
- static QSettings *configuration()
- {
- QLibrarySettings *ls = qt_library_settings();
- if (ls) {
-#ifndef QT_BUILD_QMAKE
- if (ls->reloadOnQAppAvailable && QCoreApplication::instance() != nullptr)
- ls->load();
-#endif
- return ls->settings.data();
- } else {
- return nullptr;
- }
- }
-};
-
-static const char platformsSection[] = "Platforms";
+ load();
+}
-QLibrarySettings::QLibrarySettings()
+QSettings *QLibrarySettings::configuration()
{
- load();
+ if (reloadOnQAppAvailable && QCoreApplication::instance() != nullptr)
+ load();
+ return settings.data();
}
void QLibrarySettings::load()
{
// If we get any settings here, those won't change when the application shows up.
- settings.reset(QLibraryInfoPrivate::findConfiguration());
-#ifndef QT_BUILD_QMAKE
+ settings.reset(findConfiguration());
reloadOnQAppAvailable = (settings.data() == nullptr && QCoreApplication::instance() == nullptr);
- bool haveDevicePaths;
- bool haveEffectivePaths;
- bool havePaths;
-#endif
+
if (settings) {
// This code needs to be in the regular library, as otherwise a qt.conf that
// works for qmake would break things for dynamically built Qt tools.
QStringList children = settings->childGroups();
- haveDevicePaths = children.contains(QLatin1String("DevicePaths"));
-#ifdef QT_BUILD_QMAKE
- haveEffectiveSourcePaths = children.contains(QLatin1String("EffectiveSourcePaths"));
-#else
- // EffectiveSourcePaths is for the Qt build only, so needs no backwards compat trickery.
- bool haveEffectiveSourcePaths = false;
-#endif
- haveEffectivePaths = haveEffectiveSourcePaths || children.contains(QLatin1String("EffectivePaths"));
- // Backwards compat: an existing but empty file is claimed to contain the Paths section.
- havePaths = (!haveDevicePaths && !haveEffectivePaths
- && !children.contains(QLatin1String(platformsSection)))
+ havePaths = !children.contains(QLatin1String("Platforms"))
|| children.contains(QLatin1String("Paths"));
-#ifndef QT_BUILD_QMAKE
- if (!havePaths)
- settings.reset(nullptr);
-#else
- } else {
- haveDevicePaths = false;
- haveEffectiveSourcePaths = false;
- haveEffectivePaths = false;
- havePaths = false;
-#endif
}
}
-QSettings *QLibraryInfoPrivate::findConfiguration()
+static QSettings *findConfiguration()
{
-#ifdef QT_BUILD_QMAKE
- QString qtconfig = qmake_libraryInfoFile();
- if (!qtconfig.isEmpty())
- return new QSettings(qtconfig, QSettings::IniFormat);
-#else
+ if (!QLibraryInfoPrivate::qtconfManualPath.isEmpty())
+ return new QSettings(QLibraryInfoPrivate::qtconfManualPath, QSettings::IniFormat);
+
QString qtconfig = QStringLiteral(":/qt/etc/qt.conf");
if (QFile::exists(qtconfig))
return new QSettings(qtconfig, QSettings::IniFormat);
@@ -214,10 +147,28 @@ QSettings *QLibraryInfoPrivate::findConfiguration()
if (QFile::exists(qtconfig))
return new QSettings(qtconfig, QSettings::IniFormat);
}
-#endif
return nullptr; //no luck
}
+QString QLibraryInfoPrivate::qtconfManualPath;
+
+QSettings *QLibraryInfoPrivate::configuration()
+{
+ QLibrarySettings *ls = qt_library_settings();
+ return ls ? ls->configuration() : nullptr;
+}
+
+void QLibraryInfoPrivate::reload()
+{
+ if (qt_library_settings.exists())
+ qt_library_settings->load();
+}
+
+static bool havePaths() {
+ QLibrarySettings *ls = qt_library_settings();
+ return ls && ls->havePaths;
+}
+
#endif // settings
/*!
@@ -238,8 +189,6 @@ QSettings *QLibraryInfoPrivate::findConfiguration()
\sa QSysInfo, {Using qt.conf}
*/
-#ifndef QT_BUILD_QMAKE
-
/*!
\internal
@@ -343,7 +292,6 @@ QLibraryInfo::isDebugBuild()
#endif
}
-#ifndef QT_BOOTSTRAPPED
/*!
\since 5.8
Returns the version of the Qt library.
@@ -354,17 +302,12 @@ QVersionNumber QLibraryInfo::version() noexcept
{
return QVersionNumber(QT_VERSION_MAJOR, QT_VERSION_MINOR, QT_VERSION_PATCH);
}
-#endif // QT_BOOTSTRAPPED
-
-#endif // QT_BUILD_QMAKE
/*
- * To add a new entry in QLibrary::LibraryPath, add it to the enum above the bootstrapped values and:
+ * To add a new entry in QLibraryInfo::LibraryPath, add it to the enum
+ * in qtbase/src/corelib/global/qlibraryinfo.h and:
* - add its relative path in the qtConfEntries[] array below
* (the key is what appears in a qt.conf file)
- * - add a property name in qmake/property.cpp propList[] array
- * (it's used with qmake -query)
- * - add to qt_config.prf, qt_module.prf, qt_module_fwdpri.prf
*/
static const struct {
@@ -387,43 +330,8 @@ static const struct {
{ "Translations", "translations" }, // should be ${Data}/translations
{ "Examples", "examples" },
{ "Tests", "tests" },
-#ifdef QT_BUILD_QMAKE
- { "Sysroot", "" },
- { "SysrootifyPrefix", "" },
- { "HostBinaries", "bin" },
- { "HostLibraries", "lib" },
- { "HostData", "." },
- { "TargetSpec", "" },
- { "HostSpec", "" },
- { "HostPrefix", "" },
-#endif
};
-#ifdef QT_BUILD_QMAKE
-void QLibraryInfo::reload()
-{
- QLibraryInfoPrivate::reload();
-}
-
-void QLibraryInfo::sysrootify(QString *path)
-{
- if (!QVariant::fromValue(rawLocation(SysrootifyPrefixPath, FinalPaths)).toBool())
- return;
-
- const QString sysroot = rawLocation(SysrootPath, FinalPaths);
- if (sysroot.isEmpty())
- return;
-
- if (path->length() > 2 && path->at(1) == QLatin1Char(':')
- && (path->at(2) == QLatin1Char('/') || path->at(2) == QLatin1Char('\\'))) {
- path->replace(0, 2, sysroot); // Strip out the drive on Windows targets
- } else {
- path->prepend(sysroot);
- }
-}
-#endif // QT_BUILD_QMAKE
-
-#ifndef QT_BUILD_QMAKE
static QString prefixFromAppDirHelper()
{
QString appDir;
@@ -453,9 +361,8 @@ static QString prefixFromAppDirHelper()
return appDir;
}
-#endif
-#if !defined(QT_BUILD_QMAKE) && QT_CONFIG(relocatable)
+#if QT_CONFIG(relocatable)
#if !defined(QT_STATIC) && !(defined(Q_OS_DARWIN) && QT_CONFIG(framework)) \
&& (QT_CONFIG(dlopen) || defined(Q_OS_WIN))
static QString prefixFromQtCoreLibraryHelper(const QString &qtCoreLibraryPath)
@@ -595,49 +502,33 @@ static QString getRelocatablePrefix()
}
#endif
-#if defined(QT_BUILD_QMAKE) && !defined(QT_BUILD_QMAKE_BOOTSTRAP)
-QString qmake_abslocation();
-
-static QString getPrefixFromHostBinDir(const char *hostBinDirToPrefixPath)
-{
- const QString canonicalQMakePath = QFileInfo(qmake_abslocation()).canonicalPath();
- return QDir::cleanPath(canonicalQMakePath + QLatin1Char('/')
- + QLatin1String(hostBinDirToPrefixPath));
-}
-
-static QString getExtPrefixFromHostBinDir()
-{
- return getPrefixFromHostBinDir(QT_CONFIGURE_HOSTBINDIR_TO_EXTPREFIX_PATH);
-}
-
-static QString getHostPrefixFromHostBinDir()
-{
- return getPrefixFromHostBinDir(QT_CONFIGURE_HOSTBINDIR_TO_HOSTPREFIX_PATH);
-}
-#endif
-
-#ifndef QT_BUILD_QMAKE_BOOTSTRAP
-static QString getPrefix(
-#ifdef QT_BUILD_QMAKE
- QLibraryInfo::PathGroup group
-#endif
- )
+static QString getPrefix()
{
-#if defined(QT_BUILD_QMAKE)
-# if QT_CONFIGURE_CROSSBUILD
- if (group == QLibraryInfo::DevicePaths)
- return QString::fromLocal8Bit(QT_CONFIGURE_PREFIX_PATH);
-# else
- Q_UNUSED(group);
-# endif
- return getExtPrefixFromHostBinDir();
-#elif QT_CONFIG(relocatable)
+#if QT_CONFIG(relocatable)
return getRelocatablePrefix();
#else
return QString::fromLocal8Bit(QT_CONFIGURE_PREFIX_PATH);
#endif
}
-#endif // QT_BUILD_QMAKE_BOOTSTRAP
+
+void QLibraryInfoPrivate::keyAndDefault(QLibraryInfo::LibraryPath loc, QString *key,
+ QString *value)
+{
+ if (unsigned(loc) < sizeof(qtConfEntries)/sizeof(qtConfEntries[0])) {
+ *key = QLatin1String(qtConfEntries[loc].key);
+ *value = QLatin1String(qtConfEntries[loc].value);
+ }
+#ifndef Q_OS_WIN // On Windows we use the registry
+ else if (loc == QLibraryInfo::SettingsPath) {
+ *key = QLatin1String("Settings");
+ *value = QLatin1String(".");
+ }
+#endif
+ else {
+ key->clear();
+ value->clear();
+ }
+}
/*! \fn QString QLibraryInfo::location(LibraryLocation loc)
\obsolete Use path() instead.
@@ -653,80 +544,21 @@ static QString getPrefix(
QString QLibraryInfo::path(LibraryPath p)
{
const LibraryPath loc = p;
-#ifdef QT_BUILD_QMAKE // ends inside rawLocation !
- QString ret = rawLocation(loc, FinalPaths);
-
- // Automatically prepend the sysroot to target paths
- if (loc < SysrootPath || loc > LastHostPath)
- sysrootify(&ret);
-
- return ret;
-}
-
-QString
-QLibraryInfo::rawLocation(LibraryPath loc, PathGroup group)
-{
-#endif // QT_BUILD_QMAKE, started inside path!
QString ret;
bool fromConf = false;
#if QT_CONFIG(settings)
-#ifdef QT_BUILD_QMAKE
- // Logic for choosing the right data source: if EffectivePaths are requested
- // and qt.conf with that section is present, use it, otherwise fall back to
- // FinalPaths. For FinalPaths, use qt.conf if present and contains not only
- // [EffectivePaths], otherwise fall back to builtins.
- // EffectiveSourcePaths falls back to EffectivePaths.
- // DevicePaths falls back to FinalPaths.
- PathGroup orig_group = group;
- if (QLibraryInfoPrivate::haveGroup(group)
- || (group == EffectiveSourcePaths
- && (group = EffectivePaths, QLibraryInfoPrivate::haveGroup(group)))
- || ((group == EffectivePaths || group == DevicePaths)
- && (group = FinalPaths, QLibraryInfoPrivate::haveGroup(group)))
- || (group = orig_group, false))
-#else
- if (QLibraryInfoPrivate::configuration())
-#endif
- {
+ if (havePaths()) {
fromConf = true;
QString key;
QString defaultValue;
- if (unsigned(loc) < sizeof(qtConfEntries)/sizeof(qtConfEntries[0])) {
- key = QLatin1String(qtConfEntries[loc].key);
- defaultValue = QLatin1String(qtConfEntries[loc].value);
- }
-#ifndef Q_OS_WIN // On Windows we use the registry
- else if (loc == SettingsPath) {
- key = QLatin1String("Settings");
- defaultValue = QLatin1String(".");
- }
-#endif
-
+ QLibraryInfoPrivate::keyAndDefault(loc, &key, &defaultValue);
if (!key.isNull()) {
QSettings *config = QLibraryInfoPrivate::configuration();
- config->beginGroup(QLatin1String(
-#ifdef QT_BUILD_QMAKE
- group == DevicePaths ? "DevicePaths" :
- group == EffectiveSourcePaths ? "EffectiveSourcePaths" :
- group == EffectivePaths ? "EffectivePaths" :
-#endif
- "Paths"));
+ Q_ASSERT(config != nullptr);
+ config->beginGroup(QLatin1String("Paths"));
ret = config->value(key, defaultValue).toString();
-
-#ifdef QT_BUILD_QMAKE
- if (ret.isEmpty()) {
- if (loc == HostPrefixPath)
- ret = config->value(QLatin1String(qtConfEntries[PrefixPath].key),
- QLatin1String(qtConfEntries[PrefixPath].value)).toString();
- else if (loc == TargetSpecPath || loc == HostSpecPath || loc == SysrootifyPrefixPath)
- fromConf = false;
- // The last case here is SysrootPath, which can be legitimately empty.
- // All other keys have non-empty fallbacks to start with.
- }
-#endif
-
int startIndex = 0;
forever {
startIndex = ret.indexOf(QLatin1Char('$'), startIndex);
@@ -754,7 +586,6 @@ QLibraryInfo::rawLocation(LibraryPath loc, PathGroup group)
}
#endif // settings
-#ifndef QT_BUILD_QMAKE_BOOTSTRAP
if (!fromConf) {
// "volatile" here is a hack to prevent compilers from doing a
// compile-time strlen() on "path". The issue is that Qt installers
@@ -763,60 +594,27 @@ QLibraryInfo::rawLocation(LibraryPath loc, PathGroup group)
// strlen is meaningless.
const char * volatile path = nullptr;
if (loc == PrefixPath) {
- ret = getPrefix(
-#ifdef QT_BUILD_QMAKE
- group
-#endif
- );
+ ret = getPrefix();
} else if (unsigned(loc) <= sizeof(qt_configure_str_offsets)/sizeof(qt_configure_str_offsets[0])) {
path = qt_configure_strs + qt_configure_str_offsets[loc - 1];
#ifndef Q_OS_WIN // On Windows we use the registry
} else if (loc == SettingsPath) {
path = QT_CONFIGURE_SETTINGS_PATH;
#endif
-# ifdef QT_BUILD_QMAKE
- } else if (loc == HostPrefixPath) {
- static const QByteArray hostPrefixPath = getHostPrefixFromHostBinDir().toLatin1();
- path = hostPrefixPath.constData();
-# endif
}
if (path)
ret = QString::fromLocal8Bit(path);
}
-#endif
-
-#ifdef QT_BUILD_QMAKE
- // These values aren't actually paths and thus need to be returned verbatim.
- if (loc == TargetSpecPath || loc == HostSpecPath || loc == SysrootifyPrefixPath)
- return ret;
-#endif
if (!ret.isEmpty() && QDir::isRelativePath(ret)) {
QString baseDir;
-#ifdef QT_BUILD_QMAKE
- if (loc == HostPrefixPath || loc == PrefixPath || loc == SysrootPath) {
- // We make the prefix/sysroot path absolute to the executable's directory.
- // loc == PrefixPath while a sysroot is set would make no sense here.
- // loc == SysrootPath only makes sense if qmake lives inside the sysroot itself.
- baseDir = QFileInfo(qmake_libraryInfoFile()).absolutePath();
- } else if (loc > SysrootPath && loc <= LastHostPath) {
- // We make any other host path absolute to the host prefix directory.
- baseDir = rawLocation(HostPrefixPath, group);
- } else {
- // we make any other path absolute to the prefix directory
- baseDir = rawLocation(PrefixPath, group);
- if (group == EffectivePaths)
- sysrootify(&baseDir);
- }
-#else
if (loc == PrefixPath) {
baseDir = prefixFromAppDirHelper();
} else {
// we make any other path absolute to the prefix directory
baseDir = path(PrefixPath);
}
-#endif // QT_BUILD_QMAKE
ret = QDir::cleanPath(baseDir + QLatin1Char('/') + ret);
}
return ret;
@@ -837,10 +635,10 @@ QLibraryInfo::rawLocation(LibraryPath loc, PathGroup group)
QStringList QLibraryInfo::platformPluginArguments(const QString &platformName)
{
-#if !defined(QT_BUILD_QMAKE) && QT_CONFIG(settings)
- QScopedPointer<const QSettings> settings(QLibraryInfoPrivate::findConfiguration());
+#if QT_CONFIG(settings)
+ QScopedPointer<const QSettings> settings(findConfiguration());
if (!settings.isNull()) {
- const QString key = QLatin1String(platformsSection)
+ const QString key = QLatin1String("Platforms")
+ QLatin1Char('/')
+ platformName
+ QLatin1String("Arguments");
@@ -848,7 +646,7 @@ QStringList QLibraryInfo::platformPluginArguments(const QString &platformName)
}
#else
Q_UNUSED(platformName);
-#endif // !QT_BUILD_QMAKE && settings
+#endif // settings
return QStringList();
}
@@ -866,7 +664,8 @@ QStringList QLibraryInfo::platformPluginArguments(const QString &platformName)
\value LibraryExecutablesPath The path to installed executables required by libraries at runtime.
\value BinariesPath The path to installed Qt binaries (tools and applications).
\value PluginsPath The path to installed Qt plugins.
- \value Qml2ImportsPath The path to installed QML extensions to import (QML 2.x).
+ \value QmlImportsPath The path to installed QML extensions to import.
+ \value Qml2ImportsPath The path to installed QML extensions to import.
\value ArchDataPath The path to general architecture-dependent Qt data.
\value DataPath The path to general architecture-independent Qt data.
\value TranslationsPath The path to translation information for Qt strings.
diff --git a/src/corelib/global/qlibraryinfo.h b/src/corelib/global/qlibraryinfo.h
index 04956052b3..a7d44768c8 100644
--- a/src/corelib/global/qlibraryinfo.h
+++ b/src/corelib/global/qlibraryinfo.h
@@ -65,26 +65,15 @@ public:
LibraryExecutablesPath,
BinariesPath,
PluginsPath,
- Qml2ImportsPath,
+ QmlImportsPath,
+ Qml2ImportsPath = QmlImportsPath,
ArchDataPath,
DataPath,
TranslationsPath,
ExamplesPath,
TestsPath,
// Insert new values above this line
- // Please read the comments in qlibraryinfo.cpp before adding
-#ifdef QT_BUILD_QMAKE
- // These are not subject to binary compatibility constraints
- SysrootPath,
- SysrootifyPrefixPath,
- HostBinariesPath,
- HostLibrariesPath,
- HostDataPath,
- TargetSpecPath,
- HostSpecPath,
- HostPrefixPath,
- LastHostPath = HostPrefixPath,
-#endif
+ // Please read the comments in qconfig.cpp.in before adding
SettingsPath = 100
};
static QString path(LibraryPath p);
@@ -94,13 +83,6 @@ public:
static QString location(LibraryLocation location)
{ return path(location); }
#endif
-#ifdef QT_BUILD_QMAKE
- enum PathGroup { FinalPaths, EffectivePaths, EffectiveSourcePaths, DevicePaths };
- static QString rawLocation(LibraryPath, PathGroup);
- static void reload();
- static void sysrootify(QString *path);
-#endif
-
static QStringList platformPluginArguments(const QString &platformName);
private:
diff --git a/src/corelib/global/qlibraryinfo_p.h b/src/corelib/global/qlibraryinfo_p.h
new file mode 100644
index 0000000000..a0900f010b
--- /dev/null
+++ b/src/corelib/global/qlibraryinfo_p.h
@@ -0,0 +1,78 @@
+/****************************************************************************
+**
+** Copyright (C) 2021 The Qt Company Ltd.
+** Copyright (C) 2016 Intel Corporation.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtCore module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QLIBRARYINFO_P_H
+#define QLIBRARYINFO_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of a number of Qt sources files. This header file may change from
+// version to version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "QtCore/qlibraryinfo.h"
+
+#if QT_CONFIG(settings)
+# include "QtCore/qsettings.h"
+#endif
+#include "QtCore/qstring.h"
+
+QT_BEGIN_NAMESPACE
+
+class Q_CORE_EXPORT QLibraryInfoPrivate final
+{
+public:
+#if QT_CONFIG(settings)
+ static QSettings *configuration();
+ static void reload();
+ static QString qtconfManualPath;
+#endif
+ static void keyAndDefault(QLibraryInfo::LibraryPath loc, QString *key,
+ QString *value);
+};
+
+QT_END_NAMESPACE
+
+#endif // QLIBRARYINFO_P_H
diff --git a/src/corelib/global/qnamespace.h b/src/corelib/global/qnamespace.h
index 4a051e8ff0..0f5003e593 100644
--- a/src/corelib/global/qnamespace.h
+++ b/src/corelib/global/qnamespace.h
@@ -1394,6 +1394,7 @@ namespace Qt {
ImAnchorRectangle = 0x4000,
ImInputItemClipRectangle = 0x8000,
+ ImReadOnly = 0x10000,
ImPlatformData = 0x80000000,
ImQueryInput = ImCursorRectangle | ImCursorPosition | ImSurroundingText |
ImCurrentSelection | ImAnchorRectangle | ImAnchorPosition,
diff --git a/src/corelib/global/qnamespace.qdoc b/src/corelib/global/qnamespace.qdoc
index 97d79f4b1e..411ee30424 100644
--- a/src/corelib/global/qnamespace.qdoc
+++ b/src/corelib/global/qnamespace.qdoc
@@ -1,6 +1,6 @@
/****************************************************************************
**
-** Copyright (C) 2020 The Qt Company Ltd.
+** Copyright (C) 2021 The Qt Company Ltd.
** Copyright (C) 2020 Klaralvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Giuseppe D'Angelo <giuseppe.dangelo@kdab.com>
** Contact: https://www.qt.io/licensing/
**
@@ -669,9 +669,15 @@
names will be short names in English (C locale). This effectively uses, for
a date, format \c{ddd MMM d yyyy}, for a time \c{HH:mm:ss} and combines
these as \c{ddd MMM d HH:mm:ss yyyy} for a date-time, with an optional
- suffix indicating time-zone or offset from UTC, where relevant. A fractional
- part is also recognized on the seconds of a time part, as \c{HH:mm:ss.zzz},
- when reading from a string.
+ zone-offset suffix, where relevant. When reading from a string, a
+ fractional part is also recognized on the seconds of a time part, as
+ \c{HH:mm:ss.zzz}, and some minor variants on the format may be recognized,
+ for compatibility with earlier versions of Qt and with changes to the format
+ planned for the future. In particular, the zone-offset suffix presently uses
+ \c{GMT[±tzoff]} with a \c{tzoff} in \c{HH[[:]mm]} format (two-digit hour and
+ optional two-digit minutes, with optional colon separator); this shall
+ change to use \c{UTC} in place of \c{GMT} in a future release of Qt, so the
+ planned \c{UTC} format is recognized.
\value ISODateWithMs \l{ISO 8601} extended format: uses \c{yyyy-MM-dd} for
dates, \c{HH:mm:ss.zzz} for times or \c{yyyy-MM-ddTHH:mm:ss.zzz}
@@ -721,6 +727,11 @@
of the format. The plus-or-minus character \c{'±'} here stands for either
sign character, \c{'-'} for minus or \c{'+'} for plus.
+ \note Zone offsets are measured positive to the east of Greenwich, negative
+ to the west, as is usual for UTC-based offset notations (conflicting with
+ some GMT-based zones-names, such as \c{Etc/GMT+3}, which use the opposite
+ convention).
+
\sa QDate::toString(), QTime::toString(), QDateTime::toString(),
QDate::fromString(), QTime::fromString(), QDateTime::fromString()
*/
@@ -728,10 +739,26 @@
/*!
\enum Qt::TimeSpec
- \value LocalTime Locale dependent time (Timezones and Daylight Savings Time).
- \value UTC Coordinated Universal Time, replaces Greenwich Mean Time.
+ \value LocalTime Local time, controlled by a system time-zone setting.
+ \value UTC Coordinated Universal Time.
\value OffsetFromUTC An offset in seconds from Coordinated Universal Time.
- \value TimeZone A named time zone using a specific set of Daylight Savings rules.
+ \value TimeZone A named time zone.
+
+ Both LocalTime and TimeZone will take care of transitions, such as
+ the start and end of daylight-saving time. UTC is the standard
+ time relative to which time-zones are usually specified: Greenwich
+ Mean Time has zero offset from it. Neither UTC nor OffsetFromUTC
+ has any transitions.
+
+ \note After a change to the system time-zone setting, the behavior
+ of LocalTime-based QDateTime objects created before the change is
+ undefined: QDateTime may have cached data that the change
+ invalidates. (This is not triggered by transitions of the system
+ time-zone.) In long-running processes, updates to the system's
+ time-zone data (e.g. when politicians change the rules for a zone)
+ may likewise lead to conflicts between the updated time-zone
+ information and data cached by QDateTime objects created before
+ the update, using either LocalTime or TimeZone.
*/
/*!
@@ -2649,6 +2676,7 @@
\value ImInputItemClipRectangle The actual exposed input item rectangle. Parts of the input item might be
clipped. This value will take clipping into consideration and return the actual painted
item rectangle. The rectangle is in widget coordinates.
+ \value ImReadOnly The widget is read only. This value was added in Qt 6.2.
Masks:
diff --git a/src/corelib/global/qnumeric.h b/src/corelib/global/qnumeric.h
index 63ba2bb378..49d795902c 100644
--- a/src/corelib/global/qnumeric.h
+++ b/src/corelib/global/qnumeric.h
@@ -114,9 +114,12 @@ Q_CORE_EXPORT quint64 qFloatDistance(double a, double b);
// size_t. Implementations for 8- and 16-bit types will work but may not be as
// efficient. Implementations for 64-bit may be missing on 32-bit platforms.
-#if ((defined(Q_CC_INTEL) ? (Q_CC_INTEL >= 1800 && !defined(Q_OS_WIN)) : defined(Q_CC_GNU)) \
- && Q_CC_GNU >= 500) || __has_builtin(__builtin_add_overflow)
+#if (((defined(Q_CC_INTEL) ? (Q_CC_INTEL >= 1800 && !defined(Q_OS_WIN)) : defined(Q_CC_GNU)) \
+ && Q_CC_GNU >= 500) \
+ || __has_builtin(__builtin_add_overflow)) \
+ && !(QT_POINTER_SIZE == 4 && defined(Q_CC_CLANG))
// GCC 5, ICC 18, and Clang 3.8 have builtins to detect overflows
+// 32 bit Clang has the builtins but tries to link a library which hasn't
#define Q_INTRINSIC_MUL_OVERFLOW64
template <typename T> inline
diff --git a/src/corelib/global/qoperatingsystemversion.cpp b/src/corelib/global/qoperatingsystemversion.cpp
index a9388a9795..3603852c82 100644
--- a/src/corelib/global/qoperatingsystemversion.cpp
+++ b/src/corelib/global/qoperatingsystemversion.cpp
@@ -1,6 +1,6 @@
/****************************************************************************
**
-** Copyright (C) 2016 The Qt Company Ltd.
+** Copyright (C) 2021 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the QtCore module of the Qt Toolkit.
@@ -50,7 +50,7 @@
#include <qdebug.h>
#if defined(Q_OS_ANDROID) && !defined(Q_OS_ANDROID_EMBEDDED)
-#include <private/qjni_p.h>
+#include <QJniObject>
#endif
QT_BEGIN_NAMESPACE
@@ -157,7 +157,7 @@ QOperatingSystemVersion QOperatingSystemVersion::current()
version.m_os = currentType();
#if defined(Q_OS_ANDROID) && !defined(Q_OS_ANDROID_EMBEDDED)
#ifndef QT_BOOTSTRAPPED
- const QVersionNumber v = QVersionNumber::fromString(QJNIObjectPrivate::getStaticObjectField(
+ const QVersionNumber v = QVersionNumber::fromString(QJniObject::getStaticObjectField(
"android/os/Build$VERSION", "RELEASE", "Ljava/lang/String;").toString());
if (!v.isNull()) {
version.m_major = v.majorVersion();
@@ -207,7 +207,7 @@ QOperatingSystemVersion QOperatingSystemVersion::current()
};
// This will give us at least the first 2 version components
- const size_t versionIdx = size_t(QJNIObjectPrivate::getStaticField<jint>(
+ const size_t versionIdx = size_t(QJniObject::getStaticField<jint>(
"android/os/Build$VERSION", "SDK_INT")) - 1;
if (versionIdx < sizeof(versions) / sizeof(versions[0])) {
version.m_major = versions[versionIdx].major;
@@ -584,19 +584,19 @@ const QOperatingSystemVersion QOperatingSystemVersion::AndroidPie =
QOperatingSystemVersion(QOperatingSystemVersion::Android, 9, 0);
/*!
- \variable QOperatingSystemVersion::AndroidQ
- \brief a version corresponding to Android Q (version 10.0, API level 29).
+ \variable QOperatingSystemVersion::Android10
+ \brief a version corresponding to Android 10 (version 10.0, API level 29).
\since 6.1
*/
-const QOperatingSystemVersion QOperatingSystemVersion::AndroidQ =
+const QOperatingSystemVersion QOperatingSystemVersion::Android10 =
QOperatingSystemVersion(QOperatingSystemVersion::Android, 10, 0);
/*!
- \variable QOperatingSystemVersion::AndroidR
- \brief a version corresponding to Android R (version 11.0, API level 30).
+ \variable QOperatingSystemVersion::Android11
+ \brief a version corresponding to Android 11 (version 11.0, API level 30).
\since 6.1
*/
-const QOperatingSystemVersion QOperatingSystemVersion::AndroidR =
+const QOperatingSystemVersion QOperatingSystemVersion::Android11 =
QOperatingSystemVersion(QOperatingSystemVersion::Android, 11, 0);
#ifndef QT_NO_DEBUG_STREAM
diff --git a/src/corelib/global/qoperatingsystemversion.h b/src/corelib/global/qoperatingsystemversion.h
index a693342824..c884e0e3e8 100644
--- a/src/corelib/global/qoperatingsystemversion.h
+++ b/src/corelib/global/qoperatingsystemversion.h
@@ -86,8 +86,8 @@ public:
static const QOperatingSystemVersion AndroidOreo;
static const QOperatingSystemVersion AndroidOreo_MR1;
static const QOperatingSystemVersion AndroidPie;
- static const QOperatingSystemVersion AndroidQ;
- static const QOperatingSystemVersion AndroidR;
+ static const QOperatingSystemVersion Android10;
+ static const QOperatingSystemVersion Android11;
constexpr QOperatingSystemVersion(OSType osType,
int vmajor, int vminor = -1, int vmicro = -1)
diff --git a/src/corelib/global/qrandom.cpp b/src/corelib/global/qrandom.cpp
index ca16566c1c..c978228094 100644
--- a/src/corelib/global/qrandom.cpp
+++ b/src/corelib/global/qrandom.cpp
@@ -1,6 +1,7 @@
/****************************************************************************
**
** Copyright (C) 2020 Intel Corporation.
+** Copyright (C) 2021 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the QtCore module of the Qt Toolkit.
@@ -72,10 +73,6 @@ DECLSPEC_IMPORT BOOLEAN WINAPI SystemFunction036(PVOID RandomBuffer, ULONG Rando
}
#endif
-#if defined(Q_OS_ANDROID) && !defined(Q_OS_ANDROID_EMBEDDED)
-# include <private/qjni_p.h>
-#endif
-
// This file is too low-level for regular Q_ASSERT (the logging framework may
// recurse back), so use regular assert()
#undef NDEBUG
@@ -95,6 +92,10 @@ enum {
FillBufferNoexcept = true
};
+#if defined(QT_BUILD_INTERNAL)
+QBasicAtomicInteger<uint> qt_randomdevice_control = Q_BASIC_ATOMIC_INITIALIZER(0U);
+#endif
+
struct QRandomGenerator::SystemGenerator
{
#if QT_CONFIG(getentropy)
@@ -1298,26 +1299,4 @@ quint64 QRandomGenerator::_fillRange(void *buffer, qptrdiff count)
return begin[0] | (quint64(begin[1]) << 32);
}
-namespace {
-struct QRandEngine
-{
- std::minstd_rand engine;
- QRandEngine() : engine(1) {}
-
- int generate()
- {
- std::minstd_rand::result_type v = engine();
- if (std::numeric_limits<int>::max() != RAND_MAX)
- v %= uint(RAND_MAX) + 1;
-
- return int(v);
- }
-
- void seed(std::minstd_rand::result_type q)
- {
- engine.seed(q);
- }
-};
-}
-
QT_END_NAMESPACE
diff --git a/src/corelib/global/qrandom_p.h b/src/corelib/global/qrandom_p.h
index 052aef262e..7e228b3fde 100644
--- a/src/corelib/global/qrandom_p.h
+++ b/src/corelib/global/qrandom_p.h
@@ -71,9 +71,7 @@ enum RNGType {
MersenneTwister = 1
};
-#if defined(QT_BUILD_INTERNAL) && defined(QT_BUILD_CORE_LIB)
-Q_CORE_EXPORT QBasicAtomicInteger<uint> qt_randomdevice_control = Q_BASIC_ATOMIC_INITIALIZER(0U);
-#elif defined(QT_BUILD_INTERNAL)
+#if defined(QT_BUILD_INTERNAL)
extern Q_CORE_EXPORT QBasicAtomicInteger<uint> qt_randomdevice_control;
#else
static const struct
diff --git a/src/corelib/global/qsimd_p.h b/src/corelib/global/qsimd_p.h
index 2f2d49348f..fbb5f0f021 100644
--- a/src/corelib/global/qsimd_p.h
+++ b/src/corelib/global/qsimd_p.h
@@ -274,6 +274,22 @@ QT_END_NAMESPACE
// __ARM_NEON__ is not defined on AArch64, but we need it in our NEON detection.
#define __ARM_NEON__
#endif
+
+#ifndef Q_PROCESSOR_ARM_64 // vaddv is only available on Aarch64
+inline uint16_t vaddvq_u16(uint16x8_t v8)
+{
+ const uint64x2_t v2 = vpaddlq_u32(vpaddlq_u16(v8));
+ const uint64x1_t v1 = vadd_u64(vget_low_u64(v2), vget_high_u64(v2));
+ return vget_lane_u16(vreinterpret_u16_u64(v1), 0);
+}
+
+inline uint8_t vaddv_u8(uint8x8_t v8)
+{
+ const uint64x1_t v1 = vpaddl_u32(vpaddl_u16(vpaddl_u8(v8)));
+ return vget_lane_u8(vreinterpret_u8_u64(v1), 0);
+}
+#endif
+
#endif
// AArch64/ARM64
#if defined(Q_PROCESSOR_ARM_V8) && defined(__ARM_FEATURE_CRC32)
diff --git a/src/corelib/global/qsysinfo.h b/src/corelib/global/qsysinfo.h
index 89e761c662..04379ecd7e 100644
--- a/src/corelib/global/qsysinfo.h
+++ b/src/corelib/global/qsysinfo.h
@@ -57,18 +57,9 @@ public:
WordSize = (sizeof(void *)<<3)
};
-#if defined(QT_BUILD_QMAKE)
enum Endian {
BigEndian,
LittleEndian
- };
- /* needed to bootstrap qmake */
- static const int ByteOrder;
-#elif defined(Q_BYTE_ORDER)
- enum Endian {
- BigEndian,
- LittleEndian
-
# ifdef Q_QDOC
, ByteOrder = BigEndian or LittleEndian
# elif Q_BYTE_ORDER == Q_BIG_ENDIAN
@@ -79,7 +70,6 @@ public:
# error "Undefined byte order"
# endif
};
-#endif
static QString buildCpuArchitecture();
static QString currentCpuArchitecture();
diff --git a/src/corelib/global/qt_pch.h b/src/corelib/global/qt_pch.h
index 8e59ace85d..58697517b5 100644
--- a/src/corelib/global/qt_pch.h
+++ b/src/corelib/global/qt_pch.h
@@ -69,7 +69,9 @@
#include <qlist.h>
#include <qvariant.h> /* All moc genereated code has this include */
#include <qobject.h>
-#include <qregularexpression.h>
+#if QT_CONFIG(regularexpression)
+# include <qregularexpression.h>
+#endif
#include <qscopedpointer.h>
#include <qshareddata.h>
#include <qstring.h>
diff --git a/src/corelib/global/qtypeinfo.h b/src/corelib/global/qtypeinfo.h
index e5914414f2..58f20b7be4 100644
--- a/src/corelib/global/qtypeinfo.h
+++ b/src/corelib/global/qtypeinfo.h
@@ -42,6 +42,7 @@
#include <QtCore/qcontainerfwd.h>
#include <variant>
#include <optional>
+#include <tuple>
#ifndef QTYPEINFO_H
#define QTYPEINFO_H
@@ -227,8 +228,8 @@ template <typename, typename = void>
struct is_container : std::false_type {};
template <typename T>
struct is_container<T, std::void_t<
- std::is_convertible<decltype(std::declval<T>().begin() != std::declval<T>().end()), bool>,
- typename T::value_type
+ typename T::value_type,
+ std::is_convertible<decltype(std::declval<T>().begin() != std::declval<T>().end()), bool>
>> : std::true_type {};
@@ -258,7 +259,11 @@ struct expand_operator_equal_container : expand_operator_equal_tuple<T> {};
// if T::value_type exists, check first T::value_type, then T itself
template<typename T>
struct expand_operator_equal_container<T, true> :
- std::conjunction<expand_operator_equal<typename T::value_type>, expand_operator_equal_tuple<T>> {};
+ std::conjunction<
+ std::disjunction<
+ std::is_same<T, typename T::value_type>, // avoid endless recursion
+ expand_operator_equal<typename T::value_type>
+ >, expand_operator_equal_tuple<T>> {};
// recursively check the template arguments of a tuple like object
template<typename ...T>
@@ -294,7 +299,12 @@ template<typename T, bool>
struct expand_operator_less_than_container : expand_operator_less_than_tuple<T> {};
template<typename T>
struct expand_operator_less_than_container<T, true> :
- std::conjunction<expand_operator_less_than<typename T::value_type>, expand_operator_less_than_tuple<T>> {};
+ std::conjunction<
+ std::disjunction<
+ std::is_same<T, typename T::value_type>,
+ expand_operator_less_than<typename T::value_type>
+ >, expand_operator_less_than_tuple<T>
+ > {};
template<typename ...T>
using expand_operator_less_than_recursive = std::conjunction<expand_operator_less_than<T>...>;
diff --git a/src/corelib/io/qfilesystemengine_unix.cpp b/src/corelib/io/qfilesystemengine_unix.cpp
index 167b21f8b1..e0c41bdb38 100644
--- a/src/corelib/io/qfilesystemengine_unix.cpp
+++ b/src/corelib/io/qfilesystemengine_unix.cpp
@@ -311,7 +311,7 @@ mtime(const T &statBuffer, int)
static int qt_real_statx(int fd, const char *pathname, int flags, struct statx *statxBuffer)
{
unsigned mask = STATX_BASIC_STATS | STATX_BTIME;
- int ret = statx(fd, pathname, flags, mask, statxBuffer);
+ int ret = statx(fd, pathname, flags | AT_NO_AUTOMOUNT, mask, statxBuffer);
return ret == -1 ? -errno : 0;
}
diff --git a/src/corelib/io/qfilesystemengine_win.cpp b/src/corelib/io/qfilesystemengine_win.cpp
index f27040b07c..6fa6ec2dc2 100644
--- a/src/corelib/io/qfilesystemengine_win.cpp
+++ b/src/corelib/io/qfilesystemengine_win.cpp
@@ -644,7 +644,7 @@ static inline QByteArray fileId(HANDLE handle)
// File ID for Windows starting from version 8.
QByteArray fileIdWin8(HANDLE handle)
{
-#if !defined(QT_BOOTSTRAPPED) && !defined(QT_BUILD_QMAKE)
+#if !defined(QT_BOOTSTRAPPED)
QByteArray result;
FILE_ID_INFO infoEx;
if (GetFileInformationByHandleEx(handle,
@@ -658,7 +658,7 @@ QByteArray fileIdWin8(HANDLE handle)
result = fileId(handle); // GetFileInformationByHandleEx() is observed to fail for FAT32, QTBUG-74759
}
return result;
-#else // !QT_BOOTSTRAPPED && !QT_BUILD_QMAKE
+#else // !QT_BOOTSTRAPPED
return fileId(handle);
#endif
}
diff --git a/src/corelib/io/qiodevice.cpp b/src/corelib/io/qiodevice.cpp
index 5c8c0e6a27..0bea68acc5 100644
--- a/src/corelib/io/qiodevice.cpp
+++ b/src/corelib/io/qiodevice.cpp
@@ -290,10 +290,18 @@ QIODevicePrivate::~QIODevicePrivate()
*/
/*!
+ \class QIODeviceBase
+ \inheaderfile QIODevice
+ \inmodule QtCore
+ \brief Base class for QIODevice that provides flags describing the mode in
+ which a device is opened.
+*/
+
+/*!
\enum QIODeviceBase::OpenModeFlag
- This enum is used with open() to describe the mode in which a device
- is opened. It is also returned by openMode().
+ This enum is used with QIODevice::open() to describe the mode in which a
+ device is opened. It is also returned by QIODevice::openMode().
\value NotOpen The device is not open.
\value ReadOnly The device is open for reading.
diff --git a/src/corelib/io/qlockfile.cpp b/src/corelib/io/qlockfile.cpp
index ba2dc28339..032be3b7a1 100644
--- a/src/corelib/io/qlockfile.cpp
+++ b/src/corelib/io/qlockfile.cpp
@@ -180,6 +180,25 @@ void QLockFile::setStaleLockTime(int staleLockTime)
d->staleLockTime = staleLockTime;
}
+/*! \fn void QLockFile::setStaleLockTime(std::chrono::milliseconds value)
+ \overload
+ \since 6.2
+
+ Sets the interval after which a lock file is considered stale to \a value.
+ The default value is 30 seconds.
+ If your application typically keeps the file locked for more than 30 seconds
+ (for instance while saving megabytes of data for 2 minutes), you should set
+ a bigger value using setStaleLockTime().
+
+ The value of staleLockTime() is used by lock() and tryLock() in order
+ to determine when an existing lock file is considered stale, i.e. left over
+ by a crashed process. This is useful for the case where the PID got reused
+ meanwhile, so one way to detect a stale lock file is by the fact that
+ it has been around for a long time.
+
+ \sa staleLockTime()
+*/
+
/*!
Returns the time in milliseconds after which
a lock file is considered stale.
@@ -192,6 +211,16 @@ int QLockFile::staleLockTime() const
return d->staleLockTime;
}
+/*! \fn std::chrono::milliseconds QLockFile::staleLockTimeAsDuration() const
+ \overload
+ \since 6.2
+
+ Returns a std::chrono::milliseconds object which denotes the time after
+ which a lock file is considered stale.
+
+ \sa setStaleLockTime()
+*/
+
/*!
Returns \c true if the lock was acquired by this QLockFile instance,
otherwise returns \c false.
@@ -287,6 +316,25 @@ bool QLockFile::tryLock(int timeout)
return false;
}
+/*! \fn bool QLockFile::tryLock(std::chrono::milliseconds timeout)
+ \overload
+ \since 6.2
+
+ Attempts to create the lock file. This function returns \c true if the
+ lock was obtained; otherwise it returns \c false. If another process (or
+ another thread) has created the lock file already, this function will
+ wait for at most \a timeout for the lock file to become available.
+
+ If the lock was obtained, it must be released with unlock()
+ before another process (or thread) can successfully lock it.
+
+ Calling this function multiple times on the same lock from the same
+ thread without unlocking first is not allowed, this function will
+ \e always return false when attempting to lock the file recursively.
+
+ \sa lock(), unlock()
+*/
+
/*!
\fn void QLockFile::unlock()
Releases the lock, by deleting the lock file.
diff --git a/src/corelib/io/qlockfile.h b/src/corelib/io/qlockfile.h
index ba955241b6..aa5ad5e772 100644
--- a/src/corelib/io/qlockfile.h
+++ b/src/corelib/io/qlockfile.h
@@ -43,6 +43,10 @@
#include <QtCore/qstring.h>
#include <QtCore/qscopedpointer.h>
+#if __has_include(<chrono>)
+# include <chrono>
+#endif
+
QT_BEGIN_NAMESPACE
class QLockFilePrivate;
@@ -62,6 +66,17 @@ public:
void setStaleLockTime(int);
int staleLockTime() const;
+#if __has_include(<chrono>)
+ bool tryLock(std::chrono::milliseconds timeout) { return tryLock(int(timeout.count())); }
+
+ void setStaleLockTime(std::chrono::milliseconds value) { setStaleLockTime(int(value.count())); }
+
+ std::chrono::milliseconds staleLockTimeAsDuration() const
+ {
+ return std::chrono::milliseconds(staleLockTime());
+ }
+#endif
+
bool isLocked() const;
bool getLockInfo(qint64 *pid, QString *hostname, QString *appname) const;
bool removeStaleLockFile();
diff --git a/src/corelib/io/qprocess.cpp b/src/corelib/io/qprocess.cpp
index 05aa0f021f..745c88e726 100644
--- a/src/corelib/io/qprocess.cpp
+++ b/src/corelib/io/qprocess.cpp
@@ -867,18 +867,6 @@ void QProcessPrivate::cleanup()
pid = 0;
#endif
- if (stdoutChannel.notifier) {
- delete stdoutChannel.notifier;
- stdoutChannel.notifier = nullptr;
- }
- if (stderrChannel.notifier) {
- delete stderrChannel.notifier;
- stderrChannel.notifier = nullptr;
- }
- if (stdinChannel.notifier) {
- delete stdinChannel.notifier;
- stdinChannel.notifier = nullptr;
- }
if (stateNotifier) {
delete stateNotifier;
stateNotifier = nullptr;
@@ -1052,8 +1040,6 @@ bool QProcessPrivate::tryReadFromChannel(Channel *channel)
}
if (readBytes == 0) {
// EOF
- if (channel->notifier)
- channel->notifier->setEnabled(false);
closeChannel(channel);
#if defined QPROCESS_DEBUG
qDebug("QProcessPrivate::tryReadFromChannel(%d), 0 bytes available",
@@ -1111,8 +1097,13 @@ bool QProcessPrivate::_q_canReadStandardError()
bool QProcessPrivate::_q_canWrite()
{
if (writeBuffer.isEmpty()) {
+#ifdef Q_OS_WIN
+ if (stdinChannel.closed && pipeWriterBytesToWrite() == 0)
+ closeWriteChannel();
+#else
if (stdinChannel.notifier)
stdinChannel.notifier->setEnabled(false);
+#endif
#if defined QPROCESS_DEBUG
qDebug("QProcessPrivate::canWrite(), not writing anything (empty write buffer).");
#endif
@@ -1121,10 +1112,12 @@ bool QProcessPrivate::_q_canWrite()
const bool writeSucceeded = writeToStdin();
+#ifdef Q_OS_UNIX
if (writeBuffer.isEmpty() && stdinChannel.closed)
closeWriteChannel();
else if (stdinChannel.notifier)
stdinChannel.notifier->setEnabled(!writeBuffer.isEmpty());
+#endif
return writeSucceeded;
}
@@ -1166,8 +1159,9 @@ void QProcessPrivate::processFinished()
#ifdef Q_OS_UNIX
waitForDeadChild();
-#endif
+#else
findExitCode();
+#endif
cleanup();
@@ -1210,7 +1204,6 @@ bool QProcessPrivate::_q_startupNotification()
setErrorAndEmit(QProcess::FailedToStart, errorMessage);
#ifdef Q_OS_UNIX
waitForDeadChild();
- findExitCode();
#endif
cleanup();
return false;
@@ -1224,15 +1217,7 @@ void QProcessPrivate::closeWriteChannel()
#if defined QPROCESS_DEBUG
qDebug("QProcessPrivate::closeWriteChannel()");
#endif
- if (stdinChannel.notifier) {
- delete stdinChannel.notifier;
- stdinChannel.notifier = nullptr;
- }
-#ifdef Q_OS_WIN
- // ### Find a better fix, feeding the process little by little
- // instead.
- flushPipeWriter();
-#endif
+
closeChannel(&stdinChannel);
}
@@ -1262,9 +1247,6 @@ QProcess::~QProcess()
kill();
waitForFinished();
}
-#ifdef Q_OS_UNIX
- d->findExitCode();
-#endif
d->cleanup();
}
@@ -1393,7 +1375,7 @@ void QProcess::closeWriteChannel()
{
Q_D(QProcess);
d->stdinChannel.closed = true; // closing
- if (d->writeBuffer.isEmpty())
+ if (bytesToWrite() == 0)
d->closeWriteChannel();
}
@@ -2152,11 +2134,12 @@ void QProcess::startCommand(const QString &command, OpenMode mode)
temporarily freeze.
If the function is successful then *\a pid is set to the process identifier
- of the started process. Note that the child process may exit and the PID
- may become invalid without notice. Furthermore, after the child process
- exits, the same PID may be recycled and used by a completely different
- process. User code should be careful when using this variable, especially
- if one intends to forcibly terminate the process by operating system means.
+ of the started process; otherwise, it's set to -1. Note that the child
+ process may exit and the PID may become invalid without notice.
+ Furthermore, after the child process exits, the same PID may be recycled
+ and used by a completely different process. User code should be careful
+ when using this variable, especially if one intends to forcibly terminate
+ the process by operating system means.
Only the following property setters are supported by startDetached():
\list
diff --git a/src/corelib/io/qprocess_p.h b/src/corelib/io/qprocess_p.h
index 4364d99c9b..73db9423e6 100644
--- a/src/corelib/io/qprocess_p.h
+++ b/src/corelib/io/qprocess_p.h
@@ -356,15 +356,15 @@ public:
void processFinished();
void terminateProcess();
void killProcess();
- void findExitCode();
#ifdef Q_OS_UNIX
void waitForDeadChild();
+#else
+ void findExitCode();
#endif
#ifdef Q_OS_WIN
STARTUPINFOW createStartupInfo();
bool callCreateProcess(QProcess::CreateProcessArguments *cpargs);
bool drainOutputPipes();
- void flushPipeWriter();
qint64 pipeWriterBytesToWrite() const;
#endif
diff --git a/src/corelib/io/qprocess_unix.cpp b/src/corelib/io/qprocess_unix.cpp
index 2f21e0ee1e..1a118fc911 100644
--- a/src/corelib/io/qprocess_unix.cpp
+++ b/src/corelib/io/qprocess_unix.cpp
@@ -1,7 +1,8 @@
/****************************************************************************
**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Copyright (C) 2016 Intel Corporation.
+** Copyright (C) 2020 The Qt Company Ltd.
+** Copyright (C) 2021 Intel Corporation.
+** Copyright (C) 2021 Alex Trotsenko.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the QtCore module of the Qt Toolkit.
@@ -147,6 +148,45 @@ QProcessEnvironment QProcessEnvironment::systemEnvironment()
#if QT_CONFIG(process)
namespace {
+struct AutoPipe
+{
+ int pipe[2] = { -1, -1 };
+ AutoPipe(int flags = 0)
+ {
+ qt_safe_pipe(pipe, flags);
+ }
+ ~AutoPipe()
+ {
+ for (int fd : pipe) {
+ if (fd >= 0)
+ qt_safe_close(fd);
+ }
+ }
+
+ explicit operator bool() const { return pipe[0] >= 0; }
+ int &operator[](int idx) { return pipe[idx]; }
+ int operator[](int idx) const { return pipe[idx]; }
+};
+
+struct ChildError
+{
+ qint64 code;
+ char function[8];
+};
+
+// Used for argv and envp arguments to execve()
+struct CharPointerList
+{
+ std::unique_ptr<char *[]> pointers;
+
+ CharPointerList(const QString &argv0, const QStringList &args);
+ explicit CharPointerList(const QProcessEnvironmentPrivate *env);
+
+private:
+ QByteArray data;
+ void updatePointers(qsizetype count);
+};
+
struct QProcessPoller
{
QProcessPoller(const QProcessPrivate &proc);
@@ -182,6 +222,62 @@ int QProcessPoller::poll(const QDeadlineTimer &deadline)
{
return qt_poll_msecs(pfds, n_pfds, deadline.remainingTime());
}
+
+CharPointerList::CharPointerList(const QString &program, const QStringList &args)
+{
+ qsizetype count = 1 + args.size();
+ pointers.reset(new char *[count + 1]);
+ pointers[count] = nullptr;
+
+ // we abuse the pointer array to store offsets first (QByteArray will
+ // reallocate, after all)
+ pointers[0] = reinterpret_cast<char *>(0);
+ data = QFile::encodeName(program);
+ data += '\0';
+
+ const auto end = args.end();
+ auto it = args.begin();
+ for (qsizetype i = 1; it != end; ++it, ++i) {
+ pointers[i] = reinterpret_cast<char *>(data.size());
+ data += QFile::encodeName(*it);
+ data += '\0';
+ }
+
+ updatePointers(count);
+}
+
+CharPointerList::CharPointerList(const QProcessEnvironmentPrivate *environment)
+{
+ if (!environment)
+ return;
+
+ const QProcessEnvironmentPrivate::Map &env = environment->vars;
+ qsizetype count = env.size();
+ pointers.reset(new char *[count + 1]);
+ pointers[count] = nullptr;
+
+ const auto end = env.end();
+ auto it = env.begin();
+ for (qsizetype i = 0; it != end; ++it, ++i) {
+ // we abuse the pointer array to store offsets first (QByteArray will
+ // reallocate, after all)
+ pointers[i] = reinterpret_cast<char *>(data.size());
+
+ data += it.key();
+ data += '=';
+ data += it->bytes();
+ data += '\0';
+ }
+
+ updatePointers(count);
+}
+
+void CharPointerList::updatePointers(qsizetype count)
+{
+ char *const base = const_cast<char *>(data.constBegin());
+ for (qsizetype i = 0; i < count; ++i)
+ pointers[i] = base + qptrdiff(pointers[i]);
+}
} // anonymous namespace
static bool qt_pollfd_check(const pollfd &pfd, short revents)
@@ -216,6 +312,9 @@ void QProcessPrivate::destroyPipe(int *pipe)
void QProcessPrivate::closeChannel(Channel *channel)
{
+ delete channel->notifier;
+ channel->notifier = nullptr;
+
destroyPipe(channel->pipe);
}
@@ -337,29 +436,39 @@ void QProcessPrivate::commitChannels()
}
}
-static char **_q_dupEnvironment(const QProcessEnvironmentPrivate::Map &environment, int *envc)
+static QString resolveExecutable(const QString &program)
{
- *envc = 0;
- if (environment.isEmpty())
- return nullptr;
-
- char **envp = new char *[environment.count() + 2];
- envp[environment.count()] = nullptr;
- envp[environment.count() + 1] = nullptr;
-
- auto it = environment.constBegin();
- const auto end = environment.constEnd();
- for ( ; it != end; ++it) {
- QByteArray key = it.key();
- QByteArray value = it.value().bytes();
- key.reserve(key.length() + 1 + value.length());
- key.append('=');
- key.append(value);
-
- envp[(*envc)++] = ::strdup(key.constData());
+#ifdef Q_OS_DARWIN
+ // allow invoking of .app bundles on the Mac.
+ QFileInfo fileInfo(program);
+ if (program.endsWith(QLatin1String(".app")) && fileInfo.isDir()) {
+ QCFType<CFURLRef> url = CFURLCreateWithFileSystemPath(0,
+ QCFString(fileInfo.absoluteFilePath()),
+ kCFURLPOSIXPathStyle, true);
+ {
+ // CFBundle is not reentrant, since CFBundleCreate might return a reference
+ // to a cached bundle object. Protect the bundle calls with a mutex lock.
+ static QBasicMutex cfbundleMutex;
+ const auto locker = qt_scoped_lock(cfbundleMutex);
+ QCFType<CFBundleRef> bundle = CFBundleCreate(0, url);
+ // 'executableURL' can be either relative or absolute ...
+ QCFType<CFURLRef> executableURL = CFBundleCopyExecutableURL(bundle);
+ // not to depend on caching - make sure it's always absolute.
+ url = CFURLCopyAbsoluteURL(executableURL);
+ }
+ if (url) {
+ const QCFString str = CFURLCopyFileSystemPath(url, kCFURLPOSIXPathStyle);
+ return QString::fromCFString(str);
+ }
}
+#endif
- return envp;
+ if (!program.contains(QLatin1Char('/'))) {
+ QString exeFilePath = QStandardPaths::findExecutable(program);
+ if (!exeFilePath.isEmpty())
+ return exeFilePath;
+ }
+ return program;
}
void QProcessPrivate::startProcess()
@@ -390,60 +499,9 @@ void QProcessPrivate::startProcess()
// Start the process (platform dependent)
q->setProcessState(QProcess::Starting);
- // Create argument list with right number of elements, and set the final
- // one to 0.
- char **argv = new char *[arguments.count() + 2];
- argv[arguments.count() + 1] = nullptr;
-
- // Encode the program name.
- QByteArray encodedProgramName = QFile::encodeName(program);
-#ifdef Q_OS_MAC
- // allow invoking of .app bundles on the Mac.
- QFileInfo fileInfo(program);
- if (encodedProgramName.endsWith(".app") && fileInfo.isDir()) {
- QCFType<CFURLRef> url = CFURLCreateWithFileSystemPath(0,
- QCFString(fileInfo.absoluteFilePath()),
- kCFURLPOSIXPathStyle, true);
- {
- // CFBundle is not reentrant, since CFBundleCreate might return a reference
- // to a cached bundle object. Protect the bundle calls with a mutex lock.
- static QBasicMutex cfbundleMutex;
- const auto locker = qt_scoped_lock(cfbundleMutex);
- QCFType<CFBundleRef> bundle = CFBundleCreate(0, url);
- // 'executableURL' can be either relative or absolute ...
- QCFType<CFURLRef> executableURL = CFBundleCopyExecutableURL(bundle);
- // not to depend on caching - make sure it's always absolute.
- url = CFURLCopyAbsoluteURL(executableURL);
- }
- if (url) {
- const QCFString str = CFURLCopyFileSystemPath(url, kCFURLPOSIXPathStyle);
- encodedProgramName += (QDir::separator() + QDir(program).relativeFilePath(QString::fromCFString(str))).toUtf8();
- }
- }
-#endif
-
- // Add the program name to the argument list.
- argv[0] = nullptr;
- if (!program.contains(QLatin1Char('/'))) {
- const QString &exeFilePath = QStandardPaths::findExecutable(program);
- if (!exeFilePath.isEmpty()) {
- const QByteArray &tmp = QFile::encodeName(exeFilePath);
- argv[0] = ::strdup(tmp.constData());
- }
- }
- if (!argv[0])
- argv[0] = ::strdup(encodedProgramName.constData());
-
- // Add every argument to the list
- for (int i = 0; i < arguments.count(); ++i)
- argv[i + 1] = ::strdup(QFile::encodeName(arguments.at(i)).constData());
-
- // Duplicate the environment.
- int envc = 0;
- char **envp = nullptr;
- if (environment.d.constData()) {
- envp = _q_dupEnvironment(environment.d.constData()->vars, &envc);
- }
+ // Prepare the arguments and the environment
+ const CharPointerList argv(resolveExecutable(program), arguments);
+ const CharPointerList envp(environment.d.constData());
// Encode the working directory if it's non-empty, otherwise just pass 0.
const char *workingDirPtr = nullptr;
@@ -463,21 +521,7 @@ void QProcessPrivate::startProcess()
pid_t childPid;
forkfd = ::forkfd(ffdflags , &childPid);
int lastForkErrno = errno;
- if (forkfd != FFD_CHILD_PROCESS) {
- // Parent process.
- // Clean up duplicated memory.
- for (int i = 0; i <= arguments.count(); ++i)
- free(argv[i]);
- for (int i = 0; i < envc; ++i)
- free(envp[i]);
- delete [] argv;
- delete [] envp;
- }
-
- // On QNX, if spawnChild failed, childPid will be -1 but forkfd is still 0.
- // This is intentional because we only want to handle failure to fork()
- // here, which is a rare occurrence. Handling of the failure to start is
- // done elsewhere.
+
if (forkfd == -1) {
// Cleanup, report error and return
#if defined (QPROCESS_DEBUG)
@@ -492,7 +536,7 @@ void QProcessPrivate::startProcess()
// Start the child.
if (forkfd == FFD_CHILD_PROCESS) {
- execChild(workingDirPtr, argv, envp);
+ execChild(workingDirPtr, argv.pointers.get(), envp.pointers.get());
::_exit(-1);
}
@@ -528,12 +572,6 @@ void QProcessPrivate::startProcess()
::fcntl(stderrChannel.pipe[0], F_SETFL, ::fcntl(stderrChannel.pipe[0], F_GETFL) | O_NONBLOCK);
}
-struct ChildError
-{
- int code;
- char function[8];
-};
-
void QProcessPrivate::execChild(const char *workingDir, char **argv, char **envp)
{
::signal(SIGPIPE, SIG_DFL); // reset the signal that we ignored
@@ -850,10 +888,6 @@ bool QProcessPrivate::waitForFinished(const QDeadlineTimer &deadline)
return false;
}
-void QProcessPrivate::findExitCode()
-{
-}
-
void QProcessPrivate::waitForDeadChild()
{
Q_ASSERT(forkfd != -1);
@@ -882,135 +916,106 @@ bool QProcessPrivate::startDetached(qint64 *pid)
{
QByteArray encodedWorkingDirectory = QFile::encodeName(workingDirectory);
- // To catch the startup of the child
- int startedPipe[2];
- if (qt_safe_pipe(startedPipe) != 0)
- return false;
- // To communicate the pid of the child
- int pidPipe[2];
- if (qt_safe_pipe(pidPipe) != 0) {
- qt_safe_close(startedPipe[0]);
- qt_safe_close(startedPipe[1]);
+ static_assert(PIPE_BUF >= sizeof(ChildError));
+ ChildError childStatus = { 0, {} };
+
+ AutoPipe startedPipe, pidPipe;
+ if (!startedPipe || !pidPipe) {
+ setErrorAndEmit(QProcess::FailedToStart, QLatin1String("pipe: ") + qt_error_string(errno));
return false;
}
if (!openChannelsForDetached()) {
+ // openChannel sets the error string
closeChannel(&stdinChannel);
closeChannel(&stdoutChannel);
closeChannel(&stderrChannel);
- qt_safe_close(pidPipe[0]);
- qt_safe_close(pidPipe[1]);
- qt_safe_close(startedPipe[0]);
- qt_safe_close(startedPipe[1]);
return false;
}
+ const CharPointerList argv(resolveExecutable(program), arguments);
+ const CharPointerList envp(environment.d.constData());
+
pid_t childPid = fork();
if (childPid == 0) {
- struct sigaction noaction;
- memset(&noaction, 0, sizeof(noaction));
- noaction.sa_handler = SIG_IGN;
- ::sigaction(SIGPIPE, &noaction, nullptr);
-
+ ::signal(SIGPIPE, SIG_DFL); // reset the signal that we ignored
::setsid();
qt_safe_close(startedPipe[0]);
qt_safe_close(pidPipe[0]);
+ auto reportFailed = [&](const char *function) {
+ childStatus.code = errno;
+ strcpy(childStatus.function, function);
+ qt_safe_write(startedPipe[1], &childStatus, sizeof(childStatus));
+ ::_exit(1);
+ };
+
+ if (!encodedWorkingDirectory.isEmpty()) {
+ if (QT_CHDIR(encodedWorkingDirectory.constData()) < 0)
+ reportFailed("chdir: ");
+ }
+
pid_t doubleForkPid = fork();
if (doubleForkPid == 0) {
- qt_safe_close(pidPipe[1]);
-
// Render channels configuration.
commitChannels();
- if (!encodedWorkingDirectory.isEmpty()) {
- if (QT_CHDIR(encodedWorkingDirectory.constData()) == -1)
- qWarning("QProcessPrivate::startDetached: failed to chdir to %s", encodedWorkingDirectory.constData());
- }
-
- char **argv = new char *[arguments.size() + 2];
- for (int i = 0; i < arguments.size(); ++i)
- argv[i + 1] = ::strdup(QFile::encodeName(arguments.at(i)).constData());
- argv[arguments.size() + 1] = nullptr;
-
- // Duplicate the environment.
- int envc = 0;
- char **envp = nullptr;
- if (environment.d.constData()) {
- envp = _q_dupEnvironment(environment.d.constData()->vars, &envc);
- }
-
- QByteArray tmp;
- if (!program.contains(QLatin1Char('/'))) {
- const QString &exeFilePath = QStandardPaths::findExecutable(program);
- if (!exeFilePath.isEmpty())
- tmp = QFile::encodeName(exeFilePath);
- }
- if (tmp.isEmpty())
- tmp = QFile::encodeName(program);
- argv[0] = tmp.data();
-
- if (envp)
- qt_safe_execve(argv[0], argv, envp);
+ if (envp.pointers)
+ qt_safe_execve(argv.pointers[0], argv.pointers.get(), envp.pointers.get());
else
- qt_safe_execv(argv[0], argv);
-
- struct sigaction noaction;
- memset(&noaction, 0, sizeof(noaction));
- noaction.sa_handler = SIG_IGN;
- ::sigaction(SIGPIPE, &noaction, nullptr);
+ qt_safe_execv(argv.pointers[0], argv.pointers.get());
- // '\1' means execv failed
- char c = '\1';
- qt_safe_write(startedPipe[1], &c, 1);
- qt_safe_close(startedPipe[1]);
- ::_exit(1);
+ reportFailed("execv: ");
} else if (doubleForkPid == -1) {
- struct sigaction noaction;
- memset(&noaction, 0, sizeof(noaction));
- noaction.sa_handler = SIG_IGN;
- ::sigaction(SIGPIPE, &noaction, nullptr);
-
- // '\2' means internal error
- char c = '\2';
- qt_safe_write(startedPipe[1], &c, 1);
+ reportFailed("fork: ");
}
- qt_safe_close(startedPipe[1]);
- qt_safe_write(pidPipe[1], (const char *)&doubleForkPid, sizeof(pid_t));
- if (QT_CHDIR("/") == -1)
- qWarning("QProcessPrivate::startDetached: failed to chdir to /");
+ // success
+ qt_safe_write(pidPipe[1], &doubleForkPid, sizeof(pid_t));
::_exit(1);
}
+ int savedErrno = errno;
closeChannel(&stdinChannel);
closeChannel(&stdoutChannel);
closeChannel(&stderrChannel);
- qt_safe_close(startedPipe[1]);
- qt_safe_close(pidPipe[1]);
if (childPid == -1) {
- qt_safe_close(startedPipe[0]);
- qt_safe_close(pidPipe[0]);
+ setErrorAndEmit(QProcess::FailedToStart, QLatin1String("fork: ") + qt_error_string(savedErrno));
return false;
}
- char reply = '\0';
- int startResult = qt_safe_read(startedPipe[0], &reply, 1);
+ // close the writing ends of the pipes so we can properly get EOFs
+ qt_safe_close(pidPipe[1]);
+ qt_safe_close(startedPipe[1]);
+ pidPipe[1] = startedPipe[1] = -1;
+
+ // This read() will block until we're cleared to proceed. If it returns 0
+ // (EOF), it means the direct child has exited and the grandchild
+ // successfully execve()'d the target process. If it returns any positive
+ // result, it means one of the two children wrote an error result. Negative
+ // values should not happen.
+ ssize_t startResult = qt_safe_read(startedPipe[0], &childStatus, sizeof(childStatus));
+
+ // reap the intermediate child
int result;
- qt_safe_close(startedPipe[0]);
qt_safe_waitpid(childPid, &result, 0);
- bool success = (startResult != -1 && reply == '\0');
+
+ bool success = (startResult == 0); // nothing written -> no error
if (success && pid) {
- pid_t actualPid = 0;
- if (qt_safe_read(pidPipe[0], (char *)&actualPid, sizeof(pid_t)) == sizeof(pid_t)) {
- *pid = actualPid;
- } else {
- *pid = 0;
- }
+ pid_t actualPid;
+ if (qt_safe_read(pidPipe[0], &actualPid, sizeof(pid_t)) != sizeof(pid_t))
+ actualPid = 0; // this shouldn't happen!
+ *pid = actualPid;
+ } else if (!success) {
+ if (pid)
+ *pid = -1;
+ QString msg;
+ if (startResult == sizeof(childStatus))
+ msg = QLatin1String(childStatus.function) + qt_error_string(childStatus.code);
+ setErrorAndEmit(QProcess::FailedToStart, msg);
}
- qt_safe_close(pidPipe[0]);
return success;
}
diff --git a/src/corelib/io/qprocess_win.cpp b/src/corelib/io/qprocess_win.cpp
index dc8c50127d..d0e57b51fa 100644
--- a/src/corelib/io/qprocess_win.cpp
+++ b/src/corelib/io/qprocess_win.cpp
@@ -85,6 +85,46 @@ QProcessEnvironment QProcessEnvironment::systemEnvironment()
#if QT_CONFIG(process)
+namespace {
+struct QProcessPoller
+{
+ QProcessPoller(const QProcessPrivate &proc);
+
+ int poll(const QDeadlineTimer &deadline);
+
+ enum { maxHandles = 4 };
+ HANDLE handles[maxHandles];
+ DWORD handleCount = 0;
+};
+
+QProcessPoller::QProcessPoller(const QProcessPrivate &proc)
+{
+ if (proc.stdinChannel.writer)
+ handles[handleCount++] = proc.stdinChannel.writer->syncEvent();
+ if (proc.stdoutChannel.reader)
+ handles[handleCount++] = proc.stdoutChannel.reader->syncEvent();
+ if (proc.stderrChannel.reader)
+ handles[handleCount++] = proc.stderrChannel.reader->syncEvent();
+
+ handles[handleCount++] = proc.pid->hProcess;
+}
+
+int QProcessPoller::poll(const QDeadlineTimer &deadline)
+{
+ DWORD waitRet;
+
+ do {
+ waitRet = WaitForMultipleObjectsEx(handleCount, handles, FALSE,
+ deadline.remainingTime(), TRUE);
+ } while (waitRet == WAIT_IO_COMPLETION);
+
+ if (waitRet - WAIT_OBJECT_0 < handleCount)
+ return 1;
+
+ return (waitRet == WAIT_TIMEOUT) ? 0 : -1;
+}
+} // anonymous namespace
+
static bool qt_create_pipe(Q_PIPE *pipe, bool isInputPipe, BOOL defInheritFlag)
{
// Anomymous pipes do not support asynchronous I/O. Thus we
@@ -658,30 +698,33 @@ bool QProcessPrivate::drainOutputPipes()
bool QProcessPrivate::waitForReadyRead(const QDeadlineTimer &deadline)
{
- QIncrementalSleepTimer timer(deadline.remainingTime());
-
forever {
if (!writeBuffer.isEmpty() && !_q_canWrite())
return false;
- if (stdinChannel.writer && stdinChannel.writer->waitForWrite(0))
- timer.resetIncrements();
- if ((stdoutChannel.reader && stdoutChannel.reader->waitForReadyRead(0))
- || (stderrChannel.reader && stderrChannel.reader->waitForReadyRead(0)))
+ QProcessPoller poller(*this);
+ int ret = poller.poll(deadline);
+ if (ret < 0)
+ return false;
+ if (ret == 0)
+ break;
+
+ if (stdinChannel.writer)
+ stdinChannel.writer->checkForWrite();
+
+ if ((stdoutChannel.reader && stdoutChannel.reader->checkForReadyRead())
+ || (stderrChannel.reader && stderrChannel.reader->checkForReadyRead()))
return true;
if (!pid)
return false;
- if (WaitForSingleObjectEx(pid->hProcess, 0, false) == WAIT_OBJECT_0) {
+
+ if (WaitForSingleObject(pid->hProcess, 0) == WAIT_OBJECT_0) {
bool readyReadEmitted = drainOutputPipes();
if (pid)
processFinished();
return readyReadEmitted;
}
-
- Sleep(timer.nextSleepTime());
- if (timer.hasTimedOut())
- break;
}
setError(QProcess::Timedout);
@@ -690,58 +733,43 @@ bool QProcessPrivate::waitForReadyRead(const QDeadlineTimer &deadline)
bool QProcessPrivate::waitForBytesWritten(const QDeadlineTimer &deadline)
{
- QIncrementalSleepTimer timer(deadline.remainingTime());
-
forever {
- bool pendingDataInPipe = stdinChannel.writer && stdinChannel.writer->bytesToWrite();
-
- // If we don't have pending data, and our write buffer is
- // empty, we fail.
- if (!pendingDataInPipe && writeBuffer.isEmpty())
+ // If no write is pending, try to start one. However, at entry into
+ // the loop the write buffer can be empty to start with, in which
+ // case _q_caWrite() fails immediately.
+ if (pipeWriterBytesToWrite() == 0 && !_q_canWrite())
return false;
- // If we don't have pending data and we do have data in our
- // write buffer, try to flush that data over to the pipe
- // writer. Fail on error.
- if (!pendingDataInPipe) {
- if (!_q_canWrite())
- return false;
- }
+ QProcessPoller poller(*this);
+ int ret = poller.poll(deadline);
+ if (ret < 0)
+ return false;
+ if (ret == 0)
+ break;
- // Wait for the pipe writer to acknowledge that it has
- // written. This will succeed if either the pipe writer has
- // already written the data, or if it manages to write data
- // within the given timeout. If the write buffer was non-empty
- // and the stdinChannel.writer is now dead, that means _q_canWrite()
- // destroyed the writer after it successfully wrote the last
- // batch.
- if (!stdinChannel.writer || stdinChannel.writer->waitForWrite(0))
+ Q_ASSERT(stdinChannel.writer);
+ if (stdinChannel.writer->checkForWrite())
return true;
// If we wouldn't write anything, check if we can read stdout.
- if (stdoutChannel.reader && stdoutChannel.reader->waitForReadyRead(0))
- timer.resetIncrements();
+ if (stdoutChannel.reader)
+ stdoutChannel.reader->checkForReadyRead();
// Check if we can read stderr.
- if (stderrChannel.reader && stderrChannel.reader->waitForReadyRead(0))
- timer.resetIncrements();
+ if (stderrChannel.reader)
+ stderrChannel.reader->checkForReadyRead();
// Check if the process died while reading.
if (!pid)
return false;
- // Wait for the process to signal any change in its state,
- // such as incoming data, or if the process died.
- if (WaitForSingleObjectEx(pid->hProcess, 0, false) == WAIT_OBJECT_0) {
+ // Check if the process is signaling completion.
+ if (WaitForSingleObject(pid->hProcess, 0) == WAIT_OBJECT_0) {
drainOutputPipes();
if (pid)
processFinished();
return false;
}
-
- // Only wait for as long as we've been asked.
- if (timer.hasTimedOut())
- break;
}
setError(QProcess::Timedout);
@@ -754,30 +782,33 @@ bool QProcessPrivate::waitForFinished(const QDeadlineTimer &deadline)
qDebug("QProcessPrivate::waitForFinished(%lld)", deadline.remainingTime());
#endif
- QIncrementalSleepTimer timer(deadline.remainingTime());
-
forever {
if (!writeBuffer.isEmpty() && !_q_canWrite())
return false;
- if (stdinChannel.writer && stdinChannel.writer->waitForWrite(0))
- timer.resetIncrements();
- if (stdoutChannel.reader && stdoutChannel.reader->waitForReadyRead(0))
- timer.resetIncrements();
- if (stderrChannel.reader && stderrChannel.reader->waitForReadyRead(0))
- timer.resetIncrements();
+
+ QProcessPoller poller(*this);
+ int ret = poller.poll(deadline);
+ if (ret < 0)
+ return false;
+ if (ret == 0)
+ break;
+
+ if (stdinChannel.writer)
+ stdinChannel.writer->checkForWrite();
+ if (stdoutChannel.reader)
+ stdoutChannel.reader->checkForReadyRead();
+ if (stderrChannel.reader)
+ stderrChannel.reader->checkForReadyRead();
if (!pid)
return true;
- if (WaitForSingleObject(pid->hProcess, timer.nextSleepTime()) == WAIT_OBJECT_0) {
+ if (WaitForSingleObject(pid->hProcess, 0) == WAIT_OBJECT_0) {
drainOutputPipes();
if (pid)
processFinished();
return true;
}
-
- if (timer.hasTimedOut())
- break;
}
setError(QProcess::Timedout);
@@ -796,12 +827,6 @@ void QProcessPrivate::findExitCode()
}
}
-void QProcessPrivate::flushPipeWriter()
-{
- if (stdinChannel.writer && stdinChannel.writer->bytesToWrite() > 0)
- stdinChannel.writer->waitForWrite(ULONG_MAX);
-}
-
qint64 QProcessPrivate::pipeWriterBytesToWrite() const
{
return stdinChannel.writer ? stdinChannel.writer->bytesToWrite() : qint64(0);
@@ -869,6 +894,7 @@ bool QProcessPrivate::startDetached(qint64 *pid)
static const DWORD errorElevationRequired = 740;
if (!openChannelsForDetached()) {
+ // openChannel sets the error string
closeChannel(&stdinChannel);
closeChannel(&stdoutChannel);
closeChannel(&stderrChannel);
@@ -913,6 +939,11 @@ bool QProcessPrivate::startDetached(qint64 *pid)
success = startDetachedUacPrompt(program, arguments, nativeArguments,
workingDirectory, pid);
}
+ if (!success) {
+ if (pid)
+ *pid = -1;
+ setErrorAndEmit(QProcess::FailedToStart);
+ }
closeChannel(&stdinChannel);
closeChannel(&stdoutChannel);
diff --git a/src/corelib/io/qsettings.cpp b/src/corelib/io/qsettings.cpp
index 7f16a82199..97e3bf884b 100644
--- a/src/corelib/io/qsettings.cpp
+++ b/src/corelib/io/qsettings.cpp
@@ -60,9 +60,7 @@
#include "qrect.h"
#endif // !QT_NO_GEOM_VARIANT
-#ifndef QT_BUILD_QMAKE
-# include "qcoreapplication.h"
-#endif
+#include "qcoreapplication.h"
#ifndef QT_BOOTSTRAPPED
#include "qsavefile.h"
@@ -2661,7 +2659,6 @@ QSettings::QSettings(const QString &fileName, Format format)
d_ptr->q_ptr = this;
}
-# ifndef QT_BUILD_QMAKE
QSettings::QSettings(Scope scope)
: d_ptr(QSettingsPrivate::create(globalDefaultFormat, scope,
# ifdef Q_OS_DARWIN
@@ -2678,7 +2675,6 @@ QSettings::QSettings(Scope scope)
{
d_ptr->q_ptr = this;
}
-# endif
#endif
/*!
diff --git a/src/corelib/io/qsettings.h b/src/corelib/io/qsettings.h
index 8fde893c23..05b30b562b 100644
--- a/src/corelib/io/qsettings.h
+++ b/src/corelib/io/qsettings.h
@@ -138,9 +138,7 @@ public:
QSettings(Format format, Scope scope, const QString &organization,
const QString &application = QString());
QSettings(const QString &fileName, Format format);
-# ifndef QT_BUILD_QMAKE
explicit QSettings(Scope scope = UserScope);
-# endif
#endif
~QSettings();
diff --git a/src/corelib/io/qstandardpaths_android.cpp b/src/corelib/io/qstandardpaths_android.cpp
index 3aa310e168..129ae03158 100644
--- a/src/corelib/io/qstandardpaths_android.cpp
+++ b/src/corelib/io/qstandardpaths_android.cpp
@@ -1,6 +1,6 @@
/****************************************************************************
**
-** Copyright (C) 2016 The Qt Company Ltd.
+** Copyright (C) 2021 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the QtCore module of the Qt Toolkit.
@@ -41,11 +41,13 @@
#ifndef QT_NO_STANDARDPATHS
-#include <QtCore/private/qjni_p.h>
-#include <QtCore/private/qjnihelpers_p.h>
+#include <QtCore/qjniobject.h>
#include <QtCore/qmap.h>
+#include <QtCore/qcoreapplication.h>
#include <QDir>
+using namespace QNativeInterface;
+
QT_BEGIN_NAMESPACE
typedef QMap<QString, QString> AndroidDirCache;
@@ -57,28 +59,10 @@ static QString testDir()
: QLatin1String("");
}
-static QJNIObjectPrivate applicationContext()
-{
- static QJNIObjectPrivate appCtx;
- if (appCtx.isValid())
- return appCtx;
-
- QJNIObjectPrivate context(QtAndroidPrivate::activity());
- if (!context.isValid()) {
- context = QtAndroidPrivate::service();
- if (!context.isValid())
- return appCtx;
- }
-
- appCtx = context.callObjectMethod("getApplicationContext",
- "()Landroid/content/Context;");
- return appCtx;
-}
-
-static inline QString getAbsolutePath(const QJNIObjectPrivate &file)
+static inline QString getAbsolutePath(const QJniObject &file)
{
- QJNIObjectPrivate path = file.callObjectMethod("getAbsolutePath",
- "()Ljava/lang/String;");
+ QJniObject path = file.callObjectMethod("getAbsolutePath",
+ "()Ljava/lang/String;");
if (!path.isValid())
return QString();
@@ -95,22 +79,22 @@ static QString getExternalFilesDir(const char *directoryField = nullptr)
if (!path.isEmpty())
return path;
- QJNIObjectPrivate appCtx = applicationContext();
+ QJniObject appCtx = QAndroidApplication::context();
if (!appCtx.isValid())
return QString();
- QJNIObjectPrivate dirField = QJNIObjectPrivate::fromString(QLatin1String(""));
+ QJniObject dirField = QJniObject::fromString(QLatin1String(""));
if (directoryField && strlen(directoryField) > 0) {
- dirField = QJNIObjectPrivate::getStaticObjectField("android/os/Environment",
- directoryField,
- "Ljava/lang/String;");
+ dirField = QJniObject::getStaticObjectField("android/os/Environment",
+ directoryField,
+ "Ljava/lang/String;");
if (!dirField.isValid())
return QString();
}
- QJNIObjectPrivate file = appCtx.callObjectMethod("getExternalFilesDir",
- "(Ljava/lang/String;)Ljava/io/File;",
- dirField.object());
+ QJniObject file = appCtx.callObjectMethod("getExternalFilesDir",
+ "(Ljava/lang/String;)Ljava/io/File;",
+ dirField.object());
if (!file.isValid())
return QString();
@@ -128,12 +112,12 @@ static QString getExternalCacheDir()
if (!path.isEmpty())
return path;
- QJNIObjectPrivate appCtx = applicationContext();
+ QJniObject appCtx = QAndroidApplication::context();
if (!appCtx.isValid())
return QString();
- QJNIObjectPrivate file = appCtx.callObjectMethod("getExternalCacheDir",
- "()Ljava/io/File;");
+ QJniObject file = appCtx.callObjectMethod("getExternalCacheDir",
+ "()Ljava/io/File;");
if (!file.isValid())
return QString();
@@ -150,12 +134,12 @@ static QString getCacheDir()
if (!path.isEmpty())
return path;
- QJNIObjectPrivate appCtx = applicationContext();
+ QJniObject appCtx = QAndroidApplication::context();
if (!appCtx.isValid())
return QString();
- QJNIObjectPrivate file = appCtx.callObjectMethod("getCacheDir",
- "()Ljava/io/File;");
+ QJniObject file = appCtx.callObjectMethod("getCacheDir",
+ "()Ljava/io/File;");
if (!file.isValid())
return QString();
@@ -172,12 +156,12 @@ static QString getFilesDir()
if (!path.isEmpty())
return path;
- QJNIObjectPrivate appCtx = applicationContext();
+ QJniObject appCtx = QAndroidApplication::context();
if (!appCtx.isValid())
return QString();
- QJNIObjectPrivate file = appCtx.callObjectMethod("getFilesDir",
- "()Ljava/io/File;");
+ QJniObject file = appCtx.callObjectMethod("getFilesDir",
+ "()Ljava/io/File;");
if (!file.isValid())
return QString();
diff --git a/src/corelib/io/qstandardpaths_mac.mm b/src/corelib/io/qstandardpaths_mac.mm
index 11b5cc8c37..a95e6a3bb1 100644
--- a/src/corelib/io/qstandardpaths_mac.mm
+++ b/src/corelib/io/qstandardpaths_mac.mm
@@ -230,8 +230,10 @@ QString QStandardPaths::displayName(StandardLocation type)
// The temporary directory returned by the old Carbon APIs is ~/Library/Caches/TemporaryItems,
// the display name of which ("TemporaryItems") isn't translated by the system. The standard
// temporary directory has no reasonable display name either, so use something more sensible.
- if (QStandardPaths::TempLocation == type)
+ if (QStandardPaths::TempLocation == type) {
+ //: macOS: Temporary directory
return QCoreApplication::translate("QStandardPaths", "Temporary Items");
+ }
// standardLocations() may return an empty list on some platforms
if (QStandardPaths::ApplicationsLocation == type)
diff --git a/src/corelib/io/qurl.h b/src/corelib/io/qurl.h
index a78fbfcd3f..f777a1a629 100644
--- a/src/corelib/io/qurl.h
+++ b/src/corelib/io/qurl.h
@@ -130,7 +130,7 @@ public:
};
// encoding / toString values
- enum UrlFormattingOption {
+ enum UrlFormattingOption : unsigned int {
None = 0x0,
RemoveScheme = 0x1,
RemovePassword = 0x2,
@@ -147,7 +147,7 @@ public:
NormalizePathSegments = 0x1000
};
- enum ComponentFormattingOption {
+ enum ComponentFormattingOption : unsigned int {
PrettyDecoded = 0x000000,
EncodeSpaces = 0x100000,
EncodeUnicode = 0x200000,
diff --git a/src/corelib/io/qwindowspipereader.cpp b/src/corelib/io/qwindowspipereader.cpp
index b525e88282..b8ea89b8e3 100644
--- a/src/corelib/io/qwindowspipereader.cpp
+++ b/src/corelib/io/qwindowspipereader.cpp
@@ -1,6 +1,7 @@
/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
+** Copyright (C) 2021 Alex Trotsenko <alex1973tr@gmail.com>
** Contact: https://www.qt.io/licensing/
**
** This file is part of the QtCore module of the Qt Toolkit.
@@ -38,58 +39,63 @@
****************************************************************************/
#include "qwindowspipereader_p.h"
-#include "qiodevice_p.h"
-#include <qelapsedtimer.h>
#include <qscopedvaluerollback.h>
+#include <qcoreapplication.h>
+#include <QMutexLocker>
QT_BEGIN_NAMESPACE
static const DWORD minReadBufferSize = 4096;
-QWindowsPipeReader::Overlapped::Overlapped(QWindowsPipeReader *reader)
- : pipeReader(reader)
-{
-}
-
-void QWindowsPipeReader::Overlapped::clear()
-{
- ZeroMemory(this, sizeof(OVERLAPPED));
-}
-
-
QWindowsPipeReader::QWindowsPipeReader(QObject *parent)
: QObject(parent),
handle(INVALID_HANDLE_VALUE),
- overlapped(this),
+ eventHandle(CreateEvent(NULL, FALSE, FALSE, NULL)),
+ syncHandle(CreateEvent(NULL, TRUE, FALSE, NULL)),
+ waitObject(NULL),
readBufferMaxSize(0),
actualReadBufferSize(0),
- bytesPending(0),
+ pendingReadBytes(0),
+ lastError(ERROR_SUCCESS),
state(Stopped),
readSequenceStarted(false),
- notifiedCalled(false),
pipeBroken(false),
readyReadPending(false),
+ winEventActPosted(false),
inReadyRead(false)
{
- connect(this, &QWindowsPipeReader::_q_queueReadyRead,
- this, &QWindowsPipeReader::emitPendingReadyRead, Qt::QueuedConnection);
+ ZeroMemory(&overlapped, sizeof(OVERLAPPED));
+ overlapped.hEvent = eventHandle;
+ waitObject = CreateThreadpoolWait(waitCallback, this, NULL);
+ if (waitObject == NULL)
+ qErrnoWarning("QWindowsPipeReader: CreateThreadpollWait failed.");
}
QWindowsPipeReader::~QWindowsPipeReader()
{
stop();
+
+ // Wait for thread pool callback to complete, as it can be still
+ // executing some completion code.
+ WaitForThreadpoolWaitCallbacks(waitObject, FALSE);
+ CloseThreadpoolWait(waitObject);
+ CloseHandle(eventHandle);
+ CloseHandle(syncHandle);
}
/*!
Sets the handle to read from. The handle must be valid.
+ Do not call this function while the pipe is running.
*/
void QWindowsPipeReader::setHandle(HANDLE hPipeReadEnd)
{
readBuffer.clear();
actualReadBufferSize = 0;
- bytesPending = 0;
+ readyReadPending = false;
+ pendingReadBytes = 0;
handle = hPipeReadEnd;
pipeBroken = false;
+ lastError = ERROR_SUCCESS;
}
/*!
@@ -98,8 +104,7 @@ void QWindowsPipeReader::setHandle(HANDLE hPipeReadEnd)
*/
void QWindowsPipeReader::stop()
{
- state = Stopped;
- cancelAsyncRead();
+ cancelAsyncRead(Stopped);
}
/*!
@@ -108,16 +113,27 @@ void QWindowsPipeReader::stop()
*/
void QWindowsPipeReader::drainAndStop()
{
- state = Draining;
- cancelAsyncRead();
+ cancelAsyncRead(Draining);
+
+ // Note that signals are not emitted in the call below, as the caller
+ // is expected to do that synchronously.
+ consumePending();
}
/*!
Stops the asynchronous read sequence.
*/
-void QWindowsPipeReader::cancelAsyncRead()
+void QWindowsPipeReader::cancelAsyncRead(State newState)
{
+ if (state != Running)
+ return;
+
+ QMutexLocker locker(&mutex);
+ state = newState;
if (readSequenceStarted) {
+ // This can legitimately fail due to the GetOverlappedResult()
+ // in the callback not being locked. We ignore ERROR_NOT_FOUND
+ // in this case.
if (!CancelIoEx(handle, &overlapped)) {
const DWORD dwError = GetLastError();
if (dwError != ERROR_NOT_FOUND) {
@@ -125,11 +141,37 @@ void QWindowsPipeReader::cancelAsyncRead()
handle);
}
}
- waitForNotification(-1);
+
+ // Wait for callback to complete.
+ do {
+ locker.unlock();
+ waitForNotification(QDeadlineTimer(-1));
+ locker.relock();
+ } while (readSequenceStarted);
}
}
/*!
+ Sets the size of internal read buffer.
+ */
+void QWindowsPipeReader::setMaxReadBufferSize(qint64 size)
+{
+ QMutexLocker locker(&mutex);
+ readBufferMaxSize = size;
+}
+
+/*!
+ Returns \c true if async operation is in progress, there is
+ pending data to read, or a read error is pending.
+*/
+bool QWindowsPipeReader::isReadOperationActive() const
+{
+ QMutexLocker locker(&mutex);
+ return readSequenceStarted || readyReadPending
+ || (lastError != ERROR_SUCCESS && !pipeBroken);
+}
+
+/*!
Returns the number of bytes we've read so far.
*/
qint64 QWindowsPipeReader::bytesAvailable() const
@@ -145,6 +187,7 @@ qint64 QWindowsPipeReader::read(char *data, qint64 maxlen)
if (pipeBroken && actualReadBufferSize == 0)
return 0; // signal EOF
+ mutex.lock();
qint64 readSoFar;
// If startAsyncRead() has read data, copy it to its destination.
if (maxlen == 1 && actualReadBufferSize > 0) {
@@ -155,6 +198,7 @@ qint64 QWindowsPipeReader::read(char *data, qint64 maxlen)
readSoFar = readBuffer.read(data, qMin(actualReadBufferSize, maxlen));
actualReadBufferSize -= readSoFar;
}
+ mutex.unlock();
if (!pipeBroken) {
if (state == Running)
@@ -166,197 +210,276 @@ qint64 QWindowsPipeReader::read(char *data, qint64 maxlen)
return readSoFar;
}
+/*!
+ Returns \c true if a complete line of data can be read from the buffer.
+ */
bool QWindowsPipeReader::canReadLine() const
{
+ QMutexLocker locker(&mutex);
return readBuffer.indexOf('\n', actualReadBufferSize) >= 0;
}
/*!
- \internal
- Will be called whenever the read operation completes.
+ Starts an asynchronous read sequence on the pipe.
*/
-void QWindowsPipeReader::notified(DWORD errorCode, DWORD numberOfBytesRead)
+void QWindowsPipeReader::startAsyncRead()
{
- notifiedCalled = true;
- readSequenceStarted = false;
-
- switch (errorCode) {
- case ERROR_SUCCESS:
- break;
- case ERROR_MORE_DATA:
- // This is not an error. We're connected to a message mode
- // pipe and the message didn't fit into the pipe's system
- // buffer. We will read the remaining data in the next call.
- break;
- case ERROR_BROKEN_PIPE:
- case ERROR_PIPE_NOT_CONNECTED:
- pipeBroken = true;
- break;
- case ERROR_OPERATION_ABORTED:
- if (state != Running)
- break;
- Q_FALLTHROUGH();
- default:
- emit winError(errorCode, QLatin1String("QWindowsPipeReader::notified"));
- pipeBroken = true;
- break;
- }
+ QMutexLocker locker(&mutex);
- // After the reader was stopped, the only reason why this function can be called is the
- // completion of a cancellation. No signals should be emitted, and no new read sequence should
- // be started in this case.
- if (state == Stopped)
+ if (readSequenceStarted || lastError != ERROR_SUCCESS)
return;
- if (pipeBroken) {
- emitPipeClosed();
+ state = Running;
+ startAsyncReadLocked();
+
+ // Do not post the event, if the read operation will be completed asynchronously.
+ if (!readyReadPending && lastError == ERROR_SUCCESS)
return;
- }
- actualReadBufferSize += numberOfBytesRead;
- readBuffer.truncate(actualReadBufferSize);
+ if (!winEventActPosted) {
+ winEventActPosted = true;
+ locker.unlock();
+ QCoreApplication::postEvent(this, new QEvent(QEvent::WinEventAct));
+ } else {
+ locker.unlock();
+ }
- // Read all pending data from the pipe's buffer in 'Draining' state.
- if (state == Draining) {
- // Determine the number of pending bytes on the first iteration.
- if (bytesPending == 0)
- bytesPending = checkPipeState();
- else
- bytesPending -= numberOfBytesRead;
+ SetEvent(syncHandle);
+}
- if (bytesPending == 0) // all data received
- return; // unblock waitForNotification() in cancelAsyncRead()
+/*!
+ Starts a new read sequence. Thread-safety should be ensured
+ by the caller.
+ */
+void QWindowsPipeReader::startAsyncReadLocked()
+{
+ // Determine the number of bytes to read.
+ qint64 bytesToRead = qMax(checkPipeState(), state == Running ? minReadBufferSize : 0);
- startAsyncReadHelper(bytesPending);
- if (readSequenceStarted)
- notifiedCalled = false; // wait for more data
+ // This can happen only while draining; just do nothing in this case.
+ if (bytesToRead == 0)
return;
- }
- startAsyncRead();
- if (!readyReadPending) {
- readyReadPending = true;
- emit _q_queueReadyRead(QWindowsPipeReader::QPrivateSignal());
+ while (lastError == ERROR_SUCCESS) {
+ if (readBufferMaxSize && bytesToRead > (readBufferMaxSize - readBuffer.size())) {
+ bytesToRead = readBufferMaxSize - readBuffer.size();
+ if (bytesToRead <= 0) {
+ // Buffer is full. User must read data from the buffer
+ // before we can read more from the pipe.
+ return;
+ }
+ }
+
+ char *ptr = readBuffer.reserve(bytesToRead);
+
+ // ReadFile() returns true, if the read operation completes synchronously.
+ // We don't need to call GetOverlappedResult() additionally, because
+ // 'numberOfBytesRead' is valid in this case.
+ DWORD numberOfBytesRead;
+ DWORD errorCode = ERROR_SUCCESS;
+ if (!ReadFile(handle, ptr, bytesToRead, &numberOfBytesRead, &overlapped)) {
+ errorCode = GetLastError();
+ if (errorCode == ERROR_IO_PENDING) {
+ Q_ASSERT(state == Running);
+ // Operation has been queued and will complete in the future.
+ readSequenceStarted = true;
+ SetThreadpoolWait(waitObject, eventHandle, NULL);
+ return;
+ }
+ }
+
+ if (!readCompleted(errorCode, numberOfBytesRead))
+ return;
+
+ // In the 'Draining' state, we have to get all the data with one call
+ // to ReadFile(). Note that message mode pipes are not supported here.
+ if (state == Draining) {
+ Q_ASSERT(bytesToRead == qint64(numberOfBytesRead));
+ return;
+ }
+
+ // We need to loop until all pending data has been read and an
+ // operation is queued for asynchronous completion.
+ // If the pipe is configured to work in message mode, we read
+ // the data in chunks.
+ bytesToRead = qMax(checkPipeState(), minReadBufferSize);
}
}
/*!
\internal
- Starts an asynchronous read sequence on the pipe.
+
+ Thread pool callback procedure.
*/
-void QWindowsPipeReader::startAsyncRead()
+void QWindowsPipeReader::waitCallback(PTP_CALLBACK_INSTANCE instance, PVOID context,
+ PTP_WAIT wait, TP_WAIT_RESULT waitResult)
{
- if (readSequenceStarted)
- return;
+ Q_UNUSED(instance);
+ Q_UNUSED(wait);
+ Q_UNUSED(waitResult);
+ QWindowsPipeReader *pipeReader = reinterpret_cast<QWindowsPipeReader *>(context);
+
+ // Get the result of the asynchronous operation.
+ DWORD numberOfBytesTransfered = 0;
+ DWORD errorCode = ERROR_SUCCESS;
+ if (!GetOverlappedResult(pipeReader->handle, &pipeReader->overlapped,
+ &numberOfBytesTransfered, FALSE))
+ errorCode = GetLastError();
+
+ pipeReader->mutex.lock();
+
+ pipeReader->readSequenceStarted = false;
+
+ // Do not overwrite error code, if error has been detected by
+ // checkPipeState() in waitForPipeClosed(). Also, if the reader was
+ // stopped, the only reason why this function can be called is the
+ // completion of a cancellation. No signals should be emitted, and
+ // no new read sequence should be started in this case.
+ if (pipeReader->lastError == ERROR_SUCCESS && pipeReader->state != Stopped) {
+ // Ignore ERROR_OPERATION_ABORTED. We have canceled the I/O operation
+ // specifically for flushing the pipe.
+ if (pipeReader->state == Draining && errorCode == ERROR_OPERATION_ABORTED)
+ errorCode = ERROR_SUCCESS;
+
+ if (pipeReader->readCompleted(errorCode, numberOfBytesTransfered))
+ pipeReader->startAsyncReadLocked();
+
+ if (pipeReader->state == Running && !pipeReader->winEventActPosted) {
+ pipeReader->winEventActPosted = true;
+ pipeReader->mutex.unlock();
+ QCoreApplication::postEvent(pipeReader, new QEvent(QEvent::WinEventAct));
+ } else {
+ pipeReader->mutex.unlock();
+ }
+ } else {
+ pipeReader->mutex.unlock();
+ }
- state = Running;
- startAsyncReadHelper(qMax(checkPipeState(), minReadBufferSize));
+ // We set the event only after unlocking to avoid additional context
+ // switches due to the released thread immediately running into the lock.
+ SetEvent(pipeReader->syncHandle);
}
/*!
- \internal
- Starts a new read sequence.
+ Will be called whenever the read operation completes. Returns \c true if
+ no error occurred; otherwise returns \c false.
*/
-void QWindowsPipeReader::startAsyncReadHelper(qint64 bytesToRead)
+bool QWindowsPipeReader::readCompleted(DWORD errorCode, DWORD numberOfBytesRead)
{
- Q_ASSERT(bytesToRead != 0);
+ // ERROR_MORE_DATA is not an error. We're connected to a message mode
+ // pipe and the message didn't fit into the pipe's system
+ // buffer. We will read the remaining data in the next call.
+ if (errorCode == ERROR_SUCCESS || errorCode == ERROR_MORE_DATA) {
+ readyReadPending = true;
+ pendingReadBytes += numberOfBytesRead;
+ readBuffer.truncate(actualReadBufferSize + pendingReadBytes);
+ return true;
+ }
- if (pipeBroken)
- return;
+ lastError = errorCode;
+ return false;
+}
- if (readBufferMaxSize && bytesToRead > (readBufferMaxSize - readBuffer.size())) {
- bytesToRead = readBufferMaxSize - readBuffer.size();
- if (bytesToRead <= 0) {
- // Buffer is full. User must read data from the buffer
- // before we can read more from the pipe.
- return;
- }
+/*!
+ Receives notification that the read operation has completed.
+ */
+bool QWindowsPipeReader::event(QEvent *e)
+{
+ if (e->type() == QEvent::WinEventAct) {
+ consumePendingAndEmit(true);
+ return true;
}
+ return QObject::event(e);
+}
- char *ptr = readBuffer.reserve(bytesToRead);
-
- readSequenceStarted = true;
- overlapped.clear();
- if (!ReadFileEx(handle, ptr, bytesToRead, &overlapped, &readFileCompleted)) {
- readSequenceStarted = false;
-
- const DWORD dwError = GetLastError();
- switch (dwError) {
- case ERROR_BROKEN_PIPE:
- case ERROR_PIPE_NOT_CONNECTED:
- // It may happen, that the other side closes the connection directly
- // after writing data. Then we must set the appropriate socket state.
- pipeBroken = true;
- emit pipeClosed();
- break;
- default:
- emit winError(dwError, QLatin1String("QWindowsPipeReader::startAsyncRead"));
- break;
- }
+/*!
+ Updates the read buffer size and emits pending signals in the main thread.
+ Returns \c true, if readyRead() was emitted.
+ */
+bool QWindowsPipeReader::consumePendingAndEmit(bool allowWinActPosting)
+{
+ ResetEvent(syncHandle);
+ mutex.lock();
+
+ // Enable QEvent::WinEventAct posting.
+ if (allowWinActPosting)
+ winEventActPosted = false;
+
+ const bool emitReadyRead = consumePending();
+ const DWORD dwError = lastError;
+
+ mutex.unlock();
+
+ // Trigger 'pipeBroken' only once. This flag must be updated before
+ // emitting the readyRead() signal. Otherwise, the read sequence will
+ // be considered not finished, and we may hang if a slot connected
+ // to readyRead() calls waitForReadyRead().
+ const bool emitPipeClosed = (dwError != ERROR_SUCCESS && !pipeBroken);
+ if (emitPipeClosed)
+ pipeBroken = true;
+
+ // Disable any further processing, if the pipe was stopped.
+ // We are not allowed to emit signals in either 'Stopped'
+ // or 'Draining' state.
+ if (state != Running)
+ return false;
+
+ if (emitReadyRead && !inReadyRead) {
+ QScopedValueRollback<bool> guard(inReadyRead, true);
+ emit readyRead();
+ }
+ if (emitPipeClosed) {
+ if (dwError != ERROR_BROKEN_PIPE && dwError != ERROR_PIPE_NOT_CONNECTED)
+ emit winError(dwError, QLatin1String("QWindowsPipeReader::consumePendingAndEmit"));
+ emit pipeClosed();
}
+
+ return emitReadyRead;
}
/*!
- \internal
- Called when ReadFileEx finished the read operation.
+ Updates the read buffer size. Returns \c true, if readyRead()
+ should be emitted. Thread-safety should be ensured by the caller.
*/
-void QWindowsPipeReader::readFileCompleted(DWORD errorCode, DWORD numberOfBytesTransfered,
- OVERLAPPED *overlappedBase)
+bool QWindowsPipeReader::consumePending()
{
- Overlapped *overlapped = static_cast<Overlapped *>(overlappedBase);
- overlapped->pipeReader->notified(errorCode, numberOfBytesTransfered);
+ if (readyReadPending) {
+ readyReadPending = false;
+ actualReadBufferSize += pendingReadBytes;
+ pendingReadBytes = 0;
+ return true;
+ }
+
+ return false;
}
/*!
- \internal
Returns the number of available bytes in the pipe.
- Sets QWindowsPipeReader::pipeBroken to true if the connection is broken.
*/
DWORD QWindowsPipeReader::checkPipeState()
{
DWORD bytes;
if (PeekNamedPipe(handle, nullptr, 0, nullptr, &bytes, nullptr))
return bytes;
- if (!pipeBroken) {
- pipeBroken = true;
- emitPipeClosed();
- }
+
+ lastError = GetLastError();
return 0;
}
-bool QWindowsPipeReader::waitForNotification(int timeout)
+bool QWindowsPipeReader::waitForNotification(const QDeadlineTimer &deadline)
{
- QElapsedTimer t;
- t.start();
- notifiedCalled = false;
- int msecs = timeout;
- while (SleepEx(msecs == -1 ? INFINITE : msecs, TRUE) == WAIT_IO_COMPLETION) {
- if (notifiedCalled)
+ do {
+ DWORD waitRet = WaitForSingleObjectEx(syncHandle, deadline.remainingTime(), TRUE);
+ if (waitRet == WAIT_OBJECT_0)
return true;
- // Some other I/O completion routine was called. Wait some more.
- msecs = qt_subtract_from_timeout(timeout, t.elapsed());
- if (!msecs)
- break;
- }
- return notifiedCalled;
-}
+ if (waitRet != WAIT_IO_COMPLETION)
+ return false;
-void QWindowsPipeReader::emitPendingReadyRead()
-{
- if (readyReadPending) {
- readyReadPending = false;
- QScopedValueRollback<bool> guard(inReadyRead, true);
- emit readyRead();
- }
-}
+ // Some I/O completion routine was called. Wait some more.
+ } while (!deadline.hasExpired());
-void QWindowsPipeReader::emitPipeClosed()
-{
- // We are not allowed to emit signals in either 'Stopped'
- // or 'Draining' state.
- if (state == Running)
- emit pipeClosed();
+ return false;
}
/*!
@@ -366,22 +489,12 @@ void QWindowsPipeReader::emitPipeClosed()
*/
bool QWindowsPipeReader::waitForReadyRead(int msecs)
{
- if (readyReadPending) {
- if (!inReadyRead)
- emitPendingReadyRead();
- return true;
- }
+ QDeadlineTimer timer(msecs);
- if (!readSequenceStarted)
- return false;
-
- if (!waitForNotification(msecs))
- return false;
-
- if (readyReadPending) {
- if (!inReadyRead)
- emitPendingReadyRead();
- return true;
+ // Make sure that 'syncHandle' was triggered by the thread pool callback.
+ while (isReadOperationActive() && waitForNotification(timer)) {
+ if (consumePendingAndEmit(false))
+ return true;
}
return false;
@@ -393,15 +506,26 @@ bool QWindowsPipeReader::waitForReadyRead(int msecs)
bool QWindowsPipeReader::waitForPipeClosed(int msecs)
{
const int sleepTime = 10;
- QElapsedTimer stopWatch;
- stopWatch.start();
+ QDeadlineTimer timer(msecs);
+
+ while (waitForReadyRead(timer.remainingTime())) {}
+ if (pipeBroken)
+ return true;
+
+ if (timer.hasExpired())
+ return false;
+
+ // When the read buffer is full, the read sequence is not running,
+ // so we need to peek the pipe to detect disconnection.
forever {
- waitForReadyRead(0);
checkPipeState();
+ consumePendingAndEmit(false);
if (pipeBroken)
return true;
- if (stopWatch.hasExpired(msecs - sleepTime))
+
+ if (timer.hasExpired())
return false;
+
Sleep(sleepTime);
}
}
diff --git a/src/corelib/io/qwindowspipereader_p.h b/src/corelib/io/qwindowspipereader_p.h
index c61018d87d..8ae292da46 100644
--- a/src/corelib/io/qwindowspipereader_p.h
+++ b/src/corelib/io/qwindowspipereader_p.h
@@ -1,6 +1,7 @@
/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
+** Copyright (C) 2021 Alex Trotsenko <alex1973tr@gmail.com>
** Contact: https://www.qt.io/licensing/
**
** This file is part of the QtCore module of the Qt Toolkit.
@@ -52,6 +53,8 @@
//
#include <qobject.h>
+#include <qdeadlinetimer.h>
+#include <qmutex.h>
#include <private/qringbuffer_p.h>
#include <qt_windows.h>
@@ -70,7 +73,7 @@ public:
void stop();
void drainAndStop();
- void setMaxReadBufferSize(qint64 size) { readBufferMaxSize = size; }
+ void setMaxReadBufferSize(qint64 size);
qint64 maxReadBufferSize() const { return readBufferMaxSize; }
bool isPipeClosed() const { return pipeBroken; }
@@ -78,48 +81,50 @@ public:
qint64 read(char *data, qint64 maxlen);
bool canReadLine() const;
bool waitForReadyRead(int msecs);
+ bool checkForReadyRead() { return consumePendingAndEmit(false); }
bool waitForPipeClosed(int msecs);
- bool isReadOperationActive() const { return readSequenceStarted; }
+ bool isReadOperationActive() const;
+ HANDLE syncEvent() const { return syncHandle; }
Q_SIGNALS:
void winError(ulong, const QString &);
void readyRead();
void pipeClosed();
- void _q_queueReadyRead(QPrivateSignal);
+
+protected:
+ bool event(QEvent *e) override;
private:
- void startAsyncReadHelper(qint64 bytesToRead);
- void cancelAsyncRead();
- static void CALLBACK readFileCompleted(DWORD errorCode, DWORD numberOfBytesTransfered,
- OVERLAPPED *overlappedBase);
- void notified(DWORD errorCode, DWORD numberOfBytesRead);
+ enum State { Stopped, Running, Draining };
+
+ void startAsyncReadLocked();
+ void cancelAsyncRead(State newState);
+ static void CALLBACK waitCallback(PTP_CALLBACK_INSTANCE instance, PVOID context,
+ PTP_WAIT wait, TP_WAIT_RESULT waitResult);
+ bool readCompleted(DWORD errorCode, DWORD numberOfBytesRead);
DWORD checkPipeState();
- bool waitForNotification(int timeout);
- void emitPendingReadyRead();
- void emitPipeClosed();
-
- class Overlapped : public OVERLAPPED
- {
- Q_DISABLE_COPY_MOVE(Overlapped)
- public:
- explicit Overlapped(QWindowsPipeReader *reader);
- void clear();
- QWindowsPipeReader *pipeReader;
- };
+ bool waitForNotification(const QDeadlineTimer &deadline);
+ bool consumePendingAndEmit(bool allowWinActPosting);
+ bool consumePending();
HANDLE handle;
- Overlapped overlapped;
+ HANDLE eventHandle;
+ HANDLE syncHandle;
+ PTP_WAIT waitObject;
+ OVERLAPPED overlapped;
qint64 readBufferMaxSize;
QRingBuffer readBuffer;
qint64 actualReadBufferSize;
- qint64 bytesPending;
+ qint64 pendingReadBytes;
+ mutable QMutex mutex;
+ DWORD lastError;
- enum State { Stopped, Running, Draining } state;
+ State state;
bool readSequenceStarted;
- bool notifiedCalled;
bool pipeBroken;
bool readyReadPending;
+ bool winEventActPosted;
bool inReadyRead;
};
diff --git a/src/corelib/io/qwindowspipewriter.cpp b/src/corelib/io/qwindowspipewriter.cpp
index e374034a06..4b075549d0 100644
--- a/src/corelib/io/qwindowspipewriter.cpp
+++ b/src/corelib/io/qwindowspipewriter.cpp
@@ -1,6 +1,7 @@
/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
+** Copyright (C) 2021 Alex Trotsenko <alex1973tr@gmail.com>
** Contact: https://www.qt.io/licensing/
**
** This file is part of the QtCore module of the Qt Toolkit.
@@ -38,189 +39,298 @@
****************************************************************************/
#include "qwindowspipewriter_p.h"
-#include "qiodevice_p.h"
#include <qscopedvaluerollback.h>
+#include <qcoreapplication.h>
+#include <QMutexLocker>
QT_BEGIN_NAMESPACE
-QWindowsPipeWriter::Overlapped::Overlapped(QWindowsPipeWriter *pipeWriter)
- : pipeWriter(pipeWriter)
-{
-}
-
-void QWindowsPipeWriter::Overlapped::clear()
-{
- ZeroMemory(this, sizeof(OVERLAPPED));
-}
-
-
QWindowsPipeWriter::QWindowsPipeWriter(HANDLE pipeWriteEnd, QObject *parent)
: QObject(parent),
handle(pipeWriteEnd),
- overlapped(this),
+ eventHandle(CreateEvent(NULL, FALSE, FALSE, NULL)),
+ syncHandle(CreateEvent(NULL, TRUE, FALSE, NULL)),
+ waitObject(NULL),
pendingBytesWrittenValue(0),
+ lastError(ERROR_SUCCESS),
stopped(true),
writeSequenceStarted(false),
- notifiedCalled(false),
bytesWrittenPending(false),
+ winEventActPosted(false),
inBytesWritten(false)
{
- connect(this, &QWindowsPipeWriter::_q_queueBytesWritten,
- this, &QWindowsPipeWriter::emitPendingBytesWrittenValue, Qt::QueuedConnection);
+ ZeroMemory(&overlapped, sizeof(OVERLAPPED));
+ overlapped.hEvent = eventHandle;
+ waitObject = CreateThreadpoolWait(waitCallback, this, NULL);
+ if (waitObject == NULL)
+ qErrnoWarning("QWindowsPipeWriter: CreateThreadpollWait failed.");
}
QWindowsPipeWriter::~QWindowsPipeWriter()
{
stop();
+ CloseThreadpoolWait(waitObject);
+ CloseHandle(eventHandle);
+ CloseHandle(syncHandle);
}
-bool QWindowsPipeWriter::waitForWrite(int msecs)
+/*!
+ Stops the asynchronous write sequence.
+ If the write sequence is running then the I/O operation is canceled.
+ */
+void QWindowsPipeWriter::stop()
{
- if (bytesWrittenPending) {
- emitPendingBytesWrittenValue();
- return true;
- }
-
- if (!writeSequenceStarted)
- return false;
-
- if (!waitForNotification(msecs))
- return false;
+ if (stopped)
+ return;
- if (bytesWrittenPending) {
- emitPendingBytesWrittenValue();
- return true;
+ mutex.lock();
+ stopped = true;
+ if (writeSequenceStarted) {
+ // Trying to disable callback before canceling the operation.
+ // Callback invocation is unnecessary here.
+ SetThreadpoolWait(waitObject, NULL, NULL);
+ if (!CancelIoEx(handle, &overlapped)) {
+ const DWORD dwError = GetLastError();
+ if (dwError != ERROR_NOT_FOUND) {
+ qErrnoWarning(dwError, "QWindowsPipeWriter: CancelIoEx on handle %p failed.",
+ handle);
+ }
+ }
+ writeSequenceStarted = false;
}
+ mutex.unlock();
- return false;
+ WaitForThreadpoolWaitCallbacks(waitObject, TRUE);
}
+/*!
+ Returns \c true if async operation is in progress or a bytesWritten
+ signal is pending.
+ */
+bool QWindowsPipeWriter::isWriteOperationActive() const
+{
+ QMutexLocker locker(&mutex);
+ return writeSequenceStarted || bytesWrittenPending;
+}
+
+/*!
+ Returns the number of bytes that are waiting to be written.
+ */
qint64 QWindowsPipeWriter::bytesToWrite() const
{
- return buffer.size() + pendingBytesWrittenValue;
+ QMutexLocker locker(&mutex);
+ return writeBuffer.size() + pendingBytesWrittenValue;
}
-void QWindowsPipeWriter::emitPendingBytesWrittenValue()
+/*!
+ Writes data to the pipe.
+ */
+bool QWindowsPipeWriter::write(const QByteArray &ba)
{
- if (bytesWrittenPending) {
- // Reset the state even if we don't emit bytesWritten().
- // It's a defined behavior to not re-emit this signal recursively.
- bytesWrittenPending = false;
- const qint64 bytes = pendingBytesWrittenValue;
- pendingBytesWrittenValue = 0;
-
- emit canWrite();
- if (!inBytesWritten) {
- QScopedValueRollback<bool> guard(inBytesWritten, true);
- emit bytesWritten(bytes);
- }
+ QMutexLocker locker(&mutex);
+
+ if (lastError != ERROR_SUCCESS)
+ return false;
+
+ writeBuffer.append(ba);
+ if (writeSequenceStarted)
+ return true;
+
+ stopped = false;
+ startAsyncWriteLocked();
+
+ // Do not post the event, if the write operation will be completed asynchronously.
+ if (!bytesWrittenPending)
+ return true;
+
+ if (!winEventActPosted) {
+ winEventActPosted = true;
+ locker.unlock();
+ QCoreApplication::postEvent(this, new QEvent(QEvent::WinEventAct));
+ } else {
+ locker.unlock();
}
+
+ SetEvent(syncHandle);
+ return true;
}
-void QWindowsPipeWriter::writeFileCompleted(DWORD errorCode, DWORD numberOfBytesTransfered,
- OVERLAPPED *overlappedBase)
+/*!
+ Starts a new write sequence. Thread-safety should be ensured by the caller.
+ */
+void QWindowsPipeWriter::startAsyncWriteLocked()
{
- Overlapped *overlapped = static_cast<Overlapped *>(overlappedBase);
- overlapped->pipeWriter->notified(errorCode, numberOfBytesTransfered);
+ while (!writeBuffer.isEmpty()) {
+ // WriteFile() returns true, if the write operation completes synchronously.
+ // We don't need to call GetOverlappedResult() additionally, because
+ // 'numberOfBytesWritten' is valid in this case.
+ DWORD numberOfBytesWritten;
+ DWORD errorCode = ERROR_SUCCESS;
+ if (!WriteFile(handle, writeBuffer.readPointer(), writeBuffer.nextDataBlockSize(),
+ &numberOfBytesWritten, &overlapped)) {
+ errorCode = GetLastError();
+ if (errorCode == ERROR_IO_PENDING) {
+ // Operation has been queued and will complete in the future.
+ writeSequenceStarted = true;
+ SetThreadpoolWait(waitObject, eventHandle, NULL);
+ return;
+ }
+ }
+
+ if (!writeCompleted(errorCode, numberOfBytesWritten))
+ return;
+ }
}
/*!
\internal
- Will be called whenever the write operation completes.
+ Thread pool callback procedure.
*/
-void QWindowsPipeWriter::notified(DWORD errorCode, DWORD numberOfBytesWritten)
+void QWindowsPipeWriter::waitCallback(PTP_CALLBACK_INSTANCE instance, PVOID context,
+ PTP_WAIT wait, TP_WAIT_RESULT waitResult)
{
- notifiedCalled = true;
- writeSequenceStarted = false;
- Q_ASSERT(errorCode != ERROR_SUCCESS || numberOfBytesWritten == DWORD(buffer.size()));
- buffer.clear();
-
- switch (errorCode) {
- case ERROR_SUCCESS:
- break;
- case ERROR_OPERATION_ABORTED:
- if (stopped)
- break;
- Q_FALLTHROUGH();
- default:
- qErrnoWarning(errorCode, "QWindowsPipeWriter: asynchronous write failed.");
- break;
- }
+ Q_UNUSED(instance);
+ Q_UNUSED(wait);
+ Q_UNUSED(waitResult);
+ QWindowsPipeWriter *pipeWriter = reinterpret_cast<QWindowsPipeWriter *>(context);
+
+ // Get the result of the asynchronous operation.
+ DWORD numberOfBytesTransfered = 0;
+ DWORD errorCode = ERROR_SUCCESS;
+ if (!GetOverlappedResult(pipeWriter->handle, &pipeWriter->overlapped,
+ &numberOfBytesTransfered, FALSE))
+ errorCode = GetLastError();
+
+ QMutexLocker locker(&pipeWriter->mutex);
// After the writer was stopped, the only reason why this function can be called is the
- // completion of a cancellation. No signals should be emitted, and no new write sequence should
- // be started in this case.
- if (stopped)
+ // completion of a cancellation. No signals should be emitted, and no new write sequence
+ // should be started in this case.
+ if (pipeWriter->stopped)
return;
- pendingBytesWrittenValue += qint64(numberOfBytesWritten);
- if (!bytesWrittenPending) {
- bytesWrittenPending = true;
- emit _q_queueBytesWritten(QWindowsPipeWriter::QPrivateSignal());
+ pipeWriter->writeSequenceStarted = false;
+
+ if (pipeWriter->writeCompleted(errorCode, numberOfBytesTransfered))
+ pipeWriter->startAsyncWriteLocked();
+
+ if (pipeWriter->lastError == ERROR_SUCCESS && !pipeWriter->winEventActPosted) {
+ pipeWriter->winEventActPosted = true;
+ locker.unlock();
+ QCoreApplication::postEvent(pipeWriter, new QEvent(QEvent::WinEventAct));
+ } else {
+ locker.unlock();
}
+
+ // We set the event only after unlocking to avoid additional context
+ // switches due to the released thread immediately running into the lock.
+ SetEvent(pipeWriter->syncHandle);
}
-bool QWindowsPipeWriter::waitForNotification(int timeout)
+/*!
+ Will be called whenever the write operation completes. Returns \c true if
+ no error occurred; otherwise returns \c false.
+ */
+bool QWindowsPipeWriter::writeCompleted(DWORD errorCode, DWORD numberOfBytesWritten)
{
- QElapsedTimer t;
- t.start();
- notifiedCalled = false;
- int msecs = timeout;
- while (SleepEx(msecs == -1 ? INFINITE : msecs, TRUE) == WAIT_IO_COMPLETION) {
- if (notifiedCalled)
- return true;
+ if (errorCode == ERROR_SUCCESS) {
+ Q_ASSERT(numberOfBytesWritten == DWORD(writeBuffer.nextDataBlockSize()));
- // Some other I/O completion routine was called. Wait some more.
- msecs = qt_subtract_from_timeout(timeout, t.elapsed());
- if (!msecs)
- break;
+ bytesWrittenPending = true;
+ pendingBytesWrittenValue += numberOfBytesWritten;
+ writeBuffer.free(numberOfBytesWritten);
+ return true;
}
- return notifiedCalled;
+
+ lastError = errorCode;
+ writeBuffer.clear();
+ // The other end has closed the pipe. This can happen in QLocalSocket. Do not warn.
+ if (errorCode != ERROR_OPERATION_ABORTED && errorCode != ERROR_NO_DATA)
+ qErrnoWarning(errorCode, "QWindowsPipeWriter: write failed.");
+ return false;
}
-bool QWindowsPipeWriter::write(const QByteArray &ba)
+/*!
+ Receives notification that the write operation has completed.
+ */
+bool QWindowsPipeWriter::event(QEvent *e)
{
- if (writeSequenceStarted)
+ if (e->type() == QEvent::WinEventAct) {
+ consumePendingAndEmit(true);
+ return true;
+ }
+ return QObject::event(e);
+}
+
+/*!
+ Updates the state and emits pending signals in the main thread.
+ Returns \c true, if bytesWritten() was emitted.
+ */
+bool QWindowsPipeWriter::consumePendingAndEmit(bool allowWinActPosting)
+{
+ ResetEvent(syncHandle);
+ QMutexLocker locker(&mutex);
+
+ // Enable QEvent::WinEventAct posting.
+ if (allowWinActPosting)
+ winEventActPosted = false;
+
+ if (!bytesWrittenPending)
return false;
- overlapped.clear();
- buffer = ba;
- stopped = false;
- writeSequenceStarted = true;
- if (!WriteFileEx(handle, buffer.constData(), buffer.size(),
- &overlapped, &writeFileCompleted)) {
- writeSequenceStarted = false;
- buffer.clear();
-
- const DWORD errorCode = GetLastError();
- switch (errorCode) {
- case ERROR_NO_DATA: // "The pipe is being closed."
- // The other end has closed the pipe. This can happen in QLocalSocket. Do not warn.
- break;
- default:
- qErrnoWarning(errorCode, "QWindowsPipeWriter::write failed.");
- }
+ // Reset the state even if we don't emit bytesWritten().
+ // It's a defined behavior to not re-emit this signal recursively.
+ bytesWrittenPending = false;
+ qint64 numberOfBytesWritten = pendingBytesWrittenValue;
+ pendingBytesWrittenValue = 0;
+
+ locker.unlock();
+
+ // Disable any further processing, if the pipe was stopped.
+ if (stopped)
return false;
+
+ emit canWrite();
+ if (!inBytesWritten) {
+ QScopedValueRollback<bool> guard(inBytesWritten, true);
+ emit bytesWritten(numberOfBytesWritten);
}
return true;
}
-void QWindowsPipeWriter::stop()
+bool QWindowsPipeWriter::waitForNotification(const QDeadlineTimer &deadline)
{
- stopped = true;
- bytesWrittenPending = false;
- pendingBytesWrittenValue = 0;
- if (writeSequenceStarted) {
- if (!CancelIoEx(handle, &overlapped)) {
- const DWORD dwError = GetLastError();
- if (dwError != ERROR_NOT_FOUND) {
- qErrnoWarning(dwError, "QWindowsPipeWriter: CancelIoEx on handle %p failed.",
- handle);
- }
- }
- waitForNotification(-1);
+ do {
+ DWORD waitRet = WaitForSingleObjectEx(syncHandle, deadline.remainingTime(), TRUE);
+ if (waitRet == WAIT_OBJECT_0)
+ return true;
+
+ if (waitRet != WAIT_IO_COMPLETION)
+ return false;
+
+ // Some I/O completion routine was called. Wait some more.
+ } while (!deadline.hasExpired());
+
+ return false;
+}
+
+/*!
+ Waits for the completion of the asynchronous write operation.
+ Returns \c true, if we've emitted the bytesWritten signal (non-recursive case)
+ or bytesWritten will be emitted by the event loop (recursive case).
+ */
+bool QWindowsPipeWriter::waitForWrite(int msecs)
+{
+ QDeadlineTimer timer(msecs);
+
+ // Make sure that 'syncHandle' was triggered by the thread pool callback.
+ while (isWriteOperationActive() && waitForNotification(timer)) {
+ if (consumePendingAndEmit(false))
+ return true;
}
+
+ return false;
}
QT_END_NAMESPACE
diff --git a/src/corelib/io/qwindowspipewriter_p.h b/src/corelib/io/qwindowspipewriter_p.h
index 39e8ffe40a..d33c2753a8 100644
--- a/src/corelib/io/qwindowspipewriter_p.h
+++ b/src/corelib/io/qwindowspipewriter_p.h
@@ -1,6 +1,7 @@
/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
+** Copyright (C) 2021 Alex Trotsenko <alex1973tr@gmail.com>
** Contact: https://www.qt.io/licensing/
**
** This file is part of the QtCore module of the Qt Toolkit.
@@ -51,62 +52,15 @@
// We mean it.
//
-#include <QtCore/private/qglobal_p.h>
-#include <qelapsedtimer.h>
#include <qobject.h>
-#include <qbytearray.h>
+#include <qdeadlinetimer.h>
+#include <qmutex.h>
+#include <private/qringbuffer_p.h>
+
#include <qt_windows.h>
QT_BEGIN_NAMESPACE
-#define SLEEPMIN 10
-#define SLEEPMAX 500
-
-class QIncrementalSleepTimer
-{
-
-public:
- QIncrementalSleepTimer(int msecs)
- : totalTimeOut(msecs)
- , nextSleep(qMin(SLEEPMIN, totalTimeOut))
- {
- if (totalTimeOut == -1)
- nextSleep = SLEEPMIN;
- timer.start();
- }
-
- int nextSleepTime()
- {
- int tmp = nextSleep;
- nextSleep = qMin(nextSleep * 2, qMin(SLEEPMAX, timeLeft()));
- return tmp;
- }
-
- int timeLeft() const
- {
- if (totalTimeOut == -1)
- return SLEEPMAX;
- return qMax(int(totalTimeOut - timer.elapsed()), 0);
- }
-
- bool hasTimedOut() const
- {
- if (totalTimeOut == -1)
- return false;
- return timer.elapsed() >= totalTimeOut;
- }
-
- void resetIncrements()
- {
- nextSleep = qMin(SLEEPMIN, timeLeft());
- }
-
-private:
- QElapsedTimer timer;
- int totalTimeOut;
- int nextSleep;
-};
-
class Q_CORE_EXPORT QWindowsPipeWriter : public QObject
{
Q_OBJECT
@@ -117,39 +71,39 @@ public:
bool write(const QByteArray &ba);
void stop();
bool waitForWrite(int msecs);
- bool isWriteOperationActive() const { return writeSequenceStarted; }
+ bool checkForWrite() { return consumePendingAndEmit(false); }
+ bool isWriteOperationActive() const;
qint64 bytesToWrite() const;
+ HANDLE syncEvent() const { return syncHandle; }
Q_SIGNALS:
void canWrite();
void bytesWritten(qint64 bytes);
- void _q_queueBytesWritten(QPrivateSignal);
-
-private:
- static void CALLBACK writeFileCompleted(DWORD errorCode, DWORD numberOfBytesTransfered,
- OVERLAPPED *overlappedBase);
- void notified(DWORD errorCode, DWORD numberOfBytesWritten);
- bool waitForNotification(int timeout);
- void emitPendingBytesWrittenValue();
- class Overlapped : public OVERLAPPED
- {
- Q_DISABLE_COPY_MOVE(Overlapped)
- public:
- explicit Overlapped(QWindowsPipeWriter *pipeWriter);
- void clear();
+protected:
+ bool event(QEvent *e) override;
- QWindowsPipeWriter *pipeWriter;
- };
+private:
+ void startAsyncWriteLocked();
+ static void CALLBACK waitCallback(PTP_CALLBACK_INSTANCE instance, PVOID context,
+ PTP_WAIT wait, TP_WAIT_RESULT waitResult);
+ bool writeCompleted(DWORD errorCode, DWORD numberOfBytesWritten);
+ bool waitForNotification(const QDeadlineTimer &deadline);
+ bool consumePendingAndEmit(bool allowWinActPosting);
HANDLE handle;
- Overlapped overlapped;
- QByteArray buffer;
+ HANDLE eventHandle;
+ HANDLE syncHandle;
+ PTP_WAIT waitObject;
+ OVERLAPPED overlapped;
+ QRingBuffer writeBuffer;
qint64 pendingBytesWrittenValue;
+ mutable QMutex mutex;
+ DWORD lastError;
bool stopped;
bool writeSequenceStarted;
- bool notifiedCalled;
bool bytesWrittenPending;
+ bool winEventActPosted;
bool inBytesWritten;
};
diff --git a/src/corelib/itemmodels/qconcatenatetablesproxymodel.cpp b/src/corelib/itemmodels/qconcatenatetablesproxymodel.cpp
index 09deee4429..72ff6c1f36 100644
--- a/src/corelib/itemmodels/qconcatenatetablesproxymodel.cpp
+++ b/src/corelib/itemmodels/qconcatenatetablesproxymodel.cpp
@@ -622,9 +622,14 @@ void QConcatenateTablesProxyModelPrivate::_q_slotDataChanged(const QModelIndex &
Q_Q(QConcatenateTablesProxyModel);
Q_ASSERT(from.isValid());
Q_ASSERT(to.isValid());
+ if (from.column() >= m_columnCount)
+ return;
+ QModelIndex adjustedTo = to;
+ if (to.column() >= m_columnCount)
+ adjustedTo = to.siblingAtColumn(m_columnCount - 1);
const QModelIndex myFrom = q->mapFromSource(from);
Q_ASSERT(q->checkIndex(myFrom, QAbstractItemModel::CheckIndexOption::IndexIsValid));
- const QModelIndex myTo = q->mapFromSource(to);
+ const QModelIndex myTo = q->mapFromSource(adjustedTo);
Q_ASSERT(q->checkIndex(myTo, QAbstractItemModel::CheckIndexOption::IndexIsValid));
emit q->dataChanged(myFrom, myTo, roles);
}
diff --git a/src/corelib/itemmodels/qsortfilterproxymodel.cpp b/src/corelib/itemmodels/qsortfilterproxymodel.cpp
index 38417bbba6..a5bb56245a 100644
--- a/src/corelib/itemmodels/qsortfilterproxymodel.cpp
+++ b/src/corelib/itemmodels/qsortfilterproxymodel.cpp
@@ -1714,7 +1714,10 @@ void QSortFilterProxyModelPrivate::_q_sourceColumnsRemoved(
source_sort_column = -1;
}
- proxy_sort_column = q->mapFromSource(model->index(0,source_sort_column, source_parent)).column();
+ if (source_sort_column >= 0)
+ proxy_sort_column = q->mapFromSource(model->index(0,source_sort_column, source_parent)).column();
+ else
+ proxy_sort_column = -1;
}
void QSortFilterProxyModelPrivate::_q_sourceColumnsAboutToBeMoved(
@@ -2484,7 +2487,6 @@ Qt::SortOrder QSortFilterProxyModel::sortOrder() const
return d->sort_order;
}
-#if QT_CONFIG(regularexpression)
/*!
\since 5.12
\property QSortFilterProxyModel::filterRegularExpression
@@ -2512,7 +2514,6 @@ void QSortFilterProxyModel::setFilterRegularExpression(const QRegularExpression
d->filter_data = regularExpression;
d->filter_changed(QSortFilterProxyModelPrivate::Direction::Rows);
}
-#endif
/*!
\property QSortFilterProxyModel::filterKeyColumn
@@ -2640,7 +2641,6 @@ void QSortFilterProxyModel::setSortLocaleAware(bool on)
emit sortLocaleAwareChanged(on);
}
-#if QT_CONFIG(regularexpression)
/*!
\since 5.12
@@ -2661,7 +2661,6 @@ void QSortFilterProxyModel::setFilterRegularExpression(const QString &pattern)
d->filter_data.setPattern(pattern);
d->filter_changed(QSortFilterProxyModelPrivate::Direction::Rows);
}
-#endif
/*!
Sets the wildcard expression used to filter the contents
@@ -3002,8 +3001,9 @@ bool QSortFilterProxyModel::filterAcceptsRow(int source_row, const QModelIndex &
if (d->filter_data.pattern().isEmpty())
return true;
+
+ int column_count = d->model->columnCount(source_parent);
if (d->filter_column == -1) {
- int column_count = d->model->columnCount(source_parent);
for (int column = 0; column < column_count; ++column) {
QModelIndex source_index = d->model->index(source_row, column, source_parent);
QString key = d->model->data(source_index, d->filter_role).toString();
@@ -3012,9 +3012,10 @@ bool QSortFilterProxyModel::filterAcceptsRow(int source_row, const QModelIndex &
}
return false;
}
- QModelIndex source_index = d->model->index(source_row, d->filter_column, source_parent);
- if (!source_index.isValid()) // the column may not exist
+
+ if (d->filter_column >= column_count) // the column may not exist
return true;
+ QModelIndex source_index = d->model->index(source_row, d->filter_column, source_parent);
QString key = d->model->data(source_index, d->filter_role).toString();
return d->filter_data.match(key).hasMatch();
}
diff --git a/src/corelib/itemmodels/qsortfilterproxymodel.h b/src/corelib/itemmodels/qsortfilterproxymodel.h
index d2c5054351..026da9ecf7 100644
--- a/src/corelib/itemmodels/qsortfilterproxymodel.h
+++ b/src/corelib/itemmodels/qsortfilterproxymodel.h
@@ -42,9 +42,7 @@
#include <QtCore/qabstractproxymodel.h>
-#if QT_CONFIG(regularexpression)
-# include <QtCore/qregularexpression.h>
-#endif
+#include <QtCore/qregularexpression.h>
QT_REQUIRE_CONFIG(sortfilterproxymodel);
@@ -61,9 +59,7 @@ class Q_CORE_EXPORT QSortFilterProxyModel : public QAbstractProxyModel
friend class QSortFilterProxyModelGreaterThan;
Q_OBJECT
-#if QT_CONFIG(regularexpression)
Q_PROPERTY(QRegularExpression filterRegularExpression READ filterRegularExpression WRITE setFilterRegularExpression)
-#endif
Q_PROPERTY(int filterKeyColumn READ filterKeyColumn WRITE setFilterKeyColumn)
Q_PROPERTY(bool dynamicSortFilter READ dynamicSortFilter WRITE setDynamicSortFilter)
Q_PROPERTY(Qt::CaseSensitivity filterCaseSensitivity READ filterCaseSensitivity WRITE setFilterCaseSensitivity NOTIFY filterCaseSensitivityChanged)
@@ -86,9 +82,7 @@ public:
QItemSelection mapSelectionToSource(const QItemSelection &proxySelection) const override;
QItemSelection mapSelectionFromSource(const QItemSelection &sourceSelection) const override;
-#if QT_CONFIG(regularexpression)
QRegularExpression filterRegularExpression() const;
-#endif
int filterKeyColumn() const;
void setFilterKeyColumn(int column);
@@ -121,10 +115,8 @@ public:
void setAutoAcceptChildRows(bool accept);
public Q_SLOTS:
-#if QT_CONFIG(regularexpression)
void setFilterRegularExpression(const QString &pattern);
void setFilterRegularExpression(const QRegularExpression &regularExpression);
-#endif
void setFilterWildcard(const QString &pattern);
void setFilterFixedString(const QString &pattern);
void invalidate();
diff --git a/src/corelib/kernel/qcore_mac.mm b/src/corelib/kernel/qcore_mac.mm
index a3d1669b81..6bf61b1155 100644
--- a/src/corelib/kernel/qcore_mac.mm
+++ b/src/corelib/kernel/qcore_mac.mm
@@ -67,6 +67,21 @@ QT_BEGIN_NAMESPACE
// --------------------------------------------------------------------------
+static void initializeStandardUserDefaults()
+{
+ // The standard user defaults are initialized from an ordered list of domains,
+ // as documented by NSUserDefaults.standardUserDefaults. This includes e.g.
+ // parsing command line arguments, such as -AppleFooBar "baz", as well as
+ // global defaults. To ensure that these defaults are available through
+ // the lower level Core Foundation preferences APIs, we need to initialize
+ // them as early as possible via the Foundation-API, as the lower level APIs
+ // do not do this initialization.
+ Q_UNUSED(NSUserDefaults.standardUserDefaults);
+}
+Q_CONSTRUCTOR_FUNCTION(initializeStandardUserDefaults);
+
+// --------------------------------------------------------------------------
+
QCFString::operator QString() const
{
if (string.isEmpty() && value)
diff --git a/src/corelib/kernel/qcoreapplication.cpp b/src/corelib/kernel/qcoreapplication.cpp
index d9a74c2828..5ea01ec53d 100644
--- a/src/corelib/kernel/qcoreapplication.cpp
+++ b/src/corelib/kernel/qcoreapplication.cpp
@@ -1,6 +1,6 @@
/****************************************************************************
**
-** Copyright (C) 2020 The Qt Company Ltd.
+** Copyright (C) 2021 The Qt Company Ltd.
** Copyright (C) 2016 Intel Corporation.
** Contact: https://www.qt.io/licensing/
**
@@ -91,8 +91,7 @@
#endif // QT_NO_QOBJECT
#if defined(Q_OS_ANDROID) && !defined(Q_OS_ANDROID_EMBEDDED)
-# include <private/qjni_p.h>
-# include <private/qjnihelpers_p.h>
+#include <QtCore/qjniobject.h>
#endif
#ifdef Q_OS_MAC
@@ -170,17 +169,17 @@ QString QCoreApplicationPrivate::appVersion() const
# ifdef Q_OS_DARWIN
applicationVersion = infoDictionaryStringProperty(QStringLiteral("CFBundleVersion"));
# elif defined(Q_OS_ANDROID) && !defined(Q_OS_ANDROID_EMBEDDED)
- QJNIObjectPrivate context(QtAndroidPrivate::context());
+ QJniObject context(QNativeInterface::QAndroidApplication::context());
if (context.isValid()) {
- QJNIObjectPrivate pm = context.callObjectMethod(
+ QJniObject pm = context.callObjectMethod(
"getPackageManager", "()Landroid/content/pm/PackageManager;");
- QJNIObjectPrivate pn = context.callObjectMethod<jstring>("getPackageName");
+ QJniObject pn = context.callObjectMethod<jstring>("getPackageName");
if (pm.isValid() && pn.isValid()) {
- QJNIObjectPrivate packageInfo = pm.callObjectMethod(
+ QJniObject packageInfo = pm.callObjectMethod(
"getPackageInfo", "(Ljava/lang/String;I)Landroid/content/pm/PackageInfo;",
pn.object(), 0);
if (packageInfo.isValid()) {
- QJNIObjectPrivate versionName = packageInfo.getObjectField(
+ QJniObject versionName = packageInfo.getObjectField(
"versionName", "Ljava/lang/String;");
if (versionName.isValid())
return versionName.toString();
@@ -1400,6 +1399,12 @@ void QCoreApplicationPrivate::execCleanup()
function \e does return to the caller -- it is event processing that
stops.
+ Note also that this function is not thread-safe. It should be called only
+ from the main thread (the thread that the QCoreApplication object is
+ processing events on). To ask the application to exit from another thread,
+ either use QCoreApplication::quit() or instead call this function from the
+ main thread with QMetaMethod::invokeMethod().
+
\sa quit(), exec()
*/
void QCoreApplication::exit(int returnCode)
@@ -1951,6 +1956,8 @@ void QCoreApplicationPrivate::maybeQuit()
}
/*!
+ \threadsafe
+
Asks the application to quit.
The request may be ignored if the application prevents the quit,
@@ -1962,7 +1969,7 @@ void QCoreApplicationPrivate::maybeQuit()
code 0 (success).
To exit the application without a chance of being interrupted, call
- exit() directly.
+ exit() directly. Note that method is not thread-safe.
It's good practice to always connect signals to this slot using a
\l{Qt::}{QueuedConnection}. If a signal connected (non-queued) to this slot
@@ -1975,6 +1982,11 @@ void QCoreApplicationPrivate::maybeQuit()
\snippet code/src_corelib_kernel_qcoreapplication.cpp 1
+ \b{Thread-safety note}: this function may be called from any thread to
+ thread-safely cause the currently-running main application loop to exit.
+ However, thread-safety is not guaranteed if the QCoreApplication object is
+ being destroyed at the same time.
+
\sa exit(), aboutToQuit()
*/
void QCoreApplication::quit()
diff --git a/src/corelib/kernel/qcoreapplication.h b/src/corelib/kernel/qcoreapplication.h
index a9a4765941..f581d9b85c 100644
--- a/src/corelib/kernel/qcoreapplication.h
+++ b/src/corelib/kernel/qcoreapplication.h
@@ -1,6 +1,6 @@
/****************************************************************************
**
-** Copyright (C) 2020 The Qt Company Ltd.
+** Copyright (C) 2021 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the QtCore module of the Qt Toolkit.
@@ -120,7 +120,6 @@ public:
static int exec();
static void processEvents(QEventLoop::ProcessEventsFlags flags = QEventLoop::AllEvents);
static void processEvents(QEventLoop::ProcessEventsFlags flags, int maxtime);
- static void exit(int retcode = 0);
static bool sendEvent(QObject *receiver, QEvent *event);
static void postEvent(QObject *receiver, QEvent *event, int priority = Qt::NormalEventPriority);
@@ -165,6 +164,7 @@ public:
public Q_SLOTS:
static void quit();
+ static void exit(int retcode = 0);
Q_SIGNALS:
void aboutToQuit(QPrivateSignal);
@@ -214,7 +214,6 @@ private:
friend bool qt_sendSpontaneousEvent(QObject *, QEvent *);
#endif
friend Q_CORE_EXPORT QString qAppName();
- friend class QClassFactory;
friend class QCommandLineParserPrivate;
};
@@ -247,4 +246,6 @@ Q_CORE_EXPORT QDebug operator<<(QDebug, const MSG &);
QT_END_NAMESPACE
+#include <QtCore/qcoreapplication_platform.h>
+
#endif // QCOREAPPLICATION_H
diff --git a/src/corelib/kernel/qcoreapplication_p.h b/src/corelib/kernel/qcoreapplication_p.h
index 7b60ef5698..a888c4310f 100644
--- a/src/corelib/kernel/qcoreapplication_p.h
+++ b/src/corelib/kernel/qcoreapplication_p.h
@@ -198,7 +198,7 @@ public:
void processCommandLineArguments();
QString qmljs_debug_arguments; // a string containing arguments for js/qml debugging.
- inline QString qmljsDebugArgumentsString() { return qmljs_debug_arguments; }
+ inline QString qmljsDebugArgumentsString() const { return qmljs_debug_arguments; }
#ifdef QT_NO_QOBJECT
QCoreApplication *q_ptr;
diff --git a/src/corelib/kernel/qcoreapplication_platform.h b/src/corelib/kernel/qcoreapplication_platform.h
new file mode 100644
index 0000000000..8dc1199135
--- /dev/null
+++ b/src/corelib/kernel/qcoreapplication_platform.h
@@ -0,0 +1,66 @@
+/****************************************************************************
+**
+** Copyright (C) 2021 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtCore module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QCOREAPPLICATION_PLATFORM_H
+#define QCOREAPPLICATION_PLATFORM_H
+
+#include <QtCore/qglobal.h>
+
+#if defined(Q_OS_ANDROID) && !defined(Q_OS_ANDROID_EMBEDDED)
+class _jobject;
+typedef _jobject* jobject;
+#endif
+
+QT_BEGIN_NAMESPACE
+
+namespace QNativeInterface
+{
+#if defined(Q_OS_ANDROID) && !defined(Q_OS_ANDROID_EMBEDDED) || defined(Q_CLANG_QDOC)
+struct Q_CORE_EXPORT QAndroidApplication
+{
+ QT_DECLARE_NATIVE_INTERFACE(QAndroidApplication)
+ static jobject context();
+ static bool isActivityContext();
+};
+#endif
+}
+
+QT_END_NAMESPACE
+
+#endif // QCOREAPPLICATION_PLATFORM_H
diff --git a/src/corelib/kernel/qeventdispatcher_glib.cpp b/src/corelib/kernel/qeventdispatcher_glib.cpp
index 59c937362b..86f6b0b4aa 100644
--- a/src/corelib/kernel/qeventdispatcher_glib.cpp
+++ b/src/corelib/kernel/qeventdispatcher_glib.cpp
@@ -62,6 +62,7 @@ struct GSocketNotifierSource
{
GSource source;
QList<GPollFDWithQSocketNotifier *> pollfds;
+ int activeNotifierPos;
};
static gboolean socketNotifierSourcePrepare(GSource *, gint *timeout)
@@ -100,8 +101,9 @@ static gboolean socketNotifierSourceDispatch(GSource *source, GSourceFunc, gpoin
QEvent event(QEvent::SockAct);
GSocketNotifierSource *src = reinterpret_cast<GSocketNotifierSource *>(source);
- for (int i = 0; i < src->pollfds.count(); ++i) {
- GPollFDWithQSocketNotifier *p = src->pollfds.at(i);
+ for (src->activeNotifierPos = 0; src->activeNotifierPos < src->pollfds.count();
+ ++src->activeNotifierPos) {
+ GPollFDWithQSocketNotifier *p = src->pollfds.at(src->activeNotifierPos);
if ((p->pollfd.revents & p->pollfd.events) != 0)
QCoreApplication::sendEvent(p->socketNotifier, &event);
@@ -501,6 +503,10 @@ void QEventDispatcherGlib::unregisterSocketNotifier(QSocketNotifier *notifier)
d->socketNotifierSource->pollfds.removeAt(i);
delete p;
+ // Keep a position in the list for the next item.
+ if (i <= d->socketNotifierSource->activeNotifierPos)
+ --d->socketNotifierSource->activeNotifierPos;
+
return;
}
}
diff --git a/src/corelib/kernel/qeventdispatcher_win.cpp b/src/corelib/kernel/qeventdispatcher_win.cpp
index 3609bfe389..80297feef1 100644
--- a/src/corelib/kernel/qeventdispatcher_win.cpp
+++ b/src/corelib/kernel/qeventdispatcher_win.cpp
@@ -96,7 +96,7 @@ LRESULT QT_WIN_CALLBACK qt_internal_proc(HWND hwnd, UINT message, WPARAM wp, LPA
QEventDispatcherWin32Private::QEventDispatcherWin32Private()
: interrupt(false), internalHwnd(0),
- getMessageHook(0), sendPostedEventsTimerId(0), wakeUps(0),
+ sendPostedEventsTimerId(0), wakeUps(0),
activateNotifiersPosted(false)
{
}
@@ -251,30 +251,21 @@ LRESULT QT_WIN_CALLBACK qt_internal_proc(HWND hwnd, UINT message, WPARAM wp, LPA
static const UINT mask = inputQueueMask();
if (HIWORD(GetQueueStatus(mask)) == 0)
q->sendPostedEvents();
+ else
+ d->startPostedEventsTimer();
return 0;
} // switch (message)
return DefWindowProc(hwnd, message, wp, lp);
}
-LRESULT QT_WIN_CALLBACK qt_GetMessageHook(int code, WPARAM wp, LPARAM lp)
+void QEventDispatcherWin32Private::startPostedEventsTimer()
{
- QEventDispatcherWin32 *q = qobject_cast<QEventDispatcherWin32 *>(QAbstractEventDispatcher::instance());
- Q_ASSERT(q != nullptr);
- QEventDispatcherWin32Private *d = q->d_func();
- MSG *msg = reinterpret_cast<MSG *>(lp);
- // Windows unexpectedly passes PM_NOYIELD flag to the hook procedure,
- // if ::PeekMessage(..., PM_REMOVE | PM_NOYIELD) is called from the event loop.
- // So, retrieve 'removed' tag as a bit field.
- const bool messageRemoved = (wp & PM_REMOVE) != 0;
-
- if (msg->hwnd == d->internalHwnd && msg->message == WM_QT_SENDPOSTEDEVENTS
- && messageRemoved && d->sendPostedEventsTimerId == 0) {
+ if (sendPostedEventsTimerId == 0) {
// Start a timer to deliver posted events when the message queue is emptied.
- d->sendPostedEventsTimerId = SetTimer(d->internalHwnd, SendPostedEventsTimerId,
- USER_TIMER_MINIMUM, NULL);
+ sendPostedEventsTimerId = SetTimer(internalHwnd, SendPostedEventsTimerId,
+ USER_TIMER_MINIMUM, NULL);
}
- return d->getMessageHook ? CallNextHookEx(0, code, wp, lp) : 0;
}
// Provide class name and atom for the message window used by
@@ -458,14 +449,6 @@ QEventDispatcherWin32::QEventDispatcherWin32(QEventDispatcherWin32Private &dd, Q
Q_D(QEventDispatcherWin32);
d->internalHwnd = qt_create_internal_window(this);
-
- // setup GetMessage hook needed to drive our posted events
- d->getMessageHook = SetWindowsHookEx(WH_GETMESSAGE, (HOOKPROC) qt_GetMessageHook, NULL, GetCurrentThreadId());
- if (Q_UNLIKELY(!d->getMessageHook)) {
- int errorCode = GetLastError();
- qFatal("Qt: INTERNAL ERROR: failed to install GetMessage hook: %d, %ls",
- errorCode, qUtf16Printable(qt_error_string(errorCode)));
- }
}
QEventDispatcherWin32::~QEventDispatcherWin32()
@@ -492,13 +475,17 @@ bool QEventDispatcherWin32::processEvents(QEventLoop::ProcessEventsFlags flags)
{
Q_D(QEventDispatcherWin32);
- d->interrupt.storeRelaxed(false);
+ // We don't know _when_ the interrupt occurred so we have to honor it.
+ const bool wasInterrupted = d->interrupt.fetchAndStoreRelaxed(false);
emit awake();
// To prevent livelocks, send posted events once per iteration.
// QCoreApplication::sendPostedEvents() takes care about recursions.
sendPostedEvents();
+ if (wasInterrupted)
+ return false;
+
auto threadData = d->threadData.loadRelaxed();
bool canWait;
bool retVal = false;
@@ -536,6 +523,7 @@ bool QEventDispatcherWin32::processEvents(QEventLoop::ProcessEventsFlags flags)
}
if (d->internalHwnd == msg.hwnd && msg.message == WM_QT_SENDPOSTEDEVENTS) {
+ d->startPostedEventsTimer();
// Set result to 'true' because the message was sent by wakeUp().
retVal = true;
continue;
@@ -865,10 +853,6 @@ void QEventDispatcherWin32::closingDown()
d->closingDown = true;
- if (d->getMessageHook)
- UnhookWindowsHookEx(d->getMessageHook);
- d->getMessageHook = 0;
-
if (d->sendPostedEventsTimerId != 0)
KillTimer(d->internalHwnd, d->sendPostedEventsTimerId);
d->sendPostedEventsTimerId = 0;
diff --git a/src/corelib/kernel/qeventdispatcher_win_p.h b/src/corelib/kernel/qeventdispatcher_win_p.h
index 90a818b0f5..49a6d3ebbc 100644
--- a/src/corelib/kernel/qeventdispatcher_win_p.h
+++ b/src/corelib/kernel/qeventdispatcher_win_p.h
@@ -104,7 +104,6 @@ protected:
private:
friend LRESULT QT_WIN_CALLBACK qt_internal_proc(HWND hwnd, UINT message, WPARAM wp, LPARAM lp);
- friend LRESULT QT_WIN_CALLBACK qt_GetMessageHook(int, WPARAM, LPARAM);
};
struct QSockNot {
@@ -154,11 +153,11 @@ public:
// internal window handle used for socketnotifiers/timers/etc
HWND internalHwnd;
- HHOOK getMessageHook;
// for controlling when to send posted events
UINT_PTR sendPostedEventsTimerId;
QAtomicInt wakeUps;
+ void startPostedEventsTimer();
// timers
WinTimerDict timerDict;
diff --git a/src/corelib/kernel/qfunctions_winrt_p.h b/src/corelib/kernel/qfunctions_winrt_p.h
index aa32747bc8..d98571906e 100644
--- a/src/corelib/kernel/qfunctions_winrt_p.h
+++ b/src/corelib/kernel/qfunctions_winrt_p.h
@@ -118,7 +118,7 @@ static inline HRESULT _await_impl(const Microsoft::WRL::ComPtr<T> &asyncOp, Awai
while (SUCCEEDED(hr = asyncInfo->get_Status(&status)) && status == AsyncStatus::Started) {
QCoreApplication::processEvents();
if (timeout && t.hasExpired(timeout))
- return ERROR_TIMEOUT;
+ return HRESULT_FROM_WIN32(ERROR_TIMEOUT);
}
break;
case ProcessThreadEvents:
@@ -126,7 +126,7 @@ static inline HRESULT _await_impl(const Microsoft::WRL::ComPtr<T> &asyncOp, Awai
while (SUCCEEDED(hr = asyncInfo->get_Status(&status)) && status == AsyncStatus::Started) {
dispatcher->processEvents(QEventLoop::AllEvents);
if (timeout && t.hasExpired(timeout))
- return ERROR_TIMEOUT;
+ return HRESULT_FROM_WIN32(ERROR_TIMEOUT);
}
break;
}
@@ -136,7 +136,7 @@ static inline HRESULT _await_impl(const Microsoft::WRL::ComPtr<T> &asyncOp, Awai
while (SUCCEEDED(hr = asyncInfo->get_Status(&status)) && status == AsyncStatus::Started) {
QThread::yieldCurrentThread();
if (timeout && t.hasExpired(timeout))
- return ERROR_TIMEOUT;
+ return HRESULT_FROM_WIN32(ERROR_TIMEOUT);
}
break;
}
diff --git a/src/corelib/kernel/qjni.cpp b/src/corelib/kernel/qjni.cpp
index b593483e59..3750fdb9bc 100644
--- a/src/corelib/kernel/qjni.cpp
+++ b/src/corelib/kernel/qjni.cpp
@@ -280,9 +280,7 @@ jclass QJNIEnvironmentPrivate::findClass(const char *className, JNIEnv *env)
bool isCached = false;
jclass clazz = getCachedClass(classDotEnc, &isCached);
- const bool found = (clazz != 0) || (clazz == 0 && isCached);
-
- if (found)
+ if (clazz || isCached)
return clazz;
const QLatin1String key(classDotEnc);
diff --git a/src/corelib/kernel/qjni_p.h b/src/corelib/kernel/qjni_p.h
index edca36e2bd..57ec8a39b5 100644
--- a/src/corelib/kernel/qjni_p.h
+++ b/src/corelib/kernel/qjni_p.h
@@ -47,6 +47,7 @@
//
// We mean it.
//
+// FIXME: Remove this once the JNI API is used by other modules.
#ifndef QJNI_P_H
#define QJNI_P_H
diff --git a/src/corelib/kernel/qjnienvironment.cpp b/src/corelib/kernel/qjnienvironment.cpp
new file mode 100644
index 0000000000..0c9aa27e82
--- /dev/null
+++ b/src/corelib/kernel/qjnienvironment.cpp
@@ -0,0 +1,303 @@
+/****************************************************************************
+**
+** Copyright (C) 2021 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtCore module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qjnienvironment.h"
+#include "qjniobject.h"
+#include "qjnihelpers_p.h"
+
+#include <QtCore/QThread>
+#include <QtCore/QThreadStorage>
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \class QJniEnvironment
+ \inmodule QtCore
+ \since 6.1
+ \brief The QJniEnvironment class provides access to the JNI Environment (JNIEnv).
+
+ When using JNI, the \l {JNI tips: JavaVM and JNIEnv}{JNIEnv} class is a pointer to a function
+ table and a member function for each JNI function that indirects through the table. \c JNIEnv
+ provides most of the JNI functions. Every C++ native function receives a \c JNIEnv as the first
+ argument. The JNI environment cannot be shared between threads.
+
+ Since \c JNIEnv doesn't do much error checking, such as exception checking and clearing,
+ QJniEnvironment allows you to do that easily.
+
+ \note This API has been designed and tested for use with Android.
+ It has not been tested for other platforms.
+*/
+
+static const char qJniThreadName[] = "QtThread";
+
+class QJniEnvironmentPrivate
+{
+public:
+ JNIEnv *jniEnv = nullptr;
+};
+
+class QJniEnvironmentPrivateTLS
+{
+public:
+ inline ~QJniEnvironmentPrivateTLS()
+ {
+ QtAndroidPrivate::javaVM()->DetachCurrentThread();
+ }
+};
+
+struct QJniLocalRefDeleterPrivate
+{
+ static void cleanup(jobject obj)
+ {
+ if (!obj)
+ return;
+
+ QJniEnvironment env;
+ env->DeleteLocalRef(obj);
+ }
+};
+
+// To simplify this we only define it for jobjects.
+typedef QScopedPointer<_jobject, QJniLocalRefDeleterPrivate> QJniScopedLocalRefPrivate;
+
+
+Q_GLOBAL_STATIC(QThreadStorage<QJniEnvironmentPrivateTLS *>, jniEnvTLS)
+
+
+
+/*!
+ \fn QJniEnvironment::QJniEnvironment()
+
+ Constructs a new JNI Environment object and attaches the current thread to the Java VM.
+*/
+QJniEnvironment::QJniEnvironment()
+ : d(new QJniEnvironmentPrivate{})
+{
+ JavaVM *vm = QtAndroidPrivate::javaVM();
+ const jint ret = vm->GetEnv((void**)&d->jniEnv, JNI_VERSION_1_6);
+ if (ret == JNI_OK) // Already attached
+ return;
+
+ if (ret == JNI_EDETACHED) { // We need to (re-)attach
+ JavaVMAttachArgs args = { JNI_VERSION_1_6, qJniThreadName, nullptr };
+ if (vm->AttachCurrentThread(&d->jniEnv, &args) != JNI_OK)
+ return;
+
+ if (!jniEnvTLS->hasLocalData()) // If we attached the thread we own it.
+ jniEnvTLS->setLocalData(new QJniEnvironmentPrivateTLS);
+ }
+}
+
+/*!
+ \fn QJniEnvironment::~QJniEnvironment()
+
+ Detaches the current thread from the Java VM and destroys the QJniEnvironment object.
+ This will clear any pending exception by calling checkAndClearExceptions().
+*/
+QJniEnvironment::~QJniEnvironment()
+{
+ checkAndClearExceptions();
+}
+
+/*!
+ \fn JNIEnv *QJniEnvironment::operator->()
+
+ Provides access to the QJniEnvironment's JNIEnv pointer.
+*/
+JNIEnv *QJniEnvironment::operator->()
+{
+ return d->jniEnv;
+}
+
+/*!
+ \fn QJniEnvironment::operator JNIEnv *() const
+
+ Returns the JNI Environment pointer.
+*/
+QJniEnvironment::operator JNIEnv* () const
+{
+ return d->jniEnv;
+}
+
+/*!
+ \fn jclass QJniEnvironment::findClass(const char *className)
+
+ Searches for \a className using all available class loaders. Qt on Android
+ uses a custom class loader to load all the .jar files and it must be used
+ to find any classes that are created by that class loader because these
+ classes are not visible when using the default class loader.
+
+ Returns the class pointer or null if is not found.
+
+ A use case for this function is searching for a custom class then calling
+ its member method. The following code snippet creates an instance of the
+ class \c CustomClass and then calls the \c printFromJava() method:
+
+ \code
+ QJniEnvironment env;
+ jclass javaClass = env.findClass("org/qtproject/example/android/CustomClass");
+ QJniObject classObject(javaClass);
+
+ QJniObject javaMessage = QJniObject::fromString("findClass example");
+ classObject.callMethod<void>("printFromJava",
+ "(Ljava/lang/String;)V",
+ javaMessage.object<jstring>());
+ \endcode
+*/
+jclass QJniEnvironment::findClass(const char *className)
+{
+ return QtAndroidPrivate::findClass(className, d->jniEnv);
+}
+
+/*!
+ \fn JavaVM *QJniEnvironment::javaVM()
+
+ Returns the Java VM interface for the current process. Although it might
+ be possible to have multiple Java VMs per process, Android allows only one.
+
+*/
+JavaVM *QJniEnvironment::javaVM()
+{
+ return QtAndroidPrivate::javaVM();
+}
+
+/*!
+ \fn bool QJniEnvironment::registerNativeMethods(const char *className, JNINativeMethod methods[], int size)
+
+ Registers the Java methods in the array \a methods of size \a size, each of
+ which can call native C++ functions from class \a className. These methods
+ must be registered before any attempt to call them.
+
+ Returns True if the registration is successful, otherwise False.
+
+ Each element in the methods array consists of:
+ \list
+ \li The Java method name
+ \li Method signature
+ \li The C++ functions that will be executed
+ \endlist
+
+ \code
+ JNINativeMethod methods[] {{"callNativeOne", "(I)V", reinterpret_cast<void *>(fromJavaOne)},
+ {"callNativeTwo", "(I)V", reinterpret_cast<void *>(fromJavaTwo)}};
+ QJniEnvironment env;
+ env.registerNativeMethods("org/qtproject/android/TestJavaClass", methods, 2);
+ \endcode
+*/
+bool QJniEnvironment::registerNativeMethods(const char *className, JNINativeMethod methods[], int size)
+{
+ QJniObject classObject(className);
+
+ if (!classObject.isValid())
+ return false;
+
+ jclass clazz = d->jniEnv->GetObjectClass(classObject.object());
+ if (d->jniEnv->RegisterNatives(clazz, methods, size) < 0) {
+ checkAndClearExceptions();
+ d->jniEnv->DeleteLocalRef(clazz);
+ return false;
+ }
+
+ d->jniEnv->DeleteLocalRef(clazz);
+
+ return true;
+}
+
+/*!
+ \enum QJniEnvironment::OutputMode
+
+ \value Silent The exceptions are cleaned silently
+ \value Verbose Prints the exceptions and their stack backtrace as an error
+ to \c stderr stream.
+*/
+
+/*!
+ \fn QJniEnvironment::checkAndClearExceptions(OutputMode outputMode = OutputMode::Verbose)
+
+ Cleans any pending exceptions either silently or reporting stack backtrace,
+ depending on the \a outputMode.
+
+ In contrast to \l QJniObject, which handles exceptions internally, if you
+ make JNI calls directly via \c JNIEnv, you need to clear any potential
+ exceptions after the call using this function. For more information about
+ \c JNIEnv calls that can throw an exception, see \l {Oracle: JNI Functions}{JNI Functions}.
+
+ \return \c true when a pending exception was cleared.
+*/
+bool QJniEnvironment::checkAndClearExceptions(QJniEnvironment::OutputMode outputMode)
+{
+ if (Q_UNLIKELY(d->jniEnv->ExceptionCheck())) {
+ if (outputMode != OutputMode::Silent)
+ d->jniEnv->ExceptionDescribe();
+ d->jniEnv->ExceptionClear();
+
+ return true;
+ }
+
+ return false;
+}
+
+/*!
+ \fn QJniEnvironment::checkAndClearExceptions(JNIEnv *env, OutputMode outputMode = OutputMode::Verbose)
+
+ Cleans any pending exceptions for \a env, either silently or reporting
+ stack backtrace, depending on the \a outputMode. This is useful when you
+ already have a \c JNIEnv pointer such as in a native function implementation.
+
+ In contrast to \l QJniObject, which handles exceptions internally, if you
+ make JNI calls directly via \c JNIEnv, you need to clear any potential
+ exceptions after the call using this function. For more information about
+ \c JNIEnv calls that can throw an exception, see \l {Oracle: JNI Functions}{JNI Functions}.
+
+ \return \c true when a pending exception was cleared.
+*/
+bool QJniEnvironment::checkAndClearExceptions(JNIEnv *env, QJniEnvironment::OutputMode outputMode)
+{
+ if (Q_UNLIKELY(env->ExceptionCheck())) {
+ if (outputMode != OutputMode::Silent)
+ env->ExceptionDescribe();
+ env->ExceptionClear();
+
+ return true;
+ }
+
+ return false;
+}
+
+QT_END_NAMESPACE
diff --git a/src/corelib/kernel/qjnienvironment.h b/src/corelib/kernel/qjnienvironment.h
new file mode 100644
index 0000000000..a098e6fe07
--- /dev/null
+++ b/src/corelib/kernel/qjnienvironment.h
@@ -0,0 +1,85 @@
+/****************************************************************************
+**
+** Copyright (C) 2021 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtCore module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QJNI_ENVIRONMENT_H
+#define QJNI_ENVIRONMENT_H
+
+#include <QtCore/QScopedPointer>
+
+#if defined(Q_OS_ANDROID) && !defined(Q_OS_ANDROID_EMBEDDED)
+#include <jni.h>
+#else
+class JNIEnv;
+class JNINativeMethod;
+class JavaVM;
+class jclass;
+#endif
+
+QT_BEGIN_NAMESPACE
+
+class QJniEnvironmentPrivate;
+
+class Q_CORE_EXPORT QJniEnvironment
+{
+public:
+ QJniEnvironment();
+ ~QJniEnvironment();
+ JNIEnv *operator->();
+ operator JNIEnv *() const;
+ jclass findClass(const char *className);
+ static JavaVM *javaVM();
+ bool registerNativeMethods(const char *className, JNINativeMethod methods[], int size);
+
+ enum class OutputMode {
+ Silent,
+ Verbose
+ };
+
+ bool checkAndClearExceptions(OutputMode outputMode = OutputMode::Verbose);
+ static bool checkAndClearExceptions(JNIEnv *env, OutputMode outputMode = OutputMode::Verbose);
+
+
+private:
+ Q_DISABLE_COPY_MOVE(QJniEnvironment)
+ QScopedPointer<QJniEnvironmentPrivate> d;
+};
+
+QT_END_NAMESPACE
+
+#endif // QJNI_ENVIRONMENT_H
diff --git a/src/corelib/kernel/qjnihelpers.cpp b/src/corelib/kernel/qjnihelpers.cpp
index 14f9f389e8..0ad5988297 100644
--- a/src/corelib/kernel/qjnihelpers.cpp
+++ b/src/corelib/kernel/qjnihelpers.cpp
@@ -37,14 +37,16 @@
**
****************************************************************************/
+#include "qcoreapplication.h"
+#include "qjnienvironment.h"
#include "qjnihelpers_p.h"
-#include "qjni_p.h"
-#include "qmutex.h"
+#include "qjniobject.h"
#include "qlist.h"
+#include "qmutex.h"
#include "qsemaphore.h"
#include "qsharedpointer.h"
#include "qthread.h"
-#include "qcoreapplication.h"
+
#include <QtCore/qrunnable.h>
#include <deque>
@@ -146,7 +148,7 @@ static void sendRequestPermissionsResult(JNIEnv *env, jobject /*obj*/, jint requ
std::unique_ptr<jint[]> results(new jint[size]);
env->GetIntArrayRegion(grantResults, 0, size, results.get());
for (int i = 0 ; i < size; ++i) {
- const auto &permission = QJNIObjectPrivate(env->GetObjectArrayElement(permissions, i)).toString();
+ const auto &permission = QJniObject(env->GetObjectArrayElement(permissions, i)).toString();
auto value = results[i] == PERMISSION_GRANTED ?
QtAndroidPrivate::PermissionsResult::Granted :
QtAndroidPrivate::PermissionsResult::Denied;
@@ -286,27 +288,14 @@ void QtAndroidPrivate::handleResume()
listeners.at(i)->handleResume();
}
-static inline bool exceptionCheck(JNIEnv *env)
-{
- if (env->ExceptionCheck()) {
-#ifdef QT_DEBUG
- env->ExceptionDescribe();
-#endif // QT_DEBUG
- env->ExceptionClear();
- return true;
- }
-
- return false;
-}
-
static void setAndroidSdkVersion(JNIEnv *env)
{
jclass androidVersionClass = env->FindClass("android/os/Build$VERSION");
- if (exceptionCheck(env))
+ if (QJniEnvironment::checkAndClearExceptions(env))
return;
jfieldID androidSDKFieldID = env->GetStaticFieldID(androidVersionClass, "SDK_INT", "I");
- if (exceptionCheck(env))
+ if (QJniEnvironment::checkAndClearExceptions(env))
return;
g_androidSdkVersion = env->GetStaticIntField(androidVersionClass, androidSDKFieldID);
@@ -342,42 +331,42 @@ jint QtAndroidPrivate::initJNI(JavaVM *vm, JNIEnv *env)
{
jclass jQtNative = env->FindClass("org/qtproject/qt/android/QtNative");
- if (exceptionCheck(env))
+ if (QJniEnvironment::checkAndClearExceptions(env))
return JNI_ERR;
jmethodID activityMethodID = env->GetStaticMethodID(jQtNative,
"activity",
"()Landroid/app/Activity;");
- if (exceptionCheck(env))
+ if (QJniEnvironment::checkAndClearExceptions(env))
return JNI_ERR;
jobject activity = env->CallStaticObjectMethod(jQtNative, activityMethodID);
- if (exceptionCheck(env))
+ if (QJniEnvironment::checkAndClearExceptions(env))
return JNI_ERR;
jmethodID serviceMethodID = env->GetStaticMethodID(jQtNative,
"service",
"()Landroid/app/Service;");
- if (exceptionCheck(env))
+ if (QJniEnvironment::checkAndClearExceptions(env))
return JNI_ERR;
jobject service = env->CallStaticObjectMethod(jQtNative, serviceMethodID);
- if (exceptionCheck(env))
+ if (QJniEnvironment::checkAndClearExceptions(env))
return JNI_ERR;
jmethodID classLoaderMethodID = env->GetStaticMethodID(jQtNative,
"classLoader",
"()Ljava/lang/ClassLoader;");
- if (exceptionCheck(env))
+ if (QJniEnvironment::checkAndClearExceptions(env))
return JNI_ERR;
jobject classLoader = env->CallStaticObjectMethod(jQtNative, classLoaderMethodID);
- if (exceptionCheck(env))
+ if (QJniEnvironment::checkAndClearExceptions(env))
return JNI_ERR;
setAndroidSdkVersion(env);
@@ -405,7 +394,7 @@ jint QtAndroidPrivate::initJNI(JavaVM *vm, JNIEnv *env)
const bool regOk = (env->RegisterNatives(jQtNative, methods, sizeof(methods) / sizeof(methods[0])) == JNI_OK);
- if (!regOk && exceptionCheck(env))
+ if (!regOk && QJniEnvironment::checkAndClearExceptions(env))
return JNI_ERR;
g_runPendingCppRunnablesMethodID = env->GetStaticMethodID(jQtNative,
@@ -495,7 +484,10 @@ void QtAndroidPrivate::runOnAndroidThreadSync(const QtAndroidPrivate::Runnable &
waitForSemaphore(timeoutMs, sem);
}
-void QtAndroidPrivate::requestPermissions(JNIEnv *env, const QStringList &permissions, const QtAndroidPrivate::PermissionsResultFunc &callbackFunc, bool directCall)
+void QtAndroidPrivate::requestPermissions(JNIEnv *env,
+ const QStringList &permissions,
+ const QtAndroidPrivate::PermissionsResultFunc &callbackFunc,
+ bool directCall)
{
if (androidSdkVersion() < 23 || !activity()) {
QHash<QString, QtAndroidPrivate::PermissionsResult> res;
@@ -517,12 +509,17 @@ void QtAndroidPrivate::requestPermissions(JNIEnv *env, const QStringList &permis
(*g_pendingPermissionRequests)[requestCode] = new PermissionsResultClass(callbackFunc);
}
- QJNIEnvironmentPrivate env;
- auto array = env->NewObjectArray(permissions.size(), env->FindClass("java/lang/String"), nullptr);
+ QJniEnvironment env;
+ jclass clazz = env->FindClass("java/lang/String");
+
+ if (env.checkAndClearExceptions())
+ return;
+
+ auto array = env->NewObjectArray(permissions.size(), clazz, nullptr);
int index = 0;
for (const auto &perm : permissions)
- env->SetObjectArrayElement(array, index++, QJNIObjectPrivate::fromString(perm).object());
- QJNIObjectPrivate(activity()).callMethod<void>("requestPermissions", "([Ljava/lang/String;I)V", array, requestCode);
+ env->SetObjectArrayElement(array, index++, QJniObject::fromString(perm).object());
+ QJniObject(activity()).callMethod<void>("requestPermissions", "([Ljava/lang/String;I)V", array, requestCode);
env->DeleteLocalRef(array);
}, env);
}
@@ -543,10 +540,10 @@ QtAndroidPrivate::PermissionsHash QtAndroidPrivate::requestPermissionsSync(JNIEn
QtAndroidPrivate::PermissionsResult QtAndroidPrivate::checkPermission(const QString &permission)
{
- const auto res = QJNIObjectPrivate::callStaticMethod<jint>("org/qtproject/qt/android/QtNative",
- "checkSelfPermission",
- "(Ljava/lang/String;)I",
- QJNIObjectPrivate::fromString(permission).object());
+ const auto res = QJniObject::callStaticMethod<jint>("org/qtproject/qt/android/QtNative",
+ "checkSelfPermission",
+ "(Ljava/lang/String;)I",
+ QJniObject::fromString(permission).object());
return res == PERMISSION_GRANTED ? PermissionsResult::Granted : PermissionsResult::Denied;
}
@@ -555,8 +552,9 @@ bool QtAndroidPrivate::shouldShowRequestPermissionRationale(const QString &permi
if (androidSdkVersion() < 23 || !activity())
return false;
- return QJNIObjectPrivate(activity()).callMethod<jboolean>("shouldShowRequestPermissionRationale", "(Ljava/lang/String;)Z",
- QJNIObjectPrivate::fromString(permission).object());
+ return QJniObject(activity()).callMethod<jboolean>("shouldShowRequestPermissionRationale",
+ "(Ljava/lang/String;)Z",
+ QJniObject::fromString(permission).object());
}
void QtAndroidPrivate::registerGenericMotionEventListener(QtAndroidPrivate::GenericMotionEventListener *listener)
diff --git a/src/corelib/kernel/qjnihelpers_p.h b/src/corelib/kernel/qjnihelpers_p.h
index c849013ba2..a0d9c219d7 100644
--- a/src/corelib/kernel/qjnihelpers_p.h
+++ b/src/corelib/kernel/qjnihelpers_p.h
@@ -119,6 +119,7 @@ namespace QtAndroidPrivate
Q_CORE_EXPORT jobject context();
Q_CORE_EXPORT JavaVM *javaVM();
Q_CORE_EXPORT jint initJNI(JavaVM *vm, JNIEnv *env);
+ Q_CORE_EXPORT jclass findClass(const char *className, JNIEnv *env);
jobject classLoader();
Q_CORE_EXPORT jint androidSdkVersion();
Q_CORE_EXPORT void runOnAndroidThread(const Runnable &runnable, JNIEnv *env);
diff --git a/src/corelib/kernel/qjniobject.cpp b/src/corelib/kernel/qjniobject.cpp
new file mode 100644
index 0000000000..8a09ae5f5f
--- /dev/null
+++ b/src/corelib/kernel/qjniobject.cpp
@@ -0,0 +1,1892 @@
+/****************************************************************************
+**
+** Copyright (C) 2021 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtCore module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qjniobject.h"
+
+#include "qjnienvironment.h"
+#include "qjnihelpers_p.h"
+
+#include <QtCore/QReadWriteLock>
+#include <QtCore/QHash>
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \class QJniObject
+ \inmodule QtCore
+ \since 6.1
+ \brief A convenience wrapper around the Java Native Interface (JNI).
+
+ The QJniObject class wraps a reference to a Java object, ensuring it isn't
+ gargage-collected and providing access to most \c JNIEnv method calls
+ (member, static) and fields (setter, getter). It eliminates much
+ boiler-plate that would normally be needed, with direct JNI access, for
+ every operation, including exception-handling.
+
+ \note This API has been designed and tested for use with Android.
+ It has not been tested for other platforms.
+
+ \sa QJniEnvironment
+
+ \section1 General Notes
+
+ \list
+ \li Class names need to be fully-qualified, for example: \c "java/lang/String".
+ \li Method signatures are written as \c "(ArgumentsTypes)ReturnType", see \l {JNI Types}.
+ \li All object types are returned as a QJniObject.
+ \endlist
+
+ \section1 Method Signatures
+
+ For functions that take no arguments, QJniObject provides convenience functions that will use
+ the correct signature based on the provided template type. For example:
+
+ \code
+ jint x = QJniObject::callMethod<jint>("getSize");
+ QJniObject::callMethod<void>("touch");
+ \endcode
+
+ In other cases you will need to supply the signature yourself, and it is important that the
+ signature matches the function you want to call. The signature structure is
+ \c "(ArgumentsTypes)ReturnType". Array types in the signature must have the \c {[} prefix,
+ and the fully-qualified \c Object type names must have the \c L prefix and the \c ; suffix.
+
+ The example below demonstrates how to call two different static functions:
+
+ \code
+ // Java class
+ package org.qtproject.qt;
+ class TestClass
+ {
+ static String fromNumber(int x) { ... }
+ static String[] stringArray(String s1, String s2) { ... }
+ }
+ \endcode
+
+ The signature for the first function is \c {"(I)Ljava/lang/String;"}:
+
+ \code
+ // C++ code
+ QJniObject stringNumber = QJniObject::callStaticObjectMethod("org/qtproject/qt/TestClass",
+ "fromNumber"
+ "(I)Ljava/lang/String;",
+ 10);
+ \endcode
+
+ and the signature for the second function is
+ \c {"(Ljava/lang/String;Ljava/lang/String;)[Ljava/lang/String;"}:
+
+ \code
+ // C++ code
+ QJniObject string1 = QJniObject::fromString("String1");
+ QJniObject string2 = QJniObject::fromString("String2");
+ QJniObject stringArray = QJniObject::callStaticObjectMethod("org/qtproject/qt/TestClass",
+ "stringArray"
+ "(Ljava/lang/String;Ljava/lang/String;)[Ljava/lang/String;"
+ string1.object<jstring>(),
+ string2.object<jstring>());
+ \endcode
+
+ \section1 Handling Java Exception
+
+ After calling Java functions that might throw exceptions, it is important
+ to check for, handle and clear out any exception before continuing. All
+ QJniObject functions handle exceptions internally by reporting and clearing them,
+ saving client code the need to handle exceptions.
+
+ \note The user must handle exceptions manually when doing JNI calls using \c JNIEnv directly.
+ It is unsafe to make other JNI calls when exceptions are pending. For more information, see
+ QJniEnvironment::checkAndClearExceptions().
+
+ \section1 Java Native Methods
+
+ Java native methods makes it possible to call native code from Java, this is done by creating a
+ function declaration in Java and prefixing it with the \c native keyword.
+ Before a native function can be called from Java, you need to map the Java native function to a
+ native function in your code. Mapping functions can be done by calling
+ QJniEnvironment::registerNativeMethods().
+
+ The example below demonstrates how this could be done.
+
+ Java implementation:
+ \snippet jni/src_qjniobject.cpp Java native methods
+
+ C++ Implementation:
+ \snippet jni/src_qjniobject.cpp C++ native methods
+
+ \section1 The Lifetime of a Java Object
+
+ Most \l{Object types}{objects} received from Java will be local references
+ and will only stay valid until you return from the native method. After that,
+ the object becomes eligible for garbage collection. If your code creates
+ many local references in a loop you should delete them manually with each
+ iteration, otherwise you might run out of memory. For more information, see
+ \l {JNI Design Overview: Global and Local References}. Local references
+ created outside a native method scope must be deleted manually, since
+ the garbage collector will not free them automatically because we are using
+ \c AttachCurrentThread. For more information, see
+ \l {JNI tips: Local and global references}.
+
+ If you want to keep a Java object alive you need to either create a new global
+ reference to the object and release it when you are done, or construct a new
+ QJniObject and let it manage the lifetime of the Java object.
+
+ \sa object()
+
+ \note The QJniObject only manages its own references, if you construct a QJniObject from a
+ global or local reference that reference will not be released by the QJniObject.
+
+ \section1 JNI Types
+
+ \section2 Object Types
+ \table
+ \header
+ \li Type
+ \li Signature
+ \row
+ \li jobject
+ \li Ljava/lang/Object;
+ \row
+ \li jclass
+ \li Ljava/lang/Class;
+ \row
+ \li jstring
+ \li Ljava/lang/String;
+ \row
+ \li jthrowable
+ \li Ljava/lang/Throwable;
+ \row
+ \li jobjectArray
+ \li [Ljava/lang/Object;
+ \row
+ \li jarray
+ \li [\e<type>
+ \row
+ \li jbooleanArray
+ \li [Z
+ \row
+ \li jbyteArray
+ \li [B
+ \row
+ \li jcharArray
+ \li [C
+ \row
+ \li jshortArray
+ \li [S
+ \row
+ \li jintArray
+ \li [I
+ \row
+ \li jlongArray
+ \li [J
+ \row
+ \li jfloatArray
+ \li [F
+ \row
+ \li jdoubleArray
+ \li [D
+ \endtable
+
+ \section2 Primitive Types
+ \table
+ \header
+ \li Type
+ \li Signature
+ \row
+ \li jboolean
+ \li Z
+ \row
+ \li jbyte
+ \li B
+ \row
+ \li jchar
+ \li C
+ \row
+ \li jshort
+ \li S
+ \row
+ \li jint
+ \li I
+ \row
+ \li jlong
+ \li J
+ \row
+ \li jfloat
+ \li F
+ \row
+ \li jdouble
+ \li D
+ \endtable
+
+ \section2 Other
+ \table
+ \header
+ \li Type
+ \li Signature
+ \row
+ \li void
+ \li V
+ \row
+ \li \e{Custom type}
+ \li L\e<fully-qualified-name>;
+ \endtable
+
+ For more information about JNI, see \l {Java Native Interface Specification}.
+*/
+
+/*!
+ \fn bool operator==(const QJniObject &o1, const QJniObject &o2)
+
+ \relates QJniObject
+
+ Returns true if both objects, \a o1 and \a o2, are referencing the same Java object, or if both
+ are NULL. In any other cases false will be returned.
+*/
+
+/*!
+ \fn bool operator!=(const QJniObject &o1, const QJniObject &o2)
+ \relates QJniObject
+
+ Returns true if \a o1 holds a reference to a different object than \a o2.
+*/
+
+static inline QLatin1String keyBase()
+{
+ return QLatin1String("%1%2:%3");
+}
+
+static QString qt_convertJString(jstring string)
+{
+ QJniEnvironment env;
+ int strLength = env->GetStringLength(string);
+ QString res(strLength, Qt::Uninitialized);
+ env->GetStringRegion(string, 0, strLength, reinterpret_cast<jchar *>(res.data()));
+ return res;
+}
+
+typedef QHash<QString, jclass> JClassHash;
+Q_GLOBAL_STATIC(JClassHash, cachedClasses)
+Q_GLOBAL_STATIC(QReadWriteLock, cachedClassesLock)
+
+static QByteArray toBinaryEncClassName(const QByteArray &className)
+{
+ return QByteArray(className).replace('/', '.');
+}
+
+static jclass getCachedClass(const QByteArray &classBinEnc, bool *isCached = nullptr)
+{
+ QReadLocker locker(cachedClassesLock);
+ const QHash<QString, jclass>::const_iterator &it = cachedClasses->constFind(QString::fromLatin1(classBinEnc));
+ const bool found = (it != cachedClasses->constEnd());
+
+ if (isCached)
+ *isCached = found;
+
+ return found ? it.value() : 0;
+}
+
+inline static jclass loadClass(const QByteArray &className, JNIEnv *env, bool binEncoded = false)
+{
+ const QByteArray &binEncClassName = binEncoded ? className : toBinaryEncClassName(className);
+
+ bool isCached = false;
+ jclass clazz = getCachedClass(binEncClassName, &isCached);
+ if (clazz || isCached)
+ return clazz;
+
+ QJniObject classLoader(QtAndroidPrivate::classLoader());
+ if (!classLoader.isValid())
+ return nullptr;
+
+ QWriteLocker locker(cachedClassesLock);
+ // did we lose the race?
+ const QLatin1String key(binEncClassName);
+ const QHash<QString, jclass>::const_iterator &it = cachedClasses->constFind(key);
+ if (it != cachedClasses->constEnd())
+ return it.value();
+
+ QJniObject stringName = QJniObject::fromString(key);
+ QJniObject classObject = classLoader.callObjectMethod("loadClass",
+ "(Ljava/lang/String;)Ljava/lang/Class;",
+ stringName.object());
+
+ if (!QJniEnvironment::checkAndClearExceptions(env) && classObject.isValid())
+ clazz = static_cast<jclass>(env->NewGlobalRef(classObject.object()));
+
+ cachedClasses->insert(key, clazz);
+ return clazz;
+}
+
+typedef QHash<QString, jmethodID> JMethodIDHash;
+Q_GLOBAL_STATIC(JMethodIDHash, cachedMethodID)
+Q_GLOBAL_STATIC(QReadWriteLock, cachedMethodIDLock)
+
+static inline jmethodID getMethodID(JNIEnv *env,
+ jclass clazz,
+ const char *name,
+ const char *signature,
+ bool isStatic = false)
+{
+ jmethodID id = isStatic ? env->GetStaticMethodID(clazz, name, signature)
+ : env->GetMethodID(clazz, name, signature);
+
+ if (QJniEnvironment::checkAndClearExceptions(env))
+ return nullptr;
+
+ return id;
+}
+
+static jmethodID getCachedMethodID(JNIEnv *env,
+ jclass clazz,
+ const QByteArray &className,
+ const char *name,
+ const char *signature,
+ bool isStatic = false)
+{
+ if (className.isEmpty())
+ return getMethodID(env, clazz, name, signature, isStatic);
+
+ const QString key = keyBase().arg(QLatin1String(className),
+ QLatin1String(name),
+ QLatin1String(signature));
+ QHash<QString, jmethodID>::const_iterator it;
+
+ {
+ QReadLocker locker(cachedMethodIDLock);
+ it = cachedMethodID->constFind(key);
+ if (it != cachedMethodID->constEnd())
+ return it.value();
+ }
+
+ {
+ QWriteLocker locker(cachedMethodIDLock);
+ it = cachedMethodID->constFind(key);
+ if (it != cachedMethodID->constEnd())
+ return it.value();
+
+ jmethodID id = getMethodID(env, clazz, name, signature, isStatic);
+
+ cachedMethodID->insert(key, id);
+ return id;
+ }
+}
+
+typedef QHash<QString, jfieldID> JFieldIDHash;
+Q_GLOBAL_STATIC(JFieldIDHash, cachedFieldID)
+Q_GLOBAL_STATIC(QReadWriteLock, cachedFieldIDLock)
+
+static inline jfieldID getFieldID(JNIEnv *env,
+ jclass clazz,
+ const char *name,
+ const char *signature,
+ bool isStatic = false)
+{
+ jfieldID id = isStatic ? env->GetStaticFieldID(clazz, name, signature)
+ : env->GetFieldID(clazz, name, signature);
+
+ if (QJniEnvironment::checkAndClearExceptions(env))
+ return nullptr;
+
+ return id;
+}
+
+static jfieldID getCachedFieldID(JNIEnv *env,
+ jclass clazz,
+ const QByteArray &className,
+ const char *name,
+ const char *signature,
+ bool isStatic = false)
+{
+ if (className.isNull())
+ return getFieldID(env, clazz, name, signature, isStatic);
+
+ const QString key = keyBase().arg(QLatin1String(className),
+ QLatin1String(name),
+ QLatin1String(signature));
+ QHash<QString, jfieldID>::const_iterator it;
+
+ {
+ QReadLocker locker(cachedFieldIDLock);
+ it = cachedFieldID->constFind(key);
+ if (it != cachedFieldID->constEnd())
+ return it.value();
+ }
+
+ {
+ QWriteLocker locker(cachedFieldIDLock);
+ it = cachedFieldID->constFind(key);
+ if (it != cachedFieldID->constEnd())
+ return it.value();
+
+ jfieldID id = getFieldID(env, clazz, name, signature, isStatic);
+
+ cachedFieldID->insert(key, id);
+ return id;
+ }
+}
+
+jclass QtAndroidPrivate::findClass(const char *className, JNIEnv *env)
+{
+ const QByteArray &classDotEnc = toBinaryEncClassName(className);
+ bool isCached = false;
+ jclass clazz = getCachedClass(classDotEnc, &isCached);
+
+ if (clazz || isCached)
+ return clazz;
+
+ const QLatin1String key(classDotEnc);
+ if (env) { // We got an env. pointer (We expect this to be the right env. and call FindClass())
+ QWriteLocker locker(cachedClassesLock);
+ const QHash<QString, jclass>::const_iterator &it = cachedClasses->constFind(key);
+ // Did we lose the race?
+ if (it != cachedClasses->constEnd())
+ return it.value();
+
+ jclass fclazz = env->FindClass(className);
+ if (!QJniEnvironment::checkAndClearExceptions(env)) {
+ clazz = static_cast<jclass>(env->NewGlobalRef(fclazz));
+ env->DeleteLocalRef(fclazz);
+ }
+
+ if (clazz)
+ cachedClasses->insert(key, clazz);
+ }
+
+ if (!clazz) // We didn't get an env. pointer or we got one with the WRONG class loader...
+ clazz = loadClass(classDotEnc, QJniEnvironment(), true);
+
+ return clazz;
+}
+
+class QJniObjectPrivate
+{
+public:
+ QJniObjectPrivate() = default;
+ ~QJniObjectPrivate() {
+ QJniEnvironment env;
+ if (m_jobject)
+ env->DeleteGlobalRef(m_jobject);
+ if (m_jclass && m_own_jclass)
+ env->DeleteGlobalRef(m_jclass);
+ }
+
+ jobject m_jobject = nullptr;
+ jclass m_jclass = nullptr;
+ bool m_own_jclass = true;
+ QByteArray m_className;
+};
+
+/*!
+ \fn QJniObject::QJniObject()
+
+ Constructs an invalid JNI object.
+
+ \sa isValid()
+*/
+QJniObject::QJniObject()
+ : d(new QJniObjectPrivate())
+{
+}
+
+/*!
+ \fn QJniObject::QJniObject(const char *className)
+
+ Constructs a new JNI object by calling the default constructor of \a className.
+
+ \code
+ QJniObject myJavaString("java/lang/String");
+ \endcode
+*/
+QJniObject::QJniObject(const char *className)
+ : d(new QJniObjectPrivate())
+{
+ QJniEnvironment env;
+ d->m_className = toBinaryEncClassName(className);
+ d->m_jclass = loadClass(d->m_className, env, true);
+ d->m_own_jclass = false;
+ if (d->m_jclass) {
+ // get default constructor
+ jmethodID constructorId = getCachedMethodID(env, d->m_jclass, d->m_className, "<init>", "()V");
+ if (constructorId) {
+ jobject obj = env->NewObject(d->m_jclass, constructorId);
+ if (obj) {
+ d->m_jobject = env->NewGlobalRef(obj);
+ env->DeleteLocalRef(obj);
+ }
+ }
+ }
+}
+
+/*!
+ \fn QJniObject::QJniObject(const char *className, const char *signature, ...)
+
+ Constructs a new JNI object by calling the constructor of \a className with
+ \a signature specifying the types of any subsequent arguments.
+
+ \code
+ QJniEnvironment env;
+ char* str = "Hello";
+ jstring myJStringArg = env->NewStringUTF(str);
+ QJniObject myNewJavaString("java/lang/String", "(Ljava/lang/String;)V", myJStringArg);
+ \endcode
+*/
+QJniObject::QJniObject(const char *className, const char *signature, ...)
+ : d(new QJniObjectPrivate())
+{
+ QJniEnvironment env;
+ d->m_className = toBinaryEncClassName(className);
+ d->m_jclass = loadClass(d->m_className, env, true);
+ d->m_own_jclass = false;
+ if (d->m_jclass) {
+ jmethodID constructorId = getCachedMethodID(env, d->m_jclass, d->m_className, "<init>", signature);
+ if (constructorId) {
+ va_list args;
+ va_start(args, signature);
+ jobject obj = env->NewObjectV(d->m_jclass, constructorId, args);
+ va_end(args);
+ if (obj) {
+ d->m_jobject = env->NewGlobalRef(obj);
+ env->DeleteLocalRef(obj);
+ }
+ }
+ }
+}
+
+QJniObject::QJniObject(const char *className, const char *signature, const QVaListPrivate &args)
+ : d(new QJniObjectPrivate())
+{
+ QJniEnvironment env;
+ d->m_className = toBinaryEncClassName(className);
+ d->m_jclass = loadClass(d->m_className, env, true);
+ d->m_own_jclass = false;
+ if (d->m_jclass) {
+ jmethodID constructorId = getCachedMethodID(env, d->m_jclass, d->m_className, "<init>", signature);
+ if (constructorId) {
+ jobject obj = env->NewObjectV(d->m_jclass, constructorId, args);
+ if (obj) {
+ d->m_jobject = env->NewGlobalRef(obj);
+ env->DeleteLocalRef(obj);
+ }
+ }
+ }
+}
+
+/*!
+ \fn QJniObject::QJniObject(jclass clazz, const char *signature, ...)
+
+ Constructs a new JNI object from \a clazz by calling the constructor with
+ \a signature specifying the types of any subsequent arguments.
+
+ \code
+ QJniEnvironment env;
+ jclass myClazz = env.findClass("org/qtproject/qt/TestClass");
+ QJniObject(myClazz, "(I)V", 3);
+ \endcode
+*/
+QJniObject::QJniObject(jclass clazz, const char *signature, ...)
+ : d(new QJniObjectPrivate())
+{
+ QJniEnvironment env;
+ if (clazz) {
+ d->m_jclass = static_cast<jclass>(env->NewGlobalRef(clazz));
+ if (d->m_jclass) {
+ jmethodID constructorId = getMethodID(env, d->m_jclass, "<init>", signature);
+ if (constructorId) {
+ va_list args;
+ va_start(args, signature);
+ jobject obj = env->NewObjectV(d->m_jclass, constructorId, args);
+ va_end(args);
+ if (obj) {
+ d->m_jobject = env->NewGlobalRef(obj);
+ env->DeleteLocalRef(obj);
+ }
+ }
+ }
+ }
+}
+
+/*!
+ \fn QJniObject::QJniObject(jclass clazz)
+
+ Constructs a new JNI object by calling the default constructor of \a clazz.
+
+ \note The QJniObject will create a new reference to the class \a clazz
+ and releases it again when it is destroyed. References to the class created
+ outside the QJniObject need to be managed by the caller.
+*/
+
+QJniObject::QJniObject(jclass clazz)
+ : d(new QJniObjectPrivate())
+{
+ QJniEnvironment env;
+ d->m_jclass = static_cast<jclass>(env->NewGlobalRef(clazz));
+ if (d->m_jclass) {
+ // get default constructor
+ jmethodID constructorId = getMethodID(env, d->m_jclass, "<init>", "()V");
+ if (constructorId) {
+ jobject obj = env->NewObject(d->m_jclass, constructorId);
+ if (obj) {
+ d->m_jobject = env->NewGlobalRef(obj);
+ env->DeleteLocalRef(obj);
+ }
+ }
+ }
+}
+
+QJniObject::QJniObject(jclass clazz, const char *signature, const QVaListPrivate &args)
+ : d(new QJniObjectPrivate())
+{
+ QJniEnvironment env;
+ if (clazz) {
+ d->m_jclass = static_cast<jclass>(env->NewGlobalRef(clazz));
+ if (d->m_jclass) {
+ jmethodID constructorId = getMethodID(env, d->m_jclass, "<init>", signature);
+ if (constructorId) {
+ jobject obj = env->NewObjectV(d->m_jclass, constructorId, args);
+ if (obj) {
+ d->m_jobject = env->NewGlobalRef(obj);
+ env->DeleteLocalRef(obj);
+ }
+ }
+ }
+ }
+}
+
+/*!
+ \fn QJniObject::QJniObject(jobject object)
+
+ Constructs a new JNI object around the Java object \a object.
+
+ \note The QJniObject will hold a reference to the Java object \a object
+ and release it when destroyed. Any references to the Java object \a object
+ outside QJniObject needs to be managed by the caller. In most cases you
+ should never call this function with a local reference unless you intend
+ to manage the local reference yourself. See QJniObject::fromLocalRef()
+ for converting a local reference to a QJniObject.
+
+ \sa fromLocalRef()
+*/
+QJniObject::QJniObject(jobject obj)
+ : d(new QJniObjectPrivate())
+{
+ if (!obj)
+ return;
+
+ QJniEnvironment env;
+ d->m_jobject = env->NewGlobalRef(obj);
+ jclass cls = env->GetObjectClass(obj);
+ d->m_jclass = static_cast<jclass>(env->NewGlobalRef(cls));
+ env->DeleteLocalRef(cls);
+}
+
+/*!
+ \brief Get a JNI object from a jobject variant and do the necessary
+ exception clearing and delete the local reference before returning.
+ The JNI object can be null if there was an exception.
+*/
+inline static QJniObject getCleanJniObject(jobject obj)
+{
+ if (!obj)
+ return QJniObject();
+
+ QJniEnvironment env;
+ if (env.checkAndClearExceptions()) {
+ env->DeleteLocalRef(obj);
+ return QJniObject();
+ }
+
+ QJniObject res(obj);
+ env->DeleteLocalRef(obj);
+ return res;
+}
+
+/*!
+ \fn QJniObject::~QJniObject()
+
+ Destroys the JNI object and releases any references held by the JNI object.
+*/
+QJniObject::~QJniObject()
+{}
+
+/*!
+ \fn jobject QJniObject::object() const
+
+ Returns the object held by the QJniObject as jobject.
+
+ \code
+ jobject object = jniObject.object();
+ \endcode
+
+ \note The returned object is still kept live by this QJniObject. To keep the
+ object live beyond the lifetime of this QJniObject, for example to record it
+ for later use, the easiest approach is to store it in another QJniObject with
+ a suitable lifetime. Alternatively, you can make a new global reference to the
+ object and store it, taking care to free it when you are done with it.
+
+ \snippet jni/src_qjniobject.cpp QJniObject scope
+*/
+jobject QJniObject::object() const
+{
+ return javaObject();
+}
+
+QJniObject QJniObject::callObjectMethodV(const char *methodName,
+ const char *signature,
+ va_list args) const
+{
+ QJniEnvironment env;
+ jobject res = nullptr;
+ jmethodID id = getCachedMethodID(env, d->m_jclass, d->m_className, methodName, signature);
+ if (id) {
+ res = env->CallObjectMethodV(d->m_jobject, id, args);
+ if (env.checkAndClearExceptions()) {
+ env->DeleteLocalRef(res);
+ res = nullptr;
+ }
+ }
+
+ QJniObject obj(res);
+ env->DeleteLocalRef(res);
+ return obj;
+}
+
+QJniObject QJniObject::callStaticObjectMethodV(const char *className,
+ const char *methodName,
+ const char *signature,
+ va_list args)
+{
+ QJniEnvironment env;
+ jobject res = nullptr;
+ jclass clazz = loadClass(className, env);
+ if (clazz) {
+ jmethodID id = getCachedMethodID(env, clazz, toBinaryEncClassName(className),
+ methodName, signature, true);
+ if (id) {
+ res = env->CallStaticObjectMethodV(clazz, id, args);
+ if (env.checkAndClearExceptions()) {
+ env->DeleteLocalRef(res);
+ res = nullptr;
+ }
+ }
+ }
+
+ QJniObject obj(res);
+ env->DeleteLocalRef(res);
+ return obj;
+}
+
+QJniObject QJniObject::callStaticObjectMethodV(jclass clazz,
+ const char *methodName,
+ const char *signature,
+ va_list args)
+{
+ QJniEnvironment env;
+ jmethodID id = getMethodID(env, clazz, methodName, signature, true);
+ if (!id)
+ return QJniObject();
+
+ return getCleanJniObject(env->CallStaticObjectMethodV(clazz, id, args));
+}
+
+/*!
+ \fn template <typename T> T QJniObject::callMethod(const char *methodName, const char *signature, ...) const
+
+ Calls the object's method \a methodName with \a signature specifying the types of any
+ subsequent arguments.
+
+ \code
+ QJniObject myJavaStrin("org/qtproject/qt/TestClass");
+ jint index = myJavaString.callMethod<jint>("indexOf", "(I)I", 0x0051);
+ \endcode
+
+*/
+template <>
+Q_CORE_EXPORT void QJniObject::callMethod<void>(const char *methodName, const char *signature, ...) const
+{
+ QJniEnvironment env;
+ jmethodID id = getCachedMethodID(env, d->m_jclass, d->m_className, methodName, signature);
+ if (id) {
+ va_list args;
+ va_start(args, signature);
+ env->CallVoidMethodV(d->m_jobject, id, args);
+ va_end(args);
+ env.checkAndClearExceptions();
+ }
+}
+
+/*!
+ \fn template <typename T> T QJniObject::callMethod(const char *methodName) const
+
+ Calls the method \a methodName and returns the value.
+
+ \code
+ QJniObject myJavaStrin("org/qtproject/qt/TestClass");
+ jint size = myJavaString.callMethod<jint>("length");
+ \endcode
+*/
+template <>
+Q_CORE_EXPORT void QJniObject::callMethod<void>(const char *methodName) const
+{
+ callMethod<void>(methodName, "()V");
+}
+
+/*!
+ \fn template <typename T> T QJniObject::callStaticMethod(const char *className, const char *methodName, const char *signature, ...)
+
+ Calls the static method \a methodName from class \a className with \a signature
+ specifying the types of any subsequent arguments.
+
+ \code
+ jint a = 2;
+ jint b = 4;
+ jint max = QJniObject::callStaticMethod<jint>("java/lang/Math", "max", "(II)I", a, b);
+ \endcode
+*/
+template <>
+Q_CORE_EXPORT void QJniObject::callStaticMethod<void>(const char *className,
+ const char *methodName,
+ const char *signature,
+ ...)
+{
+ QJniEnvironment env;
+ jclass clazz = loadClass(className, env);
+ if (clazz) {
+ jmethodID id = getCachedMethodID(env, clazz, toBinaryEncClassName(className),
+ methodName, signature, true);
+ if (id) {
+ va_list args;
+ va_start(args, signature);
+ env->CallStaticVoidMethodV(clazz, id, args);
+ va_end(args);
+ env.checkAndClearExceptions();
+ }
+ }
+}
+
+/*!
+ \fn template <typename T> T QJniObject::callStaticMethod(const char *className, const char *methodName)
+
+ Calls the static method \a methodName on class \a className and returns the value.
+
+ \code
+ jint value = QJniObject::callStaticMethod<jint>("MyClass", "staticMethod");
+ \endcode
+*/
+template <>
+Q_CORE_EXPORT void QJniObject::callStaticMethod<void>(const char *className, const char *methodName)
+{
+ callStaticMethod<void>(className, methodName, "()V");
+}
+
+/*!
+ \fn template <typename T> T QJniObject::callStaticMethod(jclass clazz, const char *methodName, const char *signature, ...)
+
+ Calls the static method \a methodName from \a clazz with \a signature
+ specifying the types of any subsequent arguments.
+
+ \code
+ QJniEnvironment env;
+ jclass javaMathClass = env.findClass("java/lang/Math");
+ jint a = 2;
+ jint b = 4;
+ jint max = QJniObject::callStaticMethod<jint>(javaMathClass, "max", "(II)I", a, b);
+ \endcode
+*/
+template <>
+Q_CORE_EXPORT void QJniObject::callStaticMethod<void>(jclass clazz,
+ const char *methodName,
+ const char *signature,
+ ...)
+{
+ QJniEnvironment env;
+ if (clazz) {
+ jmethodID id = getMethodID(env, clazz, methodName, signature, true);
+ if (id) {
+ va_list args;
+ va_start(args, signature);
+ env->CallStaticVoidMethodV(clazz, id, args);
+ va_end(args);
+ env.checkAndClearExceptions();
+ }
+ }
+}
+
+template <>
+Q_CORE_EXPORT void QJniObject::callStaticMethodV<void>(const char *className,
+ const char *methodName,
+ const char *signature,
+ va_list args)
+{
+ QJniEnvironment env;
+ jclass clazz = loadClass(className, env);
+ if (clazz) {
+ jmethodID id = getCachedMethodID(env, clazz,
+ toBinaryEncClassName(className), methodName,
+ signature, true);
+ if (id) {
+ env->CallStaticVoidMethodV(clazz, id, args);
+ env.checkAndClearExceptions();
+ }
+ }
+}
+
+template <>
+Q_CORE_EXPORT void QJniObject::callStaticMethodV<void>(jclass clazz,
+ const char *methodName,
+ const char *signature,
+ va_list args)
+{
+ QJniEnvironment env;
+ jmethodID id = getMethodID(env, clazz, methodName, signature, true);
+ if (id) {
+ env->CallStaticVoidMethodV(clazz, id, args);
+ env.checkAndClearExceptions();
+ }
+}
+
+/*!
+ \fn template <typename T> T QJniObject::callStaticMethod(jclass clazz, const char *methodName)
+
+ Calls the static method \a methodName on \a clazz and returns the value.
+
+ \code
+ QJniEnvironment env;
+ jclass javaMathClass = env.findClass("java/lang/Math");
+ jdouble randNr = QJniObject::callStaticMethod<jdouble>(javaMathClass, "random");
+ \endcode
+*/
+template <>
+Q_CORE_EXPORT void QJniObject::callStaticMethod<void>(jclass clazz, const char *methodName)
+{
+ callStaticMethod<void>(clazz, methodName, "()V");
+}
+
+template <>
+Q_CORE_EXPORT void QJniObject::callMethodV<void>(const char *methodName, const char *signature,
+ va_list args) const
+{
+ QJniEnvironment env;
+ jmethodID id = getCachedMethodID(env, d->m_jclass, d->m_className, methodName, signature);
+ if (id) {
+ env->CallVoidMethodV(d->m_jobject, id, args);
+ env.checkAndClearExceptions();
+ }
+}
+
+#define MAKE_JNI_METHODS(MethodName, Type, Signature) \
+template <> Q_CORE_EXPORT Type QJniObject::callMethod<Type>(const char *methodName, \
+ const char *signature, ...) const \
+{ \
+ QJniEnvironment env; \
+ Type res = 0; \
+ jmethodID id = getCachedMethodID(env, d->m_jclass, d->m_className, methodName, signature); \
+ if (id) { \
+ va_list args; \
+ va_start(args, signature); \
+ res = env->Call##MethodName##MethodV(d->m_jobject, id, args); \
+ va_end(args); \
+ if (env.checkAndClearExceptions()) \
+ res = 0; \
+ } \
+ return res; \
+}\
+template <> Q_CORE_EXPORT Type QJniObject::callMethod<Type>(const char *methodName) const \
+{ \
+ return callMethod<Type>(methodName, Signature); \
+} \
+\
+template <> Q_CORE_EXPORT Type QJniObject::callStaticMethod<Type>(const char *className, \
+ const char *methodName, \
+ const char *signature, \
+ ...) \
+{ \
+ QJniEnvironment env; \
+ Type res = 0; \
+ jclass clazz = loadClass(className, env); \
+ if (clazz) { \
+ jmethodID id = getCachedMethodID(env, clazz, toBinaryEncClassName(className), methodName, \
+ signature, true); \
+ if (id) { \
+ va_list args; \
+ va_start(args, signature); \
+ res = env->CallStatic##MethodName##MethodV(clazz, id, args); \
+ va_end(args); \
+ if (env.checkAndClearExceptions()) \
+ res = 0; \
+ } \
+ } \
+ return res; \
+} \
+template <> Q_CORE_EXPORT Type QJniObject::callStaticMethod<Type>(const char *className, \
+ const char *methodName) \
+{ \
+ return callStaticMethod<Type>(className, methodName, Signature); \
+}\
+\
+template <> Q_CORE_EXPORT Type QJniObject::callStaticMethod<Type>(jclass clazz, \
+ const char *methodName, \
+ const char *signature, \
+ ...) \
+{ \
+ QJniEnvironment env; \
+ Type res = 0; \
+ if (clazz) { \
+ jmethodID id = getMethodID(env, clazz, methodName, signature, true); \
+ if (id) { \
+ va_list args; \
+ va_start(args, signature); \
+ res = env->CallStatic##MethodName##MethodV(clazz, id, args); \
+ va_end(args); \
+ if (env.checkAndClearExceptions()) \
+ res = 0; \
+ } \
+ } \
+ return res; \
+} \
+template <> Q_CORE_EXPORT Type QJniObject::callStaticMethod<Type>(jclass clazz, \
+ const char *methodName) \
+{ \
+ return callStaticMethod<Type>(clazz, methodName, Signature); \
+}\
+template <> \
+Q_CORE_EXPORT Type QJniObject::callMethodV<Type>(const char *methodName, const char *signature,\
+ va_list args) const\
+{\
+ QJniEnvironment env;\
+ Type res = 0;\
+ jmethodID id = getCachedMethodID(env, d->m_jclass, d->m_className, methodName, signature);\
+ if (id) {\
+ res = env->Call##MethodName##MethodV(d->m_jobject, id, args);\
+ if (env.checkAndClearExceptions()) \
+ res = 0; \
+ }\
+ return res;\
+}\
+template <>\
+Q_CORE_EXPORT Type QJniObject::callStaticMethodV<Type>(const char *className,\
+ const char *methodName,\
+ const char *signature,\
+ va_list args)\
+{\
+ QJniEnvironment env;\
+ Type res = 0;\
+ jclass clazz = loadClass(className, env);\
+ if (clazz) {\
+ jmethodID id = getCachedMethodID(env, clazz, toBinaryEncClassName(className), methodName,\
+ signature, true);\
+ if (id) {\
+ res = env->CallStatic##MethodName##MethodV(clazz, id, args);\
+ if (env.checkAndClearExceptions()) \
+ res = 0; \
+ }\
+ }\
+ return res;\
+}\
+template <>\
+Q_CORE_EXPORT Type QJniObject::callStaticMethodV<Type>(jclass clazz,\
+ const char *methodName,\
+ const char *signature,\
+ va_list args)\
+{\
+ QJniEnvironment env;\
+ Type res = 0;\
+ jmethodID id = getMethodID(env, clazz, methodName, signature, true);\
+ if (id) {\
+ res = env->CallStatic##MethodName##MethodV(clazz, id, args);\
+ if (env.checkAndClearExceptions()) \
+ res = 0; \
+ }\
+ return res;\
+}
+
+#define DECLARE_JNI_METHODS(MethodName, Type, Signature) MAKE_JNI_METHODS(MethodName, \
+ Type, \
+ Signature)
+DECLARE_JNI_METHODS(Boolean, jboolean, "()Z")
+DECLARE_JNI_METHODS(Byte, jbyte, "()B")
+DECLARE_JNI_METHODS(Char, jchar, "()C")
+DECLARE_JNI_METHODS(Short, jshort, "()S")
+DECLARE_JNI_METHODS(Int, jint, "()I")
+DECLARE_JNI_METHODS(Long, jlong, "()J")
+DECLARE_JNI_METHODS(Float, jfloat, "()F")
+DECLARE_JNI_METHODS(Double, jdouble, "()D")
+
+/*!
+ \fn QJniObject QJniObject::callObjectMethod(const char *methodName, const char *signature, ...) const
+
+ Calls the Java object's method \a methodName with \a signature specifying
+ the types of any subsequent arguments.
+
+ \code
+ QJniObject myJavaString = QJniObject::fromString("Hello, Java");
+ QJniObject mySubstring = myJavaString.callObjectMethod("substring",
+ "(II)Ljava/lang/String;", 7, 11);
+ \endcode
+*/
+QJniObject QJniObject::callObjectMethod(const char *methodName, const char *signature, ...) const
+{
+ QJniEnvironment env;
+ jmethodID id = getCachedMethodID(env, d->m_jclass, d->m_className, methodName, signature);
+ if (id) {
+ va_list args;
+ va_start(args, signature);
+ QJniObject res = getCleanJniObject(env->CallObjectMethodV(d->m_jobject, id, args));
+ va_end(args);
+ return res;
+ }
+
+ return QJniObject();
+}
+
+/*!
+ \fn QJniObject QJniObject::callStaticObjectMethod(const char *className, const char *methodName, const char *signature, ...)
+
+ Calls the static method \a methodName from the class \a className with \a signature
+ specifying the types of any subsequent arguments.
+
+ \code
+ QJniObject thread = QJniObject::callStaticObjectMethod("java/lang/Thread", "currentThread",
+ "()Ljava/lang/Thread;");
+ QJniObject string = QJniObject::callStaticObjectMethod("java/lang/String", "valueOf",
+ "(I)Ljava/lang/String;", 10);
+ \endcode
+*/
+QJniObject QJniObject::callStaticObjectMethod(const char *className,
+ const char *methodName,
+ const char *signature,
+ ...)
+{
+ QJniEnvironment env;
+ jclass clazz = loadClass(className, env);
+ if (clazz) {
+ jmethodID id = getCachedMethodID(env, clazz, toBinaryEncClassName(className),
+ methodName, signature, true);
+ if (id) {
+ va_list args;
+ va_start(args, signature);
+ QJniObject res = getCleanJniObject(env->CallStaticObjectMethodV(clazz, id, args));
+ va_end(args);
+ return res;
+ }
+ }
+
+ return QJniObject();
+}
+
+/*!
+ \fn QJniObject QJniObject::callStaticObjectMethod(jclass clazz, const char *methodName, const char *signature, ...)
+
+ Calls the static method \a methodName from class \a clazz with \a signature
+ specifying the types of any subsequent arguments.
+*/
+QJniObject QJniObject::callStaticObjectMethod(jclass clazz,
+ const char *methodName,
+ const char *signature,
+ ...)
+{
+ QJniEnvironment env;
+ if (clazz) {
+ jmethodID id = getMethodID(env, clazz, methodName, signature, true);
+ if (id) {
+ va_list args;
+ va_start(args, signature);
+ QJniObject res = getCleanJniObject(env->CallStaticObjectMethodV(clazz, id, args));
+ va_end(args);
+ return res;
+ }
+ }
+
+ return QJniObject();
+}
+
+/*!
+ \fn QJniObject QJniObject::callObjectMethod(const char *methodName) const
+
+ Calls the Java objects method \a methodName and returns a new QJniObject for
+ the returned Java object.
+
+ \code
+ QJniObject myJavaString = QJniObject::fromString("Hello, Java");
+ QJniObject myJavaString2 = myJavaString1.callObjectMethod<jstring>("toString");
+ \endcode
+*/
+
+/*!
+ \fn QJniObject QJniObject::callStaticObjectMethod(const char *className, const char *methodName)
+
+ Calls the static method with \a methodName on the class \a className.
+
+ \code
+ QJniObject string = QJniObject::callStaticObjectMethod<jstring>("CustomClass", "getClassName");
+ \endcode
+*/
+
+/*!
+ \fn QJniObject QJniObject::callStaticObjectMethod(jclass clazz, const char *methodName)
+
+ Calls the static method with \a methodName on \a clazz.
+
+*/
+
+/*!
+ \fn template <typename T> T QJniObject::object() const
+
+ Returns the object held by the QJniObject as type T.
+ T can be one of \l {Object Types}{JNI Object Types}.
+
+ \code
+ QJniObject string = QJniObject::fromString("Hello, JNI");
+ jstring jstring = string.object<jstring>();
+ \endcode
+
+ \note The returned object is still kept live by this QJniObject. To keep the
+ object live beyond the lifetime of this QJniObject, for example to record it
+ for later use, the easiest approach is to store it in another QJniObject with
+ a suitable lifetime. Alternatively, you can make a new global reference to the
+ object and store it, taking care to free it when you are done with it.
+
+ \snippet jni/src_qjniobject.cpp QJniObject scope
+*/
+
+/*!
+ \fn template <typename T> QJniObject &QJniObject::operator=(T object)
+
+ Replace the current object with \a object. The old Java object will be released.
+*/
+#define MAKE_JNI_OBJECT_METHODS(Type, Signature) \
+template <> \
+Q_CORE_EXPORT QJniObject QJniObject::callObjectMethod<Type>(const char *methodName) const \
+{ \
+ return callObjectMethod(methodName, Signature); \
+} \
+template <> \
+Q_CORE_EXPORT QJniObject QJniObject::callStaticObjectMethod<Type>(const char *className, \
+ const char *methodName) \
+{ \
+ return callStaticObjectMethod(className, methodName, Signature); \
+} \
+template <> \
+Q_CORE_EXPORT QJniObject QJniObject::callStaticObjectMethod<Type>(jclass clazz, \
+ const char *methodName) \
+{ \
+ return callStaticObjectMethod(clazz, methodName, Signature); \
+}\
+template <>\
+Q_CORE_EXPORT Type QJniObject::object<Type>() const\
+{\
+ return static_cast<Type>(javaObject());\
+}\
+template <>\
+Q_CORE_EXPORT QJniObject &QJniObject::operator=(Type obj)\
+{\
+ assign(static_cast<jobject>(obj));\
+ return *this;\
+}
+
+#define DECLARE_JNI_OBJECT_METHODS(Type, Signature) MAKE_JNI_OBJECT_METHODS(Type, Signature)
+
+DECLARE_JNI_OBJECT_METHODS(jobject, "()Ljava/lang/Object;")
+DECLARE_JNI_OBJECT_METHODS(jclass, "()Ljava/lang/Class;")
+DECLARE_JNI_OBJECT_METHODS(jstring, "()Ljava/lang/String;")
+DECLARE_JNI_OBJECT_METHODS(jobjectArray, "()[Ljava/lang/Object;")
+DECLARE_JNI_OBJECT_METHODS(jbooleanArray, "()[Z")
+DECLARE_JNI_OBJECT_METHODS(jbyteArray, "()[B")
+DECLARE_JNI_OBJECT_METHODS(jshortArray, "()[S")
+DECLARE_JNI_OBJECT_METHODS(jintArray, "()[I")
+DECLARE_JNI_OBJECT_METHODS(jlongArray, "()[J")
+DECLARE_JNI_OBJECT_METHODS(jfloatArray, "()[F")
+DECLARE_JNI_OBJECT_METHODS(jdoubleArray, "()[D")
+DECLARE_JNI_OBJECT_METHODS(jcharArray, "()[C")
+DECLARE_JNI_OBJECT_METHODS(jthrowable, "()Ljava/lang/Throwable;")
+
+/*!
+ \fn template <typename T> void QJniObject::setStaticField(const char *className, const char *fieldName, const char *signature, T value);
+
+ Sets the static field \a fieldName on the class \a className to \a value
+ using the setter with \a signature.
+
+*/
+template <>
+Q_CORE_EXPORT void QJniObject::setStaticField<jobject>(const char *className,
+ const char *fieldName,
+ const char *signature,
+ jobject value)
+{
+ QJniEnvironment env;
+ jclass clazz = loadClass(className, env);
+
+ if (!clazz)
+ return;
+
+ jfieldID id = getCachedFieldID(env, clazz, className, fieldName, signature, true);
+ if (id) {
+ env->SetStaticObjectField(clazz, id, value);
+ env.checkAndClearExceptions();
+ }
+}
+
+/*!
+ \fn template <typename T> void QJniObject::setStaticField(jclass clazz, const char *fieldName, const char *signature, T value);
+
+ Sets the static field \a fieldName on the class \a clazz to \a value using
+ the setter with \a signature.
+*/
+template <> Q_CORE_EXPORT void QJniObject::setStaticField<jobject>(jclass clazz,
+ const char *fieldName,
+ const char *signature,
+ jobject value)
+{
+ QJniEnvironment env;
+ jfieldID id = getFieldID(env, clazz, fieldName, signature, true);
+
+ if (id) {
+ env->SetStaticObjectField(clazz, id, value);
+ env.checkAndClearExceptions();
+ }
+}
+
+/*!
+ \fn T QJniObject::getField(const char *fieldName) const
+
+ Retrieves the value of the field \a fieldName.
+
+ \code
+ QJniObject volumeControl("org/qtproject/qt/TestClass");
+ jint fieldValue = volumeControl.getField<jint>("FIELD_NAME");
+ \endcode
+*/
+
+/*!
+ \fn T QJniObject::getStaticField(const char *className, const char *fieldName)
+
+ Retrieves the value from the static field \a fieldName on the class \a className.
+*/
+
+/*!
+ \fn T QJniObject::getStaticField(jclass clazz, const char *fieldName)
+
+ Retrieves the value from the static field \a fieldName on \a clazz.
+*/
+
+/*!
+ \fn template <typename T> void QJniObject::setStaticField(const char *className, const char *fieldName, T value)
+
+ Sets the static field \a fieldName of the class \a className to \a value.
+*/
+
+/*!
+ \fn template <typename T> void QJniObject::setStaticField(jclass clazz, const char *fieldName, T value)
+
+ Sets the static field \a fieldName of the class \a clazz to \a value.
+*/
+#define MAKE_JNI_PRIMITIVE_FIELDS(FieldName, Type, Signature) \
+template <> Q_CORE_EXPORT Type QJniObject::getField<Type>(const char *fieldName) const \
+{ \
+ QJniEnvironment env; \
+ Type res = 0; \
+ jfieldID id = getCachedFieldID(env, d->m_jclass, d->m_className, fieldName, Signature); \
+ if (id) {\
+ res = env->Get##FieldName##Field(d->m_jobject, id); \
+ if (env.checkAndClearExceptions()) \
+ res = 0; \
+ } \
+ return res;\
+} \
+template <> \
+Q_CORE_EXPORT Type QJniObject::getStaticField<Type>(const char *className, const char *fieldName) \
+{ \
+ QJniEnvironment env; \
+ jclass clazz = loadClass(className, env); \
+ if (!clazz) \
+ return 0; \
+ jfieldID id = getCachedFieldID(env, clazz, toBinaryEncClassName(className), fieldName, \
+ Signature, true); \
+ if (!id) \
+ return 0; \
+ Type res = env->GetStatic##FieldName##Field(clazz, id); \
+ if (env.checkAndClearExceptions()) \
+ res = 0; \
+ return res;\
+} \
+template <>\
+Q_CORE_EXPORT Type QJniObject::getStaticField<Type>(jclass clazz, const char *fieldName)\
+{\
+ QJniEnvironment env;\
+ Type res = 0;\
+ jfieldID id = getFieldID(env, clazz, fieldName, Signature, true);\
+ if (id) {\
+ res = env->GetStatic##FieldName##Field(clazz, id);\
+ if (env.checkAndClearExceptions()) \
+ res = 0; \
+ }\
+ return res;\
+}\
+template <> Q_CORE_EXPORT void QJniObject::setStaticField<Type>(const char *className, \
+ const char *fieldName, \
+ Type value) \
+{ \
+ QJniEnvironment env; \
+ jclass clazz = loadClass(className, env); \
+ if (!clazz) \
+ return; \
+ jfieldID id = getCachedFieldID(env, clazz, className, fieldName, Signature, true); \
+ if (!id) \
+ return; \
+ env->SetStatic##FieldName##Field(clazz, id, value); \
+ env.checkAndClearExceptions(); \
+}\
+template <> Q_CORE_EXPORT void QJniObject::setStaticField<Type>(jclass clazz,\
+ const char *fieldName,\
+ Type value)\
+{\
+ QJniEnvironment env;\
+ jfieldID id = getFieldID(env, clazz, fieldName, Signature, true);\
+ if (id) {\
+ env->SetStatic##FieldName##Field(clazz, id, value);\
+ env.checkAndClearExceptions();\
+ }\
+}\
+template <> Q_CORE_EXPORT void QJniObject::setField<Type>(const char *fieldName, Type value) \
+{ \
+ QJniEnvironment env; \
+ jfieldID id = getCachedFieldID(env, d->m_jclass, d->m_className, fieldName, Signature); \
+ if (id) { \
+ env->Set##FieldName##Field(d->m_jobject, id, value); \
+ env.checkAndClearExceptions(); \
+ } \
+} \
+
+#define DECLARE_JNI_PRIMITIVE_FIELDS(FieldName, Type, Signature) MAKE_JNI_PRIMITIVE_FIELDS(FieldName, Type, \
+ Signature)
+DECLARE_JNI_PRIMITIVE_FIELDS(Boolean, jboolean, "Z")
+DECLARE_JNI_PRIMITIVE_FIELDS(Byte, jbyte, "B")
+DECLARE_JNI_PRIMITIVE_FIELDS(Char, jchar, "C")
+DECLARE_JNI_PRIMITIVE_FIELDS(Short, jshort, "S")
+DECLARE_JNI_PRIMITIVE_FIELDS(Int, jint, "I")
+DECLARE_JNI_PRIMITIVE_FIELDS(Long, jlong, "J")
+DECLARE_JNI_PRIMITIVE_FIELDS(Float, jfloat, "F")
+DECLARE_JNI_PRIMITIVE_FIELDS(Double, jdouble, "D")
+
+/*!
+ \fn QJniObject QJniObject::getStaticObjectField(const char *className, const char *fieldName, const char *signature)
+
+ Retrieves a JNI object from the field \a filedName with \a signature from
+ class \a className.
+
+ \note This function can be used without a template type.
+
+ \code
+ QJniObject jobj = QJniObject::getStaticObjectField("class/with/Fields", "FIELD_NAME",
+ "Ljava/lang/String;");
+ \endcode
+*/
+QJniObject QJniObject::getStaticObjectField(const char *className,
+ const char *fieldName,
+ const char *signature)
+{
+ QJniEnvironment env;
+ jclass clazz = loadClass(className, env);
+ if (!clazz)
+ return QJniObject();
+ jfieldID id = getCachedFieldID(env, clazz, toBinaryEncClassName(className), fieldName,
+ signature, true);
+ if (!id)
+ return QJniObject();
+
+ return getCleanJniObject(env->GetStaticObjectField(clazz, id));
+}
+
+/*!
+ \fn QJniObject QJniObject::getStaticObjectField(jclass clazz, const char *fieldName, const char *signature)
+
+ Retrieves a JNI object from the field \a fieldName with \a signature from
+ class \a clazz.
+
+ \note This function can be used without a template type.
+
+ \code
+ QJniObject jobj = QJniObject::getStaticObjectField(clazz, "FIELD_NAME", "Ljava/lang/String;");
+ \endcode
+*/
+QJniObject QJniObject::getStaticObjectField(jclass clazz,
+ const char *fieldName,
+ const char *signature)
+{
+ QJniEnvironment env;
+ jfieldID id = getFieldID(env, clazz, fieldName, signature, true);
+ if (!id)
+ return QJniObject();
+
+ return getCleanJniObject(env->GetStaticObjectField(clazz, id));
+}
+
+/*!
+ \fn QJniObject QJniObject::getStaticObjectField<jobject>(jclass clazz, const char *fieldName, const char *signature)
+
+ Retrieves a JNI object for \c jobject from the static field \a fieldName with
+ \a signature from \a clazz.
+*/
+template <>
+Q_CORE_EXPORT QJniObject QJniObject::getStaticObjectField<jobject>(jclass clazz,
+ const char *fieldName,
+ const char *signature)
+{
+ return getStaticObjectField(clazz, fieldName, signature);
+}
+
+/*!
+ \fn QJniObject QJniObject::getStaticObjectField<jobject>(const char *className, const char *fieldName, const char *signature)
+
+ Retrieves a JNI object for \c jobject from the static field \a fieldName with
+ \a signature from class \a className.
+*/
+template <>
+Q_CORE_EXPORT QJniObject QJniObject::getStaticObjectField<jobject>(const char *className,
+ const char *fieldName,
+ const char *signature)
+{
+ return getStaticObjectField(className, fieldName, signature);
+}
+
+/*!
+ \fn QJniObject QJniObject::getStaticObjectField<jobjectArray>(jclass clazz, const char *fieldName, const char *signature)
+
+ Retrieves a JNI object for \c jobjectArray from the static field \a fieldName
+ with \a signature from class \a clazz.
+*/
+template <>
+Q_CORE_EXPORT QJniObject QJniObject::getStaticObjectField<jobjectArray>(jclass clazz,
+ const char *fieldName,
+ const char *signature)
+{
+ return getStaticObjectField(clazz, fieldName, signature);
+}
+
+/*!
+ \fn QJniObject QJniObject::getStaticObjectField<jobjectArray>(const char *className, const char *fieldName, const char *signature)
+
+ Retrieves a JNI object for \c jobjectArray from the static field \a fieldName
+ with \a signature from class \a className.
+*/
+template <>
+Q_CORE_EXPORT QJniObject QJniObject::getStaticObjectField<jobjectArray>(const char *className,
+ const char *fieldName,
+ const char *signature)
+{
+ return getStaticObjectField(className, fieldName, signature);
+}
+
+/*!
+ \fn template <typename T> void QJniObject::setField(const char *fieldName, const char *signature, T value)
+
+ Sets the value of \a fieldName with \a signature to \a value.
+
+ \code
+ QJniObject stringArray = ...;
+ QJniObject obj = ...;
+ obj.setField<jobjectArray>("KEY_VALUES", "([Ljava/lang/String;)V",
+ stringArray.object<jobjectArray>())
+ \endcode
+*/
+template <> Q_CORE_EXPORT
+void QJniObject::setField<jobject>(const char *fieldName, const char *signature, jobject value)
+{
+ QJniEnvironment env;
+ jfieldID id = getCachedFieldID(env, d->m_jclass, d->m_className, fieldName, signature);
+ if (id) {
+ env->SetObjectField(d->m_jobject, id, value);
+ env.checkAndClearExceptions();
+ }
+}
+
+template <> Q_CORE_EXPORT
+void QJniObject::setField<jobjectArray>(const char *fieldName,
+ const char *signature,
+ jobjectArray value)
+{
+ QJniEnvironment env;
+ jfieldID id = getCachedFieldID(env, d->m_jclass, d->m_className, fieldName, signature);
+ if (id) {
+ env->SetObjectField(d->m_jobject, id, value);
+ env.checkAndClearExceptions();
+ }
+}
+
+/*!
+ \fn QJniObject QJniObject::getObjectField(const char *fieldName) const
+
+ Retrieves a JNI object from the field \a fieldName.
+
+ \code
+ QJniObject field = jniObject.getObjectField<jstring>("FIELD_NAME");
+ \endcode
+*/
+
+/*!
+ \fn QJniObject QJniObject::getObjectField(const char *fieldName, const char *signature) const
+
+ Retrieves a JNI object from the field \a fieldName with \a signature.
+
+ \note This function can be used without a template type.
+
+ \code
+ QJniObject field = jniObject.getObjectField("FIELD_NAME", "Ljava/lang/String;");
+ \endcode
+*/
+QJniObject QJniObject::getObjectField(const char *fieldName, const char *signature) const
+{
+ QJniEnvironment env;
+ jfieldID id = getCachedFieldID(env, d->m_jclass, d->m_className, fieldName, signature);
+ if (!id)
+ return QJniObject();
+
+ return getCleanJniObject(env->GetObjectField(d->m_jobject, id));
+}
+
+/*!
+ \fn template <typename T> void QJniObject::setField(const char *fieldName, T value)
+
+ Sets the value of \a fieldName to \a value.
+
+ \code
+ QJniObject obj;
+ obj.setField<jint>("AN_INT_FIELD", 10);
+ jstring myString = ...;
+ obj.setField<jstring>("A_STRING_FIELD", myString);
+ \endcode
+*/
+
+/*!
+ \fn QJniObject QJniObject::getStaticObjectField(const char *className, const char *fieldName)
+
+ Retrieves the object from the field \a fieldName on the class \a className.
+
+ \code
+ QJniObject jobj = QJniObject::getStaticObjectField<jstring>("class/with/Fields", "FIELD_NAME");
+ \endcode
+*/
+
+/*!
+ \fn QJniObject QJniObject::getStaticObjectField(jclass clazz, const char *fieldName)
+
+ Retrieves the object from the field \a fieldName on \a clazz.
+
+ \code
+ QJniObject jobj = QJniObject::getStaticObjectField<jstring>(clazz, "FIELD_NAME");
+ \endcode
+*/
+
+#define MAKE_JNI_OBJECT_FILEDS(Type, Signature) \
+template <> Q_CORE_EXPORT void QJniObject::setField<Type>(const char *fieldName, Type value) \
+{ \
+ QJniObject::setField<jobject>(fieldName, Signature, value); \
+} \
+\
+template <> Q_CORE_EXPORT void QJniObject::setStaticField<Type>(const char *className, \
+ const char *fieldName, \
+ Type value) \
+{ \
+ QJniObject::setStaticField<jobject>(className, fieldName, Signature, value); \
+}\
+template <>\
+Q_CORE_EXPORT QJniObject QJniObject::getObjectField<Type>(const char *fieldName) const\
+{\
+ return getObjectField(fieldName, Signature);\
+}\
+template <>\
+Q_CORE_EXPORT QJniObject QJniObject::getStaticObjectField<Type>(jclass clazz,\
+ const char *fieldName)\
+{\
+ return getStaticObjectField(clazz, fieldName, Signature);\
+}\
+template <>\
+Q_CORE_EXPORT QJniObject QJniObject::getStaticObjectField<Type>(const char *className,\
+ const char *fieldName)\
+{\
+ return getStaticObjectField(className, fieldName, Signature);\
+}\
+
+#define DECLARE_JNI_OBJECT_FILEDS(Type, Signature) MAKE_JNI_OBJECT_FILEDS(Type, Signature)
+
+DECLARE_JNI_OBJECT_FILEDS(jobject, "Ljava/lang/Object;")
+DECLARE_JNI_OBJECT_FILEDS(jobjectArray, "[Ljava/lang/Object;")
+DECLARE_JNI_OBJECT_FILEDS(jstring, "Ljava/lang/String;")
+DECLARE_JNI_OBJECT_FILEDS(jclass, "Ljava/lang/Class;")
+DECLARE_JNI_OBJECT_FILEDS(jthrowable, "Ljava/lang/Throwable;")
+DECLARE_JNI_OBJECT_FILEDS(jbooleanArray, "[Z")
+DECLARE_JNI_OBJECT_FILEDS(jbyteArray, "[B")
+DECLARE_JNI_OBJECT_FILEDS(jcharArray, "[C")
+DECLARE_JNI_OBJECT_FILEDS(jshortArray, "[S")
+DECLARE_JNI_OBJECT_FILEDS(jintArray, "[I")
+DECLARE_JNI_OBJECT_FILEDS(jlongArray, "[J")
+DECLARE_JNI_OBJECT_FILEDS(jfloatArray, "[F")
+DECLARE_JNI_OBJECT_FILEDS(jdoubleArray, "[D")
+
+/*!
+ \fn QJniObject QJniObject::fromString(const QString &string)
+
+ Creates a Java string from the QString \a string and returns a QJniObject holding that string.
+
+ \code
+ QString myQString = "QString";
+ QJniObject myJavaString = QJniObject::fromString(myQString);
+ \endcode
+
+ \sa toString()
+*/
+QJniObject QJniObject::fromString(const QString &string)
+{
+ QJniEnvironment env;
+ return getCleanJniObject(env->NewString(reinterpret_cast<const jchar*>(string.constData()),
+ string.length()));
+}
+
+/*!
+ \fn QString QJniObject::toString() const
+
+ Returns a QString with a string representation of the java object.
+ Calling this function on a Java String object is a convenient way of getting the actual string
+ data.
+
+ \code
+ QJniObject string = ...; // "Hello Java"
+ QString qstring = string.toString(); // "Hello Java"
+ \endcode
+
+ \sa fromString()
+*/
+QString QJniObject::toString() const
+{
+ if (!isValid())
+ return QString();
+
+ QJniObject string = callObjectMethod<jstring>("toString");
+ return qt_convertJString(static_cast<jstring>(string.object()));
+}
+
+/*!
+ \fn bool QJniObject::isClassAvailable(const char *className)
+
+ Returns true if the Java class \a className is available.
+
+ \code
+ if (QJniObject::isClassAvailable("java/lang/String")) {
+ // condition statement
+ }
+ \endcode
+*/
+bool QJniObject::isClassAvailable(const char *className)
+{
+ QJniEnvironment env;
+
+ if (!env)
+ return false;
+
+ return loadClass(className, env);;
+}
+
+/*!
+ \fn bool QJniObject::isValid() const
+
+ Returns true if this instance holds a valid Java object.
+
+ \code
+ QJniObject qjniObject; // ==> isValid() == false
+ QJniObject qjniObject(0) // ==> isValid() == false
+ QJniObject qjniObject("could/not/find/Class") // ==> isValid() == false
+ \endcode
+*/
+bool QJniObject::isValid() const
+{
+ return d->m_jobject;
+}
+
+/*!
+ \fn QJniObject QJniObject::fromLocalRef(jobject localRef)
+
+ Creates a QJniObject from the local JNI reference \a localRef.
+ This function takes ownership of \a localRef and frees it before returning.
+
+ \note Only call this function with a local JNI reference. For example, most raw JNI calls,
+ through the JNI environment, return local references to a java object.
+
+ \code
+ jobject localRef = env->GetObjectArrayElement(array, index);
+ QJniObject element = QJniObject::fromLocalRef(localRef);
+ \endcode
+*/
+QJniObject QJniObject::fromLocalRef(jobject lref)
+{
+ QJniObject obj(lref);
+ QJniEnvironment()->DeleteLocalRef(lref);
+ return obj;
+}
+
+bool QJniObject::isSameObject(jobject obj) const
+{
+ return QJniEnvironment()->IsSameObject(d->m_jobject, obj);
+}
+
+bool QJniObject::isSameObject(const QJniObject &other) const
+{
+ return isSameObject(other.d->m_jobject);
+}
+
+void QJniObject::assign(jobject obj)
+{
+ if (isSameObject(obj))
+ return;
+
+ jobject jobj = static_cast<jobject>(obj);
+ d = QSharedPointer<QJniObjectPrivate>::create();
+ if (obj) {
+ QJniEnvironment env;
+ d->m_jobject = env->NewGlobalRef(jobj);
+ jclass objectClass = env->GetObjectClass(jobj);
+ d->m_jclass = static_cast<jclass>(env->NewGlobalRef(objectClass));
+ env->DeleteLocalRef(objectClass);
+ }
+}
+
+jobject QJniObject::javaObject() const
+{
+ return d->m_jobject;
+}
+
+QT_END_NAMESPACE
diff --git a/src/corelib/kernel/qjniobject.h b/src/corelib/kernel/qjniobject.h
new file mode 100644
index 0000000000..0f88695e73
--- /dev/null
+++ b/src/corelib/kernel/qjniobject.h
@@ -0,0 +1,213 @@
+/****************************************************************************
+**
+** Copyright (C) 2021 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtCore module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QJNIOBJECT_H
+#define QJNIOBJECT_H
+
+#include <QtCore/QSharedPointer>
+
+#if defined(Q_OS_ANDROID) && !defined(Q_OS_ANDROID_EMBEDDED)
+#include <jni.h>
+#else
+class jclass;
+class jobject;
+#endif
+
+QT_BEGIN_NAMESPACE
+
+class QJniObjectPrivate;
+
+class Q_CORE_EXPORT QJniObject
+{
+public:
+ QJniObject();
+ explicit QJniObject(const char *className);
+ explicit QJniObject(const char *className, const char *signature, ...);
+ explicit QJniObject(jclass clazz);
+ explicit QJniObject(jclass clazz, const char *signature, ...);
+ QJniObject(jobject globalRef);
+ ~QJniObject();
+
+ template <typename T>
+ T object() const;
+ jobject object() const;
+
+ template <typename T>
+ T callMethod(const char *methodName, const char *signature, ...) const;
+ template <typename T>
+ T callMethod(const char *methodName) const;
+ template <typename T>
+ QJniObject callObjectMethod(const char *methodName) const;
+ QJniObject callObjectMethod(const char *methodName, const char *signature, ...) const;
+
+ template <typename T>
+ static T callStaticMethod(const char *className, const char *methodName,
+ const char *signature, ...);
+ template <typename T>
+ static T callStaticMethod(const char *className, const char *methodName);
+ template <typename T>
+ static T callStaticMethod(jclass clazz, const char *methodName, const char *signature, ...);
+ template <typename T>
+ static T callStaticMethod(jclass clazz, const char *methodName);
+
+ template <typename T>
+ static QJniObject callStaticObjectMethod(const char *className, const char *methodName);
+ static QJniObject callStaticObjectMethod(const char *className,
+ const char *methodName,
+ const char *signature, ...);
+
+ template <typename T>
+ static QJniObject callStaticObjectMethod(jclass clazz, const char *methodName);
+ static QJniObject callStaticObjectMethod(jclass clazz,
+ const char *methodName,
+ const char *signature, ...);
+
+ template <typename T>
+ T getField(const char *fieldName) const;
+
+ template <typename T>
+ static T getStaticField(const char *className, const char *fieldName);
+ template <typename T>
+ static T getStaticField(jclass clazz, const char *fieldName);
+
+ template <typename T>
+ QJniObject getObjectField(const char *fieldName) const;
+ QJniObject getObjectField(const char *fieldName, const char *signature) const;
+
+ template <typename T>
+ static QJniObject getStaticObjectField(const char *className, const char *fieldName);
+ static QJniObject getStaticObjectField(const char *className,
+ const char *fieldName,
+ const char *signature);
+ template <typename T>
+ static QJniObject getStaticObjectField(const char *className,
+ const char *fieldName,
+ const char *signature);
+
+ template <typename T>
+ static QJniObject getStaticObjectField(jclass clazz, const char *fieldName);
+ static QJniObject getStaticObjectField(jclass clazz, const char *fieldName,
+ const char *signature);
+ template <typename T>
+ static QJniObject getStaticObjectField(jclass clazz, const char *fieldName,
+ const char *signature);
+
+ template <typename T>
+ void setField(const char *fieldName, T value);
+ template <typename T>
+ void setField(const char *fieldName, const char *signature, T value);
+ template <typename T>
+ static void setStaticField(const char *className, const char *fieldName, T value);
+ template <typename T>
+ static void setStaticField(const char *className, const char *fieldName,
+ const char *signature, T value);
+ template <typename T>
+ static void setStaticField(jclass clazz, const char *fieldName,
+ const char *signature, T value);
+
+ template <typename T>
+ static void setStaticField(jclass clazz, const char *fieldName, T value);
+
+ static QJniObject fromString(const QString &string);
+ QString toString() const;
+
+ static bool isClassAvailable(const char *className);
+ bool isValid() const;
+
+ // This function takes ownership of the jobject and releases the local ref. before returning.
+ static QJniObject fromLocalRef(jobject lref);
+
+ template <typename T> QJniObject &operator=(T obj);
+
+private:
+ struct QVaListPrivate { operator va_list &() const { return m_args; } va_list &m_args; };
+
+ QJniObject(const char *className, const char *signature, const QVaListPrivate &args);
+ QJniObject(jclass clazz, const char *signature, const QVaListPrivate &args);
+
+ template <typename T>
+ T callMethodV(const char *methodName, const char *signature, va_list args) const;
+ QJniObject callObjectMethodV(const char *methodName,
+ const char *signature,
+ va_list args) const;
+ template <typename T>
+ static T callStaticMethodV(const char *className,
+ const char *methodName,
+ const char *signature,
+ va_list args);
+ template <typename T>
+ static T callStaticMethodV(jclass clazz,
+ const char *methodName,
+ const char *signature,
+ va_list args);
+
+ static QJniObject callStaticObjectMethodV(const char *className,
+ const char *methodName,
+ const char *signature,
+ va_list args);
+
+ static QJniObject callStaticObjectMethodV(jclass clazz,
+ const char *methodName,
+ const char *signature,
+ va_list args);
+
+ bool isSameObject(jobject obj) const;
+ bool isSameObject(const QJniObject &other) const;
+ void assign(jobject obj);
+ jobject javaObject() const;
+
+ friend bool operator==(const QJniObject &, const QJniObject &);
+ friend bool operator!=(const QJniObject&, const QJniObject&);
+
+ QSharedPointer<QJniObjectPrivate> d;
+};
+
+inline bool operator==(const QJniObject &obj1, const QJniObject &obj2)
+{
+ return obj1.isSameObject(obj2);
+}
+
+inline bool operator!=(const QJniObject &obj1, const QJniObject &obj2)
+{
+ return !obj1.isSameObject(obj2);
+}
+
+QT_END_NAMESPACE
+
+#endif // QJNIOBJECT_H
diff --git a/src/corelib/kernel/qjnionload.cpp b/src/corelib/kernel/qjnionload.cpp
index ec56d3de66..dbaee48ac6 100644
--- a/src/corelib/kernel/qjnionload.cpp
+++ b/src/corelib/kernel/qjnionload.cpp
@@ -1,6 +1,6 @@
/****************************************************************************
**
-** Copyright (C) 2016 The Qt Company Ltd.
+** Copyright (C) 2021 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the QtCore module of the Qt Toolkit.
@@ -37,8 +37,9 @@
**
****************************************************************************/
-#include <jni.h>
#include "qjnihelpers_p.h"
+
+#include <jni.h>
#include <android/log.h>
static const char logTag[] = "QtCore";
diff --git a/src/corelib/kernel/qmath.h b/src/corelib/kernel/qmath.h
index 176e7d7978..18530714e0 100644
--- a/src/corelib/kernel/qmath.h
+++ b/src/corelib/kernel/qmath.h
@@ -1,6 +1,6 @@
/****************************************************************************
**
-** Copyright (C) 2016 The Qt Company Ltd.
+** Copyright (C) 2021 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the QtCore module of the Qt Toolkit.
@@ -135,6 +135,72 @@ template <typename T> auto qSqrt(T v)
return sqrt(v);
}
+namespace QtPrivate {
+template <typename R, typename F> // For qfloat16 to specialize
+struct QHypotType { using type = decltype(std::hypot(R(1), F(1))); };
+
+// Implements hypot() without limiting number of arguments:
+template <typename T>
+class QHypotHelper
+{
+ T scale, total;
+ template <typename F> friend class QHypotHelper;
+ QHypotHelper(T first, T prior) : scale(first), total(prior) {}
+public:
+ QHypotHelper(T first) : scale(qAbs(first)), total(1) {}
+ T result() const
+ { return qIsFinite(scale) ? scale > 0 ? scale * T(std::sqrt(total)) : T(0) : scale; }
+
+ template<typename F, typename ...Fs>
+ auto add(F first, Fs... rest) const
+ { return add(first).add(rest...); }
+
+ template<typename F, typename R = typename QHypotType<T, F>::type>
+ QHypotHelper<R> add(F next) const
+ {
+ if (qIsInf(scale) || (qIsNaN(scale) && !qIsInf(next)))
+ return QHypotHelper<R>(scale, R(1));
+ if (qIsNaN(next))
+ return QHypotHelper<R>(next, R(1));
+ const R val = qAbs(next);
+ if (!(scale > 0) || qIsInf(next))
+ return QHypotHelper<R>(val, R(1));
+ if (!(val > 0))
+ return QHypotHelper<R>(scale, total);
+ if (val > scale) {
+ const R ratio = scale / next;
+ return QHypotHelper<R>(val, total * ratio * ratio + 1);
+ }
+ const R ratio = next / scale;
+ return QHypotHelper<R>(scale, total + ratio * ratio);
+ }
+};
+} // QtPrivate
+
+template<typename F, typename ...Fs>
+auto qHypot(F first, Fs... rest)
+{
+ return QtPrivate::QHypotHelper<F>(first).add(rest...).result();
+}
+
+// However, where possible, use the standard library implementations:
+template <typename Tx, typename Ty>
+auto qHypot(Tx x, Ty y)
+{
+ // C99 has hypot(), hence C++11 has std::hypot()
+ using std::hypot;
+ return hypot(x, y);
+}
+
+#if __cpp_lib_hypot >= 201603L // Expected to be true
+template <typename Tx, typename Ty, typename Tz>
+auto qHypot(Tx x, Ty y, Tz z)
+{
+ using std::hypot;
+ return hypot(x, y, z);
+}
+#endif // else: no need to over-ride the arbitrarily-many-arg form
+
template <typename T> auto qLn(T v)
{
using std::log;
diff --git a/src/corelib/kernel/qmath.qdoc b/src/corelib/kernel/qmath.qdoc
index b0a52a4a52..13655bcc11 100644
--- a/src/corelib/kernel/qmath.qdoc
+++ b/src/corelib/kernel/qmath.qdoc
@@ -1,6 +1,6 @@
/****************************************************************************
**
-** Copyright (C) 2016 The Qt Company Ltd.
+** Copyright (C) 2020 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the documentation of the Qt Toolkit.
@@ -139,7 +139,7 @@
This function will return the angle (argument) of that point.
\relates <QtMath>
- \sa qAtan()
+ \sa qAtan(), qHypot()
*/
/*!
@@ -148,7 +148,57 @@
This function returns a NaN if \a v is a negative number.
\relates <QtMath>
- \sa qPow()
+ \sa qPow(), qHypot()
+*/
+
+/*!
+ \since 6.1
+ \overload
+ \fn template <typename Tx, typename Ty> auto qHypot(Tx x, Ty y)
+ Returns the distance of a point (x, y) from the origin (0, 0).
+
+ This is qSqrt(x * x + y * y), optimized.
+ In particular, underflow and overflow may be avoided.
+
+ Accepts any mix of numeric types, returning the same
+ floating-point type as std::hypot(). If either parameter is
+ infinite, so is the result; otherwise, if either is a NaN, so is
+ the result.
+
+ \relates <QtMath>
+ \sa qSqrt(), qAtan2()
+*/
+
+/*!
+ \since 6.1
+ \overload
+ \fn template <typename Tx, typename Ty, typename Tz> auto qHypot(Tx x, Ty y, Tz z)
+ Returns the distance of a point (x, y, z) from the origin (0, 0, 0).
+
+ This is qSqrt(x * x + y * y + z * z), optimized where supported.
+ In particular, underflow and overflow may be avoided.
+
+ Accepts any mix of numeric types, returning the same
+ floating-point type as std::hypot(). If any parameter is infinite,
+ so is the result; otherwise, if any is NaN, so is the result.
+
+ \relates <QtMath>
+ \sa qSqrt()
+*/
+
+/*!
+ \since 6.1
+ \fn template<typename F, typename ...Fs> auto qHypot(F first, Fs... rest)
+ Returns the distance from origin in arbitrarily many dimensions
+
+ This is as for the two-argument and three-argument forms, supported by
+ std::hypot(), but with as many numeric parameters as you care to pass to
+ it. Uses \a first and each of the \a rest as co-ordinates, performing a
+ calculation equivalent to squaring each, summing and returning the square
+ root, save that underflow and overflow are avoided as far as possible.
+
+ \relates <QtMath>
+ \sa qSqrt()
*/
/*!
diff --git a/src/corelib/kernel/qmetaobject.cpp b/src/corelib/kernel/qmetaobject.cpp
index 650ea60617..a574bec179 100644
--- a/src/corelib/kernel/qmetaobject.cpp
+++ b/src/corelib/kernel/qmetaobject.cpp
@@ -384,6 +384,33 @@ QString QMetaObject::tr(const char *s, const char *c, int n) const
{
return QCoreApplication::translate(objectClassName(this), s, c, n);
}
+
+/*!
+ \since 6.2
+ Returns the metatype corresponding to this metaobject.
+ If the metaobject originates from a namespace, an invalid metatype is returned.
+ */
+QMetaType QMetaObject::metaType() const
+{
+
+ const QMetaObjectPrivate *d = priv(this->d.data);
+ if (d->revision < 10) {
+ // before revision 10, we did not store the metatype in the metatype array
+ return QMetaType::fromName(className());
+ } else {
+ /* in the metatype array, we store
+ idx: 0 propertyCount - 1 propertyCount
+ data:QMetaType(prop0), ..., QMetaType(propPropCount-1), QMetaType(class),...
+ */
+ auto iface = this->d.metaTypes[d->propertyCount];
+ if (iface == QtPrivate::qMetaTypeInterfaceForType<void>())
+ return QMetaType(); // return invalid meta-type for namespaces
+ if (iface)
+ return QMetaType(iface);
+ else // in case of a dynamic metaobject, we might have no metatype stored
+ return QMetaType::fromName(className()); // try lookup by name in that case
+ }
+}
#endif // QT_NO_TRANSLATION
/*!
diff --git a/src/corelib/kernel/qmetaobject_p.h b/src/corelib/kernel/qmetaobject_p.h
index dbd82ba27b..e35d0b5e0c 100644
--- a/src/corelib/kernel/qmetaobject_p.h
+++ b/src/corelib/kernel/qmetaobject_p.h
@@ -172,7 +172,8 @@ struct QMetaObjectPrivate
// revision 7 is Qt 5.0 everything lower is not supported
// revision 8 is Qt 5.12: It adds the enum name to QMetaEnum
// revision 9 is Qt 6.0: It adds the metatype of properties and methods
- enum { OutputRevision = 9 }; // Used by moc, qmetaobjectbuilder and qdbus
+ // revision 10 is Qt 6.2: The metatype of the metaobject is stored in the metatypes array
+ enum { OutputRevision = 10 }; // Used by moc, qmetaobjectbuilder and qdbus
enum { IntsPerMethod = QMetaMethod::Data::Size };
enum { IntsPerEnum = QMetaEnum::Data::Size };
enum { IntsPerProperty = QMetaProperty::Data::Size };
diff --git a/src/corelib/kernel/qmetaobjectbuilder.cpp b/src/corelib/kernel/qmetaobjectbuilder.cpp
index c842cd68b9..8c55bdb3ae 100644
--- a/src/corelib/kernel/qmetaobjectbuilder.cpp
+++ b/src/corelib/kernel/qmetaobjectbuilder.cpp
@@ -1203,7 +1203,7 @@ static int buildMetaObject(QMetaObjectBuilderPrivate *d, char *buf,
- int(d->methods.size()) // return "parameters" don't have names
- int(d->constructors.size()); // "this" parameters don't have names
if constexpr (mode == Construct) {
- static_assert(QMetaObjectPrivate::OutputRevision == 9, "QMetaObjectBuilder should generate the same version as moc");
+ static_assert(QMetaObjectPrivate::OutputRevision == 10, "QMetaObjectBuilder should generate the same version as moc");
pmeta->revision = QMetaObjectPrivate::OutputRevision;
pmeta->flags = d->flags;
pmeta->className = 0; // Class name is always the first string.
@@ -1281,7 +1281,8 @@ static int buildMetaObject(QMetaObjectBuilderPrivate *d, char *buf,
// Output the methods in the class.
Q_ASSERT(!buf || dataIndex == pmeta->methodData);
- int parameterMetaTypesIndex = int(d->properties.size());
+ // + 1 for metatype of this metaobject
+ int parameterMetaTypesIndex = int(d->properties.size()) + 1;
for (const auto &method : d->methods) {
[[maybe_unused]] int name = strings.enter(method.name());
int argc = method.parameterCount();
@@ -1448,6 +1449,10 @@ static int buildMetaObject(QMetaObjectBuilderPrivate *d, char *buf,
*types = reinterpret_cast<QtPrivate::QMetaTypeInterface *&>(mt);
types++;
}
+ // add metatype interface for this metaobject - must be null
+ // as we can't know our metatype
+ *types = nullptr;
+ types++;
for (const auto &method: d->methods) {
QMetaType mt(QMetaType::fromName(method.returnType).id());
*types = reinterpret_cast<QtPrivate::QMetaTypeInterface *&>(mt);
@@ -1526,274 +1531,6 @@ void QMetaObjectBuilder::setStaticMetacallFunction
d->staticMetacallFunction = value;
}
-#ifndef QT_NO_DATASTREAM
-
-/*!
- Serializes the contents of the meta object builder onto \a stream.
-
- \sa deserialize()
-*/
-void QMetaObjectBuilder::serialize(QDataStream &stream) const
-{
- int index;
-
- // Write the class and super class names.
- stream << d->className;
- if (d->superClass)
- stream << QByteArray(d->superClass->className());
- else
- stream << QByteArray();
-
- // Write the counts for each type of class member.
- stream << int(d->classInfoNames.size());
- stream << int(d->methods.size());
- stream << int(d->properties.size());
- stream << int(d->enumerators.size());
- stream << int(d->constructors.size());
- stream << int(d->relatedMetaObjects.size());
-
- // Write the items of class information.
- for (index = 0; index < d->classInfoNames.size(); ++index) {
- stream << d->classInfoNames[index];
- stream << d->classInfoValues[index];
- }
-
- // Write the methods.
- for (const auto &method : d->methods) {
- stream << method.signature;
- stream << method.returnType;
- stream << method.parameterNames;
- stream << method.tag;
- stream << method.attributes;
- if (method.revision)
- stream << method.revision;
- }
-
- // Write the properties.
- for (const auto &property : d->properties) {
- stream << property.name;
- stream << property.type;
- stream << property.flags;
- stream << property.notifySignal;
- stream << property.revision;
- }
-
- // Write the enumerators.
- for (const auto &enumerator : d->enumerators) {
- stream << enumerator.name;
- stream << enumerator.isFlag;
- stream << enumerator.isScoped;
- stream << enumerator.keys;
- stream << enumerator.values;
- }
-
- // Write the constructors.
- for (const auto &ctor : d->constructors) {
- stream << ctor.signature;
- stream << ctor.returnType;
- stream << ctor.parameterNames;
- stream << ctor.tag;
- stream << ctor.attributes;
- }
-
- // Write the related meta objects.
- for (index = 0; index < d->relatedMetaObjects.size(); ++index) {
- const QMetaObject *meta = d->relatedMetaObjects[index];
- stream << QByteArray(meta->className());
- }
-
- // Add an extra empty QByteArray for additional data in future versions.
- // This should help maintain backwards compatibility, allowing older
- // versions to read newer data.
- stream << QByteArray();
-}
-
-// Resolve a class name using the name reference map.
-static const QMetaObject *resolveClassName(const QMap<QByteArray, const QMetaObject *> &references,
- const QByteArray &name)
-{
- if (name == QByteArray("QObject"))
- return &QObject::staticMetaObject;
- else
- return references.value(name, nullptr);
-}
-
-/*!
- Deserializes a meta object builder from \a stream into
- this meta object builder.
-
- The \a references parameter specifies a mapping from class names
- to QMetaObject instances for resolving the super class name and
- related meta objects in the object that is deserialized.
- The meta object for QObject is implicitly added to \a references
- and does not need to be supplied.
-
- The QDataStream::status() value on \a stream will be set to
- QDataStream::ReadCorruptData if the input data is corrupt.
- The status will be set to QDataStream::ReadPastEnd if the
- input was exhausted before the full meta object was read.
-
- \sa serialize()
-*/
-void QMetaObjectBuilder::deserialize
- (QDataStream& stream,
- const QMap<QByteArray, const QMetaObject *>& references)
-{
- QByteArray name;
- const QMetaObject *cl;
- int index;
-
- // Clear all members in the builder to their default states.
- d->className.clear();
- d->superClass = &QObject::staticMetaObject;
- d->classInfoNames.clear();
- d->classInfoValues.clear();
- d->methods.clear();
- d->properties.clear();
- d->enumerators.clear();
- d->constructors.clear();
- d->relatedMetaObjects.clear();
- d->staticMetacallFunction = nullptr;
-
- // Read the class and super class names.
- stream >> d->className;
- stream >> name;
- if (name.isEmpty()) {
- d->superClass = nullptr;
- } else if ((cl = resolveClassName(references, name)) != nullptr) {
- d->superClass = cl;
- } else {
- stream.setStatus(QDataStream::ReadCorruptData);
- return;
- }
-
- // Read the counts for each type of class member.
- int classInfoCount, methodCount, propertyCount;
- int enumeratorCount, constructorCount, relatedMetaObjectCount;
- stream >> classInfoCount;
- stream >> methodCount;
- stream >> propertyCount;
- stream >> enumeratorCount;
- stream >> constructorCount;
- stream >> relatedMetaObjectCount;
- if (classInfoCount < 0 || methodCount < 0 ||
- propertyCount < 0 || enumeratorCount < 0 ||
- constructorCount < 0 || relatedMetaObjectCount < 0) {
- stream.setStatus(QDataStream::ReadCorruptData);
- return;
- }
-
- // Read the items of class information.
- for (index = 0; index < classInfoCount; ++index) {
- if (stream.status() != QDataStream::Ok)
- return;
- QByteArray value;
- stream >> name;
- stream >> value;
- addClassInfo(name, value);
- }
-
- // Read the member methods.
- for (index = 0; index < methodCount; ++index) {
- if (stream.status() != QDataStream::Ok)
- return;
- stream >> name;
- addMethod(name);
- QMetaMethodBuilderPrivate &method = d->methods[index];
- stream >> method.returnType;
- stream >> method.parameterNames;
- stream >> method.tag;
- stream >> method.attributes;
- if (method.attributes & MethodRevisioned)
- stream >> method.revision;
- if (method.methodType() == QMetaMethod::Constructor) {
- // Cannot add a constructor in this set of methods.
- stream.setStatus(QDataStream::ReadCorruptData);
- return;
- }
- }
-
- // Read the properties.
- for (index = 0; index < propertyCount; ++index) {
- if (stream.status() != QDataStream::Ok)
- return;
- QByteArray type;
- stream >> name;
- stream >> type;
- addProperty(name, type);
- QMetaPropertyBuilderPrivate &property = d->properties[index];
- stream >> property.flags;
- stream >> property.notifySignal;
- if (property.notifySignal < -1 ||
- property.notifySignal >= int(d->methods.size())) {
- // Notify signal method index is out of range.
- stream.setStatus(QDataStream::ReadCorruptData);
- return;
- }
- if (property.notifySignal >= 0 &&
- d->methods[property.notifySignal].methodType() != QMetaMethod::Signal) {
- // Notify signal method index does not refer to a signal.
- stream.setStatus(QDataStream::ReadCorruptData);
- return;
- }
- stream >> property.revision;
- }
-
- // Read the enumerators.
- for (index = 0; index < enumeratorCount; ++index) {
- if (stream.status() != QDataStream::Ok)
- return;
- stream >> name;
- addEnumerator(name);
- QMetaEnumBuilderPrivate &enumerator = d->enumerators[index];
- stream >> enumerator.isFlag;
- stream >> enumerator.isScoped;
- stream >> enumerator.keys;
- stream >> enumerator.values;
- if (enumerator.keys.size() != enumerator.values.size()) {
- // Mismatch between number of keys and number of values.
- stream.setStatus(QDataStream::ReadCorruptData);
- return;
- }
- }
-
- // Read the constructor methods.
- for (index = 0; index < constructorCount; ++index) {
- if (stream.status() != QDataStream::Ok)
- return;
- stream >> name;
- addConstructor(name);
- QMetaMethodBuilderPrivate &method = d->constructors[index];
- stream >> method.returnType;
- stream >> method.parameterNames;
- stream >> method.tag;
- stream >> method.attributes;
- if (method.methodType() != QMetaMethod::Constructor) {
- // The type must be Constructor.
- stream.setStatus(QDataStream::ReadCorruptData);
- return;
- }
- }
-
- // Read the related meta objects.
- for (index = 0; index < relatedMetaObjectCount; ++index) {
- if (stream.status() != QDataStream::Ok)
- return;
- stream >> name;
- cl = resolveClassName(references, name);
- if (!cl) {
- stream.setStatus(QDataStream::ReadCorruptData);
- return;
- }
- addRelatedMetaObject(cl);
- }
-
- // Read the extra data block, which is reserved for future use.
- stream >> name;
-}
-
-#endif // !QT_NO_DATASTREAM
-
/*!
\class QMetaMethodBuilder
\inmodule QtCore
diff --git a/src/corelib/kernel/qmetaobjectbuilder_p.h b/src/corelib/kernel/qmetaobjectbuilder_p.h
index 0f545008f2..0435e9cd97 100644
--- a/src/corelib/kernel/qmetaobjectbuilder_p.h
+++ b/src/corelib/kernel/qmetaobjectbuilder_p.h
@@ -168,13 +168,6 @@ public:
QMetaObject *toMetaObject() const;
-#ifndef QT_NO_DATASTREAM
- void serialize(QDataStream& stream) const;
- void deserialize
- (QDataStream& stream,
- const QMap<QByteArray, const QMetaObject *>& references);
-#endif
-
private:
Q_DISABLE_COPY_MOVE(QMetaObjectBuilder)
diff --git a/src/corelib/kernel/qmetatype.cpp b/src/corelib/kernel/qmetatype.cpp
index 859a8946a4..43e633faaf 100644
--- a/src/corelib/kernel/qmetatype.cpp
+++ b/src/corelib/kernel/qmetatype.cpp
@@ -37,7 +37,9 @@
**
****************************************************************************/
+#define QT_QMETATYPE_BC_COMPAT 1
#include "qmetatype.h"
+#undef QT_QMETATYPE_BC_COMPAT
#include "qmetatype_p.h"
#include "qobjectdefs.h"
#include "qdatetime.h"
@@ -422,6 +424,7 @@ Q_GLOBAL_STATIC(QMetaTypeCustomRegistry, customTypeRegistry)
\omitvalue IsGadget \omit This type is a Q_GADGET and it's corresponding QMetaObject can be accessed with QMetaType::metaObject Since 5.5. \endomit
\omitvalue PointerToGadget
\omitvalue IsQmlList
+ \value IsConst Indicates that values of this types are immutable; for instance because they are pointers to const objects.
*/
/*!
@@ -487,21 +490,35 @@ bool QMetaType::isRegistered() const
Returns id type hold by this QMetatype instance.
*/
+
+// keep in sync with version in header
+// ### Qt 7::remove BC helper
int QMetaType::id() const
{
if (d_ptr) {
- if (d_ptr->typeId)
- return d_ptr->typeId;
- auto reg = customTypeRegistry();
- if (reg) {
- return reg->registerCustomType(d_ptr);
- }
+ if (int id = d_ptr->typeId.loadRelaxed())
+ return id;
+ return idHelper();
+ }
+ return 0;
+}
+
+/*!
+ \internal
+ The slowpath of id(). Precondition: d_ptr != nullptr
+*/
+int QMetaType::idHelper() const
+{
+ Q_ASSERT(d_ptr);
+ auto reg = customTypeRegistry();
+ if (reg) {
+ return reg->registerCustomType(d_ptr);
}
return 0;
}
/*!
- \fn constexpr bool QMetaType::sizeOf() const
+ \fn constexpr qsizetype QMetaType::sizeOf() const
\since 5.0
Returns the size of the type in bytes (i.e. sizeof(T),
@@ -571,7 +588,7 @@ int QMetaType::id() const
*/
void *QMetaType::create(const void *copy) const
{
- if (d_ptr) {
+ if (d_ptr && (copy ? !!d_ptr->copyCtr : !!d_ptr->defaultCtr)) {
void *where =
#ifdef __STDCPP_DEFAULT_NEW_ALIGNMENT__
d_ptr->alignment > __STDCPP_DEFAULT_NEW_ALIGNMENT__ ?
@@ -594,8 +611,9 @@ void *QMetaType::create(const void *copy) const
*/
void QMetaType::destroy(void *data) const
{
- if (d_ptr && d_ptr->dtor) {
- d_ptr->dtor(d_ptr, data);
+ if (d_ptr) {
+ if (d_ptr->dtor)
+ d_ptr->dtor(d_ptr, data);
if (d_ptr->alignment > __STDCPP_DEFAULT_NEW_ALIGNMENT__) {
operator delete(data, std::align_val_t(d_ptr->alignment));
} else {
@@ -854,8 +872,12 @@ static const struct : QMetaTypeModuleHelper
{
Q_ASSERT(fromTypeId != toTypeId);
+ // canConvert calls with two nullptr
bool onlyCheck = (from == nullptr && to == nullptr);
+ // other callers must provide two valid pointers
+ Q_ASSERT(onlyCheck || (bool(from) && bool(to)));
+
using Char = char;
using SChar = signed char;
using UChar = unsigned char;
@@ -1827,7 +1849,7 @@ static bool convertFromEnum(QMetaType fromType, const void *from, QMetaType toTy
static bool convertToEnum(QMetaType fromType, const void *from, QMetaType toType, void *to)
{
int fromTypeId = fromType.id();
- qlonglong value;
+ qlonglong value = -1;
bool ok = false;
#ifndef QT_NO_QOBJECT
if (fromTypeId == QMetaType::QString || fromTypeId == QMetaType::QByteArray) {
@@ -2156,12 +2178,18 @@ static bool convertQObject(QMetaType fromType, const void *from, QMetaType toTyp
Converts the object at \a from from \a fromTypeId to the preallocated space at \a to
typed \a toTypeId. Returns \c true, if the conversion succeeded, otherwise false.
+
+ Both \a from and \a to have to be valid pointers.
+
\since 5.2
*/
/*!
Converts the object at \a from from \a fromType to the preallocated space at \a to
typed \a toType. Returns \c true, if the conversion succeeded, otherwise false.
+
+ Both \a from and \a to have to be valid pointers.
+
\since 5.2
*/
bool QMetaType::convert(QMetaType fromType, const void *from, QMetaType toType, void *to)
diff --git a/src/corelib/kernel/qmetatype.h b/src/corelib/kernel/qmetatype.h
index e45c40c372..c08a87efd9 100644
--- a/src/corelib/kernel/qmetatype.h
+++ b/src/corelib/kernel/qmetatype.h
@@ -258,7 +258,42 @@ struct QMetaObject;
namespace QtPrivate
{
-class QMetaTypeInterface;
+class QMetaTypeInterface
+{
+public:
+ ushort revision; // 0 in Qt 6.0. Can increase if new field are added
+ ushort alignment;
+ uint size;
+ uint flags;
+ mutable QBasicAtomicInt typeId;
+
+ using MetaObjectFn = const QMetaObject *(*)(const QMetaTypeInterface *);
+ MetaObjectFn metaObjectFn;
+
+ const char *name;
+
+ using DefaultCtrFn = void (*)(const QMetaTypeInterface *, void *);
+ DefaultCtrFn defaultCtr;
+ using CopyCtrFn = void (*)(const QMetaTypeInterface *, void *, const void *);
+ CopyCtrFn copyCtr;
+ using MoveCtrFn = void (*)(const QMetaTypeInterface *, void *, void *);
+ MoveCtrFn moveCtr;
+ using DtorFn = void (*)(const QMetaTypeInterface *, void *);
+ DtorFn dtor;
+ using EqualsFn = bool (*)(const QMetaTypeInterface *, const void *, const void *);
+ EqualsFn equals;
+ using LessThanFn = bool (*)(const QMetaTypeInterface *, const void *, const void *);
+ LessThanFn lessThan;
+ using DebugStreamFn = void (*)(const QMetaTypeInterface *, QDebug &, const void *);
+ DebugStreamFn debugStream;
+ using DataStreamOutFn = void (*)(const QMetaTypeInterface *, QDataStream &, const void *);
+ DataStreamOutFn dataStreamOut;
+ using DataStreamInFn = void (*)(const QMetaTypeInterface *, QDataStream &, void *);
+ DataStreamInFn dataStreamIn;
+
+ using LegacyRegisterOp = void (*)();
+ LegacyRegisterOp legacyRegisterOp;
+};
/*!
This template is used for implicit conversion from type From to type To.
@@ -363,6 +398,7 @@ public:
PointerToGadget = 0x400,
IsPointer = 0x800,
IsQmlList =0x1000, // used in the QML engine to recognize QQmlListProperty<T> and list<T>
+ IsConst = 0x2000,
};
Q_DECLARE_FLAGS(TypeFlags, TypeFlag)
@@ -408,7 +444,21 @@ public:
bool isValid() const;
bool isRegistered() const;
+#if defined(QT_QMETATYPE_BC_COMPAT)
int id() const;
+#else
+ // ### Qt 7: Remove traces of out of line version
+ // unused int parameter is used to avoid ODR violation
+ int id(int = 0) const
+ {
+ if (d_ptr) {
+ if (int id = d_ptr->typeId.loadRelaxed())
+ return id;
+ return idHelper();
+ }
+ return 0;
+ };
+#endif
constexpr qsizetype sizeOf() const;
constexpr qsizetype alignOf() const;
constexpr TypeFlags flags() const;
@@ -444,7 +494,17 @@ public:
constexpr static QMetaType fromType();
static QMetaType fromName(QByteArrayView name);
- friend bool operator==(QMetaType a, QMetaType b) { return a.d_ptr == b.d_ptr || a.id() == b.id(); }
+ friend bool operator==(QMetaType a, QMetaType b)
+ {
+ if (a.d_ptr == b.d_ptr)
+ return true;
+ if (!a.d_ptr || !b.d_ptr)
+ return false; // one type is undefined, the other is defined
+ // avoid id call if we already have the id
+ const int aId = a.id();
+ const int bId = b.id();
+ return aId == bId;
+ }
friend bool operator!=(QMetaType a, QMetaType b) { return !(a == b); }
public:
@@ -662,6 +722,7 @@ public:
const QtPrivate::QMetaTypeInterface *iface() { return d_ptr; }
private:
+ int idHelper() const;
friend class QVariant;
const QtPrivate::QMetaTypeInterface *d_ptr = nullptr;
};
@@ -773,6 +834,7 @@ namespace QtPrivate
#ifndef QT_NO_QOBJECT
static yes_type checkType(QObject* );
+ static yes_type checkType(const QObject* );
#endif
static no_type checkType(...);
static_assert(sizeof(T), "Type argument of Q_PROPERTY or Q_DECLARE_METATYPE(T*) must be fully defined");
@@ -1103,6 +1165,7 @@ namespace QtPrivate {
| (QTypeInfo<T>::isPointer ? QMetaType::IsPointer : 0)
| (IsUnsignedEnum<T> ? QMetaType::IsUnsignedEnumeration : 0)
| (IsQmlListType<T> ? QMetaType::IsQmlList : 0)
+ | (std::is_const_v<std::remove_pointer_t<T>> ? QMetaType::IsConst : 0)
};
};
@@ -1603,43 +1666,6 @@ struct AssociativeKeyTypeIsMetaType<T, true> : AssociativeMappedTypeIsMetaType<T
}
};
-class QMetaTypeInterface
-{
-public:
- ushort revision; // 0 in Qt 6.0. Can increase if new field are added
- ushort alignment;
- uint size;
- uint flags;
- mutable QBasicAtomicInt typeId;
-
- using MetaObjectFn = const QMetaObject *(*)(const QMetaTypeInterface *);
- const MetaObjectFn metaObjectFn;
-
- const char *name;
-
- using DefaultCtrFn = void (*)(const QMetaTypeInterface *, void *);
- DefaultCtrFn defaultCtr;
- using CopyCtrFn = void (*)(const QMetaTypeInterface *, void *, const void *);
- CopyCtrFn copyCtr;
- using MoveCtrFn = void (*)(const QMetaTypeInterface *, void *, void *);
- MoveCtrFn moveCtr;
- using DtorFn = void (*)(const QMetaTypeInterface *, void *);
- DtorFn dtor;
- using EqualsFn = bool (*)(const QMetaTypeInterface *, const void *, const void *);
- EqualsFn equals;
- using LessThanFn = bool (*)(const QMetaTypeInterface *, const void *, const void *);
- LessThanFn lessThan;
- using DebugStreamFn = void (*)(const QMetaTypeInterface *, QDebug &, const void *);
- DebugStreamFn debugStream;
- using DataStreamOutFn = void (*)(const QMetaTypeInterface *, QDataStream &, const void *);
- DataStreamOutFn dataStreamOut;
- using DataStreamInFn = void (*)(const QMetaTypeInterface *, QDataStream &, void *);
- DataStreamInFn dataStreamIn;
-
- using LegacyRegisterOp = void (*)();
- LegacyRegisterOp legacyRegisterOp;
-};
-
struct QTypeNormalizer
{
char *output;
@@ -2083,6 +2109,8 @@ constexpr auto typenameHelper()
"auto __cdecl QtPrivate::typenameHelper<"
#elif defined(Q_CC_CLANG)
"auto QtPrivate::typenameHelper() [T = "
+#elif defined(Q_CC_GHS)
+ "auto QtPrivate::typenameHelper<T>() [with T = "
#else
"constexpr auto QtPrivate::typenameHelper() [with T = "
#endif
@@ -2218,7 +2246,7 @@ public:
static constexpr QMetaTypeInterface::DtorFn getDtor()
{
- if constexpr (std::is_destructible_v<S>)
+ if constexpr (std::is_destructible_v<S> && !std::is_trivially_destructible_v<S>)
return [](const QMetaTypeInterface *, void *addr) {
reinterpret_cast<S *>(addr)->~S();
};
diff --git a/src/corelib/kernel/qobject.cpp b/src/corelib/kernel/qobject.cpp
index a729962d4f..343922581c 100644
--- a/src/corelib/kernel/qobject.cpp
+++ b/src/corelib/kernel/qobject.cpp
@@ -167,7 +167,6 @@ extern "C" Q_CORE_EXPORT void qt_removeObject(QObject *)
#endif
void (*QAbstractDeclarativeData::destroyed)(QAbstractDeclarativeData *, QObject *) = nullptr;
-void (*QAbstractDeclarativeData::parentChanged)(QAbstractDeclarativeData *, QObject *, QObject *) = nullptr;
void (*QAbstractDeclarativeData::signalEmitted)(QAbstractDeclarativeData *, QObject *, int, void **) = nullptr;
int (*QAbstractDeclarativeData::receivers)(QAbstractDeclarativeData *, const QObject *, int) = nullptr;
bool (*QAbstractDeclarativeData::isSignalConnected)(QAbstractDeclarativeData *, const QObject *, int) = nullptr;
@@ -2110,8 +2109,6 @@ void QObjectPrivate::setParent_helper(QObject *o)
}
}
}
- if (!wasDeleted && !isDeletingChildren && declarativeData && QAbstractDeclarativeData::parentChanged)
- QAbstractDeclarativeData::parentChanged(declarativeData, q, o);
}
/*!
@@ -5246,5 +5243,4 @@ bool QMetaObject::Connection::isConnected_helper() const
QT_END_NAMESPACE
-#include "moc_qnamespace.cpp"
#include "moc_qobject.cpp"
diff --git a/src/corelib/kernel/qobject_p.h b/src/corelib/kernel/qobject_p.h
index 4483e2b6ce..019473018f 100644
--- a/src/corelib/kernel/qobject_p.h
+++ b/src/corelib/kernel/qobject_p.h
@@ -89,7 +89,6 @@ class Q_CORE_EXPORT QAbstractDeclarativeData
{
public:
static void (*destroyed)(QAbstractDeclarativeData *, QObject *);
- static void (*parentChanged)(QAbstractDeclarativeData *, QObject *, QObject *);
static void (*signalEmitted)(QAbstractDeclarativeData *, QObject *, int, void **);
static int (*receivers)(QAbstractDeclarativeData *, const QObject *, int);
static bool (*isSignalConnected)(QAbstractDeclarativeData *, const QObject *, int);
diff --git a/src/corelib/kernel/qobjectdefs.h b/src/corelib/kernel/qobjectdefs.h
index 5ade222ac5..00a6b62eab 100644
--- a/src/corelib/kernel/qobjectdefs.h
+++ b/src/corelib/kernel/qobjectdefs.h
@@ -173,6 +173,8 @@ struct Q_CORE_EXPORT QMetaObject
QString tr(const char *s, const char *c, int n = -1) const;
#endif // QT_NO_TRANSLATION
+ QMetaType metaType() const;
+
int methodOffset() const;
int enumeratorOffset() const;
int propertyOffset() const;
diff --git a/src/corelib/kernel/qproperty.cpp b/src/corelib/kernel/qproperty.cpp
index 686ec484ca..d03991eac1 100644
--- a/src/corelib/kernel/qproperty.cpp
+++ b/src/corelib/kernel/qproperty.cpp
@@ -100,15 +100,15 @@ void QPropertyBindingPrivate::unlinkAndDeref()
void QPropertyBindingPrivate::markDirtyAndNotifyObservers()
{
- if (dirty)
- return;
- dirty = true;
if (eagerlyUpdating) {
error = QPropertyBindingError(QPropertyBindingError::BindingLoop);
if (isQQmlPropertyBinding)
errorCallBack(this);
return;
}
+ if (dirty)
+ return;
+ dirty = true;
eagerlyUpdating = true;
QScopeGuard guard([&](){eagerlyUpdating = false;});
@@ -709,7 +709,7 @@ QString QPropertyBindingError::description() const
\fn QUntypedBindable::QUntypedBindable()
Default-constructs a QUntypedBindable. It is in an invalid state.
- \sa isValid()
+ \sa isValid()
*/
/*!
@@ -723,21 +723,21 @@ QString QPropertyBindingError::description() const
*/
/*!
- \fn bool QUntypedBindble::isValid()
+ \fn bool QUntypedBindable::isValid() const
Returns true if the QUntypedBindable is valid. Methods called on an invalid
QUntypedBindable generally have no effect, unless otherwise noted.
*/
/*!
- \fn bool QUntypedBindble::isReadOnly()
+ \fn bool QUntypedBindable::isReadOnly() const
\since 6.1
Returns true if the QUntypedBindable is read-only.
*/
/*!
- \fn bool QUntypedBindable::isBindable()
+ \fn bool QUntypedBindable::isBindable() const
\internal
Returns true if the underlying property's binding can be queried
@@ -749,9 +749,9 @@ QString QPropertyBindingError::description() const
*/
/*!
- \fn QUntypedPropertyBinding QUntypedBindable::makeBinding()
+ \fn QUntypedPropertyBinding QUntypedBindable::makeBinding(const QPropertyBindingSourceLocation &location)
- Creates a binding returning the underlying properties' value.
+ Creates a binding returning the underlying properties' value, using a specified source \a location.
*/
/*!
@@ -767,19 +767,19 @@ QString QPropertyBindingError::description() const
Installs \a f as a change handler. Whenever the underlying property changes, \a f will be called, as
long as the returned \c QPropertyChangeHandler and the property are kept alive.
- \sa template<typename T> QProperty::onValueChanged(), subscribe()
+ \sa onValueChanged(), subscribe()
*/
/*!
- template<typename Functor> QPropertyChangeHandler<Functor> QUntypedBindable::subscribe(Functor f)
+ \fn template<typename Functor> QPropertyChangeHandler<Functor> QUntypedBindable::subscribe(Functor f)
Behaves like a call to \a f followed by \c onValueChanged(f),
- \sa onValueChanged
+ \sa onValueChanged()
*/
/*!
- \fn QUntypedPropertyBinding QUntypedBindable::binding()
+ \fn QUntypedPropertyBinding QUntypedBindable::binding() const
Returns the underlying property's binding if there is any, or a default
constructed QUntypedPropertyBinding otherwise.
@@ -788,87 +788,133 @@ QString QPropertyBindingError::description() const
*/
/*!
+ \fn QUntypedPropertyBinding QUntypedBindable::takeBinding()
+
+ Removes the currently set binding from the property and returns it.
+ Returns a default-constructed QUntypedPropertyBinding if no binding is set.
+
+ \since 6.1
+*/
+
+/*!
\fn bool QUntypedBindable::setBinding(const QUntypedPropertyBinding &binding)
Sets the underlying property's binding to \a binding. This does not have any effect
- if the QUntypedBindable is read-only, invalid or if \a binding's type does match the
+ if the QUntypedBindable is read-only, null or if \a binding's type does match the
underlying property's type.
+ \return \c true when the binding was successfully set.
+
\sa QUntypedPropertyBinding::valueMetaType()
*/
/*!
- \fn bool QUntypedBindable::hasBinding()
+ \fn bool QUntypedBindable::hasBinding() const
- Returns true if the underlying property has a binding.
+ Returns \c true if the underlying property has a binding.
*/
/*!
- \class QProperty
+ \class QBindable
\inmodule QtCore
- \brief The QProperty class is a template class that enables automatic property bindings.
- \since 6.0
+ \brief QBindable is a wrapper class around binding-enabled properties. It allows type-safe
+ operations while abstracting the differences between the various property classes away.
+ \inherits QUntypedBindable
- \ingroup tools
+ \ingroup tools
- QProperty\<T\> is a generic container that holds an instance of T. You can assign
- a value to it and you can read it via the value() function or the T conversion
- operator. You can also tie the property to an expression that computes the value
- dynamically, the binding expression. It is represented as a C++ lambda and
- can be used to express relationships between different properties in your
- application.
+ QBindable\<T\> helps to integrate Qt's traditional Q_PROPERTY with binding-enabled properties.
+ If a property is backed by a QProperty, QObjectBindableProperty or QObjectComputedProperty,
+ you can add \c BINDABLE bindablePropertyName to the Q_PROPERTY
+ declaration, where bindablePropertyName is a function returning an instance of QBindable
+ constructed from the QProperty. The returned QBindable allows users of the property to set
+ and query bindings of the property, without having to know the exact kind of binding-enabled
+ property used.
- The binding expression computes the value by reading other QProperty values.
- Behind the scenes this dependency is tracked. Whenever a change in any property's
- dependency is detected, the binding expression is re-evaluated and the new
- result is applied to the property. This happens lazily, by marking the binding
- as dirty and evaluating it only when the property's value is requested. For example:
+ \snippet code/src_corelib_kernel_qproperty.cpp 0
+ \snippet code/src_corelib_kernel_qproperty.cpp 3
- \code
- QProperty<QString> firstname("John");
- QProperty<QString> lastname("Smith");
- QProperty<int> age(41);
+ \sa QMetaProperty::isBindable, QProperty, QObjectBindableProperty
+*/
- QProperty<QString> fullname;
- fullname.setBinding([&]() { return firstname.value() + " " + lastname.value() + " age: " + QString::number(age.value()); });
+/*!
+ \fn template<typename T> QPropertyBinding<T> QBindable<T>::makeBinding(const QPropertyBindingSourceLocation &location)
- qDebug() << fullname.value(); // Prints "John Smith age: 41"
+ Constructs a binding evaluating to the underlying property's value, using a specified source
+ \a location.
+*/
- firstname = "Emma"; // Marks binding expression as dirty
+/*!
+ \fn template <typename T> QPropertyBinding<T> QBindable<T>::binding() const
- qDebug() << fullname.value(); // Re-evaluates the binding expression and prints "Emma Smith age: 41"
+ Returns the currently set binding of the underlying property. If the property does not
+ have a binding, the returned \c QPropertyBinding<T> will be invalid.
- // Birthday is coming up
- age.setValue(age.value() + 1);
+ \sa setBinding, hasBinding
+ //! \sa QPropertyBinding::isValid()
+*/
- qDebug() << fullname.value(); // Re-evaluates the binding expression and prints "Emma Smith age: 42"
- \endcode
+/*!
+ \fn template <typename T> QPropertyBinding<T> QBindable<T>::takeBinding()
+
+ Removes the currently set binding of the underlying property and returns it.
+ If the property does not have a binding, the returned \c QPropertyBinding<T> will be invalid.
+
+ \sa binding, setBinding, hasBinding
+ //! \sa QPropertyBinding::isValid()
+*/
+
+
+/*!
+ \fn template <typename T> void QBindable<T>::setBinding(const QPropertyBinding<T> &binding)
+
+ Sets the underlying property's binding to \a binding. Does nothing if the QBindable is
+ read-only or invalid.
+
+ \sa binding, isReadOnly(), isValid()
+ //! \sa QPropertyBinding::isValid()
+*/
+
+/*!
+ \fn template <typename T> template <typename Functor> QPropertyBinding<T> QBindable<T>::setBinding(Functor f);
+ \overload
+
+ Creates a \c QPropertyBinding<T> from \a f, and sets it as the underlying property's binding.
+*/
+
+/*!
+ \fn template <typename T> T QBindable<T>::value() const
+
+ Returns the underlying property's current value. If the QBindable is invalid,
+ a default constructed \c T is returned.
+
+ \sa isValid()
+*/
- When a new value is assigned to the \c firstname property, the binding
- expression for \c fullname is marked as dirty. So when the last \c qDebug() statement
- tries to read the name value of the \c fullname property, the expression is
- evaluated again, \c firstname() will be called again and return the new value.
+/*!
+ \fn template <typename T> void QBindable<T>::setValue(const T &value)
- Since bindings are C++ lambda expressions, they may do anything that's possible
- in C++. This includes calling other functions. If those functions access values
- held by QProperty, they automatically become dependencies to the binding.
+ Sets the underlying property's value to \a value. This removes any currenltly set
+ binding from it. This function has no effect if the QBindable is read-only or invalid.
- Binding expressions may use properties of any type, so in the above example the age
- is an integer and folded into the string value using conversion to integer, but
- the dependency is fully tracked.
+ \sa isValid(), isReadOnly(), setBinding()
+*/
- \section1 Tracking properties
+/*!
+ \class QProperty
+ \inmodule QtCore
+ \brief The QProperty class is a template class that enables automatic property bindings.
+ \since 6.0
- Sometimes the relationships between properties cannot be expressed using
- bindings. Instead you may need to run custom code whenever the value of a property
- changes and instead of assigning the value to another property, pass it to
- other parts of your application. For example writing data into a network socket
- or printing debug output. QProperty provides two mechanisms for tracking.
+ \ingroup tools
- You can register for a callback function to be called whenever the value of
- a property changes, by using onValueChanged(). If you want the callback to also
- be called for the current value of the property, register your callback using
- subscribe() instead.
+ QProperty\<T\> is one of the classes implementing \l {Qt Bindable Properties}.
+ It is a container that holds an instance of T. You can assign
+ a value to it and you can read it via the value() function or the T conversion
+ operator. You can also tie the property to an expression that computes the value
+ dynamically, the binding expression. It is represented as a C++ lambda and
+ can be used to express relationships between different properties in your
+ application.
*/
/*!
@@ -996,6 +1042,8 @@ QString QPropertyBindingError::description() const
is read, the binding is evaluated by invoking the call operator () of \a f.
Whenever a dependency of the binding changes, the binding will be re-evaluated
the next time the value of this property is read.
+
+ \sa {Formulating a Property Binding}
*/
/*!
@@ -1070,16 +1118,24 @@ QString QPropertyBindingError::description() const
\ingroup tools
QObjectBindableProperty is a generic container that holds an
- instance of T and behaves mostly like \l QProperty. The extra template
- parameters are used to identify the surrounding class and a member function of
- that class. The member function will be called whenever the value held by the
- property changes.
+ instance of T and behaves mostly like \l QProperty.
+ It is one of the classes implementing \l {Qt Bindable Properties}.
+ Unlike QProperty, it stores its management data structure in
+ the sourrounding QObject.
+ The extra template parameters are used to identify the surrounding
+ class and a member function of that class acting as a change handler.
You can use QObjectBindableProperty to add binding support to code that uses Q_PROPERTY.
- The getter and setter methods are easy to adapt for accessing a \l QObjectBindableProperty
- rather than the plain value. In order to invoke the change signal on property changes, use
+ The getter and setter methods must be adapted carefully according to the
+ rules described in \l {Bindable Property Getters and Setters}.
+
+ In order to invoke the change signal on property changes, use
QObjectBindableProperty and pass the change signal as a callback.
+ A simple example is given in the following.
+
+ \snippet code/src_corelib_kernel_qproperty.cpp 4
+
QObjectBindableProperty is usually not used directly, instead an instance of it is created by
using the Q_OBJECT_BINDABLE_PROPERTY macro.
@@ -1101,12 +1157,191 @@ QString QPropertyBindingError::description() const
\snippet code/src_corelib_kernel_qproperty.cpp 2
If the property does not need a changed notification, you can leave out the
- "NOFITY xChanged" in the Q_PROPERTY macro as well as the last argument
+ "NOTIFY xChanged" in the Q_PROPERTY macro as well as the last argument
of the Q_OBJECT_BINDABLE_PROPERTY and Q_OBJECT_BINDABLE_PROPERTY_WITH_ARGS
macros.
*/
/*!
+ \macro Q_OBJECT_BINDABLE_PROPERTY(containingClass, type, name, signal)
+ \since 6.0
+ \relates QObjectBindableProperty
+ \brief Declares a \l QObjectBindableProperty inside \a containingClass
+ of type \a type with name \a name. If the optional argument \a signal is given,
+ this signal will be emitted when the property is marked dirty.
+
+ \sa {Qt's Property System}, {Qt Bindable Properties}
+*/
+
+/*!
+ \macro Q_OBJECT_BINDABLE_PROPERTY_WITH_ARGS(containingClass, type, name, initialvalue, signal)
+ \since 6.0
+ \relates QObjectBindableProperty
+ \brief Declares a \l QObjectBindableProperty inside \a containingClass
+ of type \a type with name \a name which is initialized to \a initialvalue.
+ If the optional argument \a signal is given, this signal will be emitted when
+ the property is marked dirty.
+
+ \sa {Qt's Property System}, {Qt Bindable Properties}
+*/
+
+/*!
+ \class QObjectCompatProperty
+ \inmodule QtCore
+ \brief The QObjectCompatProperty class is a template class to help port old
+ properties to the bindable property system.
+ \since 6.0
+ \ingroup tools
+ \internal
+
+ QObjectCompatProperty is a generic container that holds an
+ instance of \c T and behaves mostly like QProperty, just like
+ QObjectBindableProperty. It's one of the Qt internal classes implementing
+ \l {Qt Bindable Properties}. Like QObjectBindableProperty,
+ QObjectCompatProperty stores its management data structure in the surrounding
+ QObject. The last template parameter specifies a method (of the owning
+ class) to be called when the property is changed through the binding.
+ This is usually a setter.
+
+ As explained in \l {Qt Bindable Properties}, getters and setters for bindable
+ properties have to be almost trivial to be correct. However, in legacy code,
+ there is often complex logic in the setter. QObjectCompatProperty is a helper
+ to port these properties to the bindable property system.
+
+ With QObjectCompatProperty, the same rules as described in
+ \l {Bindable Property Getters and Setters} hold for the getter.
+ For the setter, the rules are different. It remains that every possible code
+ path in the setter must write to the underlying QObjectCompatProperty,
+ otherwise calling the setter might not remove a pre-existing binding, as
+ it should. However, as QObjectCompatProperty will call the setter on every
+ change, the setter is allowed to contain code like updating class internals
+ or emitting signals. Every write to the QObjectCompatProperty has to
+ be analyzed carefully to comply with the rules given in
+ \l {Writing to a Bindable Property}.
+
+ \sa Q_OBJECT_COMPAT_PROPERTY, QObjectBindableProperty, {Qt's Property System}, {Qt Bindable
+ Properties}
+*/
+
+/*!
+ \macro Q_OBJECT_COMPAT_PROPERTY(containingClass, type, name, callback)
+ \since 6.0
+ \relates QObjectCompatProperty
+ \internal
+ \brief Declares a \l QObjectCompatProperty inside \a containingClass
+ of type \a type with name \a name. The argument \a callback specifies
+ a setter function to be called when the property is changed through the binding.
+
+ \sa QObjectBindableProperty, {Qt's Property System}, {Qt Bindable Properties}
+*/
+
+/*!
+ \macro Q_OBJECT_COMPAT_PROPERTY_WITH_ARGS(containingClass, type, name, callback, value)
+ \since 6.0
+ \relates QObjectCompatProperty
+ \internal
+ \brief Declares a \l QObjectCompatProperty inside of \a containingClass
+ of type \a type with name \a name. The argument \a callback specifies
+ a setter function to be called when the property is changed through the binding.
+ \a value specifies an initialization value.
+*/
+
+/*!
+ \class QObjectComputedProperty
+ \inmodule QtCore
+ \brief The QObjectComputedProperty class is a template class to help port old
+ properties to the bindable property system.
+ \since 6.0
+ \ingroup tools
+ \internal
+
+ QObjectComputedProperty is a read-only property which is recomputed on each read.
+ It does not store the computed value.
+ It is one of the Qt internal classes implementing \l {Qt Bindable Properties}.
+ QObjectComputedProperty is usually not used directly, instead an instance of it is created by
+ using the Q_OBJECT_COMPUTED_PROPERTY macro.
+
+ See the following example.
+
+ \code
+ class Client{};
+
+ class MyClassPrivate : public QObjectPrivate
+ {
+ public:
+ QList<Client> clients;
+ bool hasClientsActualCalculation() const { return clients.size() > 0; }
+ Q_OBJECT_COMPUTED_PROPERTY(MyClassPrivate, bool, hasClientsData,
+ &MyClassPrivate::hasClientsActualCalculation)
+ };
+
+ class MyClass : public QObject
+ {
+ // add q-object macro here (confuses qdoc if we do it here)
+ Q_PROPERTY(bool hasClients READ hasClients STORED false BINDABLE bindableHasClients)
+ public:
+ QBindable<bool> bindableHasClients()
+ {
+ return QBindable<bool>(&d_func()->hasClientsData);
+ }
+ bool hasClients() const
+ {
+ return d_func()->hasClientsData.value();
+ }
+ void addClient(const Client &c)
+ {
+ Q_D(MyClass);
+ d->clients.push_back(c);
+ // notify that the value could have changed
+ d->hasClientsData.markDirty();
+ }
+ private:
+ Q_DECLARE_PRIVATE(MyClass)
+ };
+ \endcode
+
+ The rules for getters in \l {Bindable Property Getters and Setters}
+ also apply for QObjectComputedProperty. Especially, the getter
+ should be trivial and only return the value of the QObjectComputedProperty object.
+ The callback given to the QObjectComputedProperty should usually be a private
+ method which is only called by the QObjectComputedProperty.
+
+ No setter is required or allowed, as QObjectComputedProperty is read-only.
+
+ To correctly participate in dependency handling, QObjectComputedProperty
+ has to know when its value, the result of the callback given to it, might
+ have changed. Whenever a bindable property used in the callback changes,
+ this happens automatically. If the result of the callback might change
+ because of a change in a value which is not a bindable property,
+ it is the developer's responsibility to call markDirty
+ on the QObjectComputedProperty object.
+ This will inform dependent properties about the potential change.
+
+ Note that calling markDirty might trigger change handlers in dependent
+ properties, which might in turn use the object the QObjectComputedProperty
+ is a member of. So markDirty must not be called when in a transitional
+ or invalid state.
+
+ QObjectComputedProperty is not suitable for use with a computation that depends
+ on any input that might change without notice, such as the contents of a file.
+
+ \sa Q_OBJECT_COMPUTED_PROPERTY, QObjectBindableProperty, {Qt's Property System},
+ {Qt Bindable Properties}
+*/
+
+/*!
+ \macro Q_OBJECT_COMPUTED_PROPERTY(containingClass, type, name, callback)
+ \since 6.0
+ \relates QObjectCompatProperty
+ \internal
+ \brief Declares a \l QObjectComputedProperty inside \a containingClass
+ of type \a type with name \a name. The argument \a callback specifies
+ a GETTER function to be called when the property is evaluated.
+
+ \sa QObjectBindableProperty, {Qt's Property System}, {Qt Bindable Properties}
+*/
+
+/*!
\fn template <typename Class, typename T, auto offset, auto Callback> QObjectBindableProperty<Class, T, offset, Callback>::QObjectBindableProperty()
Constructs a property with a default constructed instance of T.
@@ -1208,6 +1443,8 @@ QString QPropertyBindingError::description() const
Whenever a dependency of the binding changes, the binding will be re-evaluated
the next time the value of this property is read. When the property value
changes, the owner is notified via the Callback function.
+
+ \sa {Formulating a Property Binding}
*/
/*!
@@ -1296,6 +1533,8 @@ QString QPropertyBindingError::description() const
/*!
\class QPropertyAlias
\inmodule QtCore
+ \internal
+
\brief The QPropertyAlias class is a safe alias for a QProperty with same template parameter.
\ingroup tools
@@ -1432,6 +1671,8 @@ QString QPropertyBindingError::description() const
Returns any previous binding associated with the property, or a
default-constructed QPropertyBinding<T>.
+
+ \sa {Formulating a Property Binding}
*/
/*!
@@ -1655,6 +1896,7 @@ QPropertyBindingData *QBindingStorage::bindingData_helper(QUntypedPropertyData *
}
+namespace QtPrivate {
BindingEvaluationState *suspendCurrentBindingStatus()
{
auto ret = bindingStatus.currentlyEvaluatingBinding;
@@ -1667,7 +1909,6 @@ void restoreBindingStatus(BindingEvaluationState *status)
bindingStatus.currentlyEvaluatingBinding = status;
}
-namespace QtPrivate {
/*!
\internal
This function can be used to detect whether we are currently
diff --git a/src/corelib/kernel/qproperty.h b/src/corelib/kernel/qproperty.h
index f853eb8634..a085aac3d1 100644
--- a/src/corelib/kernel/qproperty.h
+++ b/src/corelib/kernel/qproperty.h
@@ -581,6 +581,22 @@ public:
{
return iface ? iface->makeBinding(data, location) : QUntypedPropertyBinding();
}
+
+ QUntypedPropertyBinding takeBinding()
+ {
+ if (!iface)
+ return QUntypedPropertyBinding {};
+ // We do not have a dedicated takeBinding function pointer in the interface
+ // therefore we synthesize takeBinding by retrieving the binding with binding
+ // and calling setBinding with a default constructed QUntypedPropertyBinding
+ // afterwards.
+ if (!(iface->getBinding && iface->setBinding))
+ return QUntypedPropertyBinding {};
+ QUntypedPropertyBinding binding = iface->getBinding(data);
+ iface->setBinding(data, QUntypedPropertyBinding{});
+ return binding;
+ }
+
void observe(QPropertyObserver *observer)
{
if (iface)
@@ -650,6 +666,12 @@ public:
{
return static_cast<QPropertyBinding<T> &&>(QUntypedBindable::binding());
}
+
+ QPropertyBinding<T> takeBinding()
+ {
+ return static_cast<QPropertyBinding<T> &&>(QUntypedBindable::takeBinding());
+ }
+
using QUntypedBindable::setBinding;
QPropertyBinding<T> setBinding(const QPropertyBinding<T> &binding)
{
@@ -822,7 +844,7 @@ class Q_CORE_EXPORT QBindingStorage
mutable QBindingStorageData *d = nullptr;
QBindingStatus *bindingStatus = nullptr;
- template<typename Class, typename T, auto Offset, auto Setter>
+ template<typename Class, typename T, auto Offset, auto Setter, auto Signal>
friend class QObjectCompatProperty;
public:
QBindingStorage();
@@ -1066,8 +1088,7 @@ private:
#define Q_OBJECT_BINDABLE_PROPERTY_WITH_ARGS4(Class, Type, name, value) \
static constexpr size_t _qt_property_##name##_offset() \
{ \
- QT_WARNING_PUSH QT_WARNING_DISABLE_INVALID_OFFSETOF return offsetof(Class, name); \
- QT_WARNING_POP \
+ return offsetof(Class, name); \
} \
QObjectBindableProperty<Class, Type, Class::_qt_property_##name##_offset, nullptr> name = \
QObjectBindableProperty<Class, Type, Class::_qt_property_##name##_offset, nullptr>( \
@@ -1076,15 +1097,16 @@ private:
#define Q_OBJECT_BINDABLE_PROPERTY_WITH_ARGS5(Class, Type, name, value, Signal) \
static constexpr size_t _qt_property_##name##_offset() \
{ \
- QT_WARNING_PUSH QT_WARNING_DISABLE_INVALID_OFFSETOF return offsetof(Class, name); \
- QT_WARNING_POP \
+ return offsetof(Class, name); \
} \
QObjectBindableProperty<Class, Type, Class::_qt_property_##name##_offset, Signal> name = \
QObjectBindableProperty<Class, Type, Class::_qt_property_##name##_offset, Signal>( \
value);
#define Q_OBJECT_BINDABLE_PROPERTY_WITH_ARGS(...) \
- QT_OVERLOADED_MACRO(Q_OBJECT_BINDABLE_PROPERTY_WITH_ARGS, __VA_ARGS__)
+ QT_WARNING_PUSH QT_WARNING_DISABLE_INVALID_OFFSETOF \
+ QT_OVERLOADED_MACRO(Q_OBJECT_BINDABLE_PROPERTY_WITH_ARGS, __VA_ARGS__) \
+ QT_WARNING_POP
template<typename Class, typename T, auto Offset, auto Getter>
class QObjectComputedProperty : public QUntypedPropertyData
diff --git a/src/corelib/kernel/qproperty_p.h b/src/corelib/kernel/qproperty_p.h
index 27cb8c907f..d459433528 100644
--- a/src/corelib/kernel/qproperty_p.h
+++ b/src/corelib/kernel/qproperty_p.h
@@ -45,8 +45,8 @@
// -------------
//
// This file is not part of the Qt API. It exists for the convenience
-// of qapplication_*.cpp, qwidget*.cpp and qfiledialog.cpp. This header
-// file may change from version to version without notice, or even be removed.
+// of a number of Qt sources files. This header file may change from
+// version to version without notice, or even be removed.
//
// We mean it.
//
@@ -193,6 +193,7 @@ private:
QPropertyObserverPointer firstObserver; // list of observers observing us
QScopedPointer<std::vector<QPropertyObserver>> heapObservers; // for things we are observing
+protected:
QUntypedPropertyData *propertyDataPtr = nullptr;
/* For bindings set up from C++, location stores where the binding was created in the C++ source
@@ -203,7 +204,6 @@ private:
*/
static_assert(std::is_trivially_destructible_v<QPropertyBindingSourceLocation>);
static_assert(std::is_trivially_destructible_v<std::byte[sizeof(QPropertyBindingSourceLocation)]>);
-protected:
using DeclarativeErrorCallback = void(*)(QPropertyBindingPrivate *);
union {
QPropertyBindingSourceLocation location;
@@ -298,7 +298,15 @@ public:
return {&heapObservers->emplace_back()};
}
- QPropertyBindingSourceLocation sourceLocation() const { return location; }
+ QPropertyBindingSourceLocation sourceLocation() const
+ {
+ if (!hasCustomVTable())
+ return this->location;
+ QPropertyBindingSourceLocation location;
+ constexpr auto msg = "Custom location";
+ location.fileName = msg;
+ return location;
+ }
QPropertyBindingError bindingError() const { return error; }
QMetaType valueMetaType() const { return metaType; }
@@ -330,8 +338,13 @@ public:
static QPropertyBindingPrivate *currentlyEvaluatingBinding();
+ bool hasCustomVTable() const
+ {
+ return vtable->size == 0;
+ }
+
static void destroyAndFreeMemory(QPropertyBindingPrivate *priv) {
- if (priv->vtable->size == 0) {
+ if (priv->hasCustomVTable()) {
// special hack for QQmlPropertyBinding which has a
// different memory layout than normal QPropertyBindings
priv->vtable->destroy(priv);
@@ -340,6 +353,7 @@ public:
delete[] reinterpret_cast<std::byte *>(priv);
}
}
+
private:
bool evaluateIfDirtyAndReturnTrueIfValueChanged_helper(const QUntypedPropertyData *data, QBindingStatus *status = nullptr);
};
@@ -371,10 +385,11 @@ inline QPropertyObserverPointer QPropertyBindingDataPointer::firstObserver() con
return { reinterpret_cast<QPropertyObserver *>(ptr->d_ptr) };
}
-template<typename Class, typename T, auto Offset, auto Setter>
+template<typename Class, typename T, auto Offset, auto Setter, auto Signal=nullptr>
class QObjectCompatProperty : public QPropertyData<T>
{
- using ThisType = QObjectCompatProperty<Class, T, Offset, Setter>;
+ using ThisType = QObjectCompatProperty<Class, T, Offset, Setter, Signal>;
+ using SignalTakesValue = std::is_invocable<decltype(Signal), Class, T>;
Class *owner()
{
char *that = reinterpret_cast<char *>(this);
@@ -453,12 +468,7 @@ public:
const bool inWrapper = inBindingWrapper(storage);
if (bd && !inWrapper)
bd->removeBinding();
- if constexpr (QTypeTraits::has_operator_equal_v<T>)
- if (this->val == t)
- return;
this->val = t;
- if (!inWrapper)
- notify(bd);
}
QObjectCompatProperty &operator=(parameter_type newValue)
@@ -510,6 +520,31 @@ public:
}
}
+ void removeBindingUnlessInWrapper()
+ {
+ QBindingStorage *storage = qGetBindingStorage(owner());
+ auto *bd = storage->bindingData(this);
+ // make sure we don't remove the binding if called from the bindingWrapper
+ const bool inWrapper = inBindingWrapper(storage);
+ if (bd && !inWrapper)
+ bd->removeBinding();
+ }
+
+ void notify()
+ {
+ QBindingStorage *storage = qGetBindingStorage(owner());
+ auto bd = storage->bindingData(this, false);
+ const bool inWrapper = inBindingWrapper(storage);
+ if (bd && !inWrapper)
+ notify(bd);
+ if constexpr (Signal != nullptr) {
+ if constexpr (SignalTakesValue::value)
+ (owner()->*Signal)(value());
+ else
+ (owner()->*Signal)();
+ }
+ }
+
QPropertyBinding<T> binding() const
{
auto *bd = qGetBindingStorage(owner())->bindingData(this);
@@ -549,24 +584,50 @@ private:
}
};
-#define Q_OBJECT_COMPAT_PROPERTY(Class, Type, name, setter) \
+#define Q_OBJECT_COMPAT_PROPERTY4(Class, Type, name, setter) \
static constexpr size_t _qt_property_##name##_offset() { \
- QT_WARNING_PUSH QT_WARNING_DISABLE_INVALID_OFFSETOF \
return offsetof(Class, name); \
- QT_WARNING_POP \
} \
QObjectCompatProperty<Class, Type, Class::_qt_property_##name##_offset, setter> name;
-#define Q_OBJECT_COMPAT_PROPERTY_WITH_ARGS(Class, Type, name, setter, value) \
- static constexpr size_t _qt_property_##name##_offset() \
- { \
- QT_WARNING_PUSH QT_WARNING_DISABLE_INVALID_OFFSETOF return offsetof(Class, name); \
- QT_WARNING_POP \
- } \
+#define Q_OBJECT_COMPAT_PROPERTY5(Class, Type, name, setter, signal) \
+ static constexpr size_t _qt_property_##name##_offset() { \
+ return offsetof(Class, name); \
+ } \
+ QObjectCompatProperty<Class, Type, Class::_qt_property_##name##_offset, setter, signal> name;
+
+#define Q_OBJECT_COMPAT_PROPERTY(...) \
+ QT_WARNING_PUSH QT_WARNING_DISABLE_INVALID_OFFSETOF \
+ QT_OVERLOADED_MACRO(Q_OBJECT_COMPAT_PROPERTY, __VA_ARGS__) \
+ QT_WARNING_POP
+
+#define Q_OBJECT_COMPAT_PROPERTY_WITH_ARGS5(Class, Type, name, setter, value) \
+ static constexpr size_t _qt_property_##name##_offset() { \
+ return offsetof(Class, name); \
+ } \
QObjectCompatProperty<Class, Type, Class::_qt_property_##name##_offset, setter> name = \
QObjectCompatProperty<Class, Type, Class::_qt_property_##name##_offset, setter>( \
value);
+#define Q_OBJECT_COMPAT_PROPERTY_WITH_ARGS6(Class, Type, name, setter, signal, value) \
+ static constexpr size_t _qt_property_##name##_offset() { \
+ return offsetof(Class, name); \
+ } \
+ QObjectCompatProperty<Class, Type, Class::_qt_property_##name##_offset, setter, signal> name = \
+ QObjectCompatProperty<Class, Type, Class::_qt_property_##name##_offset, setter, \
+ signal>(value);
+
+#define Q_OBJECT_COMPAT_PROPERTY_WITH_ARGS(...) \
+ QT_WARNING_PUSH QT_WARNING_DISABLE_INVALID_OFFSETOF \
+ QT_OVERLOADED_MACRO(Q_OBJECT_COMPAT_PROPERTY_WITH_ARGS, __VA_ARGS__) \
+ QT_WARNING_POP
+
+
+namespace QtPrivate {
+Q_CORE_EXPORT BindingEvaluationState *suspendCurrentBindingStatus();
+Q_CORE_EXPORT void restoreBindingStatus(BindingEvaluationState *status);
+}
+
QT_END_NAMESPACE
#endif // QPROPERTY_P_H
diff --git a/src/corelib/kernel/qpropertyprivate.h b/src/corelib/kernel/qpropertyprivate.h
index c53e15c2ec..896ad17395 100644
--- a/src/corelib/kernel/qpropertyprivate.h
+++ b/src/corelib/kernel/qpropertyprivate.h
@@ -44,9 +44,9 @@
// W A R N I N G
// -------------
//
-// This file is not part of the Qt API. It exists for the convenience
-// of qapplication_*.cpp, qwidget*.cpp and qfiledialog.cpp. This header
-// file may change from version to version without notice, or even be removed.
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
//
// We mean it.
//
@@ -69,6 +69,7 @@ struct RefCounted {
};
}
+class QQmlPropertyBinding;
class QPropertyBindingPrivate;
class QPropertyBindingPrivatePtr
{
@@ -223,6 +224,7 @@ class Q_CORE_EXPORT QPropertyBindingData
// notification later when the value changes.
mutable quintptr d_ptr = 0;
friend struct QT_PREPEND_NAMESPACE(QPropertyBindingDataPointer);
+ friend class QT_PREPEND_NAMESPACE(QQmlPropertyBinding);
Q_DISABLE_COPY(QPropertyBindingData)
public:
QPropertyBindingData() = default;
diff --git a/src/corelib/kernel/qsocketnotifier.h b/src/corelib/kernel/qsocketnotifier.h
index cd7d531687..9c806235fc 100644
--- a/src/corelib/kernel/qsocketnotifier.h
+++ b/src/corelib/kernel/qsocketnotifier.h
@@ -84,7 +84,7 @@ Q_SIGNALS:
// This means the PMF-based connect(..) will automatically, on recompile, pick up the new
// version while the old-style connect(..) can query the metaobject system for this version.
#if defined(Q_MOC_RUN) || defined(BUILDING_QSOCKETNOTIFIER) || defined(Q_QDOC)
- void activated(int socket, QPrivateSignal);
+ QT_MOC_COMPAT void activated(int socket, QPrivateSignal);
#endif
protected:
diff --git a/src/corelib/kernel/qsystemsemaphore.h b/src/corelib/kernel/qsystemsemaphore.h
index d6a4301df6..262d089e04 100644
--- a/src/corelib/kernel/qsystemsemaphore.h
+++ b/src/corelib/kernel/qsystemsemaphore.h
@@ -40,6 +40,7 @@
#ifndef QSYSTEMSEMAPHORE_H
#define QSYSTEMSEMAPHORE_H
+#include <QtCore/qcoreapplication.h>
#include <QtCore/qstring.h>
#include <QtCore/qscopedpointer.h>
@@ -52,7 +53,7 @@ class QSystemSemaphorePrivate;
class Q_CORE_EXPORT QSystemSemaphore
{
-
+ Q_DECLARE_TR_FUNCTIONS(QSystemSemaphore)
public:
enum AccessMode
{
diff --git a/src/corelib/kernel/qsystemsemaphore_p.h b/src/corelib/kernel/qsystemsemaphore_p.h
index 3b55d80276..56619d7318 100644
--- a/src/corelib/kernel/qsystemsemaphore_p.h
+++ b/src/corelib/kernel/qsystemsemaphore_p.h
@@ -55,6 +55,7 @@
#ifndef QT_NO_SYSTEMSEMAPHORE
+#include "qcoreapplication.h"
#include "qsharedmemory_p.h"
#include <sys/types.h>
#ifdef QT_POSIX_IPC
diff --git a/src/corelib/kernel/qsystemsemaphore_posix.cpp b/src/corelib/kernel/qsystemsemaphore_posix.cpp
index 2bff5de4e5..567e154e0d 100644
--- a/src/corelib/kernel/qsystemsemaphore_posix.cpp
+++ b/src/corelib/kernel/qsystemsemaphore_posix.cpp
@@ -70,7 +70,7 @@ bool QSystemSemaphorePrivate::handle(QSystemSemaphore::AccessMode mode)
return true; // we already have a semaphore
if (fileName.isEmpty()) {
- errorString = QCoreApplication::translate("%1: key is empty", "QSystemSemaphore").arg(QLatin1String("QSystemSemaphore::handle"));
+ errorString = QSystemSemaphore::tr("%1: key is empty").arg(QLatin1String("QSystemSemaphore::handle"));
error = QSystemSemaphore::KeyError;
return false;
}
diff --git a/src/corelib/kernel/qsystemsemaphore_systemv.cpp b/src/corelib/kernel/qsystemsemaphore_systemv.cpp
index 2c38d74d2d..d861a35358 100644
--- a/src/corelib/kernel/qsystemsemaphore_systemv.cpp
+++ b/src/corelib/kernel/qsystemsemaphore_systemv.cpp
@@ -72,13 +72,8 @@ QT_BEGIN_NAMESPACE
key_t QSystemSemaphorePrivate::handle(QSystemSemaphore::AccessMode mode)
{
if (key.isEmpty()){
- errorString =
-#if QT_CONFIG(translation)
- QCoreApplication::tr("%1: key is empty", "QSystemSemaphore")
-#else
- QLatin1String("%1: key is empty")
-#endif
- .arg(QLatin1String("QSystemSemaphore::handle:"));
+ errorString = QSystemSemaphore::tr("%1: key is empty")
+ .arg(QLatin1String("QSystemSemaphore::handle:"));
error = QSystemSemaphore::KeyError;
return -1;
}
@@ -90,13 +85,8 @@ key_t QSystemSemaphorePrivate::handle(QSystemSemaphore::AccessMode mode)
// Create the file needed for ftok
int built = QSharedMemoryPrivate::createUnixKeyFile(fileName);
if (-1 == built) {
- errorString =
-#if QT_CONFIG(translation)
- QCoreApplication::tr("%1: unable to make key", "QSystemSemaphore")
-#else
- QLatin1String("%1: unable to make key")
-#endif
- .arg(QLatin1String("QSystemSemaphore::handle:"));
+ errorString = QSystemSemaphore::tr("%1: unable to make key")
+ .arg(QLatin1String("QSystemSemaphore::handle:"));
error = QSystemSemaphore::KeyError;
return -1;
}
@@ -107,13 +97,8 @@ key_t QSystemSemaphorePrivate::handle(QSystemSemaphore::AccessMode mode)
unix_key = ftok(QFile::encodeName(fileName).constData(), 'Q');
#endif
if (-1 == unix_key) {
- errorString =
-#if QT_CONFIG(translation)
- QCoreApplication::tr("%1: ftok failed", "QSystemSemaphore")
-#else
- QLatin1String("%1: ftok failed")
-#endif
- .arg(QLatin1String("QSystemSemaphore::handle:"));
+ errorString = QSystemSemaphore::tr("%1: ftok failed")
+ .arg(QLatin1String("QSystemSemaphore::handle:"));
error = QSystemSemaphore::KeyError;
return -1;
}
diff --git a/src/corelib/kernel/qsystemsemaphore_unix.cpp b/src/corelib/kernel/qsystemsemaphore_unix.cpp
index f12a808891..25da6eb333 100644
--- a/src/corelib/kernel/qsystemsemaphore_unix.cpp
+++ b/src/corelib/kernel/qsystemsemaphore_unix.cpp
@@ -73,24 +73,24 @@ void QSystemSemaphorePrivate::setErrorString(const QString &function)
switch (errno) {
case EPERM:
case EACCES:
- errorString = QCoreApplication::translate("QSystemSemaphore", "%1: permission denied").arg(function);
+ errorString = QSystemSemaphore::tr("%1: permission denied").arg(function);
error = QSystemSemaphore::PermissionDenied;
break;
case EEXIST:
- errorString = QCoreApplication::translate("QSystemSemaphore", "%1: already exists").arg(function);
+ errorString = QSystemSemaphore::tr("%1: already exists").arg(function);
error = QSystemSemaphore::AlreadyExists;
break;
case ENOENT:
- errorString = QCoreApplication::translate("QSystemSemaphore", "%1: does not exist").arg(function);
+ errorString = QSystemSemaphore::tr("%1: does not exist").arg(function);
error = QSystemSemaphore::NotFound;
break;
case ERANGE:
case ENOSPC:
- errorString = QCoreApplication::translate("QSystemSemaphore", "%1: out of resources").arg(function);
+ errorString = QSystemSemaphore::tr("%1: out of resources").arg(function);
error = QSystemSemaphore::OutOfResources;
break;
default:
- errorString = QCoreApplication::translate("QSystemSemaphore", "%1: unknown error %2").arg(function).arg(errno);
+ errorString = QSystemSemaphore::tr("%1: unknown error %2").arg(function).arg(errno);
error = QSystemSemaphore::UnknownError;
#if defined QSYSTEMSEMAPHORE_DEBUG
qDebug() << errorString << "key" << key << "errno" << errno << EINVAL;
diff --git a/src/corelib/kernel/qtimer.cpp b/src/corelib/kernel/qtimer.cpp
index 6e3d34214f..333e6c24ba 100644
--- a/src/corelib/kernel/qtimer.cpp
+++ b/src/corelib/kernel/qtimer.cpp
@@ -44,6 +44,7 @@
#include "qobject_p.h"
#include "qthread.h"
#include "qcoreapplication_p.h"
+#include "qproperty_p.h"
QT_BEGIN_NAMESPACE
@@ -51,12 +52,18 @@ static constexpr int INV_TIMER = -1; // invalid timer id
class QTimerPrivate : public QObjectPrivate
{
+ Q_DECLARE_PUBLIC(QTimer)
public:
+ void setInterval(int msec) { q_func()->setInterval(msec); }
+ bool isActiveActualCalculation() const { return id >= 0; }
+
int id = INV_TIMER;
- int inter = 0;
- bool single = false;
+ Q_OBJECT_COMPAT_PROPERTY_WITH_ARGS(QTimerPrivate, int, inter, &QTimerPrivate::setInterval, 0)
+ Q_OBJECT_BINDABLE_PROPERTY_WITH_ARGS(QTimerPrivate, bool, single, false)
bool nulltimer = false;
- Qt::TimerType type = Qt::CoarseTimer;
+ Q_OBJECT_BINDABLE_PROPERTY_WITH_ARGS(QTimerPrivate, Qt::TimerType, type, Qt::CoarseTimer)
+ Q_OBJECT_COMPUTED_PROPERTY(QTimerPrivate, bool, isActiveData,
+ &QTimerPrivate::isActiveActualCalculation)
};
/*!
@@ -198,7 +205,12 @@ QTimer::~QTimer()
*/
bool QTimer::isActive() const
{
- return d_func()->id >= 0;
+ return d_func()->isActiveData.value();
+}
+
+QBindable<bool> QTimer::bindableActive()
+{
+ return QBindable<bool>(&d_func()->isActiveData);
}
/*!
@@ -229,6 +241,7 @@ void QTimer::start()
stop();
d->nulltimer = (!d->inter && d->single);
d->id = QObject::startTimer(d->inter, d->type);
+ d->isActiveData.markDirty();
}
/*!
@@ -244,8 +257,11 @@ void QTimer::start()
void QTimer::start(int msec)
{
Q_D(QTimer);
- d->inter = msec;
+ const bool intervalChanged = msec != d->inter;
+ d->inter.setValue(msec);
start();
+ if (intervalChanged)
+ d->inter.notify();
}
@@ -262,6 +278,7 @@ void QTimer::stop()
if (d->id != INV_TIMER) {
QObject::killTimer(d->id);
d->id = INV_TIMER;
+ d->isActiveData.markDirty();
}
}
@@ -718,6 +735,11 @@ bool QTimer::isSingleShot() const
return d_func()->single;
}
+QBindable<bool> QTimer::bindableSingleShot()
+{
+ return QBindable<bool>(&d_func()->single);
+}
+
/*!
\property QTimer::interval
\brief the timeout interval in milliseconds
@@ -733,11 +755,17 @@ bool QTimer::isSingleShot() const
void QTimer::setInterval(int msec)
{
Q_D(QTimer);
- d->inter = msec;
+ const bool intervalChanged = msec != d->inter;
+ d->inter.setValue(msec);
if (d->id != INV_TIMER) { // create new timer
QObject::killTimer(d->id); // restart timer
d->id = QObject::startTimer(msec, d->type);
+ // No need to call markDirty() for d->isActiveData here,
+ // as timer state actually does not change
}
+
+ if (intervalChanged)
+ d->inter.markDirty();
}
int QTimer::interval() const
@@ -745,6 +773,11 @@ int QTimer::interval() const
return d_func()->inter;
}
+QBindable<int> QTimer::bindableInterval()
+{
+ return QBindable<int>(&d_func()->inter);
+}
+
/*!
\property QTimer::remainingTime
\since 5.0
@@ -784,6 +817,11 @@ Qt::TimerType QTimer::timerType() const
return d_func()->type;
}
+QBindable<Qt::TimerType> QTimer::bindableTimerType()
+{
+ return QBindable<Qt::TimerType>(&d_func()->type);
+}
+
QT_END_NAMESPACE
#include "qtimer.moc"
diff --git a/src/corelib/kernel/qtimer.h b/src/corelib/kernel/qtimer.h
index d4da91e172..2b262f6311 100644
--- a/src/corelib/kernel/qtimer.h
+++ b/src/corelib/kernel/qtimer.h
@@ -57,28 +57,32 @@ class QTimerPrivate;
class Q_CORE_EXPORT QTimer : public QObject
{
Q_OBJECT
- Q_PROPERTY(bool singleShot READ isSingleShot WRITE setSingleShot)
- Q_PROPERTY(int interval READ interval WRITE setInterval)
+ Q_PROPERTY(bool singleShot READ isSingleShot WRITE setSingleShot BINDABLE bindableSingleShot)
+ Q_PROPERTY(int interval READ interval WRITE setInterval BINDABLE bindableInterval)
Q_PROPERTY(int remainingTime READ remainingTime)
- Q_PROPERTY(Qt::TimerType timerType READ timerType WRITE setTimerType)
- Q_PROPERTY(bool active READ isActive)
+ Q_PROPERTY(Qt::TimerType timerType READ timerType WRITE setTimerType BINDABLE bindableTimerType)
+ Q_PROPERTY(bool active READ isActive STORED false BINDABLE bindableActive)
public:
explicit QTimer(QObject *parent = nullptr);
~QTimer();
bool isActive() const;
+ QBindable<bool> bindableActive();
int timerId() const;
void setInterval(int msec);
int interval() const;
+ QBindable<int> bindableInterval();
int remainingTime() const;
void setTimerType(Qt::TimerType atype);
Qt::TimerType timerType() const;
+ QBindable<Qt::TimerType> bindableTimerType();
void setSingleShot(bool singleShot);
bool isSingleShot() const;
+ QBindable<bool> bindableSingleShot();
static void singleShot(int msec, const QObject *receiver, const char *member);
static void singleShot(int msec, Qt::TimerType timerType, const QObject *receiver, const char *member);
diff --git a/src/corelib/kernel/qtimerinfo_unix.cpp b/src/corelib/kernel/qtimerinfo_unix.cpp
index bcaaf7af05..db4d4b1188 100644
--- a/src/corelib/kernel/qtimerinfo_unix.cpp
+++ b/src/corelib/kernel/qtimerinfo_unix.cpp
@@ -1,6 +1,6 @@
/****************************************************************************
**
-** Copyright (C) 2016 The Qt Company Ltd.
+** Copyright (C) 2021 The Qt Company Ltd.
** Copyright (C) 2016 Intel Corporation.
** Contact: https://www.qt.io/licensing/
**
@@ -631,13 +631,16 @@ int QTimerInfoList::activateTimers()
if (currentTimerInfo->interval > 0)
n_act++;
+ // Send event, but don't allow it to recurse:
if (!currentTimerInfo->activateRef) {
- // send event, but don't allow it to recurse
currentTimerInfo->activateRef = &currentTimerInfo;
QTimerEvent e(currentTimerInfo->id);
QCoreApplication::sendEvent(currentTimerInfo->obj, &e);
+ // Storing currentTimerInfo's address in its activateRef allows the
+ // handling of that event to clear this local variable on deletion
+ // of the object it points to - if it didn't, clear activateRef:
if (currentTimerInfo)
currentTimerInfo->activateRef = nullptr;
}
diff --git a/src/corelib/kernel/qvariant.cpp b/src/corelib/kernel/qvariant.cpp
index 0f92307d8c..97623727a8 100644
--- a/src/corelib/kernel/qvariant.cpp
+++ b/src/corelib/kernel/qvariant.cpp
@@ -248,32 +248,45 @@ static qreal qConvertToRealNumber(const QVariant::Private *d, bool *ok)
// the type of d has already been set, but other field are not set
static void customConstruct(QVariant::Private *d, const void *copy)
{
- const QMetaType type = d->type();
- const uint size = type.sizeOf();
- if (!size) {
+ QtPrivate::QMetaTypeInterface *iface = d->typeInterface();
+ if (!(iface && iface->size)) {
*d = QVariant::Private();
return;
}
- if (QVariant::Private::canUseInternalSpace(type)) {
- type.construct(&d->data, copy);
+ if (QVariant::Private::canUseInternalSpace(iface)) {
+ // QVariant requires type to be copy and default constructible
+ Q_ASSERT(iface->copyCtr);
+ Q_ASSERT(iface->defaultCtr);
+ if (copy)
+ iface->copyCtr(iface, &d->data, copy);
+ else
+ iface->defaultCtr(iface, &d->data);
d->is_shared = false;
} else {
- d->data.shared = QVariant::PrivateShared::create(type);
- type.construct(d->data.shared->data(), copy);
+ d->data.shared = QVariant::PrivateShared::create(iface);
+ if (copy)
+ iface->copyCtr(iface, d->data.shared->data(), copy);
+ else
+ iface->defaultCtr(iface, d->data.shared->data());
d->is_shared = true;
}
// need to check for nullptr_t here, as this can get called by fromValue(nullptr). fromValue() uses
// std::addressof(value) which in this case returns the address of the nullptr object.
- d->is_null = !copy || type == QMetaType::fromType<std::nullptr_t>();
+ d->is_null = !copy || QMetaType(iface) == QMetaType::fromType<std::nullptr_t>();
}
static void customClear(QVariant::Private *d)
{
+ auto iface = reinterpret_cast<QtPrivate::QMetaTypeInterface *>(d->packedType << 2);
+ if (!iface)
+ return;
if (!d->is_shared) {
- d->type().destruct(&d->data);
+ if (iface->dtor)
+ iface->dtor(iface, &d->data);
} else {
- d->type().destruct(d->data.shared->data());
+ if (iface->dtor)
+ iface->dtor(iface, d->data.shared->data());
QVariant::PrivateShared::free(d->data.shared);
}
}
@@ -466,11 +479,23 @@ static void customClear(QVariant::Private *d)
Constructs a variant private of type \a type, and initializes with \a copy if
\a copy is not \nullptr.
-*/
+*/
+//### Qt 7: Remove in favor of QMetaType overload
void QVariant::create(int type, const void *copy)
{
- d = Private(QMetaType(type));
+ create(QMetaType(type), copy);
+}
+
+/*!
+ \fn QVariant::create(int type, const void *copy)
+
+ \internal
+ \overload
+*/
+void QVariant::create(QMetaType type, const void *copy)
+{
+ d = Private(type);
customConstruct(&d, copy);
}
@@ -505,9 +530,14 @@ QVariant::QVariant(const QVariant &p)
d.data.shared->ref.ref();
return;
}
- QMetaType t = d.type();
- if (t.isValid())
- t.construct(&d, p.constData());
+ QtPrivate::QMetaTypeInterface *iface = d.typeInterface();
+ auto other = p.constData();
+ if (iface) {
+ if (other)
+ iface->copyCtr(iface, &d, other);
+ else
+ iface->defaultCtr(iface, &d);
+ }
}
/*!
@@ -793,131 +823,131 @@ QVariant::QVariant(QMetaType type, const void *copy) : d(type)
}
QVariant::QVariant(int val)
- : d(QMetaType::Int)
+ : d(QMetaType::fromType<int>())
{ d.set(val); }
QVariant::QVariant(uint val)
- : d(QMetaType::UInt)
+ : d(QMetaType::fromType<uint>())
{ d.set(val); }
QVariant::QVariant(qlonglong val)
- : d(QMetaType::LongLong)
+ : d(QMetaType::fromType<qlonglong>())
{ d.set(val); }
QVariant::QVariant(qulonglong val)
- : d(QMetaType::ULongLong)
+ : d(QMetaType::fromType<qulonglong>())
{ d.set(val); }
QVariant::QVariant(bool val)
- : d(QMetaType::Bool)
+ : d(QMetaType::fromType<bool>())
{ d.set(val); }
QVariant::QVariant(double val)
- : d(QMetaType::Double)
+ : d(QMetaType::fromType<double>())
{ d.set(val); }
QVariant::QVariant(float val)
- : d(QMetaType::Float)
+ : d(QMetaType::fromType<float>())
{ d.set(val); }
QVariant::QVariant(const QByteArray &val)
- : d(QMetaType::QByteArray)
+ : d(QMetaType::fromType<QByteArray>())
{ v_construct<QByteArray>(&d, val); }
QVariant::QVariant(const QBitArray &val)
- : d(QMetaType::QBitArray)
+ : d(QMetaType::fromType<QBitArray>())
{ v_construct<QBitArray>(&d, val); }
QVariant::QVariant(const QString &val)
- : d(QMetaType::QString)
+ : d(QMetaType::fromType<QString>())
{ v_construct<QString>(&d, val); }
QVariant::QVariant(QChar val)
- : d(QMetaType::QChar)
+ : d(QMetaType::fromType<QChar>())
{ v_construct<QChar>(&d, val); }
QVariant::QVariant(QLatin1String val)
- : d(QMetaType::QString)
+ : d(QMetaType::fromType<QString>())
{ v_construct<QString>(&d, val); }
QVariant::QVariant(const QStringList &val)
- : d(QMetaType::QStringList)
+ : d(QMetaType::fromType<QStringList>())
{ v_construct<QStringList>(&d, val); }
QVariant::QVariant(QDate val)
- : d(QMetaType::QDate)
+ : d(QMetaType::fromType<QDate>())
{ v_construct<QDate>(&d, val); }
QVariant::QVariant(QTime val)
- : d(QMetaType::QTime)
+ : d(QMetaType::fromType<QTime>())
{ v_construct<QTime>(&d, val); }
QVariant::QVariant(const QDateTime &val)
- : d(QMetaType::QDateTime)
+ : d(QMetaType::fromType<QDateTime>())
{ v_construct<QDateTime>(&d, val); }
#if QT_CONFIG(easingcurve)
QVariant::QVariant(const QEasingCurve &val)
- : d(QMetaType::QEasingCurve)
+ : d(QMetaType::fromType<QEasingCurve>())
{ v_construct<QEasingCurve>(&d, val); }
#endif
QVariant::QVariant(const QList<QVariant> &list)
- : d(QMetaType::QVariantList)
+ : d(QMetaType::fromType<QList<QVariant>>())
{ v_construct<QVariantList>(&d, list); }
QVariant::QVariant(const QMap<QString, QVariant> &map)
- : d(QMetaType::QVariantMap)
+ : d(QMetaType::fromType<QMap<QString, QVariant>>())
{ v_construct<QVariantMap>(&d, map); }
QVariant::QVariant(const QHash<QString, QVariant> &hash)
- : d(QMetaType::QVariantHash)
+ : d(QMetaType::fromType<QHash<QString, QVariant>>())
{ v_construct<QVariantHash>(&d, hash); }
#ifndef QT_NO_GEOM_VARIANT
QVariant::QVariant(const QPoint &pt)
- : d(QMetaType::QPoint)
+ : d(QMetaType::fromType<QPoint>())
{ v_construct<QPoint>(&d, pt); }
QVariant::QVariant(const QPointF &pt)
- : d(QMetaType::QPointF)
+ : d(QMetaType::fromType<QPointF>())
{ v_construct<QPointF>(&d, pt); }
QVariant::QVariant(const QRectF &r)
- : d(QMetaType::QRectF)
+ : d(QMetaType::fromType<QRectF>())
{ v_construct<QRectF>(&d, r); }
QVariant::QVariant(const QLineF &l)
- : d(QMetaType::QLineF)
+ : d(QMetaType::fromType<QLineF>())
{ v_construct<QLineF>(&d, l); }
QVariant::QVariant(const QLine &l)
- : d(QMetaType::QLine)
+ : d(QMetaType::fromType<QLine>())
{ v_construct<QLine>(&d, l); }
QVariant::QVariant(const QRect &r)
- : d(QMetaType::QRect)
+ : d(QMetaType::fromType<QRect>())
{ v_construct<QRect>(&d, r); }
QVariant::QVariant(const QSize &s)
- : d(QMetaType::QSize)
+ : d(QMetaType::fromType<QSize>())
{ v_construct<QSize>(&d, s); }
QVariant::QVariant(const QSizeF &s)
- : d(QMetaType::QSizeF)
+ : d(QMetaType::fromType<QSizeF>())
{ v_construct<QSizeF>(&d, s); }
#endif
#ifndef QT_BOOTSTRAPPED
QVariant::QVariant(const QUrl &u)
- : d(QMetaType::QUrl)
+ : d(QMetaType::fromType<QUrl>())
{ v_construct<QUrl>(&d, u); }
#endif
QVariant::QVariant(const QLocale &l)
- : d(QMetaType::QLocale)
+ : d(QMetaType::fromType<QLocale>())
{ v_construct<QLocale>(&d, l); }
#if QT_CONFIG(regularexpression)
QVariant::QVariant(const QRegularExpression &re)
- : d(QMetaType::QRegularExpression)
+ : d(QMetaType::fromType<QRegularExpression>())
{ v_construct<QRegularExpression>(&d, re); }
#endif // QT_CONFIG(regularexpression)
QVariant::QVariant(const QUuid &uuid)
- : d(QMetaType::QUuid)
+ : d(QMetaType::fromType<QUuid>())
{ v_construct<QUuid>(&d, uuid); }
#ifndef QT_BOOTSTRAPPED
QVariant::QVariant(const QJsonValue &jsonValue)
- : d(QMetaType::QJsonValue)
+ : d(QMetaType::fromType<QJsonValue>())
{ v_construct<QJsonValue>(&d, jsonValue); }
QVariant::QVariant(const QJsonObject &jsonObject)
- : d(QMetaType::QJsonObject)
+ : d(QMetaType::fromType<QJsonObject>())
{ v_construct<QJsonObject>(&d, jsonObject); }
QVariant::QVariant(const QJsonArray &jsonArray)
- : d(QMetaType::QJsonArray)
+ : d(QMetaType::fromType<QJsonArray>())
{ v_construct<QJsonArray>(&d, jsonArray); }
QVariant::QVariant(const QJsonDocument &jsonDocument)
- : d(QMetaType::QJsonDocument)
+ : d(QMetaType::fromType<QJsonDocument>())
{ v_construct<QJsonDocument>(&d, jsonDocument); }
#endif // QT_BOOTSTRAPPED
#if QT_CONFIG(itemmodel)
QVariant::QVariant(const QModelIndex &modelIndex)
- : d(QMetaType::QModelIndex)
+ : d(QMetaType::fromType<QModelIndex>())
{ v_construct<QModelIndex>(&d, modelIndex); }
QVariant::QVariant(const QPersistentModelIndex &modelIndex)
- : d(QMetaType::QPersistentModelIndex)
+ : d(QMetaType::fromType<QPersistentModelIndex>())
{ v_construct<QPersistentModelIndex>(&d, modelIndex); }
#endif
@@ -990,9 +1020,14 @@ QVariant &QVariant::operator=(const QVariant &variant)
d = variant.d;
} else {
d = variant.d;
- QMetaType t = d.type();
- if (t.isValid())
- t.construct(&d, variant.constData());
+ QtPrivate::QMetaTypeInterface *iface = d.typeInterface();
+ const void *other = variant.constData();
+ if (iface) {
+ if (other)
+ iface->copyCtr(iface, &d, other);
+ else
+ iface->defaultCtr(iface, &d);
+ }
}
return *this;
@@ -2016,7 +2051,7 @@ bool QVariant::convert(QMetaType targetType)
QVariant oldValue = *this;
clear();
- create(targetType.id(), nullptr);
+ create(targetType, nullptr);
if (!oldValue.canConvert(targetType))
return false;
diff --git a/src/corelib/kernel/qvariant.h b/src/corelib/kernel/qvariant.h
index b7d347a61e..d95d3cd0fb 100644
--- a/src/corelib/kernel/qvariant.h
+++ b/src/corelib/kernel/qvariant.h
@@ -426,10 +426,11 @@ public:
private:
inline PrivateShared() : ref(1) { }
public:
- static PrivateShared *create(QMetaType type)
+ static PrivateShared *create(const QtPrivate::QMetaTypeInterface *type)
{
- size_t size = type.sizeOf();
- size_t align = type.alignOf();
+ Q_ASSERT(type);
+ size_t size = type->size;
+ size_t align = type->alignment;
size += sizeof(PrivateShared);
if (align > sizeof(PrivateShared)) {
@@ -463,10 +464,11 @@ public:
static constexpr size_t MaxInternalSize = 3*sizeof(void *);
template<typename T>
static constexpr bool CanUseInternalSpace = (QTypeInfo<T>::isRelocatable && sizeof(T) <= MaxInternalSize && alignof(T) <= alignof(double));
- static constexpr bool canUseInternalSpace(QMetaType type)
+ static constexpr bool canUseInternalSpace(QtPrivate::QMetaTypeInterface *type)
{
- return type.flags() & QMetaType::RelocatableType &&
- size_t(type.sizeOf()) <= MaxInternalSize && size_t(type.alignOf()) <= alignof(double);
+ Q_ASSERT(type);
+ return QMetaType::TypeFlags(type->flags) & QMetaType::RelocatableType &&
+ size_t(type->size) <= MaxInternalSize && size_t(type->alignment) <= alignof(double);
}
union
@@ -506,6 +508,12 @@ public:
{
return QMetaType(reinterpret_cast<QtPrivate::QMetaTypeInterface *>(packedType << 2));
}
+
+ inline QtPrivate::QMetaTypeInterface * typeInterface() const
+ {
+ return reinterpret_cast<QtPrivate::QMetaTypeInterface *>(packedType << 2);
+ }
+
inline int typeId() const
{
return type().id();
@@ -531,6 +539,7 @@ private:
protected:
Private d;
void create(int type, const void *copy);
+ void create(QMetaType type, const void *copy);
bool equals(const QVariant &other) const;
bool convert(int type, void *ptr) const;
bool view(int type, void *ptr);
diff --git a/src/corelib/kernel/qvariant_p.h b/src/corelib/kernel/qvariant_p.h
index f0b15012ec..ef6042d745 100644
--- a/src/corelib/kernel/qvariant_p.h
+++ b/src/corelib/kernel/qvariant_p.h
@@ -65,7 +65,7 @@ inline void v_construct(QVariant::Private *x, const T &t)
new (&x->data) T(t);
x->is_shared = false;
} else {
- x->data.shared = QVariant::PrivateShared::create(QMetaType::fromType<T>());
+ x->data.shared = QVariant::PrivateShared::create(QtPrivate::qMetaTypeInterfaceForType<T>());
new (x->data.shared->data()) T(t);
x->is_shared = true;
}
diff --git a/src/corelib/kernel/qwineventnotifier.cpp b/src/corelib/kernel/qwineventnotifier.cpp
index f9e91e73d7..3371e101b6 100644
--- a/src/corelib/kernel/qwineventnotifier.cpp
+++ b/src/corelib/kernel/qwineventnotifier.cpp
@@ -117,10 +117,7 @@ QWinEventNotifier::QWinEventNotifier(QObject *parent)
QWinEventNotifier::QWinEventNotifier(HANDLE hEvent, QObject *parent)
: QObject(*new QWinEventNotifierPrivate(hEvent, false), parent)
{
- Q_D(QWinEventNotifier);
-
- d->registerWaitObject();
- d->enabled = true;
+ setEnabled(true);
}
/*!
@@ -198,9 +195,20 @@ void QWinEventNotifier::setEnabled(bool enable)
// event shall be ignored.
d->winEventActPosted.testAndSetRelaxed(QWinEventNotifierPrivate::Posted,
QWinEventNotifierPrivate::IgnorePosted);
- d->registerWaitObject();
- } else if (d->waitHandle != NULL) {
- d->unregisterWaitObject();
+ // The notifier can't be registered, if 'enabled' flag was false.
+ // The code in the else branch ensures that.
+ Q_ASSERT(!d->registered);
+ SetThreadpoolWait(d->waitObject, d->handleToEvent, NULL);
+ d->registered = true;
+ } else if (d->registered) {
+ // Stop waiting for an event. However, there may be a callback queued
+ // already after the call.
+ SetThreadpoolWait(d->waitObject, NULL, NULL);
+ // So, to avoid a race condition after a possible call to
+ // setEnabled(true), wait for a possibly outstanding callback
+ // to complete.
+ WaitForThreadpoolWaitCallbacks(d->waitObject, TRUE);
+ d->registered = false;
}
}
@@ -226,12 +234,16 @@ bool QWinEventNotifier::event(QEvent * e)
// again.
if (d->winEventActPosted.fetchAndStoreRelaxed(QWinEventNotifierPrivate::NotPosted)
== QWinEventNotifierPrivate::Posted && d->enabled) {
- d->unregisterWaitObject();
+ // Clear the flag, as the wait object is implicitly unregistered
+ // when the callback is queued.
+ d->registered = false;
emit activated(d->handleToEvent, QPrivateSignal());
- if (d->enabled && d->waitHandle == NULL)
- d->registerWaitObject();
+ if (d->enabled && !d->registered) {
+ SetThreadpoolWait(d->waitObject, d->handleToEvent, NULL);
+ d->registered = true;
+ }
}
return true;
default:
@@ -240,8 +252,25 @@ bool QWinEventNotifier::event(QEvent * e)
return QObject::event(e);
}
-void CALLBACK QWinEventNotifierPrivate::wfsoCallback(void *context, BOOLEAN /*ignore*/)
+QWinEventNotifierPrivate::QWinEventNotifierPrivate(HANDLE h, bool e)
+ : handleToEvent(h), enabled(e), registered(false)
+{
+ waitObject = CreateThreadpoolWait(waitCallback, this, NULL);
+ if (waitObject == NULL)
+ qErrnoWarning("QWinEventNotifier:: CreateThreadpollWait failed.");
+}
+
+QWinEventNotifierPrivate::~QWinEventNotifierPrivate()
+{
+ CloseThreadpoolWait(waitObject);
+}
+
+void QWinEventNotifierPrivate::waitCallback(PTP_CALLBACK_INSTANCE instance, PVOID context,
+ PTP_WAIT wait, TP_WAIT_RESULT waitResult)
{
+ Q_UNUSED(instance);
+ Q_UNUSED(wait);
+ Q_UNUSED(waitResult);
QWinEventNotifierPrivate *nd = reinterpret_cast<QWinEventNotifierPrivate *>(context);
// Do not post an event, if an event is already in the message queue. Note
@@ -252,23 +281,4 @@ void CALLBACK QWinEventNotifierPrivate::wfsoCallback(void *context, BOOLEAN /*ig
}
}
-bool QWinEventNotifierPrivate::registerWaitObject()
-{
- if (RegisterWaitForSingleObject(&waitHandle, handleToEvent, wfsoCallback, this,
- INFINITE, WT_EXECUTEONLYONCE) == 0) {
- qErrnoWarning("QWinEventNotifier: RegisterWaitForSingleObject failed.");
- return false;
- }
- return true;
-}
-
-void QWinEventNotifierPrivate::unregisterWaitObject()
-{
- // Unregister the wait handle and wait for pending callbacks to finish.
- if (UnregisterWaitEx(waitHandle, INVALID_HANDLE_VALUE))
- waitHandle = NULL;
- else
- qErrnoWarning("QWinEventNotifier: UnregisterWaitEx failed.");
-}
-
QT_END_NAMESPACE
diff --git a/src/corelib/kernel/qwineventnotifier_p.h b/src/corelib/kernel/qwineventnotifier_p.h
index da9d8ff9a0..4e402ec826 100644
--- a/src/corelib/kernel/qwineventnotifier_p.h
+++ b/src/corelib/kernel/qwineventnotifier_p.h
@@ -63,21 +63,20 @@ class QWinEventNotifierPrivate : public QObjectPrivate
{
Q_DECLARE_PUBLIC(QWinEventNotifier)
public:
- QWinEventNotifierPrivate()
- : handleToEvent(0), enabled(false) {}
- QWinEventNotifierPrivate(HANDLE h, bool e)
- : handleToEvent(h), enabled(e) {}
+ QWinEventNotifierPrivate() : QWinEventNotifierPrivate(0, false) {}
+ QWinEventNotifierPrivate(HANDLE h, bool e);
+ virtual ~QWinEventNotifierPrivate();
- static void CALLBACK wfsoCallback(void *context, BOOLEAN /*ignore*/);
- bool registerWaitObject();
- void unregisterWaitObject();
+ static void CALLBACK waitCallback(PTP_CALLBACK_INSTANCE instance, PVOID context,
+ PTP_WAIT wait, TP_WAIT_RESULT waitResult);
HANDLE handleToEvent;
- HANDLE waitHandle = NULL;
+ PTP_WAIT waitObject = NULL;
enum PostingState { NotPosted = 0, Posted, IgnorePosted };
QAtomicInt winEventActPosted;
bool enabled;
+ bool registered;
};
QT_END_NAMESPACE
diff --git a/src/corelib/mimetypes/mime/generate.pl b/src/corelib/mimetypes/mime/generate.pl
index 1427658e59..4f2d7d5f2b 100644
--- a/src/corelib/mimetypes/mime/generate.pl
+++ b/src/corelib/mimetypes/mime/generate.pl
@@ -71,6 +71,10 @@ if (checkCommand("xmlstarlet")) {
# Minify the data before compressing
$cmd = "xmlstarlet sel -D -B -t -c / $fname";
$cmd .= "| $compress" if $compress;
+} elsif (checkCommand("xml")) {
+ # Minify the data before compressing
+ $cmd = "xml sel -D -B -t -c / $fname";
+ $cmd .= "| $compress" if $compress;
} elsif ($compress) {
$cmd = "$compress < $fname"
}
diff --git a/src/corelib/mimetypes/qmimedatabase.cpp b/src/corelib/mimetypes/qmimedatabase.cpp
index 691d578f13..2bbe6c3ad7 100644
--- a/src/corelib/mimetypes/qmimedatabase.cpp
+++ b/src/corelib/mimetypes/qmimedatabase.cpp
@@ -321,7 +321,7 @@ static inline bool isTextFile(const QByteArray &data)
const char *p = data.constData();
const char *e = p + qMin(128, data.size());
for ( ; p < e; ++p) {
- if ((unsigned char)(*p) < 32 && *p != 9 && *p !=10 && *p != 13)
+ if (static_cast<unsigned char>(*p) < 32 && *p != 9 && *p !=10 && *p != 13)
return false;
}
diff --git a/src/corelib/platform/android/qandroidnativeinterface.cpp b/src/corelib/platform/android/qandroidnativeinterface.cpp
new file mode 100644
index 0000000000..74d21c5d4c
--- /dev/null
+++ b/src/corelib/platform/android/qandroidnativeinterface.cpp
@@ -0,0 +1,85 @@
+/****************************************************************************
+**
+** Copyright (C) 2021 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtCore module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QtCore/qcoreapplication.h>
+#include <QtCore/private/qjnihelpers_p.h>
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \class QNativeInterface::QAndroidApplication
+ \since 6.2
+ \brief Native interface to a core application on Android.
+
+ Accessed through QCoreApplication::nativeInterface().
+
+ \inmodule QtCore
+ \inheaderfile QCoreApplication
+ \ingroup native-interfaces
+ \ingroup native-interfaces-qcoreapplication
+*/
+QT_DEFINE_NATIVE_INTERFACE(QAndroidApplication);
+
+/*!
+ \fn jobject QNativeInterface::QAndroidApplication::context()
+
+ Returns the Android context as a \c jobject. The context is an \c Activity
+ if the main activity object is valid. Otherwise, the context is a \c Service.
+
+ \since 6.2
+*/
+jobject QNativeInterface::QAndroidApplication::context()
+{
+ return QtAndroidPrivate::context();
+}
+
+/*!
+ \fn bool QNativeInterface::QAndroidApplication::isActivityContext()
+
+ Returns \c true if QAndroidApplication::context() provides an \c Activity
+ context.
+
+ \since 6.2
+*/
+bool QNativeInterface::QAndroidApplication::isActivityContext()
+{
+ return QtAndroidPrivate::activity();
+}
+
+QT_END_NAMESPACE
diff --git a/src/corelib/plugin/qlibrary_unix.cpp b/src/corelib/plugin/qlibrary_unix.cpp
index 95aeee8732..0be3d43e3d 100644
--- a/src/corelib/plugin/qlibrary_unix.cpp
+++ b/src/corelib/plugin/qlibrary_unix.cpp
@@ -53,7 +53,8 @@
#endif
#ifdef Q_OS_ANDROID
-# include <private/qjnihelpers_p.h>
+#include <private/qjnihelpers_p.h>
+#include <QtCore/qjnienvironment.h>
#endif
QT_BEGIN_NAMESPACE
@@ -244,7 +245,7 @@ bool QLibraryPrivate::load_sys()
if (hnd) {
using JniOnLoadPtr = jint (*)(JavaVM *vm, void *reserved);
JniOnLoadPtr jniOnLoad = reinterpret_cast<JniOnLoadPtr>(dlsym(hnd, "JNI_OnLoad"));
- if (jniOnLoad && jniOnLoad(QtAndroidPrivate::javaVM(), nullptr) == JNI_ERR) {
+ if (jniOnLoad && jniOnLoad(QJniEnvironment::javaVM(), nullptr) == JNI_ERR) {
dlclose(hnd);
hnd = nullptr;
}
diff --git a/src/corelib/serialization/qdatastream.cpp b/src/corelib/serialization/qdatastream.cpp
index 88207fe072..5582e7269e 100644
--- a/src/corelib/serialization/qdatastream.cpp
+++ b/src/corelib/serialization/qdatastream.cpp
@@ -556,6 +556,7 @@ void QDataStream::setByteOrder(ByteOrder bo)
\value Qt_5_15 Same as Qt_5_13
\value Qt_6_0 Version 20 (Qt 6.0)
\value Qt_6_1 Same as Qt_6_0
+ \value Qt_6_2 Same as Qt_6_0
\omitvalue Qt_DefaultCompiledVersion
\sa setVersion(), version()
diff --git a/src/corelib/serialization/qdatastream.h b/src/corelib/serialization/qdatastream.h
index c1b521c851..523dabfdc6 100644
--- a/src/corelib/serialization/qdatastream.h
+++ b/src/corelib/serialization/qdatastream.h
@@ -98,8 +98,9 @@ public:
Qt_5_15 = Qt_5_14,
Qt_6_0 = 20,
Qt_6_1 = Qt_6_0,
- Qt_DefaultCompiledVersion = Qt_6_1
-#if QT_VERSION >= 0x060200
+ Qt_6_2 = Qt_6_0,
+ Qt_DefaultCompiledVersion = Qt_6_2
+#if QT_VERSION >= 0x060300
#error Add the datastream version for this Qt version and update Qt_DefaultCompiledVersion
#endif
};
diff --git a/src/corelib/serialization/qjsoncbor.cpp b/src/corelib/serialization/qjsoncbor.cpp
index 6256028c3a..53ee73d905 100644
--- a/src/corelib/serialization/qjsoncbor.cpp
+++ b/src/corelib/serialization/qjsoncbor.cpp
@@ -201,7 +201,6 @@ QJsonValue qt_convertToJson(QCborContainerPrivate *d, qsizetype idx,
static QJsonValue convertExtendedTypeToJson(QCborContainerPrivate *d)
{
-#ifndef QT_BUILD_QMAKE
qint64 tag = d->elements.at(0).value;
switch (tag) {
@@ -222,7 +221,6 @@ static QJsonValue convertExtendedTypeToJson(QCborContainerPrivate *d)
return s;
}
}
-#endif
// for all other tags, ignore it and return the converted tagged item
return qt_convertToJson(d, 1);
diff --git a/src/corelib/serialization/qjsonobject.cpp b/src/corelib/serialization/qjsonobject.cpp
index 0c4af59163..a06af5d02d 100644
--- a/src/corelib/serialization/qjsonobject.cpp
+++ b/src/corelib/serialization/qjsonobject.cpp
@@ -617,6 +617,7 @@ QJsonValue QJsonObject::takeImpl(T key)
if (!keyExists)
return QJsonValue(QJsonValue::Undefined);
+ detach();
const QJsonValue v = QJsonPrivate::Value::fromTrustedCbor(o->extractAt(index + 1));
removeAt(index / 2);
return v;
diff --git a/src/corelib/serialization/qjsonobject.h b/src/corelib/serialization/qjsonobject.h
index 1e6cc65227..e477345643 100644
--- a/src/corelib/serialization/qjsonobject.h
+++ b/src/corelib/serialization/qjsonobject.h
@@ -153,7 +153,7 @@ public:
bool operator<(const iterator& other) const
{ Q_ASSERT(item.o == other.item.o); return item.index < other.item.index; }
bool operator<=(const iterator& other) const
- { Q_ASSERT(item.o == other.item.o); return item.index < other.item.index; }
+ { Q_ASSERT(item.o == other.item.o); return item.index <= other.item.index; }
bool operator>(const iterator& other) const { return !(*this <= other); }
bool operator>=(const iterator& other) const { return !(*this < other); }
diff --git a/src/corelib/serialization/qjsonvalue.h b/src/corelib/serialization/qjsonvalue.h
index a9dd86df19..4b59dede01 100644
--- a/src/corelib/serialization/qjsonvalue.h
+++ b/src/corelib/serialization/qjsonvalue.h
@@ -180,6 +180,10 @@ public:
QJsonArray toArray() const;
QJsonObject toObject() const;
+ const QJsonValue operator[](QStringView key) const { return toValue()[key]; }
+ const QJsonValue operator[](QLatin1String key) const { return toValue()[key]; }
+ const QJsonValue operator[](qsizetype i) const { return toValue()[i]; }
+
inline bool operator==(const QJsonValue &other) const { return toValue() == other; }
inline bool operator!=(const QJsonValue &other) const { return toValue() != other; }
diff --git a/src/corelib/text/qbytearray.cpp b/src/corelib/text/qbytearray.cpp
index a815037a73..3a0f28f444 100644
--- a/src/corelib/text/qbytearray.cpp
+++ b/src/corelib/text/qbytearray.cpp
@@ -2999,7 +2999,7 @@ void QByteArray::clear()
d.clear();
}
-#if !defined(QT_NO_DATASTREAM) || (defined(QT_BOOTSTRAPPED) && !defined(QT_BUILD_QMAKE))
+#if !defined(QT_NO_DATASTREAM) || defined(QT_BOOTSTRAPPED)
/*! \relates QByteArray
@@ -4767,6 +4767,29 @@ QByteArray QByteArray::toPercentEncoding(const QByteArray &exclude, const QByteA
*/
/*!
+ \fn QtLiterals::operator""_qba(const char *str, size_t size)
+
+ \relates QByteArray
+ \since 6.2
+
+ Literal operator that creates a QByteArray out of the first \a size characters
+ in the char string literal \a str.
+
+ The QByteArray is created at compile time, and the generated string data is stored
+ in the read-only segment of the compiled object file. Duplicate literals may share
+ the same read-only memory. This functionality is interchangeable with
+ QByteArrayLiteral, but saves typing when many string literals are present in the
+ code.
+
+ The following code creates a QByteArray:
+ \code
+ auto str = "hello"_qba;
+ \endcode
+
+ \sa QByteArrayLiteral, QtLiterals::operator""_qs(const char16_t *str, size_t size)
+*/
+
+/*!
\class QByteArray::FromBase64Result
\inmodule QtCore
\ingroup tools
diff --git a/src/corelib/text/qbytearray.h b/src/corelib/text/qbytearray.h
index e16d888d62..91c34747d8 100644
--- a/src/corelib/text/qbytearray.h
+++ b/src/corelib/text/qbytearray.h
@@ -675,7 +675,7 @@ inline std::string QByteArray::toStdString() const
inline QByteArray QByteArray::fromStdString(const std::string &s)
{ return QByteArray(s.data(), qsizetype(s.size())); }
-#if !defined(QT_NO_DATASTREAM) || (defined(QT_BOOTSTRAPPED) && !defined(QT_BUILD_QMAKE))
+#if !defined(QT_NO_DATASTREAM) || defined(QT_BOOTSTRAPPED)
Q_CORE_EXPORT QDataStream &operator<<(QDataStream &, const QByteArray &);
Q_CORE_EXPORT QDataStream &operator>>(QDataStream &, QByteArray &);
#endif
@@ -756,6 +756,13 @@ QByteArray QByteArrayView::toByteArray() const
return QByteArray(data(), size());
}
+inline namespace QtLiterals {
+inline QByteArray operator"" _qba(const char *str, size_t size) noexcept
+{
+ return QByteArray(QByteArrayData(nullptr, const_cast<char *>(str), qsizetype(size)));
+}
+} // QtLiterals
+
QT_END_NAMESPACE
#endif // QBYTEARRAY_H
diff --git a/src/corelib/text/qlocale.cpp b/src/corelib/text/qlocale.cpp
index faa49ee4bc..184b38c32f 100644
--- a/src/corelib/text/qlocale.cpp
+++ b/src/corelib/text/qlocale.cpp
@@ -87,7 +87,7 @@ public:
};
Q_GLOBAL_STATIC(QSystemLocaleSingleton, QSystemLocale_globalSystemLocale)
-static QLocaleData globalLocaleData;
+static QLocaleData systemLocaleData;
#endif
/******************************************************************************
@@ -102,7 +102,7 @@ QLocale::Language QLocalePrivate::codeToLanguage(QStringView code) noexcept
{
const auto len = code.size();
if (len != 2 && len != 3)
- return QLocale::C;
+ return QLocale::AnyLanguage;
char16_t uc1 = code[0].toLower().unicode();
char16_t uc2 = code[1].toLower().unicode();
char16_t uc3 = len > 2 ? code[2].toLower().unicode() : 0;
@@ -131,7 +131,7 @@ QLocale::Language QLocalePrivate::codeToLanguage(QStringView code) noexcept
if (uc1 == 'j' && uc2 == 'i') // ji -> yi
return QLocale::Yiddish;
}
- return QLocale::C;
+ return QLocale::AnyLanguage;
}
QLocale::Script QLocalePrivate::codeToScript(QStringView code) noexcept
@@ -574,8 +574,8 @@ QLocaleId QLocaleId::fromName(const QString &name)
return { QLocale::C, 0, 0 };
QLocale::Language langId = QLocalePrivate::codeToLanguage(lang);
- if (langId == QLocale::C)
- return QLocaleId { langId, 0, 0 };
+ if (langId == QLocale::AnyLanguage)
+ return { QLocale::C, 0, 0 };
return { langId, QLocalePrivate::codeToScript(script), QLocalePrivate::codeToCountry(land) };
}
@@ -668,7 +668,7 @@ QSystemLocale::QSystemLocale()
if (!_systemLocale)
_systemLocale = this;
- globalLocaleData.m_language_id = 0;
+ systemLocaleData.m_language_id = 0;
}
/*!
@@ -685,7 +685,7 @@ QSystemLocale::~QSystemLocale()
if (_systemLocale == this) {
_systemLocale = nullptr;
- globalLocaleData.m_language_id = 0;
+ systemLocaleData.m_language_id = 0;
}
}
@@ -706,22 +706,22 @@ static void updateSystemPrivate()
// tell the object that the system locale has changed.
sys_locale->query(QSystemLocale::LocaleChanged);
- // Populate global with fallback as basis:
- globalLocaleData = locale_data[sys_locale->fallbackUiLocaleIndex()];
+ // Populate system locale with fallback as basis
+ systemLocaleData = locale_data[sys_locale->fallbackLocaleIndex()];
QVariant res = sys_locale->query(QSystemLocale::LanguageId);
if (!res.isNull()) {
- globalLocaleData.m_language_id = res.toInt();
- globalLocaleData.m_script_id = QLocale::AnyScript; // default for compatibility
+ systemLocaleData.m_language_id = res.toInt();
+ systemLocaleData.m_script_id = QLocale::AnyScript; // default for compatibility
}
res = sys_locale->query(QSystemLocale::CountryId);
if (!res.isNull()) {
- globalLocaleData.m_country_id = res.toInt();
- globalLocaleData.m_script_id = QLocale::AnyScript; // default for compatibility
+ systemLocaleData.m_country_id = res.toInt();
+ systemLocaleData.m_script_id = QLocale::AnyScript; // default for compatibility
}
res = sys_locale->query(QSystemLocale::ScriptId);
if (!res.isNull())
- globalLocaleData.m_script_id = res.toInt();
+ systemLocaleData.m_script_id = res.toInt();
// Should we replace Any values based on likely sub-tags ?
}
@@ -739,12 +739,12 @@ static const QLocaleData *systemData()
{
static QBasicMutex systemDataMutex;
systemDataMutex.lock();
- if (globalLocaleData.m_language_id == 0)
+ if (systemLocaleData.m_language_id == 0)
updateSystemPrivate();
systemDataMutex.unlock();
}
- return &globalLocaleData;
+ return &systemLocaleData;
#else
return locale_data;
#endif
@@ -761,7 +761,7 @@ static uint defaultIndex()
{
const QLocaleData *const data = defaultData();
#ifndef QT_NO_SYSTEMLOCALE
- if (data == &globalLocaleData) {
+ if (data == &systemLocaleData) {
// Work out a suitable index matching the system data, for use when
// accessing calendar data, when not fetched from system.
return QLocaleData::findLocaleIndex(data->id());
@@ -837,7 +837,7 @@ static QLocalePrivate *findLocalePrivate(QLocale::Language language, QLocale::Sc
QString QLocaleData::decimalPoint() const
{
#ifndef QT_NO_SYSTEMLOCALE
- if (this == &globalLocaleData) {
+ if (this == &systemLocaleData) {
auto res = systemLocale()->query(QSystemLocale::DecimalPoint).toString();
if (!res.isEmpty())
return res;
@@ -850,7 +850,7 @@ QString QLocaleData::groupSeparator() const
{
// Empty => don't do grouping
#ifndef QT_NO_SYSTEMLOCALE
- if (this == &globalLocaleData) {
+ if (this == &systemLocaleData) {
QVariant res = systemLocale()->query(QSystemLocale::GroupSeparator);
if (!res.isNull())
return res.toString();
@@ -872,7 +872,7 @@ QString QLocaleData::listSeparator() const
QString QLocaleData::zeroDigit() const
{
#ifndef QT_NO_SYSTEMLOCALE
- if (this == &globalLocaleData) {
+ if (this == &systemLocaleData) {
auto res = systemLocale()->query(QSystemLocale::ZeroDigit).toString();
if (!res.isEmpty())
return res;
@@ -884,7 +884,7 @@ QString QLocaleData::zeroDigit() const
char32_t QLocaleData::zeroUcs() const
{
#ifndef QT_NO_SYSTEMLOCALE
- if (this == &globalLocaleData) {
+ if (this == &systemLocaleData) {
const auto text = systemLocale()->query(QSystemLocale::ZeroDigit).toString();
if (!text.isEmpty()) {
if (text.size() == 1 && !text.at(0).isSurrogate())
@@ -900,7 +900,7 @@ char32_t QLocaleData::zeroUcs() const
QString QLocaleData::negativeSign() const
{
#ifndef QT_NO_SYSTEMLOCALE
- if (this == &globalLocaleData) {
+ if (this == &systemLocaleData) {
auto res = systemLocale()->query(QSystemLocale::NegativeSign).toString();
if (!res.isEmpty())
return res;
@@ -912,7 +912,7 @@ QString QLocaleData::negativeSign() const
QString QLocaleData::positiveSign() const
{
#ifndef QT_NO_SYSTEMLOCALE
- if (this == &globalLocaleData) {
+ if (this == &systemLocaleData) {
auto res = systemLocale()->query(QSystemLocale::PositiveSign).toString();
if (!res.isEmpty())
return res;
@@ -1143,7 +1143,7 @@ QLocale::NumberOptions QLocale::numberOptions() const
QString QLocale::quoteString(QStringView str, QuotationStyle style) const
{
#ifndef QT_NO_SYSTEMLOCALE
- if (d->m_data == &globalLocaleData) {
+ if (d->m_data == &systemLocaleData) {
QVariant res;
if (style == QLocale::AlternateQuotation)
res = systemLocale()->query(QSystemLocale::StringToAlternateQuotation,
@@ -1178,7 +1178,7 @@ QString QLocale::createSeparatedList(const QStringList &list) const
{
// May be empty if list is empty or sole entry is empty.
#ifndef QT_NO_SYSTEMLOCALE
- if (d->m_data == &globalLocaleData) {
+ if (d->m_data == &systemLocaleData) {
QVariant res =
systemLocale()->query(QSystemLocale::ListToSeparatedString, QVariant::fromValue(list));
@@ -1262,7 +1262,7 @@ QLocale::Script QLocale::script() const
}
/*!
- Returns the country of this locale.
+ Returns the country or region of this locale.
\sa language(), script(), countryToString(), bcp47Name()
*/
@@ -1353,7 +1353,7 @@ QString QLocale::bcp47Name() const
For \c QLocale::AnyLanguage an empty string is returned.
\since 6.1
- \sa language(), name(), bcp47Name(), countryToCode(), scriptToCode()
+ \sa codeToLanguage(), language(), name(), bcp47Name(), countryToCode(), scriptToCode()
*/
QString QLocale::languageToCode(Language language)
{
@@ -1361,13 +1361,27 @@ QString QLocale::languageToCode(Language language)
}
/*!
+ Returns the QLocale::Language enum corresponding to the two- or three-letter
+ \a languageCode, as defined in the ISO 639 standards.
+
+ If the code is invalid or not known QLocale::AnyLanguage is returned.
+
+ \since 6.1
+ \sa languageToCode(), codeToCountry(), codeToScript()
+*/
+QLocale::Language QLocale::codeToLanguage(QStringView languageCode) noexcept
+{
+ return QLocalePrivate::codeToLanguage(languageCode);
+}
+
+/*!
Returns the two-letter country code for \a country, as defined
in the ISO 3166 standard.
\note For \c{QLocale::AnyCountry} an empty string is returned.
\since 6.1
- \sa country(), name(), bcp47Name(), languageToCode(), scriptToCode()
+ \sa codeToCountry(), country(), name(), bcp47Name(), languageToCode(), scriptToCode()
*/
QString QLocale::countryToCode(Country country)
{
@@ -1375,6 +1389,20 @@ QString QLocale::countryToCode(Country country)
}
/*!
+ Returns the QLocale::Country enum corresponding to the two-letter or
+ three-digit \a countryCode, as defined in the ISO 3166 standard.
+
+ If the code is invalid or not known QLocale::AnyCountry is returned.
+
+ \since 6.1
+ \sa countryToCode(), codeToLanguage(), codeToScript()
+*/
+QLocale::Country QLocale::codeToCountry(QStringView countryCode) noexcept
+{
+ return QLocalePrivate::codeToCountry(countryCode);
+}
+
+/*!
Returns the four-letter script code for \a script, as defined in the
ISO 15924 standard.
@@ -1389,6 +1417,20 @@ QString QLocale::scriptToCode(Script script)
}
/*!
+ Returns the QLocale::Script enum corresponding to the four-letter script
+ \a scriptCode, as defined in the ISO 15924 standard.
+
+ If the code is invalid or not known QLocale::AnyScript is returned.
+
+ \since 6.1
+ \sa scriptToCode(), codeToLanguage(), codeToCountry()
+*/
+QLocale::Script QLocale::codeToScript(QStringView scriptCode) noexcept
+{
+ return QLocalePrivate::codeToScript(scriptCode);
+}
+
+/*!
Returns a QString containing the name of \a language.
\sa countryToString(), scriptToString(), bcp47Name()
@@ -1911,7 +1953,7 @@ QString QLocale::toString(QDate date, FormatType format, QCalendar cal) const
return QString();
#ifndef QT_NO_SYSTEMLOCALE
- if (cal.isGregorian() && d->m_data == &globalLocaleData) {
+ if (cal.isGregorian() && d->m_data == &systemLocaleData) {
QVariant res = systemLocale()->query(format == LongFormat
? QSystemLocale::DateToStringLong
: QSystemLocale::DateToStringShort,
@@ -1935,7 +1977,7 @@ QString QLocale::toString(QDate date, FormatType format) const
return QString();
#ifndef QT_NO_SYSTEMLOCALE
- if (d->m_data == &globalLocaleData) {
+ if (d->m_data == &systemLocaleData) {
QVariant res = systemLocale()->query(format == LongFormat
? QSystemLocale::DateToStringLong
: QSystemLocale::DateToStringShort,
@@ -2019,7 +2061,7 @@ QString QLocale::toString(const QDateTime &dateTime, FormatType format, QCalenda
return QString();
#ifndef QT_NO_SYSTEMLOCALE
- if (cal.isGregorian() && d->m_data == &globalLocaleData) {
+ if (cal.isGregorian() && d->m_data == &systemLocaleData) {
QVariant res = systemLocale()->query(format == LongFormat
? QSystemLocale::DateTimeToStringLong
: QSystemLocale::DateTimeToStringShort,
@@ -2043,7 +2085,7 @@ QString QLocale::toString(const QDateTime &dateTime, FormatType format) const
return QString();
#ifndef QT_NO_SYSTEMLOCALE
- if (d->m_data == &globalLocaleData) {
+ if (d->m_data == &systemLocaleData) {
QVariant res = systemLocale()->query(format == LongFormat
? QSystemLocale::DateTimeToStringLong
: QSystemLocale::DateTimeToStringShort,
@@ -2069,7 +2111,7 @@ QString QLocale::toString(QTime time, FormatType format) const
return QString();
#ifndef QT_NO_SYSTEMLOCALE
- if (d->m_data == &globalLocaleData) {
+ if (d->m_data == &systemLocaleData) {
QVariant res = systemLocale()->query(format == LongFormat
? QSystemLocale::TimeToStringLong
: QSystemLocale::TimeToStringShort,
@@ -2098,7 +2140,7 @@ QString QLocale::toString(QTime time, FormatType format) const
QString QLocale::dateFormat(FormatType format) const
{
#ifndef QT_NO_SYSTEMLOCALE
- if (d->m_data == &globalLocaleData) {
+ if (d->m_data == &systemLocaleData) {
QVariant res = systemLocale()->query(format == LongFormat
? QSystemLocale::DateFormatLong
: QSystemLocale::DateFormatShort,
@@ -2129,7 +2171,7 @@ QString QLocale::dateFormat(FormatType format) const
QString QLocale::timeFormat(FormatType format) const
{
#ifndef QT_NO_SYSTEMLOCALE
- if (d->m_data == &globalLocaleData) {
+ if (d->m_data == &systemLocaleData) {
QVariant res = systemLocale()->query(format == LongFormat
? QSystemLocale::TimeFormatLong
: QSystemLocale::TimeFormatShort,
@@ -2160,7 +2202,7 @@ QString QLocale::timeFormat(FormatType format) const
QString QLocale::dateTimeFormat(FormatType format) const
{
#ifndef QT_NO_SYSTEMLOCALE
- if (d->m_data == &globalLocaleData) {
+ if (d->m_data == &systemLocaleData) {
QVariant res = systemLocale()->query(format == LongFormat
? QSystemLocale::DateTimeFormatLong
: QSystemLocale::DateTimeFormatShort,
@@ -2781,7 +2823,7 @@ QString QGregorianCalendar::monthName(const QLocale &locale, int month, int year
QLocale::FormatType format) const
{
#ifndef QT_NO_SYSTEMLOCALE
- if (locale.d->m_data == &globalLocaleData) {
+ if (locale.d->m_data == &systemLocaleData) {
Q_ASSERT(month >= 1 && month <= 12);
QVariant res = systemLocale()->query(format == QLocale::LongFormat
? QSystemLocale::MonthNameLong
@@ -2807,7 +2849,7 @@ QString QGregorianCalendar::standaloneMonthName(const QLocale &locale, int month
QLocale::FormatType format) const
{
#ifndef QT_NO_SYSTEMLOCALE
- if (locale.d->m_data == &globalLocaleData) {
+ if (locale.d->m_data == &systemLocaleData) {
Q_ASSERT(month >= 1 && month <= 12);
QVariant res = systemLocale()->query(format == QLocale::LongFormat
? QSystemLocale::StandaloneMonthNameLong
@@ -2830,7 +2872,7 @@ QString QCalendarBackend::weekDayName(const QLocale &locale, int day,
return QString();
#ifndef QT_NO_SYSTEMLOCALE
- if (locale.d->m_data == &globalLocaleData) {
+ if (locale.d->m_data == &systemLocaleData) {
QVariant res = systemLocale()->query(format == QLocale::LongFormat
? QSystemLocale::DayNameLong
: QSystemLocale::DayNameShort,
@@ -2850,7 +2892,7 @@ QString QCalendarBackend::standaloneWeekDayName(const QLocale &locale, int day,
return QString();
#ifndef QT_NO_SYSTEMLOCALE
- if (locale.d->m_data == &globalLocaleData) {
+ if (locale.d->m_data == &systemLocaleData) {
QVariant res = systemLocale()->query(format == QLocale::LongFormat
? QSystemLocale::DayNameLong
: QSystemLocale::DayNameShort,
@@ -2873,7 +2915,7 @@ QString QCalendarBackend::standaloneWeekDayName(const QLocale &locale, int day,
Qt::DayOfWeek QLocale::firstDayOfWeek() const
{
#ifndef QT_NO_SYSTEMLOCALE
- if (d->m_data == &globalLocaleData) {
+ if (d->m_data == &systemLocaleData) {
const auto res = systemLocale()->query(QSystemLocale::FirstDayOfWeek);
if (!res.isNull())
return static_cast<Qt::DayOfWeek>(res.toUInt());
@@ -2901,7 +2943,7 @@ QLocale::MeasurementSystem QLocalePrivate::measurementSystem() const
QList<Qt::DayOfWeek> QLocale::weekdays() const
{
#ifndef QT_NO_SYSTEMLOCALE
- if (d->m_data == &globalLocaleData) {
+ if (d->m_data == &systemLocaleData) {
auto res
= qvariant_cast<QList<Qt::DayOfWeek> >(systemLocale()->query(QSystemLocale::Weekdays));
if (!res.isEmpty())
@@ -2927,7 +2969,7 @@ QList<Qt::DayOfWeek> QLocale::weekdays() const
QLocale::MeasurementSystem QLocale::measurementSystem() const
{
#ifndef QT_NO_SYSTEMLOCALE
- if (d->m_data == &globalLocaleData) {
+ if (d->m_data == &systemLocaleData) {
const auto res = systemLocale()->query(QSystemLocale::MeasurementSystem);
if (!res.isNull())
return MeasurementSystem(res.toInt());
@@ -3040,7 +3082,7 @@ QString QLocale::toLower(const QString &str) const
QString QLocale::amText() const
{
#ifndef QT_NO_SYSTEMLOCALE
- if (d->m_data == &globalLocaleData) {
+ if (d->m_data == &systemLocaleData) {
auto res = systemLocale()->query(QSystemLocale::AMText).toString();
if (!res.isEmpty())
return res;
@@ -3060,7 +3102,7 @@ QString QLocale::amText() const
QString QLocale::pmText() const
{
#ifndef QT_NO_SYSTEMLOCALE
- if (d->m_data == &globalLocaleData) {
+ if (d->m_data == &systemLocaleData) {
auto res = systemLocale()->query(QSystemLocale::PMText).toString();
if (!res.isEmpty())
return res;
@@ -3996,7 +4038,7 @@ qulonglong QLocaleData::bytearrayToUnsLongLong(const char *num, int base, bool *
QString QLocale::currencySymbol(QLocale::CurrencySymbolFormat format) const
{
#ifndef QT_NO_SYSTEMLOCALE
- if (d->m_data == &globalLocaleData) {
+ if (d->m_data == &systemLocaleData) {
auto res = systemLocale()->query(QSystemLocale::CurrencySymbol, format).toString();
if (!res.isEmpty())
return res;
@@ -4028,7 +4070,7 @@ QString QLocale::currencySymbol(QLocale::CurrencySymbolFormat format) const
QString QLocale::toCurrencyString(qlonglong value, const QString &symbol) const
{
#ifndef QT_NO_SYSTEMLOCALE
- if (d->m_data == &globalLocaleData) {
+ if (d->m_data == &systemLocaleData) {
QSystemLocale::CurrencyToStringArgument arg(value, symbol);
auto res = systemLocale()->query(QSystemLocale::CurrencyToString,
QVariant::fromValue(arg)).toString();
@@ -4055,7 +4097,7 @@ QString QLocale::toCurrencyString(qlonglong value, const QString &symbol) const
QString QLocale::toCurrencyString(qulonglong value, const QString &symbol) const
{
#ifndef QT_NO_SYSTEMLOCALE
- if (d->m_data == &globalLocaleData) {
+ if (d->m_data == &systemLocaleData) {
QSystemLocale::CurrencyToStringArgument arg(value, symbol);
auto res = systemLocale()->query(QSystemLocale::CurrencyToString,
QVariant::fromValue(arg)).toString();
@@ -4083,7 +4125,7 @@ QString QLocale::toCurrencyString(qulonglong value, const QString &symbol) const
QString QLocale::toCurrencyString(double value, const QString &symbol, int precision) const
{
#ifndef QT_NO_SYSTEMLOCALE
- if (d->m_data == &globalLocaleData) {
+ if (d->m_data == &systemLocaleData) {
QSystemLocale::CurrencyToStringArgument arg(value, symbol);
auto res = systemLocale()->query(QSystemLocale::CurrencyToString,
QVariant::fromValue(arg)).toString();
@@ -4192,13 +4234,13 @@ QStringList QLocale::uiLanguages() const
QStringList uiLanguages;
QList<QLocale> locales;
#ifndef QT_NO_SYSTEMLOCALE
- if (d->m_data == &globalLocaleData) {
+ if (d->m_data == &systemLocaleData) {
uiLanguages = systemLocale()->query(QSystemLocale::UILanguages).toStringList();
// ... but we need to include likely-adjusted forms of each of those, too:
for (const auto &entry : uiLanguages)
locales.append(QLocale(entry));
if (locales.isEmpty())
- locales.append(systemLocale()->fallbackUiLocale());
+ locales.append(systemLocale()->fallbackLocale());
} else
#endif
{
@@ -4261,7 +4303,7 @@ QStringList QLocale::uiLanguages() const
QLocale QLocale::collation() const
{
#ifndef QT_NO_SYSTEMLOCALE
- if (d->m_data == &globalLocaleData) {
+ if (d->m_data == &systemLocaleData) {
const auto res = systemLocale()->query(QSystemLocale::Collation).toString();
if (!res.isEmpty())
return QLocale(res);
@@ -4281,7 +4323,7 @@ QLocale QLocale::collation() const
QString QLocale::nativeLanguageName() const
{
#ifndef QT_NO_SYSTEMLOCALE
- if (d->m_data == &globalLocaleData) {
+ if (d->m_data == &systemLocaleData) {
auto res = systemLocale()->query(QSystemLocale::NativeLanguageName).toString();
if (!res.isEmpty())
return res;
@@ -4301,7 +4343,7 @@ QString QLocale::nativeLanguageName() const
QString QLocale::nativeCountryName() const
{
#ifndef QT_NO_SYSTEMLOCALE
- if (d->m_data == &globalLocaleData) {
+ if (d->m_data == &systemLocaleData) {
auto res = systemLocale()->query(QSystemLocale::NativeCountryName).toString();
if (!res.isEmpty())
return res;
diff --git a/src/corelib/text/qlocale.h b/src/corelib/text/qlocale.h
index a05196946f..6a9d9b46d8 100644
--- a/src/corelib/text/qlocale.h
+++ b/src/corelib/text/qlocale.h
@@ -1068,8 +1068,11 @@ public:
QStringList uiLanguages() const;
static QString languageToCode(Language language);
+ static Language codeToLanguage(QStringView languageCode) noexcept;
static QString countryToCode(Country country);
+ static Country codeToCountry(QStringView countryCode) noexcept;
static QString scriptToCode(Script script);
+ static Script codeToScript(QStringView scriptCode) noexcept;
static QString languageToString(Language language);
static QString countryToString(Country country);
diff --git a/src/corelib/text/qlocale.qdoc b/src/corelib/text/qlocale.qdoc
index b20fce7c6d..f472560d22 100644
--- a/src/corelib/text/qlocale.qdoc
+++ b/src/corelib/text/qlocale.qdoc
@@ -460,7 +460,7 @@
/*!
\enum QLocale::Country
- This enumerated type is used to specify a country.
+ This enumerated type is used to specify a country or a region.
\value AnyCountry
@@ -1113,13 +1113,6 @@
*/
/*!
- \fn QLocale QSystemLocale::fallbackUiLocale() const
-
- \since 4.6
- Returns the fallback locale obtained from the system.
-*/
-
-/*!
\fn QVariant QSystemLocale::query(QueryType type, QVariant in = QVariant()) const
Generic query method for locale data. Provides indirection.
@@ -1130,6 +1123,17 @@
*/
/*!
+ \fn QLocale QSystemLocale::fallbackLocale() const
+
+ \since 4.6
+
+ Returns the locale used if the system locale is not able to answer a query.
+
+ Must be a QLocale instance based on the built-in CLDR data, and should
+ match what the system locale is using as closely as that data supports.
+*/
+
+/*!
\class QSystemLocale::CurrencyToStringArgument
\inmodule QtCore
diff --git a/src/corelib/text/qlocale_mac.mm b/src/corelib/text/qlocale_mac.mm
index 281fedcfab..9c1eb6dcfb 100644
--- a/src/corelib/text/qlocale_mac.mm
+++ b/src/corelib/text/qlocale_mac.mm
@@ -54,29 +54,11 @@ QT_BEGIN_NAMESPACE
** Wrappers for Mac locale system functions
*/
-static QByteArray envVarLocale()
-{
- QByteArray
-#ifdef Q_OS_UNIX
- lang = qgetenv("LC_ALL");
- if (lang.isEmpty())
- lang = qgetenv("LC_NUMERIC");
- if (lang.isEmpty())
-#endif
- lang = qgetenv("LANG");
- return lang;
-}
-
static QString getMacLocaleName()
{
- QString result = QString::fromLocal8Bit(envVarLocale());
- if (result.isEmpty()
- || (result != QLatin1String("C") && !qt_splitLocaleName(result))) {
- QCFType<CFLocaleRef> l = CFLocaleCopyCurrent();
- CFStringRef locale = CFLocaleGetIdentifier(l);
- result = QString::fromCFString(locale);
- }
- return result;
+ QCFType<CFLocaleRef> l = CFLocaleCopyCurrent();
+ CFStringRef locale = CFLocaleGetIdentifier(l);
+ return QString::fromCFString(locale);
}
static QString macMonthName(int month, QSystemLocale::QueryType type)
@@ -418,17 +400,36 @@ static QVariant macQuoteString(QSystemLocale::QueryType type, QStringView str)
#ifndef QT_NO_SYSTEMLOCALE
-QLocale QSystemLocale::fallbackUiLocale() const
+QLocale QSystemLocale::fallbackLocale() const
{
return QLocale(getMacLocaleName());
}
+template <auto CodeToValueFunction>
+static QVariant getLocaleValue(CFStringRef key)
+{
+ if (auto code = getCFLocaleValue(key); !code.isNull()) {
+ // If an invalid locale is requested with -AppleLocale, the system APIs
+ // will report invalid or empty locale values back to us, which codeToLanguage()
+ // and friends will fail to parse, resulting in returning QLocale::Any{L/C/S}.
+ // If this is the case, we fall down and return a null-variant, which
+ // QLocale's updateSystemPrivate() will interpret to use fallback logic.
+ if (auto value = CodeToValueFunction(code.toString()))
+ return value;
+ }
+ return QVariant();
+}
+
QVariant QSystemLocale::query(QueryType type, QVariant in) const
{
QMacAutoReleasePool pool;
switch(type) {
-// case Name:
-// return getMacLocaleName();
+ case LanguageId:
+ return getLocaleValue<QLocalePrivate::codeToLanguage>(kCFLocaleLanguageCode);
+ case CountryId:
+ return getLocaleValue<QLocalePrivate::codeToCountry>(kCFLocaleCountryCode);
+ case ScriptId:
+ return getLocaleValue<QLocalePrivate::codeToScript>(kCFLocaleScriptCode);
case DecimalPoint:
return getCFLocaleValue(kCFLocaleDecimalSeparator);
case GroupSeparator:
diff --git a/src/corelib/text/qlocale_p.h b/src/corelib/text/qlocale_p.h
index 1b5cc0aa55..6e4073eaa3 100644
--- a/src/corelib/text/qlocale_p.h
+++ b/src/corelib/text/qlocale_p.h
@@ -127,9 +127,9 @@ public:
StandaloneMonthNameShort // QString, in: int
};
virtual QVariant query(QueryType type, QVariant in = QVariant()) const;
- virtual QLocale fallbackUiLocale() const;
- inline uint fallbackUiLocaleIndex() const;
+ virtual QLocale fallbackLocale() const;
+ inline uint fallbackLocaleIndex() const;
private:
QSystemLocale(bool);
friend class QSystemLocaleSingleton;
@@ -148,7 +148,6 @@ namespace QIcu {
struct QLocaleId
{
- // ### Also used by Translator::languageAndCountry() in qttools:
Q_CORE_EXPORT static QLocaleId fromName(const QString &name);
inline bool operator==(QLocaleId other) const
{ return language_id == other.language_id && script_id == other.script_id && country_id == other.country_id; }
@@ -429,7 +428,7 @@ public:
};
#ifndef QT_NO_SYSTEMLOCALE
-uint QSystemLocale::fallbackUiLocaleIndex() const { return fallbackUiLocale().d->m_index; }
+uint QSystemLocale::fallbackLocaleIndex() const { return fallbackLocale().d->m_index; }
#endif
template <>
diff --git a/src/corelib/text/qlocale_unix.cpp b/src/corelib/text/qlocale_unix.cpp
index adc73ad19a..2caa6b7799 100644
--- a/src/corelib/text/qlocale_unix.cpp
+++ b/src/corelib/text/qlocale_unix.cpp
@@ -133,7 +133,7 @@ static bool contradicts(const QString &maybe, const QString &known)
return !(maybeId.acceptLanguage(knownId.language_id) && maybeId.acceptScriptCountry(knownId));
}
-QLocale QSystemLocale::fallbackUiLocale() const
+QLocale QSystemLocale::fallbackLocale() const
{
// See man 7 locale for precedence - LC_ALL beats LC_MESSAGES beats LANG:
QString lang = qEnvironmentVariable("LC_ALL");
diff --git a/src/corelib/text/qlocale_win.cpp b/src/corelib/text/qlocale_win.cpp
index f244826c33..664e82a5aa 100644
--- a/src/corelib/text/qlocale_win.cpp
+++ b/src/corelib/text/qlocale_win.cpp
@@ -595,7 +595,7 @@ QVariant QSystemLocalePrivate::uiLanguages()
{
unsigned long cnt = 0;
QVarLengthArray<wchar_t, 64> buf(64);
-# if !defined(QT_BOOTSTRAPPED) && !defined(QT_BUILD_QMAKE) // Not present in MinGW 4.9/bootstrap builds.
+# if !defined(QT_BOOTSTRAPPED) // Not present in MinGW 4.9/bootstrap builds.
unsigned long size = buf.size();
if (!GetUserPreferredUILanguages(MUI_LANGUAGE_NAME, &cnt, buf.data(), &size)) {
size = 0;
@@ -606,7 +606,7 @@ QVariant QSystemLocalePrivate::uiLanguages()
return QStringList();
}
}
-# endif // !QT_BOOTSTRAPPED && !QT_BUILD_QMAKE
+# endif // !QT_BOOTSTRAPPED
QStringList result;
result.reserve(cnt);
const wchar_t *str = buf.constData();
@@ -702,7 +702,7 @@ QString QSystemLocalePrivate::winToQtFormat(QStringView sys_fmt)
return result;
}
-QLocale QSystemLocale::fallbackUiLocale() const
+QLocale QSystemLocale::fallbackLocale() const
{
return QLocale(QString::fromLatin1(getWinLocaleName()));
}
@@ -762,8 +762,8 @@ QVariant QSystemLocale::query(QueryType type, QVariant in) const
if (type == LanguageId)
return lid.language_id;
if (type == ScriptId)
- return lid.script_id ? lid.script_id : ushort(fallbackUiLocale().script());
- return lid.country_id ? lid.country_id : ushort(fallbackUiLocale().country());
+ return lid.script_id ? lid.script_id : ushort(fallbackLocale().script());
+ return lid.country_id ? lid.country_id : ushort(fallbackLocale().country());
}
case MeasurementSystem:
return d->measurementSystem();
diff --git a/src/corelib/text/qregularexpression.cpp b/src/corelib/text/qregularexpression.cpp
index 1127b77831..72079c1996 100644
--- a/src/corelib/text/qregularexpression.cpp
+++ b/src/corelib/text/qregularexpression.cpp
@@ -2,7 +2,7 @@
**
** Copyright (C) 2020 Giuseppe D'Angelo <dangelog@gmail.com>.
** Copyright (C) 2020 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Giuseppe D'Angelo <giuseppe.dangelo@kdab.com>
-** Copyright (C) 2016 The Qt Company Ltd.
+** Copyright (C) 2021 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the QtCore module of the Qt Toolkit.
@@ -1371,12 +1371,28 @@ QRegularExpression::QRegularExpression(const QRegularExpression &re)
}
/*!
+ \fn QRegularExpression::QRegularExpression(QRegularExpression &&re)
+
+ \since 6.1
+
+ Constructs a QRegularExpression object by moving from \a re.
+
+ Note that a moved-from QRegularExpression can only be destroyed or
+ assigned to. The effect of calling other functions than the destructor
+ or one of the assignment operators is undefined.
+
+ \sa operator=()
+*/
+
+/*!
Destroys the QRegularExpression object.
*/
QRegularExpression::~QRegularExpression()
{
}
+QT_DEFINE_QESDP_SPECIALIZATION_DTOR(QRegularExpressionPrivate)
+
/*!
Assigns the regular expression \a re to this object, and returns a reference
to the copy. Both the pattern and the pattern options are copied.
@@ -1723,8 +1739,12 @@ bool QRegularExpression::operator==(const QRegularExpression &re) const
/*!
\fn QRegularExpression & QRegularExpression::operator=(QRegularExpression && re)
- Move-assigns the regular expression \a re to this object, and returns a reference
- to the copy. Both the pattern and the pattern options are copied.
+ Move-assigns the regular expression \a re to this object, and returns a
+ reference to the result. Both the pattern and the pattern options are copied.
+
+ Note that a moved-from QRegularExpression can only be destroyed or
+ assigned to. The effect of calling other functions than the destructor
+ or one of the assignment operators is undefined.
*/
/*!
@@ -2036,6 +2056,8 @@ QRegularExpressionMatch::~QRegularExpressionMatch()
{
}
+QT_DEFINE_QESDP_SPECIALIZATION_DTOR(QRegularExpressionMatchPrivate)
+
/*!
Constructs a match result by copying the result of the given \a match.
@@ -2047,6 +2069,20 @@ QRegularExpressionMatch::QRegularExpressionMatch(const QRegularExpressionMatch &
}
/*!
+ \fn QRegularExpressionMatch::QRegularExpressionMatch(QRegularExpressionMatch &&match)
+
+ \since 6.1
+
+ Constructs a match result by moving the result from the given \a match.
+
+ Note that a moved-from QRegularExpressionMatch can only be destroyed or
+ assigned to. The effect of calling other functions than the destructor
+ or one of the assignment operators is undefined.
+
+ \sa operator=()
+*/
+
+/*!
Assigns the match result \a match to this object, and returns a reference
to the copy.
*/
@@ -2059,8 +2095,12 @@ QRegularExpressionMatch &QRegularExpressionMatch::operator=(const QRegularExpres
/*!
\fn QRegularExpressionMatch &QRegularExpressionMatch::operator=(QRegularExpressionMatch &&match)
- Move-assigns the match result \a match to this object, and returns a reference
- to the copy.
+ Move-assigns the match result \a match to this object, and returns a
+ reference to the result.
+
+ Note that a moved-from QRegularExpressionMatch can only be destroyed or
+ assigned to. The effect of calling other functions than the destructor
+ or one of the assignment operators is undefined.
*/
/*!
@@ -2468,6 +2508,8 @@ QRegularExpressionMatchIterator::~QRegularExpressionMatchIterator()
{
}
+QT_DEFINE_QESDP_SPECIALIZATION_DTOR(QRegularExpressionMatchIteratorPrivate)
+
/*!
Constructs a QRegularExpressionMatchIterator object as a copy of \a
iterator.
@@ -2480,6 +2522,20 @@ QRegularExpressionMatchIterator::QRegularExpressionMatchIterator(const QRegularE
}
/*!
+ \fn QRegularExpressionMatchIterator::QRegularExpressionMatchIterator(QRegularExpressionMatchIterator &&iterator)
+
+ \since 6.1
+
+ Constructs a QRegularExpressionMatchIterator object by moving from \a iterator.
+
+ Note that a moved-from QRegularExpressionMatchIterator can only be destroyed
+ or assigned to. The effect of calling other functions than the destructor
+ or one of the assignment operators is undefined.
+
+ \sa operator=()
+*/
+
+/*!
Assigns the iterator \a iterator to this object, and returns a reference to
the copy.
*/
@@ -2492,7 +2548,12 @@ QRegularExpressionMatchIterator &QRegularExpressionMatchIterator::operator=(cons
/*!
\fn QRegularExpressionMatchIterator &QRegularExpressionMatchIterator::operator=(QRegularExpressionMatchIterator &&iterator)
- Move-assigns the \a iterator to this object.
+ Move-assigns the \a iterator to this object, and returns a reference to the
+ result.
+
+ Note that a moved-from QRegularExpressionMatchIterator can only be destroyed
+ or assigned to. The effect of calling other functions than the destructor
+ or one of the assignment operators is undefined.
*/
/*!
diff --git a/src/corelib/text/qregularexpression.h b/src/corelib/text/qregularexpression.h
index 142ec7f838..570679800c 100644
--- a/src/corelib/text/qregularexpression.h
+++ b/src/corelib/text/qregularexpression.h
@@ -2,6 +2,7 @@
**
** Copyright (C) 2020 Giuseppe D'Angelo <dangelog@gmail.com>.
** Copyright (C) 2020 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Giuseppe D'Angelo <giuseppe.dangelo@kdab.com>
+** Copyright (C) 2021 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the QtCore module of the Qt Toolkit.
@@ -60,6 +61,8 @@ class QRegularExpressionMatchIterator;
struct QRegularExpressionPrivate;
class QRegularExpression;
+QT_DECLARE_QESDP_SPECIALIZATION_DTOR_WITH_EXPORT(QRegularExpressionPrivate, Q_CORE_EXPORT)
+
Q_CORE_EXPORT size_t qHash(const QRegularExpression &key, size_t seed = 0) noexcept;
class Q_CORE_EXPORT QRegularExpression
@@ -86,6 +89,7 @@ public:
QRegularExpression();
explicit QRegularExpression(const QString &pattern, PatternOptions options = NoPatternOption);
QRegularExpression(const QRegularExpression &re);
+ QRegularExpression(QRegularExpression &&re) = default;
~QRegularExpression();
QRegularExpression &operator=(const QRegularExpression &re);
QT_MOVE_ASSIGNMENT_OPERATOR_IMPL_VIA_PURE_SWAP(QRegularExpression)
@@ -204,6 +208,7 @@ Q_CORE_EXPORT QDebug operator<<(QDebug debug, QRegularExpression::PatternOptions
#endif
struct QRegularExpressionMatchPrivate;
+QT_DECLARE_QESDP_SPECIALIZATION_DTOR_WITH_EXPORT(QRegularExpressionMatchPrivate, Q_CORE_EXPORT)
class Q_CORE_EXPORT QRegularExpressionMatch
{
@@ -211,6 +216,7 @@ public:
QRegularExpressionMatch();
~QRegularExpressionMatch();
QRegularExpressionMatch(const QRegularExpressionMatch &match);
+ QRegularExpressionMatch(QRegularExpressionMatch &&match) = default;
QRegularExpressionMatch &operator=(const QRegularExpressionMatch &match);
QRegularExpressionMatch &operator=(QRegularExpressionMatch &&match) noexcept
{ d.swap(match.d); return *this; }
@@ -278,6 +284,7 @@ class QRegularExpressionMatchIteratorRangeBasedForIteratorSentinel {};
}
struct QRegularExpressionMatchIteratorPrivate;
+QT_DECLARE_QESDP_SPECIALIZATION_DTOR_WITH_EXPORT(QRegularExpressionMatchIteratorPrivate, Q_CORE_EXPORT)
class Q_CORE_EXPORT QRegularExpressionMatchIterator
{
@@ -285,6 +292,7 @@ public:
QRegularExpressionMatchIterator();
~QRegularExpressionMatchIterator();
QRegularExpressionMatchIterator(const QRegularExpressionMatchIterator &iterator);
+ QRegularExpressionMatchIterator(QRegularExpressionMatchIterator &&iterator) = default;
QRegularExpressionMatchIterator &operator=(const QRegularExpressionMatchIterator &iterator);
QRegularExpressionMatchIterator &operator=(QRegularExpressionMatchIterator &&iterator) noexcept
{ d.swap(iterator.d); return *this; }
diff --git a/src/corelib/text/qstring.cpp b/src/corelib/text/qstring.cpp
index da212ac291..a690aac936 100644
--- a/src/corelib/text/qstring.cpp
+++ b/src/corelib/text/qstring.cpp
@@ -333,7 +333,7 @@ const char16_t *QtPrivate::qustrchr(QStringView str, char16_t c) noexcept
[=](int i) { return n[i] == c; },
[=](int i) { return n + i; });
# endif
-#elif defined(__ARM_NEON__) && defined(Q_PROCESSOR_ARM_64) // vaddv is only available on Aarch64
+#elif defined(__ARM_NEON__)
const uint16x8_t vmask = { 1, 1 << 1, 1 << 2, 1 << 3, 1 << 4, 1 << 5, 1 << 6, 1 << 7 };
const uint16x8_t ch_vec = vdupq_n_u16(c);
for (const char16_t *next = n + 8; next <= e; n = next, next += 8) {
@@ -1004,7 +1004,7 @@ static int ucstrncmp(const QChar *a, const QChar *b, size_t l)
};
return UnrollTailLoop<3>::exec(l, 0, lambda, lambda);
#endif
-#if defined(__ARM_NEON__) && defined(Q_PROCESSOR_ARM_64) // vaddv is only available on Aarch64
+#ifdef __ARM_NEON__
if (l >= 8) {
const QChar *end = a + l;
const uint16x8_t mask = { 1, 1 << 1, 1 << 2, 1 << 3, 1 << 4, 1 << 5, 1 << 6, 1 << 7 };
@@ -4569,7 +4569,7 @@ QString QString::section(const QString &sep, qsizetype start, qsizetype end, Sec
return ret;
}
-#if !(defined(QT_NO_REGEXP) && !QT_CONFIG(regularexpression))
+#if QT_CONFIG(regularexpression)
class qt_section_chunk {
public:
qt_section_chunk() {}
@@ -4637,9 +4637,7 @@ static QString extractSections(const QList<qt_section_chunk> &sections, qsizetyp
return ret;
}
-#endif
-#if QT_CONFIG(regularexpression)
/*!
\overload section()
\since 5.0
@@ -6342,9 +6340,9 @@ namespace QUnicodeTables {
\endlist
In copy-convert mode, the local variable \c{s} is detached from the input
- \a str. In the in-place convert mode, \a str is in moved-from state (which
- this function requires to be a valid, empty string) and \c{s} contains the
- only copy of the string, without reallocation (thus, \a it is still valid).
+ \a str. In the in-place convert mode, \a str is in moved-from state and
+ \c{s} contains the only copy of the string, without reallocation (thus,
+ \a it is still valid).
There is one pathological case left: when the in-place conversion needs to
reallocate memory to grow the buffer. In that case, we need to adjust the \a
@@ -6355,7 +6353,7 @@ Q_NEVER_INLINE
static QString detachAndConvertCase(T &str, QStringIterator it, QUnicodeTables::Case which)
{
Q_ASSERT(!str.isEmpty());
- QString s = std::move(str); // will copy if T is const QString
+ QString s = std::move(str); // will copy if T is const QString
QChar *pp = s.begin() + it.index(); // will detach if necessary
do {
@@ -6374,9 +6372,8 @@ static QString detachAndConvertCase(T &str, QStringIterator it, QUnicodeTables::
s.replace(outpos, 1, reinterpret_cast<const QChar *>(folded.data()), folded.size());
pp = const_cast<QChar *>(s.constBegin()) + outpos + folded.size();
- // do we need to adjust the input iterator too?
- // if it is pointing to s's data, str is empty
- if (str.isEmpty())
+ // Adjust the input iterator if we are performing an in-place conversion
+ if constexpr (!std::is_const<T>::value)
it = QStringIterator(s.constBegin(), inpos + folded.size(), s.constEnd());
}
} else {
@@ -9735,7 +9732,7 @@ QString &QString::setRawData(const QChar *unicode, qsizetype size)
equal to string \a s2; otherwise returns \c false.
*/
-#if !defined(QT_NO_DATASTREAM) || (defined(QT_BOOTSTRAPPED) && !defined(QT_BUILD_QMAKE))
+#if !defined(QT_NO_DATASTREAM) || defined(QT_BOOTSTRAPPED)
/*!
\fn QDataStream &operator<<(QDataStream &stream, const QString &string)
\relates QString
@@ -10509,6 +10506,29 @@ QString QString::toHtmlEscaped() const
*/
/*!
+ \fn QtLiterals::operator""_qs(const char16_t *str, size_t size)
+
+ \relates QString
+ \since 6.2
+
+ Literal operator that creates a QString out of the first \a size characters in
+ the char16_t string literal \a str.
+
+ The QString is created at compile time, and the generated string data is stored
+ in the read-only segment of the compiled object file. Duplicate literals may
+ share the same read-only memory. This functionality is interchangeable with
+ QStringLiteral, but saves typing when many string literals are present in the
+ code.
+
+ The following code creates a QString:
+ \code
+ auto str = u"hello"_qs;
+ \endcode
+
+ \sa QStringLiteral, QtLiterals::operator""_qba(const char *str, size_t size)
+*/
+
+/*!
\internal
*/
void QAbstractConcatenable::appendLatin1To(QLatin1String in, QChar *out) noexcept
diff --git a/src/corelib/text/qstring.h b/src/corelib/text/qstring.h
index 8926f6f3af..c6d97c9d51 100644
--- a/src/corelib/text/qstring.h
+++ b/src/corelib/text/qstring.h
@@ -1454,7 +1454,7 @@ inline std::u32string QString::toStdU32String() const
return u32str;
}
-#if !defined(QT_NO_DATASTREAM) || (defined(QT_BOOTSTRAPPED) && !defined(QT_BUILD_QMAKE))
+#if !defined(QT_NO_DATASTREAM) || defined(QT_BOOTSTRAPPED)
Q_CORE_EXPORT QDataStream &operator<<(QDataStream &, const QString &);
Q_CORE_EXPORT QDataStream &operator>>(QDataStream &, QString &);
#endif
@@ -1541,6 +1541,13 @@ qsizetype erase_if(QString &s, Predicate pred)
return QtPrivate::sequential_erase_if(s, pred);
}
+inline namespace QtLiterals {
+inline QString operator"" _qs(const char16_t *str, size_t size) noexcept
+{
+ return QString(QStringPrivate(nullptr, const_cast<char16_t *>(str), qsizetype(size)));
+}
+} // QtLiterals
+
QT_END_NAMESPACE
#if defined(QT_USE_FAST_OPERATOR_PLUS) || defined(QT_USE_QSTRINGBUILDER)
diff --git a/src/corelib/text/qstringconverter.cpp b/src/corelib/text/qstringconverter.cpp
index 524bb4cc03..df9efe7f67 100644
--- a/src/corelib/text/qstringconverter.cpp
+++ b/src/corelib/text/qstringconverter.cpp
@@ -61,7 +61,7 @@ enum { Endian = 0, Data = 1 };
static const uchar utf8bom[] = { 0xef, 0xbb, 0xbf };
#if (defined(__SSE2__) && defined(QT_COMPILER_SUPPORTS_SSE2)) \
- || (defined(__ARM_NEON__) && defined(Q_PROCESSOR_ARM_64))
+ || defined(__ARM_NEON__)
static Q_ALWAYS_INLINE uint qBitScanReverse(unsigned v) noexcept
{
#if defined(__cpp_lib_int_pow2) && __cpp_lib_int_pow2 >= 202002L
@@ -360,7 +360,7 @@ static void simdCompareAscii(const char8_t *&src8, const char8_t *end8, const ch
src8 += offset;
src16 += offset;
}
-#elif defined(__ARM_NEON__) && defined(Q_PROCESSOR_ARM_64) // vaddv is only available on Aarch64
+#elif defined(__ARM_NEON__)
static inline bool simdEncodeAscii(uchar *&dst, const ushort *&nextAscii, const ushort *&src, const ushort *end)
{
uint16x8_t maxAscii = vdupq_n_u16(0x7f);
diff --git a/src/corelib/text/qunicodetools.cpp b/src/corelib/text/qunicodetools.cpp
index 7b69da8c2c..13a7a78e2c 100644
--- a/src/corelib/text/qunicodetools.cpp
+++ b/src/corelib/text/qunicodetools.cpp
@@ -1329,7 +1329,7 @@ static th_next_cell_def th_next_cell = nullptr;
static int init_libthai() {
static bool initialized = false;
if (!initialized && (!th_brk || !th_next_cell)) {
- th_brk = (th_brk_def) QLibrary::resolve(QLatin1String("thai"), (int)LIBTHAI_MAJOR, "th_brk");
+ th_brk = reinterpret_cast<th_brk_def>(QLibrary::resolve(QLatin1String("thai"), static_cast<int>(LIBTHAI_MAJOR), "th_brk"));
th_next_cell = (th_next_cell_def)QLibrary::resolve(QLatin1String("thai"), LIBTHAI_MAJOR, "th_next_cell");
initialized = true;
}
@@ -1342,15 +1342,15 @@ static int init_libthai() {
static void to_tis620(const char16_t *string, qsizetype len, char *cstr)
{
qsizetype i;
- unsigned char *result = (unsigned char *)cstr;
+ unsigned char *result = reinterpret_cast<unsigned char *>(cstr);
for (i = 0; i < len; ++i) {
if (string[i] <= 0xa0)
- result[i] = (unsigned char)string[i];
+ result[i] = static_cast<unsigned char>(string[i]);
else if (string[i] >= 0xe01 && string[i] <= 0xe5b)
- result[i] = (unsigned char)(string[i] - 0xe00 + 0xa0);
+ result[i] = static_cast<unsigned char>(string[i] - 0xe00 + 0xa0);
else
- result[i] = (unsigned char)~0; // Same encoding as libthai uses for invalid chars
+ result[i] = static_cast<unsigned char>(~0); // Same encoding as libthai uses for invalid chars
}
result[len] = 0;
@@ -1373,7 +1373,7 @@ static void thaiAssignAttributes(const char16_t *string, qsizetype len, QCharAtt
return ;
if (len >= 128)
- cstr = (char *)malloc(len*sizeof(char) + 1);
+ cstr = static_cast<char *>(malloc (len * sizeof(char) + 1));
to_tis620(string, len, cstr);
@@ -1385,7 +1385,7 @@ static void thaiAssignAttributes(const char16_t *string, qsizetype len, QCharAtt
}
if (len > 128) {
- break_positions = (int*) malloc (sizeof(int) * len);
+ break_positions = static_cast<int *>(malloc (sizeof(int) * len));
memset (break_positions, 0, sizeof(int) * len);
brp_size = len;
}
@@ -1398,7 +1398,7 @@ static void thaiAssignAttributes(const char16_t *string, qsizetype len, QCharAtt
attributes[0].wordBreak = true;
attributes[0].wordStart = true;
attributes[0].wordEnd = false;
- numbreaks = th_brk((const unsigned char *)cstr, break_positions, brp_size);
+ numbreaks = th_brk(reinterpret_cast<const unsigned char *>(cstr), break_positions, brp_size);
for (i = 0; i < numbreaks; ++i) {
attributes[break_positions[i]].wordBreak = true;
attributes[break_positions[i]].wordStart = true;
@@ -1415,14 +1415,15 @@ static void thaiAssignAttributes(const char16_t *string, qsizetype len, QCharAtt
/* manage grapheme boundaries */
i = 0;
while (i < len) {
- cell_length = (uint)(th_next_cell((const unsigned char *)cstr + i, len - i, &tis_cell, true));
+ cell_length = static_cast<uint>(th_next_cell(reinterpret_cast<const unsigned char *>(cstr) + i, len - i, &tis_cell, true));
+
attributes[i].graphemeBoundary = true;
for (j = 1; j < cell_length; j++)
attributes[i + j].graphemeBoundary = false;
/* Set graphemeBoundary for SARA AM */
- if (cstr[i + cell_length - 1] == (char)0xd3)
+ if (cstr[i + cell_length - 1] == static_cast<char>(0xd3))
attributes[i + cell_length - 1].graphemeBoundary = true;
i += cell_length;
diff --git a/src/corelib/text/qutf8stringview.h b/src/corelib/text/qutf8stringview.h
index 8e0656660d..5069bf7b07 100644
--- a/src/corelib/text/qutf8stringview.h
+++ b/src/corelib/text/qutf8stringview.h
@@ -322,7 +322,8 @@ private:
[[nodiscard]] friend inline bool operator==(QBasicUtf8StringView lhs, QBasicUtf8StringView rhs) noexcept
{
- return QtPrivate::equalStrings(QBasicUtf8StringView<false>(lhs.data(), lhs.size()),
+ return lhs.size() == rhs.size() &&
+ QtPrivate::equalStrings(QBasicUtf8StringView<false>(lhs.data(), lhs.size()),
QBasicUtf8StringView<false>(rhs.data(), rhs.size()));
}
[[nodiscard]] friend inline bool operator!=(QBasicUtf8StringView lhs, QBasicUtf8StringView rhs) noexcept
diff --git a/src/corelib/thread/qfutex_p.h b/src/corelib/thread/qfutex_p.h
index e4bfeda5f8..40482b6fc1 100644
--- a/src/corelib/thread/qfutex_p.h
+++ b/src/corelib/thread/qfutex_p.h
@@ -53,6 +53,28 @@
#include <qglobal.h>
+#if (__has_feature(thread_sanitizer) || defined(__SANITIZE_THREAD__)) && __has_include(<sanitizer/tsan_interface.h>)
+# include <sanitizer/tsan_interface.h>
+inline void _q_tsan_acquire(void *addr, void *addr2 = nullptr)
+{
+ // A futex call ensures total ordering on the futex words
+ // (in either success or failure of the call). Instruct TSAN accordingly,
+ // as TSAN does not understand the futex(2) syscall (or equivalent).
+ __tsan_acquire(addr);
+ if (addr2)
+ __tsan_acquire(addr2);
+}
+inline void _q_tsan_release(void *addr, void *addr2 = nullptr)
+{
+ if (addr2)
+ __tsan_release(addr2);
+ __tsan_release(addr);
+}
+#else
+inline void _q_tsan_acquire(void *, void * = nullptr) {}
+inline void _q_tsan_release(void *, void * = nullptr) {}
+#endif // building for TSAN and __has_include(<sanitizer/tsan_interface.h>)
+
QT_BEGIN_NAMESPACE
namespace QtDummyFutex {
@@ -81,34 +103,12 @@ QT_END_NAMESPACE
// if not defined in linux/futex.h
# define FUTEX_PRIVATE_FLAG 128 // added in v2.6.22
-# if (__has_feature(thread_sanitizer) || defined(__SANITIZE_THREAD__)) && __has_include(<sanitizer/tsan_interface.h>)
-# include <sanitizer/tsan_interface.h>
-inline void _q_tsan_acquire(void *addr, void *addr2)
-{
- __tsan_acquire(addr);
- if (addr2)
- __tsan_acquire(addr2);
-}
-inline void _q_tsan_release(void *addr, void *addr2)
-{
- if (addr2)
- __tsan_release(addr2);
- __tsan_release(addr);
-}
-# else
-inline void _q_tsan_acquire(void *, void *) {}
-inline void _q_tsan_release(void *, void *) {}
-# endif // __has_feature(thread_sanitizer) || defined(__SANITIZE_THREAD__)
-
QT_BEGIN_NAMESPACE
namespace QtLinuxFutex {
constexpr inline bool futexAvailable() { return true; }
inline int _q_futex(int *addr, int op, int val, quintptr val2 = 0,
int *addr2 = nullptr, int val3 = 0) noexcept
{
- // A futex call ensures total ordering on the futex words
- // (in either success or failure of the call). Instruct TSAN accordingly,
- // as TSAN does not understand the futex(2) syscall.
_q_tsan_release(addr, addr2);
// we use __NR_futex because some libcs (like Android's bionic) don't
@@ -160,6 +160,38 @@ namespace QtLinuxFutex {
namespace QtFutex = QtLinuxFutex;
QT_END_NAMESPACE
+#elif defined(Q_OS_WIN)
+# include <qt_windows.h>
+
+QT_BEGIN_NAMESPACE
+namespace QtWindowsFutex {
+#define QT_ALWAYS_USE_FUTEX
+constexpr inline bool futexAvailable() { return true; }
+
+template <typename Atomic>
+inline void futexWait(Atomic &futex, typename Atomic::Type expectedValue)
+{
+ _q_tsan_release(&futex);
+ WaitOnAddress(&futex, &expectedValue, sizeof(expectedValue), INFINITE);
+ _q_tsan_acquire(&futex);
+}
+template <typename Atomic>
+inline bool futexWait(Atomic &futex, typename Atomic::Type expectedValue, qint64 nstimeout)
+{
+ BOOL r = WaitOnAddress(&futex, &expectedValue, sizeof(expectedValue), DWORD(nstimeout / 1000 / 1000));
+ return r || GetLastError() != ERROR_TIMEOUT;
+}
+template <typename Atomic> inline void futexWakeAll(Atomic &futex)
+{
+ WakeByAddressAll(&futex);
+}
+template <typename Atomic> inline void futexWakeOne(Atomic &futex)
+{
+ WakeByAddressSingle(&futex);
+}
+}
+namespace QtFutex = QtWindowsFutex;
+QT_END_NAMESPACE
#else
QT_BEGIN_NAMESPACE
diff --git a/src/corelib/thread/qfuture.h b/src/corelib/thread/qfuture.h
index a56e9ee3ff..9aa77ccf2f 100644
--- a/src/corelib/thread/qfuture.h
+++ b/src/corelib/thread/qfuture.h
@@ -48,7 +48,6 @@
#include <QtCore/qfuture_impl.h>
#include <type_traits>
-#include <vector>
QT_REQUIRE_CONFIG(future);
diff --git a/src/corelib/thread/qfuture.qdoc b/src/corelib/thread/qfuture.qdoc
index f74901379f..fd3120ff15 100644
--- a/src/corelib/thread/qfuture.qdoc
+++ b/src/corelib/thread/qfuture.qdoc
@@ -33,8 +33,6 @@
\ingroup thread
- To start a computation, use one of the APIs in the \l {Qt Concurrent} framework.
-
QFuture allows threads to be synchronized against one or more results
which will be ready at a later point in time. The result can be of any type
that has default, copy and possibly move constructors. If
@@ -138,7 +136,10 @@
be created using convenience functions QtFuture::makeReadyFuture and
QtFuture::makeExceptionalFuture.
- \sa QtFuture::connect(), QtFuture::makeReadyFuture(),
+ \note To start a computation and store results in a QFuture, use QPromise or
+ one of the APIs in the \l {Qt Concurrent} framework.
+
+ \sa QPromise, QtFuture::connect(), QtFuture::makeReadyFuture(),
QtFuture::makeExceptionalFuture(), QFutureWatcher, {Qt Concurrent}
*/
@@ -1174,6 +1175,16 @@
This is because by default \c .then() is invoked from the same thread as the parent.
+ But note that if the continuation is attached after the parent has already finished,
+ it will be invoked in the thread where the parent future lives:
+
+ \snippet code/src_corelib_thread_qfuture.cpp 20
+
+ In the above example if \c cachedResultsReady is \c true, and a ready future is
+ returned, it is possible that the first \c .then() finishes before the second one
+ is attached. In this case it will be resolved in the current thread. Therefore, when
+ in doubt, pass the context explicitly.
+
\note When calling this method, it should be guaranteed that the \a context stays alive
throughout the execution of the chain.
diff --git a/src/corelib/thread/qfuture_impl.h b/src/corelib/thread/qfuture_impl.h
index 8e7ba0c4b5..e455a74793 100644
--- a/src/corelib/thread/qfuture_impl.h
+++ b/src/corelib/thread/qfuture_impl.h
@@ -807,34 +807,34 @@ static QFuture<ArgsType<Signal>> connect(Sender *sender, Signal signal)
if constexpr (std::is_void_v<ArgsType>) {
connections->first =
QObject::connect(sender, signal, sender, [promise, connections]() mutable {
- promise.reportFinished();
QObject::disconnect(connections->first);
QObject::disconnect(connections->second);
+ promise.reportFinished();
});
} else if constexpr (QtPrivate::isTupleV<ArgsType>) {
connections->first = QObject::connect(sender, signal, sender,
[promise, connections](auto... values) mutable {
- promise.reportResult(std::make_tuple(values...));
- promise.reportFinished();
QObject::disconnect(connections->first);
QObject::disconnect(connections->second);
+ promise.reportResult(std::make_tuple(values...));
+ promise.reportFinished();
});
} else {
connections->first = QObject::connect(sender, signal, sender,
[promise, connections](ArgsType value) mutable {
- promise.reportResult(value);
- promise.reportFinished();
QObject::disconnect(connections->first);
QObject::disconnect(connections->second);
+ promise.reportResult(value);
+ promise.reportFinished();
});
}
connections->second =
QObject::connect(sender, &QObject::destroyed, sender, [promise, connections]() mutable {
- promise.reportCanceled();
- promise.reportFinished();
QObject::disconnect(connections->first);
QObject::disconnect(connections->second);
+ promise.reportCanceled();
+ promise.reportFinished();
});
return promise.future();
diff --git a/src/corelib/thread/qfutureinterface.cpp b/src/corelib/thread/qfutureinterface.cpp
index 7a8ba08103..76af95e3a3 100644
--- a/src/corelib/thread/qfutureinterface.cpp
+++ b/src/corelib/thread/qfutureinterface.cpp
@@ -600,10 +600,13 @@ void QFutureInterfaceBase::reset()
d->isValid = false;
}
+void QFutureInterfaceBase::rethrowPossibleException()
+{
+ exceptionStore().throwPossibleException();
+}
+
QFutureInterfaceBasePrivate::QFutureInterfaceBasePrivate(QFutureInterfaceBase::State initialState)
- : refCount(1), m_progressValue(0), m_progressMinimum(0), m_progressMaximum(0),
- state(initialState),
- manualProgress(false), m_expectedResultCount(0), runnable(nullptr), m_pool(nullptr)
+ : state(initialState)
{
progressTime.invalidate();
}
diff --git a/src/corelib/thread/qfutureinterface.h b/src/corelib/thread/qfutureinterface.h
index 2432503cf7..62875c5ef9 100644
--- a/src/corelib/thread/qfutureinterface.h
+++ b/src/corelib/thread/qfutureinterface.h
@@ -40,17 +40,19 @@
#ifndef QFUTUREINTERFACE_H
#define QFUTUREINTERFACE_H
-#include <QtCore/qrunnable.h>
#include <QtCore/qmutex.h>
-#include <QtCore/qexception.h>
+#include <QtCore/QMutexLocker>
#include <QtCore/qresultstore.h>
+#ifndef QT_NO_EXCEPTIONS
+#include <exception>
+#endif
#include <utility>
-#include <vector>
-#include <mutex>
QT_REQUIRE_CONFIG(future);
+QT_FORWARD_DECLARE_CLASS(QRunnable)
+QT_FORWARD_DECLARE_CLASS(QException)
QT_BEGIN_NAMESPACE
@@ -64,6 +66,8 @@ namespace QtPrivate {
template<typename Function, typename ResultType, typename ParentResultType>
class Continuation;
+class ExceptionStore;
+
template<class Function, class ResultType>
class CanceledHandler;
@@ -169,6 +173,7 @@ protected:
bool refT() const;
bool derefT() const;
void reset();
+ void rethrowPossibleException();
public:
#ifndef QFUTURE_TEST
@@ -262,7 +267,7 @@ public:
template <typename T>
inline bool QFutureInterface<T>::reportResult(const T *result, int index)
{
- std::lock_guard<QMutex> locker{mutex()};
+ QMutexLocker<QMutex> locker{&mutex()};
if (this->queryState(Canceled) || this->queryState(Finished))
return false;
@@ -283,7 +288,7 @@ inline bool QFutureInterface<T>::reportResult(const T *result, int index)
template<typename T>
bool QFutureInterface<T>::reportAndMoveResult(T &&result, int index)
{
- std::lock_guard<QMutex> locker{mutex()};
+ QMutexLocker<QMutex> locker{&mutex()};
if (queryState(Canceled) || queryState(Finished))
return false;
@@ -312,7 +317,7 @@ inline bool QFutureInterface<T>::reportResult(const T &result, int index)
template<typename T>
inline bool QFutureInterface<T>::reportResults(const QList<T> &_results, int beginIndex, int count)
{
- std::lock_guard<QMutex> locker{mutex()};
+ QMutexLocker<QMutex> locker{&mutex()};
if (this->queryState(Canceled) || this->queryState(Finished))
return false;
@@ -343,14 +348,14 @@ inline bool QFutureInterface<T>::reportFinished(const T *result)
template <typename T>
inline const T &QFutureInterface<T>::resultReference(int index) const
{
- std::lock_guard<QMutex> locker{mutex()};
+ QMutexLocker<QMutex> locker{&mutex()};
return resultStoreBase().resultAt(index).template value<T>();
}
template <typename T>
inline const T *QFutureInterface<T>::resultPointer(int index) const
{
- std::lock_guard<QMutex> locker{mutex()};
+ QMutexLocker<QMutex> locker{&mutex()};
return resultStoreBase().resultAt(index).template pointer<T>();
}
@@ -358,14 +363,14 @@ template <typename T>
inline QList<T> QFutureInterface<T>::results()
{
if (this->isCanceled()) {
- exceptionStore().throwPossibleException();
+ rethrowPossibleException();
return QList<T>();
}
QFutureInterfaceBase::waitForResult(-1);
QList<T> res;
- std::lock_guard<QMutex> locker{mutex()};
+ QMutexLocker<QMutex> locker{&mutex()};
QtPrivate::ResultIteratorBase it = resultStoreBase().begin();
while (it != resultStoreBase().end()) {
@@ -385,7 +390,7 @@ T QFutureInterface<T>::takeResult()
// not to mess with other unready results.
waitForResult(-1);
- const std::lock_guard<QMutex> locker{mutex()};
+ const QMutexLocker<QMutex> locker{&mutex()};
QtPrivate::ResultIteratorBase position = resultStoreBase().resultAt(0);
T ret(std::move_if_noexcept(position.value<T>()));
reset();
@@ -404,7 +409,7 @@ std::vector<T> QFutureInterface<T>::takeResults()
std::vector<T> res;
res.reserve(resultCount());
- const std::lock_guard<QMutex> locker{mutex()};
+ const QMutexLocker<QMutex> locker{&mutex()};
QtPrivate::ResultIteratorBase it = resultStoreBase().begin();
for (auto endIt = resultStoreBase().end(); it != endIt; ++it)
diff --git a/src/corelib/thread/qfutureinterface_p.h b/src/corelib/thread/qfutureinterface_p.h
index df71d3d4b6..44c3a48d2a 100644
--- a/src/corelib/thread/qfutureinterface_p.h
+++ b/src/corelib/thread/qfutureinterface_p.h
@@ -58,6 +58,8 @@
#include <QtCore/qwaitcondition.h>
#include <QtCore/qrunnable.h>
#include <QtCore/qthreadpool.h>
+#include <QtCore/qfutureinterface.h>
+#include <QtCore/qexception.h>
QT_REQUIRE_CONFIG(future);
@@ -159,23 +161,30 @@ public:
// T: accessed from executing thread
// Q: accessed from the waiting/querying thread
- RefCount refCount;
mutable QMutex m_mutex;
- QWaitCondition waitCondition;
+ QBasicMutex continuationMutex;
QList<QFutureCallOutInterface *> outputConnections;
- int m_progressValue; // TQ
- int m_progressMinimum; // TQ
- int m_progressMaximum; // TQ
- QAtomicInt state; // reads and writes can happen unprotected, both must be atomic
QElapsedTimer progressTime;
+ QWaitCondition waitCondition;
QWaitCondition pausedWaitCondition;
+ // ### TODO: put m_results and m_exceptionStore into a union (see QTBUG-92045)
QtPrivate::ResultStoreBase m_results;
- bool manualProgress; // only accessed from executing thread
- int m_expectedResultCount;
QtPrivate::ExceptionStore m_exceptionStore;
QString m_progressText;
- QRunnable *runnable;
- QThreadPool *m_pool;
+ QRunnable *runnable = nullptr;
+ QThreadPool *m_pool = nullptr;
+ // Wrapper for continuation
+ std::function<void(const QFutureInterfaceBase &)> continuation;
+
+ RefCount refCount = 1;
+ QAtomicInt state; // reads and writes can happen unprotected, both must be atomic
+ int m_progressValue = 0; // TQ
+ int m_progressMinimum = 0; // TQ
+ int m_progressMaximum = 0; // TQ
+ int m_expectedResultCount = 0;
+ bool manualProgress = false; // only accessed from executing thread
+ bool launchAsync = false;
+ bool isValid = false;
inline QThreadPool *pool() const
{ return m_pool ? m_pool : QThreadPool::globalInstance(); }
@@ -194,12 +203,6 @@ public:
void setState(QFutureInterfaceBase::State state);
- // Wrapper for continuation
- std::function<void(const QFutureInterfaceBase &)> continuation;
- QBasicMutex continuationMutex;
-
- bool launchAsync = false;
- bool isValid = false;
};
QT_END_NAMESPACE
diff --git a/src/corelib/thread/qgenericatomic.h b/src/corelib/thread/qgenericatomic.h
index 0fa9a9a418..e9f9218644 100644
--- a/src/corelib/thread/qgenericatomic.h
+++ b/src/corelib/thread/qgenericatomic.h
@@ -53,8 +53,10 @@ QT_END_NAMESPACE
#pragma qt_sync_stop_processing
#endif
-template<int> struct QAtomicOpsSupport { enum { IsSupported = 0 }; };
-template<> struct QAtomicOpsSupport<4> { enum { IsSupported = 1 }; };
+template<int Size> struct QAtomicOpsSupport
+{
+ enum { IsSupported = (Size == sizeof(int) || Size == sizeof(qptrdiff)) };
+};
template <typename T> struct QAtomicAdditiveType
{
diff --git a/src/corelib/thread/qmutex.cpp b/src/corelib/thread/qmutex.cpp
index 4926eae2e9..ba97ef8a42 100644
--- a/src/corelib/thread/qmutex.cpp
+++ b/src/corelib/thread/qmutex.cpp
@@ -39,20 +39,28 @@
**
****************************************************************************/
+#include "global/qglobal.h"
#include "qplatformdefs.h"
#include "qmutex.h"
#include <qdebug.h>
#include "qatomic.h"
#include "qelapsedtimer.h"
+#include "qfutex_p.h"
#include "qthread.h"
#include "qmutex_p.h"
-#ifndef QT_LINUX_FUTEX
+#ifndef QT_ALWAYS_USE_FUTEX
#include "private/qfreelist_p.h"
#endif
QT_BEGIN_NAMESPACE
+using namespace QtFutex;
+static inline QMutexPrivate *dummyFutexValue()
+{
+ return reinterpret_cast<QMutexPrivate *>(quintptr(3));
+}
+
/*
\class QBasicMutex
\inmodule QtCore
@@ -134,12 +142,12 @@ void QBasicMutex::destroyInternal(QMutexPrivate *d)
{
if (!d)
return;
-#ifndef QT_LINUX_FUTEX
- if (d != dummyLocked() && d->possiblyUnlocked.loadRelaxed() && tryLock()) {
- unlock();
- return;
+ if (!futexAvailable()) {
+ if (d != dummyLocked() && d->possiblyUnlocked.loadRelaxed() && tryLock()) {
+ unlock();
+ return;
+ }
}
-#endif
qWarning("QMutex: destroying locked mutex");
}
@@ -510,8 +518,6 @@ void QRecursiveMutex::unlock() noexcept
*/
-#ifndef QT_LINUX_FUTEX //linux implementation is in qmutex_linux.cpp
-
/*
For a rough introduction on how this works, refer to
http://woboq.com/blog/internals-of-qmutex-in-qt5.html
@@ -532,12 +538,67 @@ void QRecursiveMutex::unlock() noexcept
possiblyUnlocked flag.
*/
+/*
+ * QBasicMutex implementation with futexes (Linux, Windows 10)
+ *
+ * QBasicMutex contains one pointer value, which can contain one of four
+ * different values:
+ * 0x0 unlocked
+ * 0x1 locked, no waiters
+ * 0x3 locked, at least one waiter
+ *
+ * LOCKING:
+ *
+ * A starts in the 0x0 state, indicating that it's unlocked. When the first
+ * thread attempts to lock it, it will perform a testAndSetAcquire
+ * from 0x0 to 0x1. If that succeeds, the caller concludes that it
+ * successfully locked the mutex. That happens in fastTryLock().
+ *
+ * If that testAndSetAcquire fails, QBasicMutex::lockInternal is called.
+ *
+ * lockInternal will examine the value of the pointer. Otherwise, it will use
+ * futexes to sleep and wait for another thread to unlock. To do that, it needs
+ * to set a pointer value of 0x3, which indicates that thread is waiting. It
+ * does that by a simple fetchAndStoreAcquire operation.
+ *
+ * If the pointer value was 0x0, it means we succeeded in acquiring the mutex.
+ * For other values, it will then call FUTEX_WAIT and with an expected value of
+ * 0x3.
+ *
+ * If the pointer value changed before futex(2) managed to sleep, it will
+ * return -1 / EWOULDBLOCK, in which case we have to start over. And even if we
+ * are woken up directly by a FUTEX_WAKE, we need to acquire the mutex, so we
+ * start over again.
+ *
+ * UNLOCKING:
+ *
+ * To unlock, we need to set a value of 0x0 to indicate it's unlocked. The
+ * first attempt is a testAndSetRelease operation from 0x1 to 0x0. If that
+ * succeeds, we're done.
+ *
+ * If it fails, unlockInternal() is called. The only possibility is that the
+ * mutex value was 0x3, which indicates some other thread is waiting or was
+ * waiting in the past. We then set the mutex to 0x0 and perform a FUTEX_WAKE.
+ */
+
/*!
\internal helper for lock()
*/
void QBasicMutex::lockInternal() QT_MUTEX_LOCK_NOEXCEPT
{
- lockInternal(-1);
+ if (futexAvailable()) {
+ // note we must set to dummyFutexValue because there could be other threads
+ // also waiting
+ while (d_ptr.fetchAndStoreAcquire(dummyFutexValue()) != nullptr) {
+ // successfully set the waiting bit, now sleep
+ futexWait(d_ptr, dummyFutexValue());
+
+ // we got woken up, so try to acquire the mutex
+ }
+ Q_ASSERT(d_ptr.loadRelaxed());
+ } else {
+ lockInternal(-1);
+ }
}
/*!
@@ -545,6 +606,41 @@ void QBasicMutex::lockInternal() QT_MUTEX_LOCK_NOEXCEPT
*/
bool QBasicMutex::lockInternal(int timeout) QT_MUTEX_LOCK_NOEXCEPT
{
+ if (timeout == 0)
+ return false;
+
+ if (futexAvailable()) {
+ if (Q_UNLIKELY(timeout < 0)) {
+ lockInternal();
+ return true;
+ }
+
+ QDeadlineTimer deadlineTimer(timeout);
+ // The mutex is already locked, set a bit indicating we're waiting.
+ // Note we must set to dummyFutexValue because there could be other threads
+ // also waiting.
+ if (d_ptr.fetchAndStoreAcquire(dummyFutexValue()) == nullptr)
+ return true;
+
+ qint64 remainingTime = deadlineTimer.remainingTimeNSecs();
+ Q_FOREVER {
+ if (!futexWait(d_ptr, dummyFutexValue(), remainingTime))
+ return false;
+
+ // We got woken up, so must try to acquire the mutex. We must set
+ // to dummyFutexValue() again because there could be other threads
+ // waiting.
+ if (d_ptr.fetchAndStoreAcquire(dummyFutexValue()) == nullptr)
+ return true;
+
+ // calculate the remaining time
+ remainingTime = deadlineTimer.remainingTimeNSecs();
+ if (remainingTime <= 0)
+ return false;
+ }
+ }
+
+#if !defined(QT_ALWAYS_USE_FUTEX)
while (!fastTryLock()) {
QMutexPrivate *copy = d_ptr.loadAcquire();
if (!copy) // if d is 0, the mutex is unlocked
@@ -644,6 +740,9 @@ bool QBasicMutex::lockInternal(int timeout) QT_MUTEX_LOCK_NOEXCEPT
}
Q_ASSERT(d_ptr.loadRelaxed() != 0);
return true;
+#else
+ Q_UNREACHABLE();
+#endif
}
/*!
@@ -655,6 +754,12 @@ void QBasicMutex::unlockInternal() noexcept
Q_ASSERT(copy); //we must be locked
Q_ASSERT(copy != dummyLocked()); // testAndSetRelease(dummyLocked(), 0) failed
+ if (futexAvailable()) {
+ d_ptr.storeRelease(nullptr);
+ return futexWakeOne(d_ptr);
+ }
+
+#if !defined(QT_ALWAYS_USE_FUTEX)
QMutexPrivate *d = reinterpret_cast<QMutexPrivate *>(copy);
// If no one is waiting for the lock anymore, we should reset d to 0x0.
@@ -676,8 +781,12 @@ void QBasicMutex::unlockInternal() noexcept
d->wakeUp();
}
d->deref();
+#else
+ Q_UNUSED(copy);
+#endif
}
+#if !defined(QT_ALWAYS_USE_FUTEX)
//The freelist management
namespace {
struct FreeListConstants : QFreeListDefaultConstants {
@@ -738,8 +847,8 @@ void QMutexPrivate::derefWaiters(int value) noexcept
QT_END_NAMESPACE
-#ifdef QT_LINUX_FUTEX
-# include "qmutex_linux.cpp"
+#if defined(Q_OS_LINUX) && defined(QT_ALWAYS_USE_FUTEX)
+// nothing
#elif defined(Q_OS_MAC)
# include "qmutex_mac.cpp"
#elif defined(Q_OS_WIN)
diff --git a/src/corelib/thread/qmutex_linux.cpp b/src/corelib/thread/qmutex_linux.cpp
deleted file mode 100644
index ceb9be52a8..0000000000
--- a/src/corelib/thread/qmutex_linux.cpp
+++ /dev/null
@@ -1,179 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Copyright (C) 2016 Intel Corporation.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtCore module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#include "qplatformdefs.h"
-#include "qmutex.h"
-#include "qatomic.h"
-#include "qmutex_p.h"
-#include "qfutex_p.h"
-
-#ifndef QT_ALWAYS_USE_FUTEX
-# error "Qt build is broken: qmutex_linux.cpp is being built but futex support is not wanted"
-#endif
-
-#ifndef FUTEX_PRIVATE_FLAG
-# define FUTEX_PRIVATE_FLAG 0
-#endif
-
-QT_BEGIN_NAMESPACE
-
-using namespace QtFutex;
-
-/*
- * QBasicMutex implementation on Linux with futexes
- *
- * QBasicMutex contains one pointer value, which can contain one of four
- * different values:
- * 0x0 unlocked, non-recursive mutex
- * 0x1 locked non-recursive mutex, no waiters
- * 0x3 locked non-recursive mutex, at least one waiter
- * > 0x3 recursive mutex, points to a QMutexPrivate object
- *
- * LOCKING (non-recursive):
- *
- * A non-recursive mutex starts in the 0x0 state, indicating that it's
- * unlocked. When the first thread attempts to lock it, it will perform a
- * testAndSetAcquire from 0x0 to 0x1. If that succeeds, the caller concludes
- * that it successfully locked the mutex. That happens in fastTryLock().
- *
- * If that testAndSetAcquire fails, QBasicMutex::lockInternal is called.
- *
- * lockInternal will examine the value of the pointer. Otherwise, it will use
- * futexes to sleep and wait for another thread to unlock. To do that, it needs
- * to set a pointer value of 0x3, which indicates that thread is waiting. It
- * does that by a simple fetchAndStoreAcquire operation.
- *
- * If the pointer value was 0x0, it means we succeeded in acquiring the mutex.
- * For other values, it will then call FUTEX_WAIT and with an expected value of
- * 0x3.
- *
- * If the pointer value changed before futex(2) managed to sleep, it will
- * return -1 / EWOULDBLOCK, in which case we have to start over. And even if we
- * are woken up directly by a FUTEX_WAKE, we need to acquire the mutex, so we
- * start over again.
- *
- * UNLOCKING (non-recursive):
- *
- * To unlock, we need to set a value of 0x0 to indicate it's unlocked. The
- * first attempt is a testAndSetRelease operation from 0x1 to 0x0. If that
- * succeeds, we're done.
- *
- * If it fails, unlockInternal() is called. The only possibility is that the
- * mutex value was 0x3, which indicates some other thread is waiting or was
- * waiting in the past. We then set the mutex to 0x0 and perform a FUTEX_WAKE.
- */
-
-static inline QMutexPrivate *dummyFutexValue()
-{
- return reinterpret_cast<QMutexPrivate *>(quintptr(3));
-}
-
-template <bool IsTimed> static inline
-bool lockInternal_helper(QBasicAtomicPointer<QMutexPrivate> &d_ptr, int timeout = -1, QElapsedTimer *elapsedTimer = nullptr) noexcept
-{
- if (!IsTimed)
- timeout = -1;
-
- // we're here because fastTryLock() has just failed
- if (timeout == 0)
- return false;
-
- // the mutex is locked already, set a bit indicating we're waiting
- if (d_ptr.fetchAndStoreAcquire(dummyFutexValue()) == nullptr)
- return true;
-
- qint64 nstimeout = timeout * Q_INT64_C(1000) * 1000;
- qint64 remainingTime = nstimeout;
- forever {
- // successfully set the waiting bit, now sleep
- if (IsTimed && nstimeout >= 0) {
- bool r = futexWait(d_ptr, dummyFutexValue(), remainingTime);
- if (!r)
- return false;
-
- // we got woken up, so try to acquire the mutex
- // note we must set to dummyFutexValue because there could be other threads
- // also waiting
- if (d_ptr.fetchAndStoreAcquire(dummyFutexValue()) == nullptr)
- return true;
-
- // recalculate the timeout
- remainingTime = nstimeout - elapsedTimer->nsecsElapsed();
- if (remainingTime <= 0)
- return false;
- } else {
- futexWait(d_ptr, dummyFutexValue());
-
- // we got woken up, so try to acquire the mutex
- // note we must set to dummyFutexValue because there could be other threads
- // also waiting
- if (d_ptr.fetchAndStoreAcquire(dummyFutexValue()) == nullptr)
- return true;
- }
- }
-
- Q_ASSERT(d_ptr.loadRelaxed());
- return true;
-}
-
-void QBasicMutex::lockInternal() noexcept
-{
- lockInternal_helper<false>(d_ptr);
-}
-
-bool QBasicMutex::lockInternal(int timeout) noexcept
-{
- QElapsedTimer elapsedTimer;
- elapsedTimer.start();
- return lockInternal_helper<true>(d_ptr, timeout, &elapsedTimer);
-}
-
-void QBasicMutex::unlockInternal() noexcept
-{
- QMutexPrivate *d = d_ptr.loadRelaxed();
- Q_ASSERT(d); //we must be locked
- Q_ASSERT(d != dummyLocked()); // testAndSetRelease(dummyLocked(), 0) failed
- Q_UNUSED(d);
-
- d_ptr.storeRelease(nullptr);
- futexWakeOne(d_ptr);
-}
-
-QT_END_NAMESPACE
diff --git a/src/corelib/thread/qmutex_p.h b/src/corelib/thread/qmutex_p.h
index 96a979e66b..b7d0bdab9b 100644
--- a/src/corelib/thread/qmutex_p.h
+++ b/src/corelib/thread/qmutex_p.h
@@ -62,9 +62,6 @@
#if defined(Q_OS_MAC)
# include <mach/semaphore.h>
-#elif defined(Q_OS_LINUX) && !defined(QT_LINUXBASE)
-// use Linux mutexes everywhere except for LSB builds
-# define QT_LINUX_FUTEX
#elif defined(Q_OS_UNIX)
# if _POSIX_VERSION-0 >= 200112L || _XOPEN_VERSION-0 >= 600
# include <semaphore.h>
@@ -76,7 +73,6 @@ struct timespec;
QT_BEGIN_NAMESPACE
-#if !defined(QT_LINUX_FUTEX)
class QMutexPrivate
{
public:
@@ -134,7 +130,6 @@ public:
Qt::HANDLE event;
#endif
};
-#endif //QT_LINUX_FUTEX
#ifdef Q_OS_UNIX
diff --git a/src/corelib/thread/qsemaphore.cpp b/src/corelib/thread/qsemaphore.cpp
index ee4cee5281..213852250f 100644
--- a/src/corelib/thread/qsemaphore.cpp
+++ b/src/corelib/thread/qsemaphore.cpp
@@ -150,10 +150,11 @@ static int futexAvailCounter(quintptr v)
static bool futexNeedsWake(quintptr v)
{
- // If we're counting waiters, the number of waiters is stored in the low 31
- // bits of the high word (that is, bits 32-62). If we're not, then we use
- // bit 31 to indicate anyone is waiting. Either way, if any bit 31 or above
- // is set, there are waiters.
+ // If we're counting waiters, the number of waiters plus value is stored in the
+ // low 31 bits of the high word (that is, bits 32-62). If we're not, then we only
+ // use futexNeedsWakeAllBit to indicate anyone is waiting.
+ if constexpr (futexHasWaiterCount)
+ return (v >> 32) > (unsigned(v));
return v >> 31;
}
@@ -200,14 +201,21 @@ futexSemaphoreTryAcquire_loop(QBasicAtomicInteger<quintptr> &u, quintptr curValu
// indicate we're waiting
start_wait:
- auto ptr = futexLow32(&u);
+ auto ptr = [&u]() {
+ if constexpr (futexHasWaiterCount)
+ return futexLow32(&u);
+ else
+ return &u;
+ }();
if (n > 1 || !futexHasWaiterCount) {
u.fetchAndOrRelaxed(futexNeedsWakeAllBit);
curValue |= futexNeedsWakeAllBit;
- if (n > 1 && futexHasWaiterCount) {
- ptr = futexHigh32(&u);
- //curValue >>= 32; // but this is UB in 32-bit, so roundabout:
- curValue = quint64(curValue) >> 32;
+ if constexpr (futexHasWaiterCount) {
+ if (n > 1) {
+ ptr = futexHigh32(&u);
+ // curValue >>= 32; // but this is UB in 32-bit, so roundabout:
+ curValue = quint64(curValue) >> 32;
+ }
}
}
@@ -246,13 +254,13 @@ template <bool IsTimed> bool futexSemaphoreTryAcquire(QBasicAtomicInteger<quintp
// we need to wait
quintptr oneWaiter = quintptr(Q_UINT64_C(1) << 32); // zero on 32-bit
if (futexHasWaiterCount) {
- // increase the waiter count
- u.fetchAndAddRelaxed(oneWaiter);
-
// We don't use the fetched value from above so futexWait() fails if
// it changed after the testAndSetOrdered above.
if ((quint64(curValue) >> 32) == 0x7fffffff)
return false; // overflow!
+
+ // increase the waiter count
+ u.fetchAndAddRelaxed(oneWaiter);
curValue += oneWaiter;
// Also adjust nn to subtract oneWaiter when we succeed in acquiring.
@@ -397,7 +405,7 @@ void QSemaphore::release(int n)
futexWakeOp(*futexLow32(&u), n, INT_MAX, *futexHigh32(&u), FUTEX_OP(op, oparg, cmp, cmparg));
}
#else
- // Unset the bit and wake everyone. There are two possibibilies
+ // Unset the bit and wake everyone. There are two possibilities
// under which a thread can set the bit between the AND and the
// futexWake:
// 1) it did see the new counter value, but it wasn't enough for
diff --git a/src/corelib/thread/qthread.h b/src/corelib/thread/qthread.h
index ab32891a59..e5db44638d 100644
--- a/src/corelib/thread/qthread.h
+++ b/src/corelib/thread/qthread.h
@@ -49,6 +49,10 @@
# include <future> // for std::async
# include <functional> // for std::invoke; no guard needed as it's a C++98 header
#endif
+// internal compiler error with mingw 8.1
+#if defined(Q_CC_MSVC) && defined(Q_PROCESSOR_X86)
+#include <intrin.h>
+#endif
QT_BEGIN_NAMESPACE
@@ -194,6 +198,27 @@ inline Qt::HANDLE QThread::currentThreadId() noexcept
#elif defined(Q_PROCESSOR_X86_64) && (defined(Q_OS_LINUX) || defined(Q_OS_FREEBSD)) && !defined(Q_OS_ANDROID)
// x86_64 Linux, BSD uses FS
__asm__("movq %%fs:0, %0" : "=r" (tid) : : );
+#elif defined(Q_PROCESSOR_X86_64) && defined(Q_OS_WIN)
+ // See https://en.wikipedia.org/wiki/Win32_Thread_Information_Block
+ // First get the pointer to the TIB
+ quint8 *tib;
+# if defined(Q_CC_MINGW) // internal compiler error when using the intrinsics
+ __asm__("movq %%gs:0x30, %0" : "=r" (tib) : :);
+# else
+ tib = reinterpret_cast<quint8 *>(__readgsqword(0x30));
+# endif
+ // Then read the thread ID
+ tid = *reinterpret_cast<Qt::HANDLE *>(tib + 0x48);
+#elif defined(Q_PROCESSOR_X86_32) && defined(Q_OS_WIN)
+ // First get the pointer to the TIB
+ quint8 *tib;
+# if defined(Q_CC_MINGW) // internal compiler error when using the intrinsics
+ __asm__("movl %%fs:0x18, %0" : "=r" (tib) : :);
+# else
+ tib = reinterpret_cast<quint8 *>(__readfsdword(0x18));
+# endif
+ // Then read the thread ID
+ tid = *reinterpret_cast<Qt::HANDLE *>(tib + 0x24);
#else
tid = currentThreadIdImpl();
#endif
diff --git a/src/corelib/thread/qthreadpool.cpp b/src/corelib/thread/qthreadpool.cpp
index 6d258af9df..b3e429e25c 100644
--- a/src/corelib/thread/qthreadpool.cpp
+++ b/src/corelib/thread/qthreadpool.cpp
@@ -194,7 +194,7 @@ bool QThreadPoolPrivate::tryStart(QRunnable *task)
++activeThreads;
thread->runnable = task;
- thread->start();
+ thread->start(threadPriority);
return true;
}
@@ -257,15 +257,21 @@ bool QThreadPoolPrivate::tooManyThreadsActive() const
*/
void QThreadPoolPrivate::startThread(QRunnable *runnable)
{
+ Q_Q(QThreadPool);
Q_ASSERT(runnable != nullptr);
QScopedPointer<QThreadPoolThread> thread(new QThreadPoolThread(this));
- thread->setObjectName(QLatin1String("Thread (pooled)"));
+ QString objectName;
+ if (QString myName = q->objectName(); !myName.isEmpty())
+ objectName = myName;
+ else
+ objectName = QLatin1String("Thread (pooled)");
+ thread->setObjectName(objectName);
Q_ASSERT(!allThreads.contains(thread.data())); // if this assert hits, we have an ABA problem (deleted threads don't get removed here)
allThreads.insert(thread.data());
++activeThreads;
thread->runnable = runnable;
- thread.take()->start();
+ thread.take()->start(threadPriority);
}
/*!
@@ -698,6 +704,32 @@ uint QThreadPool::stackSize() const
return d->stackSize;
}
+/*! \property QThreadPool::threadPriority
+ \brief the thread priority for new worker threads.
+
+ The value of the property is only used when the thread pool starts
+ new threads. Changing it has no effect for already running threads.
+
+ The default value is QThread::InheritPriority, which makes QThread
+ use the same priority as the one the QThreadPool object lives in.
+
+ \sa QThread::Priority
+
+ \since 6.2
+*/
+
+void QThreadPool::setThreadPriority(QThread::Priority priority)
+{
+ Q_D(QThreadPool);
+ d->threadPriority = priority;
+}
+
+QThread::Priority QThreadPool::threadPriority() const
+{
+ Q_D(const QThreadPool);
+ return d->threadPriority;
+}
+
/*!
Releases a thread previously reserved by a call to reserveThread().
diff --git a/src/corelib/thread/qthreadpool.h b/src/corelib/thread/qthreadpool.h
index a559eff49a..f3d7e2254b 100644
--- a/src/corelib/thread/qthreadpool.h
+++ b/src/corelib/thread/qthreadpool.h
@@ -60,6 +60,7 @@ class Q_CORE_EXPORT QThreadPool : public QObject
Q_PROPERTY(int maxThreadCount READ maxThreadCount WRITE setMaxThreadCount)
Q_PROPERTY(int activeThreadCount READ activeThreadCount)
Q_PROPERTY(uint stackSize READ