diff options
213 files changed, 5311 insertions, 1503 deletions
diff --git a/.cmake.conf b/.cmake.conf index e1e19d2ed2a..09e591bd25b 100644 --- a/.cmake.conf +++ b/.cmake.conf @@ -7,7 +7,7 @@ if (NOT DEFINED QT_SUPERBUILD OR DEFINED QT_REPO_MODULE_VERSION) set(QT_EXTRA_INTERNAL_TARGET_DEFINES "QT_LEAN_HEADERS=1") endif() -set(QT_REPO_MODULE_VERSION "6.11.0") +set(QT_REPO_MODULE_VERSION "6.12.0") set(QT_REPO_MODULE_PRERELEASE_VERSION_SEGMENT "alpha1") set(QT_COPYRIGHT "Copyright (C) The Qt Company Ltd. and other contributors.") diff --git a/cmake/QtBuildHelpers.cmake b/cmake/QtBuildHelpers.cmake index 91983636712..3ef292b27bc 100644 --- a/cmake/QtBuildHelpers.cmake +++ b/cmake/QtBuildHelpers.cmake @@ -6,6 +6,7 @@ function(qt_internal_validate_cmake_generator) if(NOT warning_shown AND NOT CMAKE_GENERATOR MATCHES "Ninja" + AND NOT (IOS AND QT_INTERNAL_IS_STANDALONE_TEST) AND NOT QT_SILENCE_CMAKE_GENERATOR_WARNING AND NOT DEFINED ENV{QT_SILENCE_CMAKE_GENERATOR_WARNING}) set_property(GLOBAL PROPERTY _qt_validate_cmake_generator_warning_shown TRUE) diff --git a/cmake/QtBuildRepoHelpers.cmake b/cmake/QtBuildRepoHelpers.cmake index 05876c1ad6d..ba8e2e57660 100644 --- a/cmake/QtBuildRepoHelpers.cmake +++ b/cmake/QtBuildRepoHelpers.cmake @@ -523,6 +523,7 @@ function(qt_internal_show_extra_ide_sources) add_custom_target(${target_name}) set(recursive_glob_patterns + REUSE.toml ${QT_BUILD_EXTRA_IDE_FILE_RECURSIVE_PATTERNS} ) set(simple_glob_patterns diff --git a/cmake/QtHeadersClean.cmake b/cmake/QtHeadersClean.cmake index 3e59d68629c..9d8c86cf70d 100644 --- a/cmake/QtHeadersClean.cmake +++ b/cmake/QtHeadersClean.cmake @@ -137,6 +137,10 @@ function(qt_internal_add_headersclean_target module_target module_headers) if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU") list(APPEND hcleanFLAGS -Wzero-as-null-pointer-constant -Wdouble-promotion -Wfloat-conversion) + if(CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL "14.0.0") + list(APPEND hcleanFLAGS + -Wnrvo) + endif() endif() if ("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang|IntelLLVM") diff --git a/cmake/QtInternalTargets.cmake b/cmake/QtInternalTargets.cmake index 0815dbe8e42..f0d7d06fb84 100644 --- a/cmake/QtInternalTargets.cmake +++ b/cmake/QtInternalTargets.cmake @@ -23,6 +23,9 @@ function(qt_internal_set_warnings_are_errors_flags target target_scope) COMPILERS CLANG AppleClang OPTIONS -Werror -Wno-error=\#warnings -Wno-error=deprecated-declarations + CONDITIONS VERSION_GREATER_EQUAL 14 + OPTIONS + -Wno-error=deprecated-pragma COMMON_CONDITIONS ${common_conditions} ${clang_msvc_frontend_args} @@ -382,7 +385,10 @@ if(QT_FEATURE_stdlib_libcpp) target_compile_definitions(PlatformCommonInternal INTERFACE _LIBCPP_REMOVE_TRANSITIVE_INCLUDES) endif() -if(QT_USE_CCACHE AND CLANG AND BUILD_WITH_PCH) +if((QT_USE_CCACHE + OR (CMAKE_CXX_COMPILER_LAUNCHER MATCHES "^(.*[/\\])?sccache$")) + AND CLANG + AND BUILD_WITH_PCH) # The ccache man page says we must compile with -fno-pch-timestamp when using clang and pch. foreach(language IN ITEMS C CXX OBJC OBJCXX) target_compile_options(PlatformCommonInternal INTERFACE diff --git a/cmake/QtPrecompiledHeadersHelpers.cmake b/cmake/QtPrecompiledHeadersHelpers.cmake index b47e4e74e33..7fe94664da3 100644 --- a/cmake/QtPrecompiledHeadersHelpers.cmake +++ b/cmake/QtPrecompiledHeadersHelpers.cmake @@ -14,6 +14,18 @@ function(qt_update_precompiled_header_with_library target library) get_target_property(target_type "${library}" TYPE) if(target_type STREQUAL "INTERFACE_LIBRARY") + # If target links against QtFooPrivate then QtFoo is transitively pulled + # in. We assume that headers from QtFoo will be used and add this + # library to the target's precompiled headers too. + get_target_property(is_private_module "${library}" _qt_is_private_module) + if(is_private_module) + get_target_property(public_module_target "${library}" _qt_public_module_target_name) + qt_update_precompiled_header_with_library("${target}" + "${QT_CMAKE_EXPORT_NAMESPACE}::${public_module_target}" + ) + endif() + + # Don't handle interface libraries any further. return() endif() diff --git a/cmake/QtTestHelpers.cmake b/cmake/QtTestHelpers.cmake index 06228ac41de..781bdf4143a 100644 --- a/cmake/QtTestHelpers.cmake +++ b/cmake/QtTestHelpers.cmake @@ -1240,7 +1240,7 @@ function(qt_internal_add_test_finalizers target) # specific platforms. # TODO: Remove once we confirm that the new way of running test finalizers for all platforms # doesn't cause any issues. - if(QT_INTERNAL_SKIP_TEST_FINALIZERS_V2) + if(NOT QT_INTERNAL_SKIP_TEST_FINALIZERS_V2) return() endif() diff --git a/cmake/QtToolchainHelpers.cmake b/cmake/QtToolchainHelpers.cmake index 348a3c25603..b2a5575f130 100644 --- a/cmake/QtToolchainHelpers.cmake +++ b/cmake/QtToolchainHelpers.cmake @@ -183,6 +183,7 @@ endif()") list(LENGTH CMAKE_OSX_ARCHITECTURES _qt_osx_architectures_count) if(cmake_sysroot_name AND (MACOS OR (UIKIT AND NOT _qt_osx_architectures_count GREATER 1))) list(APPEND init_platform " +set(__qt_initial_apple_sdk \"${QT_APPLE_SDK}\") if(NOT DEFINED CMAKE_OSX_SYSROOT) set(CMAKE_OSX_SYSROOT \"${cmake_sysroot_name}\" CACHE STRING \"\") endif()") @@ -254,7 +255,8 @@ endif()") qt_internal_get_first_osx_arch(osx_first_arch) list(APPEND init_platform "if((NOT CMAKE_GENERATOR STREQUAL \"Xcode\" AND NOT __qt_toolchain_building_qt_repo) - OR (CMAKE_GENERATOR STREQUAL \"Xcode\" AND __qt_apple_sdk AND NOT QT_NO_SET_OSX_ARCHITECTURES))") + OR (CMAKE_GENERATOR STREQUAL \"Xcode\" AND __qt_initial_apple_sdk + AND NOT QT_NO_SET_OSX_ARCHITECTURES))") list(APPEND init_platform " set(CMAKE_OSX_ARCHITECTURES \"${osx_first_arch}\" CACHE STRING \"\")") list(APPEND init_platform "endif()") diff --git a/coin/instructions/cmake_run_ctest.yaml b/coin/instructions/cmake_run_ctest.yaml index 43963fc172b..03312101117 100644 --- a/coin/instructions/cmake_run_ctest.yaml +++ b/coin/instructions/cmake_run_ctest.yaml @@ -92,19 +92,10 @@ instructions: variableName: CTEST_ARGS variableValue: " --no-label-summary" - # Enable CTest's JUnit XML summary + # Enable CTest's JUnit XML summary, supported in CMake >= v3.21 - type: AppendToEnvironmentVariable variableName: CTEST_ARGS variableValue: " --output-junit {{.Env.COIN_CTEST_RESULTSDIR}}{{.Env.CI_PATH_SEP}}test_summary.ctest_junit_xml" - disable_if: # CMake < v3.21 does not support it - condition: and - conditions: - - condition: runtime - env_var: CMAKE_MIN_SUPPORTED_BIN_PATH - not_equals_value: null - - condition: runtime - env_var: PATH - contains_value: "{{.Env.CMAKE_MIN_SUPPORTED_BIN_PATH}}" - !include "{{qt/qtbase}}/coin_module_test_android_start_emulator.yaml" diff --git a/coin/instructions/coin_module_axivion_template_v2.yaml b/coin/instructions/coin_module_axivion_template_v2.yaml index 90d57de281c..9840d1138b6 100644 --- a/coin/instructions/coin_module_axivion_template_v2.yaml +++ b/coin/instructions/coin_module_axivion_template_v2.yaml @@ -16,6 +16,13 @@ analysis_instructions_axivion: &analysis_instructions_axivion condition: runtime env_var: TESTED_MODULE_COIN not_equals_value: "qtbase" + - type: EnvironmentVariable + variableName: TESTED_MODULE_BRANCH_COIN + variableValue: "unknown" + enable_if: + condition: runtime + env_var: TESTED_MODULE_BRANCH_COIN + equals_value: null - type: Group instructions: - type: Rename diff --git a/doc/global/externalsites/external-resources.qdoc b/doc/global/externalsites/external-resources.qdoc index 51756842ad0..8670305b00c 100644 --- a/doc/global/externalsites/external-resources.qdoc +++ b/doc/global/externalsites/external-resources.qdoc @@ -58,68 +58,27 @@ */ /*! - \externalpage https://www.freedesktop.org/wiki/Standards/xembed-spec/ - \title XEmbed Specification -*/ - -/*! - \externalpage https://www.freedesktop.org/wiki/Standards/icon-theme-spec/ - \title Icon Themes Specification -*/ - -/*! \externalpage https://www.cups.org/ \title Common Unix Printing System (CUPS) \keyword CUPS */ /*! - \externalpage https://www.freedesktop.org/wiki/Specifications/desktop-entry-spec/ - \title Desktop Entry Specification -*/ - -/*! - \externalpage https://kde.org/ - \title The K Desktop Environment - \keyword KDE -*/ - -/*! \externalpage https://cmake.org/cmake/help/latest/ \title CMake Documentation */ /*! - \externalpage https://cmake.org/cmake/help/latest/command/find_package.html - \title CMake find_package Documentation -*/ - -/*! \externalpage https://cmake.org/cmake/help/latest/manual/cmake-qt.7.html#automoc \title CMake AUTOMOC Documentation */ /*! - \externalpage https://cmake.org/cmake/help/latest/manual/cmake-qt.7.html#autorcc - \title CMake AUTORCC Documentation -*/ - -/*! \externalpage https://cmake.org/cmake/help/latest/manual/cmake-qt.7.html#autouic \title CMake AUTOUIC Documentation */ /*! - \externalpage https://cmake.org/cmake/help/latest/prop_tgt/LOCATION.html - \title CMake LOCATION Documentation -*/ - -/*! - \externalpage https://cmake.org/cmake/help/latest/prop_tgt/POSITION_INDEPENDENT_CODE.html - \title CMake POSITION_INDEPENDENT_CODE Documentation -*/ - -/*! \externalpage https://cmake.org/cmake/help/latest/command/target_link_libraries.html \title CMake target_link_libraries Documentation */ @@ -130,50 +89,11 @@ */ /*! - \externalpage https://conan.io/ - \title Conan -*/ - -/*! \externalpage https://www.gnome.org/ \title GNOME */ /*! - \externalpage https://www.gnu.org/software/emacs/ - \title GNU Emacs -*/ - -/*! - \externalpage https://gnuwin32.sourceforge.net/packages.html - \title GnuWin32 Project -*/ - -/*! - \externalpage https://www.w3.org/Graphics/SVG/About.html - \title About SVG - \keyword Scalable Vector Graphics -*/ - -/*! - \externalpage https://www.w3.org/TR/SVG/types.html#ColorKeywords - \title SVG color keyword names -*/ - -/*! - \externalpage https://www.w3.org/Graphics/SVG/ - \title SVG Working Group -*/ - -/*! - \externalpage https://www.w3.org/TR/SVGMobile/ - \title Mobile SVG Profiles - \omit - Mobile SVG Profiles: SVG Tiny and SVG Basic - \endomit -*/ - -/*! \externalpage https://www.w3.org/TR/SVGMobile12/ \title SVG 1.2 Tiny */ @@ -186,36 +106,16 @@ /*! - \externalpage https://jmeubank.github.io/tdm-gcc/ - \title TDM-GCC -*/ - -/*! \externalpage https://www.dependencywalker.com/ \title Dependency Walker */ /*! - \externalpage https://webkit.org/ - \title WebKit Open Source Project -*/ - -/*! - \externalpage https://www.informit.com/store/c-plus-plus-gui-programming-with-qt4-9780132354165 - \title C++ GUI Programming with Qt 4, 2nd Edition -*/ - -/*! \externalpage https://www.openssl.org/ \title OpenSSL Toolkit */ /*! - \externalpage https://www.activestate.com/platform/supported-languages/perl/ - \title ActivePerl -*/ - -/*! \externalpage https://chromium.googlesource.com/angle/angle/+/master/README.md \title ANGLE */ @@ -226,16 +126,6 @@ */ /*! - \externalpage https://www.w3.org/TR/html401/ - \title HTML 4 -*/ - -/*! - \externalpage https://html.spec.whatwg.org/multipage/ - \title HTML 5 -*/ - -/*! \externalpage https://icu.unicode.org/ \title ICU */ @@ -251,41 +141,16 @@ */ /*! - \externalpage https://pyxml.sourceforge.net/topics/xbel/ - \title XML Bookmark Exchange Language Resource Page -*/ - -/*! - \externalpage https://www.w3.org/TR/xquery/#errors - \title error handling in the XQuery language -*/ - -/*! \externalpage https://xaos-project.github.io/ \title XaoS */ /*! - \externalpage https://www.unixodbc.org - \title https://www.unixodbc.org -*/ - -/*! \externalpage https://www.postgresql.org \title https://www.postgresql.org */ /*! - \externalpage https://www.postgresql.org/docs/current/installation-platform-notes.html - \title PostgreSQL MinGW/Native Windows -*/ - -/*! - \externalpage https://www.freetds.org - \title https://www.freetds.org -*/ - -/*! \externalpage https://www.sqlite.org \title https://www.sqlite.org */ @@ -301,11 +166,6 @@ */ /*! - \externalpage https://tldp.org/HOWTO/Framebuffer-HOWTO.html - \title Framebuffer HOWTO -*/ - -/*! \externalpage https://www.w3.org/TR/scxml/ \title State Chart XML: State Machine Notation for Control Abstraction */ @@ -321,71 +181,21 @@ */ /*! - \externalpage https://www.gnu.org/licenses/old-licenses/lgpl-2.1.html - \title GNU Lesser General Public License, version 2.1 -*/ - -/*! - \externalpage https://www.qtcentre.org - \title Qt Centre -*/ - -/*! \externalpage https://kde.org \title KDE */ /*! - \externalpage https://cplusplus.com/reference/cstring/memcpy/ - \title C++ Reference - memcpy -*/ - -/*! \externalpage https://en.cppreference.com/w/cpp/symbol_index/chrono_literals.html \title chrono_literals Symbol Index */ /*! - \externalpage https://www.w3.org/TR/CSS2/selector.html - \title Standard CSS2 selector -*/ - -/*! - \externalpage https://www.w3.org/XML/Core/#Publications - \title W3C XML specifications -*/ - -/*! - \externalpage https://www.w3.org/XML/Schema - \title XML Schema -*/ - -/*! - \externalpage https://opensource.org/license/bsd-3-clause - \title New and Modified BSD Licenses -*/ - -/*! - \externalpage https://ecma-international.org/publications-and-standards/standards/ecma-262/ - \title ECMAScript Language Specification -*/ - -/*! \externalpage https://developer.mozilla.org/en-US/docs/Web/JavaScript \title JavaScript Resources */ /*! - \externalpage https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide - \title JavaScript Guide -*/ - -/*! - \externalpage https://developer.mozilla.org/en-US/docs/Web/JavaScript - \title About JavaScript -*/ - -/*! \externalpage https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Lexical_grammar#keywords \title JavaScript Reserved Words */ @@ -406,41 +216,16 @@ */ /*! - \externalpage https://registry.khronos.org/OpenGL/index_gl.php - \title OpenGL Registry -*/ - -/*! - \externalpage https://registry.khronos.org/OpenGL/index_es.php - \title Khronos OpenGL ES API Registry -*/ - -/*! \externalpage https://www.khronos.org/opengl/wiki/Array_Texture \title Array Texture */ /*! - \externalpage https://github.com/iksaif/qsslkey-p11 - \title qsslkey example -*/ - -/*! \externalpage https://www.w3.org/TR/2009/WD-webdatabase-20091029/ \title HTML5 Web Database API */ /*! - \externalpage https://lldb.llvm.org/ - \title The LLDB Debugger -*/ - -/*! - \externalpage https://account.qt.io/s/ - \title Qt Account Sign-up -*/ - -/*! \externalpage https://facebook.github.io/zstd/ \title Zstandard Site */ @@ -476,6 +261,6 @@ */ /*! - \externalpage https://specifications.freedesktop.org/trash-spec/1.0/ + \externalpage https://specifications.freedesktop.org/trash/1.0/ \title FreeDesktop.org Trash specification version 1.0 */ diff --git a/doc/global/externalsites/qt-webpages.qdoc b/doc/global/externalsites/qt-webpages.qdoc index 7b659fa5295..6cbf5f377b6 100644 --- a/doc/global/externalsites/qt-webpages.qdoc +++ b/doc/global/externalsites/qt-webpages.qdoc @@ -17,76 +17,15 @@ \title The Qt Company */ /*! - \externalpage http://qt.io/licensing/ - \title Qt Licensing Overview -*/ -/*! - \externalpage http://doc.qt.io/archives/qq/ - \title Qt Quarterly -*/ -/*! - \externalpage http://doc.qt.io/archives/qq/qq19-plurals.html - \title Qt Quarterly: Plural Form in Translation -*/ -/*! \externalpage https://code.qt.io/ \title Public Qt Repository */ /*! - \externalpage https://code.qt.io/cgit/%7bnon-gerrit%7d/qt-labs/qtestlib-tools.git/ - \title qtestlib-tools -*/ - -/*! - \externalpage http://wiki.qt.io/Qt_Coding_Style - \title Qt Coding Style -*/ -/*! - \externalpage http://doc.qt.io/archives/qt-eclipse-1.6/index.html - \title Eclipse Plugin -*/ -/*! - \externalpage http://doc.qt.io/archives/qq/qq11-events.html - \title Qt Quarterly: Another Look at Events -*/ -/*! - \externalpage https://www.youtube.com/watch?v=P4kv-AoAJ-Q - \title Livecoding video effects with Qt5 -*/ -/*! - \externalpage http://blog.qt.io/2012/02/29/pimp-my-video-shader-effects-and-multimedia/ - \title Pimp my video -*/ -/*! - \externalpage http://wiki.qt.io/QtMediaHub - \title QtMediaHub -*/ -/*! - \externalpage http://wiki.qt.io/Qt_RaspberryPi - \title QtonPi -*/ - -/*! - \externalpage http://wiki.qt.io/jom - \title jom -*/ - -/*! - \externalpage http://doc.qt.io/qt-4.8 - \title Qt 4.8 Reference Documentation -*/ - -/*! \externalpage http://wiki.qt.io/Qt_Localization \title external: Translating Qt Into Other Languages */ /*! - \externalpage http://wiki.qt.io/Qt_Multimedia_Backends - \title Qt Multimedia Backends -*/ - -/*! \externalpage https://doc.qt.io/qt3dstudio/index.html \title Qt 3D Studio Manual */ diff --git a/doc/global/externalsites/rfc.qdoc b/doc/global/externalsites/rfc.qdoc index d0c127fb41e..071977b9a0e 100644 --- a/doc/global/externalsites/rfc.qdoc +++ b/doc/global/externalsites/rfc.qdoc @@ -12,12 +12,6 @@ */ /*! - \externalpage https://datatracker.ietf.org/doc/html/rfc1179 - \title RFC 1179 - \keyword lpr -*/ - -/*! \externalpage https://datatracker.ietf.org/doc/html/rfc1738 \title RFC 1738 */ @@ -28,43 +22,16 @@ */ /*! - \externalpage https://datatracker.ietf.org/doc/html/rfc1928 - \title RFC 1928 -*/ - -/*! - \externalpage https://datatracker.ietf.org/doc/html/rfc1929 - \title RFC 1929 -*/ - -/*! \externalpage https://datatracker.ietf.org/doc/html/rfc2045 \title RFC 2045 */ /*! - \externalpage https://datatracker.ietf.org/doc/html/rfc2109 - \title RFC 2109 - HTTP State Management Mechanism -*/ - -/*! \externalpage https://datatracker.ietf.org/doc/html/rfc2822 \title RFC 2822 */ /*! - \externalpage https://datatracker.ietf.org/doc/html/rfc2965 - \title RFC 2965 - HTTP State Management Mechanism -*/ - -/*! - \externalpage https://datatracker.ietf.org/doc/html/rfc3174 - \title RFC 3174 -*/ - -/*! \externalpage https://datatracker.ietf.org/doc/html/rfc3491 \title RFC 3491 */ diff --git a/mkspecs/common/clang.conf b/mkspecs/common/clang.conf index 82c6173a037..83867e66f94 100644 --- a/mkspecs/common/clang.conf +++ b/mkspecs/common/clang.conf @@ -32,14 +32,14 @@ QMAKE_CXXFLAGS_LTCG = $$QMAKE_CFLAGS_LTCG QMAKE_CXXFLAGS_LTCG_FATOBJECTS = $$QMAKE_CFLAGS_LTCG_FATOBJECTS QMAKE_CXXFLAGS_DISABLE_LTCG = $$QMAKE_CFLAGS_DISABLE_LTCG QMAKE_CXXFLAGS_CXX11 = -std=c++11 -QMAKE_CXXFLAGS_CXX14 = -std=c++1y -QMAKE_CXXFLAGS_CXX1Z = -std=c++1z +QMAKE_CXXFLAGS_CXX14 = -std=c++14 +QMAKE_CXXFLAGS_CXX1Z = -std=c++17 QMAKE_CXXFLAGS_CXX2A = -std=c++2a QMAKE_CXXFLAGS_CXX2B = -std=c++2b QMAKE_CXXFLAGS_CXX2C = -std=c++2c QMAKE_CXXFLAGS_GNUCXX11 = -std=gnu++11 -QMAKE_CXXFLAGS_GNUCXX14 = -std=gnu++1y -QMAKE_CXXFLAGS_GNUCXX1Z = -std=gnu++1z +QMAKE_CXXFLAGS_GNUCXX14 = -std=gnu++14 +QMAKE_CXXFLAGS_GNUCXX1Z = -std=gnu++17 QMAKE_CXXFLAGS_GNUCXX2A = -std=gnu++2a QMAKE_CXXFLAGS_GNUCXX2B = -std=gnu++2b QMAKE_CXXFLAGS_GNUCXX2C = -std=gnu++2c diff --git a/mkspecs/common/g++-base.conf b/mkspecs/common/g++-base.conf index e12e41506ae..66248fb7d01 100644 --- a/mkspecs/common/g++-base.conf +++ b/mkspecs/common/g++-base.conf @@ -30,14 +30,14 @@ QMAKE_CXXFLAGS_USE_PRECOMPILE = $$QMAKE_CFLAGS_USE_PRECOMPILE QMAKE_CFLAGS_GNUC99 = -std=gnu99 QMAKE_CFLAGS_GNUC11 = -std=gnu11 QMAKE_CXXFLAGS_CXX11 = -std=c++11 -QMAKE_CXXFLAGS_CXX14 = -std=c++1y -QMAKE_CXXFLAGS_CXX1Z = -std=c++1z +QMAKE_CXXFLAGS_CXX14 = -std=c++14 +QMAKE_CXXFLAGS_CXX1Z = -std=c++17 QMAKE_CXXFLAGS_CXX2A = -std=c++2a QMAKE_CXXFLAGS_CXX2B = -std=c++2b QMAKE_CXXFLAGS_CXX2C = -std=c++2c QMAKE_CXXFLAGS_GNUCXX11 = -std=gnu++11 -QMAKE_CXXFLAGS_GNUCXX14 = -std=gnu++1y -QMAKE_CXXFLAGS_GNUCXX1Z = -std=gnu++1z +QMAKE_CXXFLAGS_GNUCXX14 = -std=gnu++14 +QMAKE_CXXFLAGS_GNUCXX1Z = -std=gnu++17 QMAKE_CXXFLAGS_GNUCXX2A = -std=gnu++2a QMAKE_CXXFLAGS_GNUCXX2B = -std=gnu++2b QMAKE_CXXFLAGS_GNUCXX2C = -std=gnu++2c diff --git a/src/3rdparty/libpng/ANNOUNCE b/src/3rdparty/libpng/ANNOUNCE index 10dee70d834..e9a94e05de3 100644 --- a/src/3rdparty/libpng/ANNOUNCE +++ b/src/3rdparty/libpng/ANNOUNCE @@ -1,4 +1,4 @@ -libpng 1.6.52 - December 3, 2025 +libpng 1.6.53 - December 5, 2025 ================================ This is a public release of libpng, intended for use in production code. @@ -9,10 +9,10 @@ Files available for download Source files: - * libpng-1.6.52.tar.xz (LZMA-compressed, recommended) - * libpng-1.6.52.tar.gz (deflate-compressed) - * lpng1652.7z (LZMA-compressed) - * lpng1652.zip (deflate-compressed) + * libpng-1.6.53.tar.xz (LZMA-compressed, recommended) + * libpng-1.6.53.tar.gz (deflate-compressed) + * lpng1653.7z (LZMA-compressed) + * lpng1653.zip (deflate-compressed) Other information: @@ -22,18 +22,14 @@ Other information: * TRADEMARK.md -Changes from version 1.6.51 to version 1.6.52 +Changes from version 1.6.52 to version 1.6.53 --------------------------------------------- - * Fixed CVE-2025-66293 (high severity): - Out-of-bounds read in `png_image_read_composite`. - (Reported by flyfish101 <[email protected]>.) - * Fixed the Paeth filter handling in the RISC-V RVV implementation. - (Reported by Filip Wasil; fixed by Liang Junzhao.) - * Improved the performance of the RISC-V RVV implementation. - (Contributed by Liang Junzhao.) - * Added allocation failure fuzzing to oss-fuzz. - (Contributed by Philippe Antoine.) + * Fixed a build failure on RISC-V RVV caused by a misspelled intrinsic. + (Contributed by Alexander Smorkalov.) + * Fixed a build failure with CMake 4.1 or newer, on Windows, when using + Visual C++ without MASM installed. + (Reported by Andrew Tribick; fixed by Luis Caro Campos.) Send comments/corrections/commendations to png-mng-implement at lists.sf.net. diff --git a/src/3rdparty/libpng/CHANGES b/src/3rdparty/libpng/CHANGES index f8ad74bbdf3..ea43101538a 100644 --- a/src/3rdparty/libpng/CHANGES +++ b/src/3rdparty/libpng/CHANGES @@ -6315,6 +6315,12 @@ Version 1.6.52 [December 3, 2025] Added allocation failure fuzzing to oss-fuzz. (Contributed by Philippe Antoine.) +Version 1.6.53 [December 5, 2025] + Fixed a build failure on RISC-V RVV caused by a misspelled intrinsic. + (Contributed by Alexander Smorkalov.) + Fixed a build failure with CMake 4.1 or newer, on Windows, when using + Visual C++ without MASM installed. + Send comments/corrections/commendations to png-mng-implement at lists.sf.net. Subscription is required; visit https://lists.sourceforge.net/lists/listinfo/png-mng-implement diff --git a/src/3rdparty/libpng/README b/src/3rdparty/libpng/README index 87e5f8b177e..4041ad86eb3 100644 --- a/src/3rdparty/libpng/README +++ b/src/3rdparty/libpng/README @@ -1,4 +1,4 @@ -README for libpng version 1.6.52 +README for libpng version 1.6.53 ================================ See the note about version numbers near the top of `png.h`. diff --git a/src/3rdparty/libpng/libpng-manual.txt b/src/3rdparty/libpng/libpng-manual.txt index f284d987ba6..750025cfdad 100644 --- a/src/3rdparty/libpng/libpng-manual.txt +++ b/src/3rdparty/libpng/libpng-manual.txt @@ -9,7 +9,7 @@ libpng-manual.txt - A description on how to use and modify libpng Based on: - libpng version 1.6.36, December 2018, through 1.6.52 - December 2025 + libpng version 1.6.36, December 2018, through 1.6.53 - December 2025 Updated and distributed by Cosmin Truta Copyright (c) 2018-2025 Cosmin Truta diff --git a/src/3rdparty/libpng/png.c b/src/3rdparty/libpng/png.c index 11b65d1f13e..85b49496520 100644 --- a/src/3rdparty/libpng/png.c +++ b/src/3rdparty/libpng/png.c @@ -13,7 +13,7 @@ #include "pngpriv.h" /* Generate a compiler error if there is an old png.h in the search path. */ -typedef png_libpng_version_1_6_52 Your_png_h_is_not_version_1_6_52; +typedef png_libpng_version_1_6_53 Your_png_h_is_not_version_1_6_53; /* Sanity check the chunks definitions - PNG_KNOWN_CHUNKS from pngpriv.h and the * corresponding macro definitions. This causes a compile time failure if @@ -817,7 +817,7 @@ png_get_copyright(png_const_structrp png_ptr) return PNG_STRING_COPYRIGHT #else return PNG_STRING_NEWLINE \ - "libpng version 1.6.52" PNG_STRING_NEWLINE \ + "libpng version 1.6.53" PNG_STRING_NEWLINE \ "Copyright (c) 2018-2025 Cosmin Truta" PNG_STRING_NEWLINE \ "Copyright (c) 1998-2002,2004,2006-2018 Glenn Randers-Pehrson" \ PNG_STRING_NEWLINE \ diff --git a/src/3rdparty/libpng/png.h b/src/3rdparty/libpng/png.h index bceb9aa45d7..bdcd243dea2 100644 --- a/src/3rdparty/libpng/png.h +++ b/src/3rdparty/libpng/png.h @@ -1,6 +1,6 @@ /* png.h - header file for PNG reference library * - * libpng version 1.6.52 + * libpng version 1.6.53 * * Copyright (c) 2018-2025 Cosmin Truta * Copyright (c) 1998-2002,2004,2006-2018 Glenn Randers-Pehrson @@ -14,7 +14,7 @@ * libpng versions 0.89, June 1996, through 0.96, May 1997: Andreas Dilger * libpng versions 0.97, January 1998, through 1.6.35, July 2018: * Glenn Randers-Pehrson - * libpng versions 1.6.36, December 2018, through 1.6.52, December 2025: + * libpng versions 1.6.36, December 2018, through 1.6.53, December 2025: * Cosmin Truta * See also "Contributing Authors", below. */ @@ -238,7 +238,7 @@ * ... * 1.5.30 15 10530 15.so.15.30[.0] * ... - * 1.6.52 16 10651 16.so.16.52[.0] + * 1.6.53 16 10651 16.so.16.53[.0] * * Henceforth the source version will match the shared-library major and * minor numbers; the shared-library major version number will be used for @@ -274,7 +274,7 @@ */ /* Version information for png.h - this should match the version in png.c */ -#define PNG_LIBPNG_VER_STRING "1.6.52" +#define PNG_LIBPNG_VER_STRING "1.6.53" #define PNG_HEADER_VERSION_STRING " libpng version " PNG_LIBPNG_VER_STRING "\n" /* The versions of shared library builds should stay in sync, going forward */ @@ -285,7 +285,7 @@ /* These should match the first 3 components of PNG_LIBPNG_VER_STRING: */ #define PNG_LIBPNG_VER_MAJOR 1 #define PNG_LIBPNG_VER_MINOR 6 -#define PNG_LIBPNG_VER_RELEASE 52 +#define PNG_LIBPNG_VER_RELEASE 53 /* This should be zero for a public release, or non-zero for a * development version. @@ -316,7 +316,7 @@ * From version 1.0.1 it is: * XXYYZZ, where XX=major, YY=minor, ZZ=release */ -#define PNG_LIBPNG_VER 10652 /* 1.6.52 */ +#define PNG_LIBPNG_VER 10653 /* 1.6.53 */ /* Library configuration: these options cannot be changed after * the library has been built. @@ -426,7 +426,7 @@ extern "C" { /* This triggers a compiler error in png.c, if png.c and png.h * do not agree upon the version number. */ -typedef char* png_libpng_version_1_6_52; +typedef char* png_libpng_version_1_6_53; /* Basic control structions. Read libpng-manual.txt or libpng.3 for more info. * diff --git a/src/3rdparty/libpng/pngconf.h b/src/3rdparty/libpng/pngconf.h index 76b5c20bdff..f4ff19209c6 100644 --- a/src/3rdparty/libpng/pngconf.h +++ b/src/3rdparty/libpng/pngconf.h @@ -1,6 +1,6 @@ /* pngconf.h - machine-configurable file for libpng * - * libpng version 1.6.52 + * libpng version 1.6.53 * * Copyright (c) 2018-2025 Cosmin Truta * Copyright (c) 1998-2002,2004,2006-2016,2018 Glenn Randers-Pehrson diff --git a/src/3rdparty/libpng/pnglibconf.h b/src/3rdparty/libpng/pnglibconf.h index f4a993441f7..27fa87045b3 100644 --- a/src/3rdparty/libpng/pnglibconf.h +++ b/src/3rdparty/libpng/pnglibconf.h @@ -1,6 +1,6 @@ /* pnglibconf.h - library build configuration */ -/* libpng version 1.6.52 */ +/* libpng version 1.6.53 */ /* Copyright (c) 2018-2025 Cosmin Truta */ /* Copyright (c) 1998-2002,2004,2006-2018 Glenn Randers-Pehrson */ diff --git a/src/3rdparty/libpng/pngread.c b/src/3rdparty/libpng/pngread.c index f8ca2b7e31d..13a93deedcb 100644 --- a/src/3rdparty/libpng/pngread.c +++ b/src/3rdparty/libpng/pngread.c @@ -3278,7 +3278,7 @@ png_image_read_composite(png_voidp argument) /* Clamp to the valid range to defend against * unforeseen cases where the data might be sRGB * instead of linear premultiplied. - * (Belt-and-suspenders for GitHub Issue #764.) + * (Belt-and-suspenders for CVE-2025-66293.) */ if (component > 255*65535) component = 255*65535; diff --git a/src/3rdparty/libpng/qt_attribution.json b/src/3rdparty/libpng/qt_attribution.json index 1f942f8f564..132e5e8d4cd 100644 --- a/src/3rdparty/libpng/qt_attribution.json +++ b/src/3rdparty/libpng/qt_attribution.json @@ -7,8 +7,8 @@ "Description": "libpng is the official PNG reference library.", "Homepage": "http://www.libpng.org/pub/png/libpng.html", - "Version": "1.6.52", - "DownloadLocation": "https://download.sourceforge.net/libpng/libpng-1.6.52.tar.xz", + "Version": "1.6.53", + "DownloadLocation": "https://download.sourceforge.net/libpng/libpng-1.6.53.tar.xz", "PURL": "pkg:github/pnggroup/libpng@v$<VERSION>", "CPE": "cpe:2.3:a:libpng:libpng:$<VERSION>:*:*:*:*:*:*:*", diff --git a/src/android/jar/src/org/qtproject/qt/android/QtWindow.java b/src/android/jar/src/org/qtproject/qt/android/QtWindow.java index f0d304f9e7f..c63cd1a77e3 100644 --- a/src/android/jar/src/org/qtproject/qt/android/QtWindow.java +++ b/src/android/jar/src/org/qtproject/qt/android/QtWindow.java @@ -32,6 +32,7 @@ class QtWindow extends QtLayout implements QtSurfaceInterface { private QtWindow m_parentWindow; private GestureDetector m_gestureDetector; private final QtEditText m_editText; + private boolean m_editTextFocusInitialized = false; private final QtInputConnection.QtInputConnectionListener m_inputConnectionListener; private boolean m_firstSafeMarginsDelivered = false; private int m_actionBarHeight = -1; @@ -62,6 +63,8 @@ class QtWindow extends QtLayout implements QtSurfaceInterface { if (!isForeignWindow && context instanceof Activity) { // TODO QTBUG-122552 - Service keyboard input not implemented m_editText = new QtEditText(context, listener); + m_editText.setFocusable(false); + m_editText.setFocusableInTouchMode(false); m_editText.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_NO); LayoutParams layoutParams = new LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT); @@ -242,6 +245,14 @@ class QtWindow extends QtLayout implements QtSurfaceInterface { @Override public boolean onTouchEvent(MotionEvent event) { + // Enable focus for the edit text on first touch event to avoid + // early QtInputConnection callbacks from blocking the UI thread. + if (!m_editTextFocusInitialized) { + m_editTextFocusInitialized = true; + m_editText.setFocusable(true); + m_editText.setFocusableInTouchMode(true); + } + windowFocusChanged(true, getId()); if (m_editText != null && m_inputConnectionListener != null) m_inputConnectionListener.onEditTextChanged(m_editText); diff --git a/src/corelib/CMakeLists.txt b/src/corelib/CMakeLists.txt index dec68c5f9f4..539ad753ca6 100644 --- a/src/corelib/CMakeLists.txt +++ b/src/corelib/CMakeLists.txt @@ -583,24 +583,21 @@ if(QT_FEATURE_async_io) SOURCES io/qrandomaccessasyncfile_darwin.mm ) - elseif(LINUX AND QT_FEATURE_liburing) + elseif((LINUX AND QT_FEATURE_liburing) OR (WIN32 AND QT_FEATURE_windows_ioring)) qt_internal_extend_target(Core SOURCES io/qrandomaccessasyncfile_qioring.cpp DEFINES QT_RANDOMACCESSASYNCFILE_QIORING ) - elseif(QT_FEATURE_thread AND QT_FEATURE_future) - # TODO: This should become the last (fallback) condition later. - # We migth also want to rewrite it so that it does not depend on - # QT_FEATURE_future. - qt_internal_extend_target(Core - SOURCES - io/qrandomaccessasyncfile_threadpool.cpp - DEFINES - QT_RANDOMACCESSASYNCFILE_THREAD - ) endif() + # This is the fallback condition that should be always available. + # TODO: try to rewrite it so that it does not depend on + # QT_FEATURE_future. + qt_internal_extend_target(Core + SOURCES + io/qrandomaccessasyncfile_threadpool.cpp + ) endif() # This needs to be done before one below adds kernel32 because the symbols we use @@ -763,6 +760,11 @@ qt_internal_extend_target(Core CONDITION QT_FEATURE_liburing uring ) +qt_internal_extend_target(Core CONDITION QT_FEATURE_windows_ioring + SOURCES + io/qioring.cpp io/qioring_win.cpp io/qioring_p.h +) + # Workaround for QTBUG-101411 # Remove if QCC (gcc version 8.3.0) for QNX 7.1.0 is no longer supported qt_internal_extend_target(Core CONDITION QCC AND (CMAKE_CXX_COMPILER_VERSION VERSION_EQUAL "8.3.0") diff --git a/src/corelib/Qt6CoreMacros.cmake b/src/corelib/Qt6CoreMacros.cmake index b69d0285de2..c1f9aee0180 100644 --- a/src/corelib/Qt6CoreMacros.cmake +++ b/src/corelib/Qt6CoreMacros.cmake @@ -3677,21 +3677,6 @@ macro(qt6_standard_project_setup) if(NOT DEFINED QT_I18N_SOURCE_LANGUAGE) set(QT_I18N_SOURCE_LANGUAGE ${__qt_sps_arg_I18N_SOURCE_LANGUAGE}) endif() - - if(CMAKE_GENERATOR STREQUAL "Xcode") - # Ensure we always use device SDK for Xcode for single-arch Qt builds - set(qt_osx_arch_count 0) - if(QT_OSX_ARCHITECTURES) - list(LENGTH QT_OSX_ARCHITECTURES qt_osx_arch_count) - endif() - if(NOT qt_osx_arch_count GREATER 1 AND "${CMAKE_OSX_SYSROOT}" MATCHES "^[a-z]+simulator$") - # Xcode expects the base SDK to be the device SDK - set(simulator_sysroot "${CMAKE_OSX_SYSROOT}") - string(REGEX REPLACE "simulator" "os" CMAKE_OSX_SYSROOT "${CMAKE_OSX_SYSROOT}") - set(CMAKE_OSX_SYSROOT "${CMAKE_OSX_SYSROOT}" CACHE STRING "" FORCE) - set(CMAKE_XCODE_ATTRIBUTE_SUPPORTED_PLATFORMS "${simulator_sysroot}") - endif() - endif() endif() endmacro() diff --git a/src/corelib/animation/qabstractanimation.cpp b/src/corelib/animation/qabstractanimation.cpp index c3e1ba4010f..8a343b15eeb 100644 --- a/src/corelib/animation/qabstractanimation.cpp +++ b/src/corelib/animation/qabstractanimation.cpp @@ -858,6 +858,7 @@ qint64 QAnimationDriver::elapsed() const */ /*! + \internal The default animation driver just spins the timer... */ QDefaultAnimationDriver::QDefaultAnimationDriver(QUnifiedTimer *timer) diff --git a/src/corelib/configure.cmake b/src/corelib/configure.cmake index c1d15c75054..a186b65f24f 100644 --- a/src/corelib/configure.cmake +++ b/src/corelib/configure.cmake @@ -605,6 +605,27 @@ int main(void) " ) +qt_config_compile_test(windows_ioring + LABEL "Windows SDK: IORing" + CODE +"#include <windows.h> +#include <ioringapi.h> + +int main(void) +{ + /* BEGIN TEST: */ + IORING_CREATE_FLAGS flags; + memset(&flags, 0, sizeof(flags)); + HIORING ioRingHandle = nullptr; + HRESULT hr = CreateIoRing(IORING_VERSION_3, flags, 1, 1, &ioRingHandle); + if (hr == IORING_E_SUBMISSION_QUEUE_FULL) // not valid, but test that this #define exists + return 0; + /* END TEST: */ + return 0; +} +" +) + # cpp_winrt qt_config_compile_test(cpp_winrt LABEL "cpp/winrt" @@ -785,6 +806,11 @@ qt_feature("winsdkicu" PRIVATE CONDITION TEST_winsdkicu DISABLE QT_FEATURE_icu ) +qt_feature("windows_ioring" PRIVATE + LABEL "Windows I/O Ring" + AUTODETECT WIN32 AND CMAKE_HOST_SYSTEM_VERSION VERSION_GREATER_EQUAL 10.0.22000 + CONDITION TEST_windows_ioring +) qt_feature("inotify" PUBLIC PRIVATE LABEL "inotify" CONDITION TEST_inotify OR TEST_fsnotify @@ -1252,14 +1278,13 @@ qt_feature("permissions" PUBLIC ) qt_feature("openssl-hash" PRIVATE LABEL "OpenSSL based cryptographic hash" - AUTODETECT OFF CONDITION QT_FEATURE_openssl_linked AND QT_FEATURE_opensslv30 PURPOSE "Uses OpenSSL based implementation of cryptographic hash algorithms." ) qt_feature("async-io" PRIVATE LABEL "Async File I/O" PURPOSE "Provides support for asynchronous file I/O." - CONDITION (QT_FEATURE_thread AND QT_FEATURE_future) OR APPLE + CONDITION QT_FEATURE_thread AND QT_FEATURE_future ) qt_configure_add_summary_section(NAME "Qt Core") @@ -1272,6 +1297,7 @@ qt_configure_add_summary_entry(ARGS "glib") qt_configure_add_summary_entry(ARGS "icu") qt_configure_add_summary_entry(ARGS "jemalloc") qt_configure_add_summary_entry(ARGS "liburing") +qt_configure_add_summary_entry(ARGS "windows_ioring") qt_configure_add_summary_entry(ARGS "timezone_tzdb") qt_configure_add_summary_entry(ARGS "system-libb2") qt_configure_add_summary_entry(ARGS "mimetype-database") diff --git a/src/corelib/doc/src/cmake/cmake-configure-variables.qdoc b/src/corelib/doc/src/cmake/cmake-configure-variables.qdoc index 0a0dc0b3c50..b8e5e038a33 100644 --- a/src/corelib/doc/src/cmake/cmake-configure-variables.qdoc +++ b/src/corelib/doc/src/cmake/cmake-configure-variables.qdoc @@ -133,7 +133,6 @@ use, for example, library files referenced from a Qt installation. \summary {Forces or disables release package signing regardless of the build type.} \cmakevariablesince 6.7 -\preliminarycmakevariable \cmakevariableandroidonly When set to \c Release, the \c --release flag is passed to the \c @@ -143,6 +142,10 @@ effectively disables release package signing even in Release or RelWithDebInfo builds. When not set, the default behavior is to use release package signing in build types other than Debug. +This variable is not supposed to be set in CMake project files. Rather set it +when configuring your project on the command line or in the CMake settings of +your IDE. + \sa {androiddeployqt} */ diff --git a/src/corelib/doc/src/external-resources.qdoc b/src/corelib/doc/src/external-resources.qdoc index 2232b49bf23..5d357c65496 100644 --- a/src/corelib/doc/src/external-resources.qdoc +++ b/src/corelib/doc/src/external-resources.qdoc @@ -28,11 +28,6 @@ */ /*! - \externalpage https://marcmutz.wordpress.com/effective-qt/containers/#containers-qlist - \title Pros and Cons of Using QList -*/ - -/*! \externalpage https://marcmutz.wordpress.com/effective-qt/containers/ \title Understand the Qt Containers */ diff --git a/src/corelib/global/patches/tlexpected/0006-Namespace-TL_-macros-with-Q23_.patch b/src/corelib/global/patches/tlexpected/0006-Namespace-TL_-macros-with-Q23_.patch new file mode 100644 index 00000000000..f3c45f07594 --- /dev/null +++ b/src/corelib/global/patches/tlexpected/0006-Namespace-TL_-macros-with-Q23_.patch @@ -0,0 +1,914 @@ +From 5676e5f83597dc1fb32b551c863633eff102e879 Mon Sep 17 00:00:00 2001 +From: Marc Mutz <[email protected]> +Date: Thu, 27 Nov 2025 07:51:19 +0100 +Subject: [PATCH] Namespace TL_ macros with Q23_ + +Change-Id: Ib5762ec8ebe81e0c750da84be29531b7179c5025 +--- + src/corelib/global/qexpected_p.h | 312 +++++++++++++++---------------- + 1 file changed, 156 insertions(+), 156 deletions(-) + +diff --git a/src/corelib/global/qexpected_p.h b/src/corelib/global/qexpected_p.h +index 24ea5be1e5e..54bcae51102 100644 +--- a/src/corelib/global/qexpected_p.h ++++ b/src/corelib/global/qexpected_p.h +@@ -16,8 +16,8 @@ + // <http://creativecommons.org/publicdomain/zero/1.0/>. + /// + +-#ifndef TL_EXPECTED_HPP +-#define TL_EXPECTED_HPP ++#ifndef Q23_TL_EXPECTED_HPP ++#define Q23_TL_EXPECTED_HPP + + // + // W A R N I N G +@@ -30,9 +30,9 @@ + // We mean it. + // + +-#define TL_EXPECTED_VERSION_MAJOR 1 +-#define TL_EXPECTED_VERSION_MINOR 1 +-#define TL_EXPECTED_VERSION_PATCH 0 ++#define Q23_TL_EXPECTED_VERSION_MAJOR 1 ++#define Q23_TL_EXPECTED_VERSION_MINOR 1 ++#define Q23_TL_EXPECTED_VERSION_PATCH 0 + + #include <QtCore/private/qglobal_p.h> + #include <QtCore/qassert.h> +@@ -44,45 +44,45 @@ + #include <type_traits> + #include <utility> + +-#define TL_ASSERT Q_ASSERT ++#define Q23_TL_ASSERT Q_ASSERT + + #if defined(__EXCEPTIONS) || defined(_CPPUNWIND) +-#define TL_EXPECTED_EXCEPTIONS_ENABLED ++#define Q23_TL_EXPECTED_EXCEPTIONS_ENABLED + #endif + +-#if defined(TL_EXPECTED_EXCEPTIONS_ENABLED) && defined(QT_NO_EXCEPTIONS) +-# undef TL_EXPECTED_EXCEPTIONS_ENABLED ++#if defined(Q23_TL_EXPECTED_EXCEPTIONS_ENABLED) && defined(QT_NO_EXCEPTIONS) ++# undef Q23_TL_EXPECTED_EXCEPTIONS_ENABLED + #endif + + #if (defined(_MSC_VER) && _MSC_VER == 1900) +-#define TL_EXPECTED_MSVC2015 +-#define TL_EXPECTED_MSVC2015_CONSTEXPR ++#define Q23_TL_EXPECTED_MSVC2015 ++#define Q23_TL_EXPECTED_MSVC2015_CONSTEXPR + #else +-#define TL_EXPECTED_MSVC2015_CONSTEXPR constexpr ++#define Q23_TL_EXPECTED_MSVC2015_CONSTEXPR constexpr + #endif + + #if (defined(__GNUC__) && __GNUC__ == 4 && __GNUC_MINOR__ <= 9 && \ + !defined(__clang__)) +-#define TL_EXPECTED_GCC49 ++#define Q23_TL_EXPECTED_GCC49 + #endif + + #if (defined(__GNUC__) && __GNUC__ == 5 && __GNUC_MINOR__ <= 4 && \ + !defined(__clang__)) +-#define TL_EXPECTED_GCC54 ++#define Q23_TL_EXPECTED_GCC54 + #endif + + #if (defined(__GNUC__) && __GNUC__ == 5 && __GNUC_MINOR__ <= 5 && \ + !defined(__clang__)) +-#define TL_EXPECTED_GCC55 ++#define Q23_TL_EXPECTED_GCC55 + #endif + +-#if !defined(TL_ASSERT) ++#if !defined(Q23_TL_ASSERT) + //can't have assert in constexpr in C++11 and GCC 4.9 has a compiler bug +-#if (TL_CPLUSPLUS > 201103L) && !defined(TL_EXPECTED_GCC49) ++#if (Q23_TL_CPLUSPLUS > 201103L) && !defined(Q23_TL_EXPECTED_GCC49) + #include <cassert> +-#define TL_ASSERT(x) assert(x) ++#define Q23_TL_ASSERT(x) assert(x) + #else +-#define TL_ASSERT(x) ++#define Q23_TL_ASSERT(x) + #endif + #endif + +@@ -90,22 +90,22 @@ + !defined(__clang__)) + // GCC < 5 doesn't support overloading on const&& for member functions + +-#define TL_EXPECTED_NO_CONSTRR ++#define Q23_TL_EXPECTED_NO_CONSTRR + // GCC < 5 doesn't support some standard C++11 type traits +-#define TL_EXPECTED_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(T) \ ++#define Q23_TL_EXPECTED_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(T) \ + std::has_trivial_copy_constructor<T> +-#define TL_EXPECTED_IS_TRIVIALLY_COPY_ASSIGNABLE(T) \ ++#define Q23_TL_EXPECTED_IS_TRIVIALLY_COPY_ASSIGNABLE(T) \ + std::has_trivial_copy_assign<T> + + // This one will be different for GCC 5.7 if it's ever supported +-#define TL_EXPECTED_IS_TRIVIALLY_DESTRUCTIBLE(T) \ ++#define Q23_TL_EXPECTED_IS_TRIVIALLY_DESTRUCTIBLE(T) \ + std::is_trivially_destructible<T> + + // GCC 5 < v < 8 has a bug in is_trivially_copy_constructible which breaks + // std::vector for non-copyable types + #elif (defined(__GNUC__) && __GNUC__ < 8 && !defined(__clang__)) +-#ifndef TL_GCC_LESS_8_TRIVIALLY_COPY_CONSTRUCTIBLE_MUTEX +-#define TL_GCC_LESS_8_TRIVIALLY_COPY_CONSTRUCTIBLE_MUTEX ++#ifndef Q23_TL_GCC_LESS_8_TRIVIALLY_COPY_CONSTRUCTIBLE_MUTEX ++#define Q23_TL_GCC_LESS_8_TRIVIALLY_COPY_CONSTRUCTIBLE_MUTEX + QT_BEGIN_NAMESPACE + namespace q23 { + namespace detail { +@@ -121,50 +121,50 @@ struct is_trivially_copy_constructible<std::vector<T, A>> : std::false_type {}; + QT_END_NAMESPACE + #endif + +-#define TL_EXPECTED_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(T) \ ++#define Q23_TL_EXPECTED_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(T) \ + q23::detail::is_trivially_copy_constructible<T> +-#define TL_EXPECTED_IS_TRIVIALLY_COPY_ASSIGNABLE(T) \ ++#define Q23_TL_EXPECTED_IS_TRIVIALLY_COPY_ASSIGNABLE(T) \ + std::is_trivially_copy_assignable<T> +-#define TL_EXPECTED_IS_TRIVIALLY_DESTRUCTIBLE(T) \ ++#define Q23_TL_EXPECTED_IS_TRIVIALLY_DESTRUCTIBLE(T) \ + std::is_trivially_destructible<T> + #else +-#define TL_EXPECTED_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(T) \ ++#define Q23_TL_EXPECTED_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(T) \ + std::is_trivially_copy_constructible<T> +-#define TL_EXPECTED_IS_TRIVIALLY_COPY_ASSIGNABLE(T) \ ++#define Q23_TL_EXPECTED_IS_TRIVIALLY_COPY_ASSIGNABLE(T) \ + std::is_trivially_copy_assignable<T> +-#define TL_EXPECTED_IS_TRIVIALLY_DESTRUCTIBLE(T) \ ++#define Q23_TL_EXPECTED_IS_TRIVIALLY_DESTRUCTIBLE(T) \ + std::is_trivially_destructible<T> + #endif + + #ifdef _MSVC_LANG +-#define TL_CPLUSPLUS _MSVC_LANG ++#define Q23_TL_CPLUSPLUS _MSVC_LANG + #else +-#define TL_CPLUSPLUS __cplusplus ++#define Q23_TL_CPLUSPLUS __cplusplus + #endif + +-#if TL_CPLUSPLUS > 201103L +-#define TL_EXPECTED_CXX14 ++#if Q23_TL_CPLUSPLUS > 201103L ++#define Q23_TL_EXPECTED_CXX14 + #endif + +-#ifdef TL_EXPECTED_GCC49 +-#define TL_EXPECTED_GCC49_CONSTEXPR ++#ifdef Q23_TL_EXPECTED_GCC49 ++#define Q23_TL_EXPECTED_GCC49_CONSTEXPR + #else +-#define TL_EXPECTED_GCC49_CONSTEXPR constexpr ++#define Q23_TL_EXPECTED_GCC49_CONSTEXPR constexpr + #endif + +-#if (TL_CPLUSPLUS == 201103L || defined(TL_EXPECTED_MSVC2015) || \ +- defined(TL_EXPECTED_GCC49)) +-#define TL_EXPECTED_11_CONSTEXPR ++#if (Q23_TL_CPLUSPLUS == 201103L || defined(Q23_TL_EXPECTED_MSVC2015) || \ ++ defined(Q23_TL_EXPECTED_GCC49)) ++#define Q23_TL_EXPECTED_11_CONSTEXPR + #else +-#define TL_EXPECTED_11_CONSTEXPR constexpr ++#define Q23_TL_EXPECTED_11_CONSTEXPR constexpr + #endif + + QT_BEGIN_NAMESPACE + namespace q23 { + template <class T, class E> class expected; + +-#ifndef TL_MONOSTATE_INPLACE_MUTEX +-#define TL_MONOSTATE_INPLACE_MUTEX ++#ifndef Q23_TL_MONOSTATE_INPLACE_MUTEX ++#define Q23_TL_MONOSTATE_INPLACE_MUTEX + class monostate {}; + + struct in_place_t { +@@ -196,8 +196,8 @@ public: + : m_val(l, std::forward<Args>(args)...) {} + + constexpr const E &error() const & { return m_val; } +- TL_EXPECTED_11_CONSTEXPR E &error() & { return m_val; } +- TL_EXPECTED_11_CONSTEXPR E &&error() && { return std::move(m_val); } ++ Q23_TL_EXPECTED_11_CONSTEXPR E &error() & { return m_val; } ++ Q23_TL_EXPECTED_11_CONSTEXPR E &&error() && { return std::move(m_val); } + constexpr const E &&error() const && { return std::move(m_val); } + + private: +@@ -245,8 +245,8 @@ static constexpr unexpect_t unexpect{}; + + namespace detail { + template <typename E> +-[[noreturn]] TL_EXPECTED_11_CONSTEXPR void throw_exception(E &&e) { +-#ifdef TL_EXPECTED_EXCEPTIONS_ENABLED ++[[noreturn]] Q23_TL_EXPECTED_11_CONSTEXPR void throw_exception(E &&e) { ++#ifdef Q23_TL_EXPECTED_EXCEPTIONS_ENABLED + throw std::forward<E>(e); + #else + (void)e; +@@ -258,8 +258,8 @@ template <typename E> + #endif + } + +-#ifndef TL_TRAITS_MUTEX +-#define TL_TRAITS_MUTEX ++#ifndef Q23_TL_TRAITS_MUTEX ++#define Q23_TL_TRAITS_MUTEX + // C++14-style aliases for brevity + template <class T> using remove_const_t = typename std::remove_const<T>::type; + template <class T> +@@ -278,13 +278,13 @@ struct conjunction<B, Bs...> + : std::conditional<bool(B::value), conjunction<Bs...>, B>::type {}; + + #if defined(_LIBCPP_VERSION) && __cplusplus == 201103L +-#define TL_TRAITS_LIBCXX_MEM_FN_WORKAROUND ++#define Q23_TL_TRAITS_LIBCXX_MEM_FN_WORKAROUND + #endif + + // In C++11 mode, there's an issue in libc++'s std::mem_fn + // which results in a hard-error when using it in a noexcept expression + // in some cases. This is a check to workaround the common failing case. +-#ifdef TL_TRAITS_LIBCXX_MEM_FN_WORKAROUND ++#ifdef Q23_TL_TRAITS_LIBCXX_MEM_FN_WORKAROUND + template <class T> + struct is_pointer_to_non_const_member_func : std::false_type {}; + template <class T, class Ret, class... Args> +@@ -315,7 +315,7 @@ template <class T> struct is_const_or_const_ref<T const> : std::true_type {}; + // https://stackoverflow.com/questions/38288042/c11-14-invoke-workaround + template < + typename Fn, typename... Args, +-#ifdef TL_TRAITS_LIBCXX_MEM_FN_WORKAROUND ++#ifdef Q23_TL_TRAITS_LIBCXX_MEM_FN_WORKAROUND + typename = enable_if_t<!(is_pointer_to_non_const_member_func<Fn>::value && + is_const_or_const_ref<Args...>::value)>, + #endif +@@ -574,7 +574,7 @@ template <class T, class E> struct expected_storage_base<T, E, true, true> { + // T is trivial, E is not. + template <class T, class E> struct expected_storage_base<T, E, true, false> { + constexpr expected_storage_base() : m_val(T{}), m_has_val(true) {} +- TL_EXPECTED_MSVC2015_CONSTEXPR expected_storage_base(no_init_t) ++ Q23_TL_EXPECTED_MSVC2015_CONSTEXPR expected_storage_base(no_init_t) + : m_no_init(), m_has_val(false) {} + + template <class... Args, +@@ -675,7 +675,7 @@ template <class E> struct expected_storage_base<void, E, false, true> { + #if __GNUC__ <= 5 + //no constexpr for GCC 4/5 bug + #else +- TL_EXPECTED_MSVC2015_CONSTEXPR ++ Q23_TL_EXPECTED_MSVC2015_CONSTEXPR + #endif + expected_storage_base() : m_has_val(true) {} + +@@ -770,7 +770,7 @@ struct expected_operations_base : expected_storage_base<T, E> { + this->m_has_val = false; + } + +-#ifdef TL_EXPECTED_EXCEPTIONS_ENABLED ++#ifdef Q23_TL_EXPECTED_EXCEPTIONS_ENABLED + + // These assign overloads ensure that the most efficient assignment + // implementation is used while maintaining the strong exception guarantee. +@@ -820,7 +820,7 @@ struct expected_operations_base : expected_storage_base<T, E> { + auto tmp = std::move(geterr()); + geterr().~unexpected<E>(); + +-#ifdef TL_EXPECTED_EXCEPTIONS_ENABLED ++#ifdef Q23_TL_EXPECTED_EXCEPTIONS_ENABLED + try { + construct(rhs.get()); + } catch (...) { +@@ -855,7 +855,7 @@ struct expected_operations_base : expected_storage_base<T, E> { + if (!this->m_has_val && rhs.m_has_val) { + auto tmp = std::move(geterr()); + geterr().~unexpected<E>(); +-#ifdef TL_EXPECTED_EXCEPTIONS_ENABLED ++#ifdef Q23_TL_EXPECTED_EXCEPTIONS_ENABLED + try { + construct(std::move(rhs).get()); + } catch (...) { +@@ -911,27 +911,27 @@ struct expected_operations_base : expected_storage_base<T, E> { + + bool has_value() const { return this->m_has_val; } + +- TL_EXPECTED_11_CONSTEXPR T &get() & { return this->m_val; } ++ Q23_TL_EXPECTED_11_CONSTEXPR T &get() & { return this->m_val; } + constexpr const T &get() const & { return this->m_val; } +- TL_EXPECTED_11_CONSTEXPR T &&get() && { return std::move(this->m_val); } +-#ifndef TL_EXPECTED_NO_CONSTRR ++ Q23_TL_EXPECTED_11_CONSTEXPR T &&get() && { return std::move(this->m_val); } ++#ifndef Q23_TL_EXPECTED_NO_CONSTRR + constexpr const T &&get() const && { return std::move(this->m_val); } + #endif + +- TL_EXPECTED_11_CONSTEXPR unexpected<E> &geterr() & { ++ Q23_TL_EXPECTED_11_CONSTEXPR unexpected<E> &geterr() & { + return this->m_unexpect; + } + constexpr const unexpected<E> &geterr() const & { return this->m_unexpect; } +- TL_EXPECTED_11_CONSTEXPR unexpected<E> &&geterr() && { ++ Q23_TL_EXPECTED_11_CONSTEXPR unexpected<E> &&geterr() && { + return std::move(this->m_unexpect); + } +-#ifndef TL_EXPECTED_NO_CONSTRR ++#ifndef Q23_TL_EXPECTED_NO_CONSTRR + constexpr const unexpected<E> &&geterr() const && { + return std::move(this->m_unexpect); + } + #endif + +- TL_EXPECTED_11_CONSTEXPR void destroy_val() { get().~T(); } ++ Q23_TL_EXPECTED_11_CONSTEXPR void destroy_val() { get().~T(); } + }; + + // This base class provides some handy member functions which can be used in +@@ -971,20 +971,20 @@ struct expected_operations_base<void, E> : expected_storage_base<void, E> { + + bool has_value() const { return this->m_has_val; } + +- TL_EXPECTED_11_CONSTEXPR unexpected<E> &geterr() & { ++ Q23_TL_EXPECTED_11_CONSTEXPR unexpected<E> &geterr() & { + return this->m_unexpect; + } + constexpr const unexpected<E> &geterr() const & { return this->m_unexpect; } +- TL_EXPECTED_11_CONSTEXPR unexpected<E> &&geterr() && { ++ Q23_TL_EXPECTED_11_CONSTEXPR unexpected<E> &&geterr() && { + return std::move(this->m_unexpect); + } +-#ifndef TL_EXPECTED_NO_CONSTRR ++#ifndef Q23_TL_EXPECTED_NO_CONSTRR + constexpr const unexpected<E> &&geterr() const && { + return std::move(this->m_unexpect); + } + #endif + +- TL_EXPECTED_11_CONSTEXPR void destroy_val() { ++ Q23_TL_EXPECTED_11_CONSTEXPR void destroy_val() { + // no-op + } + }; +@@ -992,8 +992,8 @@ struct expected_operations_base<void, E> : expected_storage_base<void, E> { + // This class manages conditionally having a trivial copy constructor + // This specialization is for when T and E are trivially copy constructible + template <class T, class E, +- bool = is_void_or<T, TL_EXPECTED_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(T)>:: +- value &&TL_EXPECTED_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(E)::value, ++ bool = is_void_or<T, Q23_TL_EXPECTED_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(T)>:: ++ value &&Q23_TL_EXPECTED_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(E)::value, + bool = (is_copy_constructible_or_void<T>::value && + std::is_copy_constructible<E>::value)> + struct expected_copy_base : expected_operations_base<T, E> { +@@ -1025,7 +1025,7 @@ struct expected_copy_base<T, E, false, true> : expected_operations_base<T, E> { + // doesn't implement an analogue to std::is_trivially_move_constructible. We + // have to make do with a non-trivial move constructor even if T is trivially + // move constructible +-#ifndef TL_EXPECTED_GCC49 ++#ifndef Q23_TL_EXPECTED_GCC49 + template <class T, class E, + bool = is_void_or<T, std::is_trivially_move_constructible<T>>::value + &&std::is_trivially_move_constructible<E>::value> +@@ -1058,12 +1058,12 @@ struct expected_move_base<T, E, false> : expected_copy_base<T, E> { + // This class manages conditionally having a trivial copy assignment operator + template <class T, class E, + bool = is_void_or< +- T, conjunction<TL_EXPECTED_IS_TRIVIALLY_COPY_ASSIGNABLE(T), +- TL_EXPECTED_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(T), +- TL_EXPECTED_IS_TRIVIALLY_DESTRUCTIBLE(T)>>::value +- &&TL_EXPECTED_IS_TRIVIALLY_COPY_ASSIGNABLE(E)::value +- &&TL_EXPECTED_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(E)::value +- &&TL_EXPECTED_IS_TRIVIALLY_DESTRUCTIBLE(E)::value, ++ T, conjunction<Q23_TL_EXPECTED_IS_TRIVIALLY_COPY_ASSIGNABLE(T), ++ Q23_TL_EXPECTED_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(T), ++ Q23_TL_EXPECTED_IS_TRIVIALLY_DESTRUCTIBLE(T)>>::value ++ &&Q23_TL_EXPECTED_IS_TRIVIALLY_COPY_ASSIGNABLE(E)::value ++ &&Q23_TL_EXPECTED_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(E)::value ++ &&Q23_TL_EXPECTED_IS_TRIVIALLY_DESTRUCTIBLE(E)::value, + bool = (is_copy_constructible_or_void<T>::value && + std::is_copy_constructible<E>::value && + is_copy_assignable_or_void<T>::value && +@@ -1093,7 +1093,7 @@ struct expected_copy_assign_base<T, E, false, true> : expected_move_base<T, E> { + // doesn't implement an analogue to std::is_trivially_move_assignable. We have + // to make do with a non-trivial move assignment operator even if T is trivially + // move assignable +-#ifndef TL_EXPECTED_GCC49 ++#ifndef Q23_TL_EXPECTED_GCC49 + template <class T, class E, + bool = + is_void_or<T, conjunction<std::is_trivially_destructible<T>, +@@ -1330,10 +1330,10 @@ class expected : private detail::expected_move_assign_base<T, E>, + + template <class U = T, + detail::enable_if_t<!std::is_void<U>::value> * = nullptr> +- TL_EXPECTED_11_CONSTEXPR U &val() { ++ Q23_TL_EXPECTED_11_CONSTEXPR U &val() { + return this->m_val; + } +- TL_EXPECTED_11_CONSTEXPR unexpected<E> &err() { return this->m_unexpect; } ++ Q23_TL_EXPECTED_11_CONSTEXPR unexpected<E> &err() { return this->m_unexpect; } + + template <class U = T, + detail::enable_if_t<!std::is_void<U>::value> * = nullptr> +@@ -1350,19 +1350,19 @@ public: + typedef E error_type; + typedef unexpected<E> unexpected_type; + +-#if defined(TL_EXPECTED_CXX14) && !defined(TL_EXPECTED_GCC49) && \ +- !defined(TL_EXPECTED_GCC54) && !defined(TL_EXPECTED_GCC55) +- template <class F> TL_EXPECTED_11_CONSTEXPR auto and_then(F &&f) & { ++#if defined(Q23_TL_EXPECTED_CXX14) && !defined(Q23_TL_EXPECTED_GCC49) && \ ++ !defined(Q23_TL_EXPECTED_GCC54) && !defined(Q23_TL_EXPECTED_GCC55) ++ template <class F> Q23_TL_EXPECTED_11_CONSTEXPR auto and_then(F &&f) & { + return and_then_impl(*this, std::forward<F>(f)); + } +- template <class F> TL_EXPECTED_11_CONSTEXPR auto and_then(F &&f) && { ++ template <class F> Q23_TL_EXPECTED_11_CONSTEXPR auto and_then(F &&f) && { + return and_then_impl(std::move(*this), std::forward<F>(f)); + } + template <class F> constexpr auto and_then(F &&f) const & { + return and_then_impl(*this, std::forward<F>(f)); + } + +-#ifndef TL_EXPECTED_NO_CONSTRR ++#ifndef Q23_TL_EXPECTED_NO_CONSTRR + template <class F> constexpr auto and_then(F &&f) const && { + return and_then_impl(std::move(*this), std::forward<F>(f)); + } +@@ -1370,13 +1370,13 @@ public: + + #else + template <class F> +- TL_EXPECTED_11_CONSTEXPR auto ++ Q23_TL_EXPECTED_11_CONSTEXPR auto + and_then(F &&f) & -> decltype(and_then_impl(std::declval<expected &>(), + std::forward<F>(f))) { + return and_then_impl(*this, std::forward<F>(f)); + } + template <class F> +- TL_EXPECTED_11_CONSTEXPR auto ++ Q23_TL_EXPECTED_11_CONSTEXPR auto + and_then(F &&f) && -> decltype(and_then_impl(std::declval<expected &&>(), + std::forward<F>(f))) { + return and_then_impl(std::move(*this), std::forward<F>(f)); +@@ -1387,7 +1387,7 @@ public: + return and_then_impl(*this, std::forward<F>(f)); + } + +-#ifndef TL_EXPECTED_NO_CONSTRR ++#ifndef Q23_TL_EXPECTED_NO_CONSTRR + template <class F> + constexpr auto and_then(F &&f) const && -> decltype(and_then_impl( + std::declval<expected const &&>(), std::forward<F>(f))) { +@@ -1396,12 +1396,12 @@ public: + #endif + #endif + +-#if defined(TL_EXPECTED_CXX14) && !defined(TL_EXPECTED_GCC49) && \ +- !defined(TL_EXPECTED_GCC54) && !defined(TL_EXPECTED_GCC55) +- template <class F> TL_EXPECTED_11_CONSTEXPR auto map(F &&f) & { ++#if defined(Q23_TL_EXPECTED_CXX14) && !defined(Q23_TL_EXPECTED_GCC49) && \ ++ !defined(Q23_TL_EXPECTED_GCC54) && !defined(Q23_TL_EXPECTED_GCC55) ++ template <class F> Q23_TL_EXPECTED_11_CONSTEXPR auto map(F &&f) & { + return expected_map_impl(*this, std::forward<F>(f)); + } +- template <class F> TL_EXPECTED_11_CONSTEXPR auto map(F &&f) && { ++ template <class F> Q23_TL_EXPECTED_11_CONSTEXPR auto map(F &&f) && { + return expected_map_impl(std::move(*this), std::forward<F>(f)); + } + template <class F> constexpr auto map(F &&f) const & { +@@ -1412,13 +1412,13 @@ public: + } + #else + template <class F> +- TL_EXPECTED_11_CONSTEXPR decltype(expected_map_impl( ++ Q23_TL_EXPECTED_11_CONSTEXPR decltype(expected_map_impl( + std::declval<expected &>(), std::declval<F &&>())) + map(F &&f) & { + return expected_map_impl(*this, std::forward<F>(f)); + } + template <class F> +- TL_EXPECTED_11_CONSTEXPR decltype(expected_map_impl(std::declval<expected>(), ++ Q23_TL_EXPECTED_11_CONSTEXPR decltype(expected_map_impl(std::declval<expected>(), + std::declval<F &&>())) + map(F &&f) && { + return expected_map_impl(std::move(*this), std::forward<F>(f)); +@@ -1430,7 +1430,7 @@ public: + return expected_map_impl(*this, std::forward<F>(f)); + } + +-#ifndef TL_EXPECTED_NO_CONSTRR ++#ifndef Q23_TL_EXPECTED_NO_CONSTRR + template <class F> + constexpr decltype(expected_map_impl(std::declval<const expected &&>(), + std::declval<F &&>())) +@@ -1440,12 +1440,12 @@ public: + #endif + #endif + +-#if defined(TL_EXPECTED_CXX14) && !defined(TL_EXPECTED_GCC49) && \ +- !defined(TL_EXPECTED_GCC54) && !defined(TL_EXPECTED_GCC55) +- template <class F> TL_EXPECTED_11_CONSTEXPR auto transform(F &&f) & { ++#if defined(Q23_TL_EXPECTED_CXX14) && !defined(Q23_TL_EXPECTED_GCC49) && \ ++ !defined(Q23_TL_EXPECTED_GCC54) && !defined(Q23_TL_EXPECTED_GCC55) ++ template <class F> Q23_TL_EXPECTED_11_CONSTEXPR auto transform(F &&f) & { + return expected_map_impl(*this, std::forward<F>(f)); + } +- template <class F> TL_EXPECTED_11_CONSTEXPR auto transform(F &&f) && { ++ template <class F> Q23_TL_EXPECTED_11_CONSTEXPR auto transform(F &&f) && { + return expected_map_impl(std::move(*this), std::forward<F>(f)); + } + template <class F> constexpr auto transform(F &&f) const & { +@@ -1456,13 +1456,13 @@ public: + } + #else + template <class F> +- TL_EXPECTED_11_CONSTEXPR decltype(expected_map_impl( ++ Q23_TL_EXPECTED_11_CONSTEXPR decltype(expected_map_impl( + std::declval<expected &>(), std::declval<F &&>())) + transform(F &&f) & { + return expected_map_impl(*this, std::forward<F>(f)); + } + template <class F> +- TL_EXPECTED_11_CONSTEXPR decltype(expected_map_impl(std::declval<expected>(), ++ Q23_TL_EXPECTED_11_CONSTEXPR decltype(expected_map_impl(std::declval<expected>(), + std::declval<F &&>())) + transform(F &&f) && { + return expected_map_impl(std::move(*this), std::forward<F>(f)); +@@ -1474,7 +1474,7 @@ public: + return expected_map_impl(*this, std::forward<F>(f)); + } + +-#ifndef TL_EXPECTED_NO_CONSTRR ++#ifndef Q23_TL_EXPECTED_NO_CONSTRR + template <class F> + constexpr decltype(expected_map_impl(std::declval<const expected &&>(), + std::declval<F &&>())) +@@ -1484,12 +1484,12 @@ public: + #endif + #endif + +-#if defined(TL_EXPECTED_CXX14) && !defined(TL_EXPECTED_GCC49) && \ +- !defined(TL_EXPECTED_GCC54) && !defined(TL_EXPECTED_GCC55) +- template <class F> TL_EXPECTED_11_CONSTEXPR auto map_error(F &&f) & { ++#if defined(Q23_TL_EXPECTED_CXX14) && !defined(Q23_TL_EXPECTED_GCC49) && \ ++ !defined(Q23_TL_EXPECTED_GCC54) && !defined(Q23_TL_EXPECTED_GCC55) ++ template <class F> Q23_TL_EXPECTED_11_CONSTEXPR auto map_error(F &&f) & { + return map_error_impl(*this, std::forward<F>(f)); + } +- template <class F> TL_EXPECTED_11_CONSTEXPR auto map_error(F &&f) && { ++ template <class F> Q23_TL_EXPECTED_11_CONSTEXPR auto map_error(F &&f) && { + return map_error_impl(std::move(*this), std::forward<F>(f)); + } + template <class F> constexpr auto map_error(F &&f) const & { +@@ -1500,13 +1500,13 @@ public: + } + #else + template <class F> +- TL_EXPECTED_11_CONSTEXPR decltype(map_error_impl(std::declval<expected &>(), ++ Q23_TL_EXPECTED_11_CONSTEXPR decltype(map_error_impl(std::declval<expected &>(), + std::declval<F &&>())) + map_error(F &&f) & { + return map_error_impl(*this, std::forward<F>(f)); + } + template <class F> +- TL_EXPECTED_11_CONSTEXPR decltype(map_error_impl(std::declval<expected &&>(), ++ Q23_TL_EXPECTED_11_CONSTEXPR decltype(map_error_impl(std::declval<expected &&>(), + std::declval<F &&>())) + map_error(F &&f) && { + return map_error_impl(std::move(*this), std::forward<F>(f)); +@@ -1518,7 +1518,7 @@ public: + return map_error_impl(*this, std::forward<F>(f)); + } + +-#ifndef TL_EXPECTED_NO_CONSTRR ++#ifndef Q23_TL_EXPECTED_NO_CONSTRR + template <class F> + constexpr decltype(map_error_impl(std::declval<const expected &&>(), + std::declval<F &&>())) +@@ -1527,12 +1527,12 @@ public: + } + #endif + #endif +-#if defined(TL_EXPECTED_CXX14) && !defined(TL_EXPECTED_GCC49) && \ +- !defined(TL_EXPECTED_GCC54) && !defined(TL_EXPECTED_GCC55) +- template <class F> TL_EXPECTED_11_CONSTEXPR auto transform_error(F &&f) & { ++#if defined(Q23_TL_EXPECTED_CXX14) && !defined(Q23_TL_EXPECTED_GCC49) && \ ++ !defined(Q23_TL_EXPECTED_GCC54) && !defined(Q23_TL_EXPECTED_GCC55) ++ template <class F> Q23_TL_EXPECTED_11_CONSTEXPR auto transform_error(F &&f) & { + return map_error_impl(*this, std::forward<F>(f)); + } +- template <class F> TL_EXPECTED_11_CONSTEXPR auto transform_error(F &&f) && { ++ template <class F> Q23_TL_EXPECTED_11_CONSTEXPR auto transform_error(F &&f) && { + return map_error_impl(std::move(*this), std::forward<F>(f)); + } + template <class F> constexpr auto transform_error(F &&f) const & { +@@ -1543,13 +1543,13 @@ public: + } + #else + template <class F> +- TL_EXPECTED_11_CONSTEXPR decltype(map_error_impl(std::declval<expected &>(), ++ Q23_TL_EXPECTED_11_CONSTEXPR decltype(map_error_impl(std::declval<expected &>(), + std::declval<F &&>())) + transform_error(F &&f) & { + return map_error_impl(*this, std::forward<F>(f)); + } + template <class F> +- TL_EXPECTED_11_CONSTEXPR decltype(map_error_impl(std::declval<expected &&>(), ++ Q23_TL_EXPECTED_11_CONSTEXPR decltype(map_error_impl(std::declval<expected &&>(), + std::declval<F &&>())) + transform_error(F &&f) && { + return map_error_impl(std::move(*this), std::forward<F>(f)); +@@ -1561,7 +1561,7 @@ public: + return map_error_impl(*this, std::forward<F>(f)); + } + +-#ifndef TL_EXPECTED_NO_CONSTRR ++#ifndef Q23_TL_EXPECTED_NO_CONSTRR + template <class F> + constexpr decltype(map_error_impl(std::declval<const expected &&>(), + std::declval<F &&>())) +@@ -1570,11 +1570,11 @@ public: + } + #endif + #endif +- template <class F> expected TL_EXPECTED_11_CONSTEXPR or_else(F &&f) & { ++ template <class F> expected Q23_TL_EXPECTED_11_CONSTEXPR or_else(F &&f) & { + return or_else_impl(*this, std::forward<F>(f)); + } + +- template <class F> expected TL_EXPECTED_11_CONSTEXPR or_else(F &&f) && { ++ template <class F> expected Q23_TL_EXPECTED_11_CONSTEXPR or_else(F &&f) && { + return or_else_impl(std::move(*this), std::forward<F>(f)); + } + +@@ -1582,7 +1582,7 @@ public: + return or_else_impl(*this, std::forward<F>(f)); + } + +-#ifndef TL_EXPECTED_NO_CONSTRR ++#ifndef Q23_TL_EXPECTED_NO_CONSTRR + template <class F> expected constexpr or_else(F &&f) const && { + return or_else_impl(std::move(*this), std::forward<F>(f)); + } +@@ -1664,7 +1664,7 @@ public: + nullptr, + detail::expected_enable_from_other<T, E, U, G, const U &, const G &> + * = nullptr> +- explicit TL_EXPECTED_11_CONSTEXPR expected(const expected<U, G> &rhs) ++ explicit Q23_TL_EXPECTED_11_CONSTEXPR expected(const expected<U, G> &rhs) + : ctor_base(detail::default_constructor_tag{}) { + if (rhs.has_value()) { + this->construct(*rhs); +@@ -1679,7 +1679,7 @@ public: + nullptr, + detail::expected_enable_from_other<T, E, U, G, const U &, const G &> + * = nullptr> +- TL_EXPECTED_11_CONSTEXPR expected(const expected<U, G> &rhs) ++ Q23_TL_EXPECTED_11_CONSTEXPR expected(const expected<U, G> &rhs) + : ctor_base(detail::default_constructor_tag{}) { + if (rhs.has_value()) { + this->construct(*rhs); +@@ -1693,7 +1693,7 @@ public: + detail::enable_if_t<!(std::is_convertible<U &&, T>::value && + std::is_convertible<G &&, E>::value)> * = nullptr, + detail::expected_enable_from_other<T, E, U, G, U &&, G &&> * = nullptr> +- explicit TL_EXPECTED_11_CONSTEXPR expected(expected<U, G> &&rhs) ++ explicit Q23_TL_EXPECTED_11_CONSTEXPR expected(expected<U, G> &&rhs) + : ctor_base(detail::default_constructor_tag{}) { + if (rhs.has_value()) { + this->construct(std::move(*rhs)); +@@ -1707,7 +1707,7 @@ public: + detail::enable_if_t<(std::is_convertible<U &&, T>::value && + std::is_convertible<G &&, E>::value)> * = nullptr, + detail::expected_enable_from_other<T, E, U, G, U &&, G &&> * = nullptr> +- TL_EXPECTED_11_CONSTEXPR expected(expected<U, G> &&rhs) ++ Q23_TL_EXPECTED_11_CONSTEXPR expected(expected<U, G> &&rhs) + : ctor_base(detail::default_constructor_tag{}) { + if (rhs.has_value()) { + this->construct(std::move(*rhs)); +@@ -1720,14 +1720,14 @@ public: + class U = T, + detail::enable_if_t<!std::is_convertible<U &&, T>::value> * = nullptr, + detail::expected_enable_forward_value<T, E, U> * = nullptr> +- explicit TL_EXPECTED_MSVC2015_CONSTEXPR expected(U &&v) ++ explicit Q23_TL_EXPECTED_MSVC2015_CONSTEXPR expected(U &&v) + : expected(in_place, std::forward<U>(v)) {} + + template < + class U = T, + detail::enable_if_t<std::is_convertible<U &&, T>::value> * = nullptr, + detail::expected_enable_forward_value<T, E, U> * = nullptr> +- TL_EXPECTED_MSVC2015_CONSTEXPR expected(U &&v) ++ Q23_TL_EXPECTED_MSVC2015_CONSTEXPR expected(U &&v) + : expected(in_place, std::forward<U>(v)) {} + + template < +@@ -1773,7 +1773,7 @@ public: + auto tmp = std::move(err()); + err().~unexpected<E>(); + +-#ifdef TL_EXPECTED_EXCEPTIONS_ENABLED ++#ifdef Q23_TL_EXPECTED_EXCEPTIONS_ENABLED + try { + ::new (valptr()) T(std::forward<U>(v)); + this->m_has_val = true; +@@ -1842,7 +1842,7 @@ public: + auto tmp = std::move(err()); + err().~unexpected<E>(); + +-#ifdef TL_EXPECTED_EXCEPTIONS_ENABLED ++#ifdef Q23_TL_EXPECTED_EXCEPTIONS_ENABLED + try { + ::new (valptr()) T(std::forward<Args>(args)...); + this->m_has_val = true; +@@ -1882,7 +1882,7 @@ public: + auto tmp = std::move(err()); + err().~unexpected<E>(); + +-#ifdef TL_EXPECTED_EXCEPTIONS_ENABLED ++#ifdef Q23_TL_EXPECTED_EXCEPTIONS_ENABLED + try { + ::new (valptr()) T(il, std::forward<Args>(args)...); + this->m_has_val = true; +@@ -1943,7 +1943,7 @@ private: + move_constructing_e_can_throw) { + auto temp = std::move(val()); + val().~T(); +-#ifdef TL_EXPECTED_EXCEPTIONS_ENABLED ++#ifdef Q23_TL_EXPECTED_EXCEPTIONS_ENABLED + try { + ::new (errptr()) unexpected_type(std::move(rhs.err())); + rhs.err().~unexpected_type(); +@@ -1966,7 +1966,7 @@ private: + e_is_nothrow_move_constructible) { + auto temp = std::move(rhs.err()); + rhs.err().~unexpected_type(); +-#ifdef TL_EXPECTED_EXCEPTIONS_ENABLED ++#ifdef Q23_TL_EXPECTED_EXCEPTIONS_ENABLED + try { + ::new (rhs.valptr()) T(std::move(val())); + val().~T(); +@@ -2008,36 +2008,36 @@ public: + } + + constexpr const T *operator->() const { +- TL_ASSERT(has_value()); ++ Q23_TL_ASSERT(has_value()); + return valptr(); + } +- TL_EXPECTED_11_CONSTEXPR T *operator->() { +- TL_ASSERT(has_value()); ++ Q23_TL_EXPECTED_11_CONSTEXPR T *operator->() { ++ Q23_TL_ASSERT(has_value()); + return valptr(); + } + + template <class U = T, + detail::enable_if_t<!std::is_void<U>::value> * = nullptr> + constexpr const U &operator*() const & { +- TL_ASSERT(has_value()); ++ Q23_TL_ASSERT(has_value()); + return val(); + } + template <class U = T, + detail::enable_if_t<!std::is_void<U>::value> * = nullptr> +- TL_EXPECTED_11_CONSTEXPR U &operator*() & { +- TL_ASSERT(has_value()); ++ Q23_TL_EXPECTED_11_CONSTEXPR U &operator*() & { ++ Q23_TL_ASSERT(has_value()); + return val(); + } + template <class U = T, + detail::enable_if_t<!std::is_void<U>::value> * = nullptr> + constexpr const U &&operator*() const && { +- TL_ASSERT(has_value()); ++ Q23_TL_ASSERT(has_value()); + return std::move(val()); + } + template <class U = T, + detail::enable_if_t<!std::is_void<U>::value> * = nullptr> +- TL_EXPECTED_11_CONSTEXPR U &&operator*() && { +- TL_ASSERT(has_value()); ++ Q23_TL_EXPECTED_11_CONSTEXPR U &&operator*() && { ++ Q23_TL_ASSERT(has_value()); + return std::move(val()); + } + +@@ -2046,47 +2046,47 @@ public: + + template <class U = T, + detail::enable_if_t<!std::is_void<U>::value> * = nullptr> +- TL_EXPECTED_11_CONSTEXPR const U &value() const & { ++ Q23_TL_EXPECTED_11_CONSTEXPR const U &value() const & { + if (!has_value()) + detail::throw_exception(bad_expected_access<E>(err().error())); + return val(); + } + template <class U = T, + detail::enable_if_t<!std::is_void<U>::value> * = nullptr> +- TL_EXPECTED_11_CONSTEXPR U &value() & { ++ Q23_TL_EXPECTED_11_CONSTEXPR U &value() & { + if (!has_value()) + detail::throw_exception(bad_expected_access<E>(err().error())); + return val(); + } + template <class U = T, + detail::enable_if_t<!std::is_void<U>::value> * = nullptr> +- TL_EXPECTED_11_CONSTEXPR const U &&value() const && { ++ Q23_TL_EXPECTED_11_CONSTEXPR const U &&value() const && { + if (!has_value()) + detail::throw_exception(bad_expected_access<E>(std::move(err()).error())); + return std::move(val()); + } + template <class U = T, + detail::enable_if_t<!std::is_void<U>::value> * = nullptr> +- TL_EXPECTED_11_CONSTEXPR U &&value() && { ++ Q23_TL_EXPECTED_11_CONSTEXPR U &&value() && { + if (!has_value()) + detail::throw_exception(bad_expected_access<E>(std::move(err()).error())); + return std::move(val()); + } + + constexpr const E &error() const & { +- TL_ASSERT(!has_value()); ++ Q23_TL_ASSERT(!has_value()); + return err().error(); + } +- TL_EXPECTED_11_CONSTEXPR E &error() & { +- TL_ASSERT(!has_value()); ++ Q23_TL_EXPECTED_11_CONSTEXPR E &error() & { ++ Q23_TL_ASSERT(!has_value()); + return err().error(); + } + constexpr const E &&error() const && { +- TL_ASSERT(!has_value()); ++ Q23_TL_ASSERT(!has_value()); + return std::move(err().error()); + } +- TL_EXPECTED_11_CONSTEXPR E &&error() && { +- TL_ASSERT(!has_value()); ++ Q23_TL_EXPECTED_11_CONSTEXPR E &&error() && { ++ Q23_TL_ASSERT(!has_value()); + return std::move(err().error()); + } + +@@ -2096,7 +2096,7 @@ public: + "T must be copy-constructible and convertible to from U&&"); + return bool(*this) ? **this : static_cast<T>(std::forward<U>(v)); + } +- template <class U> TL_EXPECTED_11_CONSTEXPR T value_or(U &&v) && { ++ template <class U> Q23_TL_EXPECTED_11_CONSTEXPR T value_or(U &&v) && { + static_assert(std::is_move_constructible<T>::value && + std::is_convertible<U &&, T>::value, + "T must be move-constructible and convertible to from U&&"); +@@ -2109,7 +2109,7 @@ template <class Exp> using exp_t = typename detail::decay_t<Exp>::value_type; + template <class Exp> using err_t = typename detail::decay_t<Exp>::error_type; + template <class Exp, class Ret> using ret_t = expected<Ret, err_t<Exp>>; + +-#ifdef TL_EXPECTED_CXX14 ++#ifdef Q23_TL_EXPECTED_CXX14 + template <class Exp, class F, + detail::enable_if_t<!std::is_void<exp_t<Exp>>::value> * = nullptr, + class Ret = decltype(detail::invoke(std::declval<F>(), +@@ -2156,7 +2156,7 @@ constexpr auto and_then_impl(Exp &&exp, F &&f) -> Ret { + } + #endif + +-#ifdef TL_EXPECTED_CXX14 ++#ifdef Q23_TL_EXPECTED_CXX14 + template <class Exp, class F, + detail::enable_if_t<!std::is_void<exp_t<Exp>>::value> * = nullptr, + class Ret = decltype(detail::invoke(std::declval<F>(), +@@ -2266,8 +2266,8 @@ auto expected_map_impl(Exp &&exp, F &&f) -> expected<void, err_t<Exp>> { + } + #endif + +-#if defined(TL_EXPECTED_CXX14) && !defined(TL_EXPECTED_GCC49) && \ +- !defined(TL_EXPECTED_GCC54) && !defined(TL_EXPECTED_GCC55) ++#if defined(Q23_TL_EXPECTED_CXX14) && !defined(Q23_TL_EXPECTED_GCC49) && \ ++ !defined(Q23_TL_EXPECTED_GCC54) && !defined(Q23_TL_EXPECTED_GCC55) + template <class Exp, class F, + detail::enable_if_t<!std::is_void<exp_t<Exp>>::value> * = nullptr, + class Ret = decltype(detail::invoke(std::declval<F>(), +@@ -2382,7 +2382,7 @@ auto map_error_impl(Exp &&exp, F &&f) -> expected<exp_t<Exp>, monostate> { + } + #endif + +-#ifdef TL_EXPECTED_CXX14 ++#ifdef Q23_TL_EXPECTED_CXX14 + template <class Exp, class F, + class Ret = decltype(detail::invoke(std::declval<F>(), + std::declval<Exp>().error())), +-- +2.25.1 + diff --git a/src/corelib/global/qcompilerdetection.h b/src/corelib/global/qcompilerdetection.h index 0b42af7686c..b2d79cca603 100644 --- a/src/corelib/global/qcompilerdetection.h +++ b/src/corelib/global/qcompilerdetection.h @@ -1404,7 +1404,7 @@ static_assert(!std::is_convertible_v<std::nullptr_t, bool>, #if defined(__cplusplus) #ifdef __cpp_constinit -# if defined(Q_CC_MSVC) && !defined(Q_CC_CLANG) +# if defined(Q_CC_MSVC) && _MSC_VER < 1940 && !defined(Q_CC_CLANG) // https://developercommunity.visualstudio.com/t/C:-constinit-for-an-optional-fails-if-/1406069 # define Q_CONSTINIT # else diff --git a/src/corelib/global/qexpected_p.h b/src/corelib/global/qexpected_p.h index 24ea5be1e5e..54bcae51102 100644 --- a/src/corelib/global/qexpected_p.h +++ b/src/corelib/global/qexpected_p.h @@ -16,8 +16,8 @@ // <http://creativecommons.org/publicdomain/zero/1.0/>. /// -#ifndef TL_EXPECTED_HPP -#define TL_EXPECTED_HPP +#ifndef Q23_TL_EXPECTED_HPP +#define Q23_TL_EXPECTED_HPP // // W A R N I N G @@ -30,9 +30,9 @@ // We mean it. // -#define TL_EXPECTED_VERSION_MAJOR 1 -#define TL_EXPECTED_VERSION_MINOR 1 -#define TL_EXPECTED_VERSION_PATCH 0 +#define Q23_TL_EXPECTED_VERSION_MAJOR 1 +#define Q23_TL_EXPECTED_VERSION_MINOR 1 +#define Q23_TL_EXPECTED_VERSION_PATCH 0 #include <QtCore/private/qglobal_p.h> #include <QtCore/qassert.h> @@ -44,45 +44,45 @@ #include <type_traits> #include <utility> -#define TL_ASSERT Q_ASSERT +#define Q23_TL_ASSERT Q_ASSERT #if defined(__EXCEPTIONS) || defined(_CPPUNWIND) -#define TL_EXPECTED_EXCEPTIONS_ENABLED +#define Q23_TL_EXPECTED_EXCEPTIONS_ENABLED #endif -#if defined(TL_EXPECTED_EXCEPTIONS_ENABLED) && defined(QT_NO_EXCEPTIONS) -# undef TL_EXPECTED_EXCEPTIONS_ENABLED +#if defined(Q23_TL_EXPECTED_EXCEPTIONS_ENABLED) && defined(QT_NO_EXCEPTIONS) +# undef Q23_TL_EXPECTED_EXCEPTIONS_ENABLED #endif #if (defined(_MSC_VER) && _MSC_VER == 1900) -#define TL_EXPECTED_MSVC2015 -#define TL_EXPECTED_MSVC2015_CONSTEXPR +#define Q23_TL_EXPECTED_MSVC2015 +#define Q23_TL_EXPECTED_MSVC2015_CONSTEXPR #else -#define TL_EXPECTED_MSVC2015_CONSTEXPR constexpr +#define Q23_TL_EXPECTED_MSVC2015_CONSTEXPR constexpr #endif #if (defined(__GNUC__) && __GNUC__ == 4 && __GNUC_MINOR__ <= 9 && \ !defined(__clang__)) -#define TL_EXPECTED_GCC49 +#define Q23_TL_EXPECTED_GCC49 #endif #if (defined(__GNUC__) && __GNUC__ == 5 && __GNUC_MINOR__ <= 4 && \ !defined(__clang__)) -#define TL_EXPECTED_GCC54 +#define Q23_TL_EXPECTED_GCC54 #endif #if (defined(__GNUC__) && __GNUC__ == 5 && __GNUC_MINOR__ <= 5 && \ !defined(__clang__)) -#define TL_EXPECTED_GCC55 +#define Q23_TL_EXPECTED_GCC55 #endif -#if !defined(TL_ASSERT) +#if !defined(Q23_TL_ASSERT) //can't have assert in constexpr in C++11 and GCC 4.9 has a compiler bug -#if (TL_CPLUSPLUS > 201103L) && !defined(TL_EXPECTED_GCC49) +#if (Q23_TL_CPLUSPLUS > 201103L) && !defined(Q23_TL_EXPECTED_GCC49) #include <cassert> -#define TL_ASSERT(x) assert(x) +#define Q23_TL_ASSERT(x) assert(x) #else -#define TL_ASSERT(x) +#define Q23_TL_ASSERT(x) #endif #endif @@ -90,22 +90,22 @@ !defined(__clang__)) // GCC < 5 doesn't support overloading on const&& for member functions -#define TL_EXPECTED_NO_CONSTRR +#define Q23_TL_EXPECTED_NO_CONSTRR // GCC < 5 doesn't support some standard C++11 type traits -#define TL_EXPECTED_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(T) \ +#define Q23_TL_EXPECTED_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(T) \ std::has_trivial_copy_constructor<T> -#define TL_EXPECTED_IS_TRIVIALLY_COPY_ASSIGNABLE(T) \ +#define Q23_TL_EXPECTED_IS_TRIVIALLY_COPY_ASSIGNABLE(T) \ std::has_trivial_copy_assign<T> // This one will be different for GCC 5.7 if it's ever supported -#define TL_EXPECTED_IS_TRIVIALLY_DESTRUCTIBLE(T) \ +#define Q23_TL_EXPECTED_IS_TRIVIALLY_DESTRUCTIBLE(T) \ std::is_trivially_destructible<T> // GCC 5 < v < 8 has a bug in is_trivially_copy_constructible which breaks // std::vector for non-copyable types #elif (defined(__GNUC__) && __GNUC__ < 8 && !defined(__clang__)) -#ifndef TL_GCC_LESS_8_TRIVIALLY_COPY_CONSTRUCTIBLE_MUTEX -#define TL_GCC_LESS_8_TRIVIALLY_COPY_CONSTRUCTIBLE_MUTEX +#ifndef Q23_TL_GCC_LESS_8_TRIVIALLY_COPY_CONSTRUCTIBLE_MUTEX +#define Q23_TL_GCC_LESS_8_TRIVIALLY_COPY_CONSTRUCTIBLE_MUTEX QT_BEGIN_NAMESPACE namespace q23 { namespace detail { @@ -121,50 +121,50 @@ struct is_trivially_copy_constructible<std::vector<T, A>> : std::false_type {}; QT_END_NAMESPACE #endif -#define TL_EXPECTED_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(T) \ +#define Q23_TL_EXPECTED_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(T) \ q23::detail::is_trivially_copy_constructible<T> -#define TL_EXPECTED_IS_TRIVIALLY_COPY_ASSIGNABLE(T) \ +#define Q23_TL_EXPECTED_IS_TRIVIALLY_COPY_ASSIGNABLE(T) \ std::is_trivially_copy_assignable<T> -#define TL_EXPECTED_IS_TRIVIALLY_DESTRUCTIBLE(T) \ +#define Q23_TL_EXPECTED_IS_TRIVIALLY_DESTRUCTIBLE(T) \ std::is_trivially_destructible<T> #else -#define TL_EXPECTED_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(T) \ +#define Q23_TL_EXPECTED_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(T) \ std::is_trivially_copy_constructible<T> -#define TL_EXPECTED_IS_TRIVIALLY_COPY_ASSIGNABLE(T) \ +#define Q23_TL_EXPECTED_IS_TRIVIALLY_COPY_ASSIGNABLE(T) \ std::is_trivially_copy_assignable<T> -#define TL_EXPECTED_IS_TRIVIALLY_DESTRUCTIBLE(T) \ +#define Q23_TL_EXPECTED_IS_TRIVIALLY_DESTRUCTIBLE(T) \ std::is_trivially_destructible<T> #endif #ifdef _MSVC_LANG -#define TL_CPLUSPLUS _MSVC_LANG +#define Q23_TL_CPLUSPLUS _MSVC_LANG #else -#define TL_CPLUSPLUS __cplusplus +#define Q23_TL_CPLUSPLUS __cplusplus #endif -#if TL_CPLUSPLUS > 201103L -#define TL_EXPECTED_CXX14 +#if Q23_TL_CPLUSPLUS > 201103L +#define Q23_TL_EXPECTED_CXX14 #endif -#ifdef TL_EXPECTED_GCC49 -#define TL_EXPECTED_GCC49_CONSTEXPR +#ifdef Q23_TL_EXPECTED_GCC49 +#define Q23_TL_EXPECTED_GCC49_CONSTEXPR #else -#define TL_EXPECTED_GCC49_CONSTEXPR constexpr +#define Q23_TL_EXPECTED_GCC49_CONSTEXPR constexpr #endif -#if (TL_CPLUSPLUS == 201103L || defined(TL_EXPECTED_MSVC2015) || \ - defined(TL_EXPECTED_GCC49)) -#define TL_EXPECTED_11_CONSTEXPR +#if (Q23_TL_CPLUSPLUS == 201103L || defined(Q23_TL_EXPECTED_MSVC2015) || \ + defined(Q23_TL_EXPECTED_GCC49)) +#define Q23_TL_EXPECTED_11_CONSTEXPR #else -#define TL_EXPECTED_11_CONSTEXPR constexpr +#define Q23_TL_EXPECTED_11_CONSTEXPR constexpr #endif QT_BEGIN_NAMESPACE namespace q23 { template <class T, class E> class expected; -#ifndef TL_MONOSTATE_INPLACE_MUTEX -#define TL_MONOSTATE_INPLACE_MUTEX +#ifndef Q23_TL_MONOSTATE_INPLACE_MUTEX +#define Q23_TL_MONOSTATE_INPLACE_MUTEX class monostate {}; struct in_place_t { @@ -196,8 +196,8 @@ public: : m_val(l, std::forward<Args>(args)...) {} constexpr const E &error() const & { return m_val; } - TL_EXPECTED_11_CONSTEXPR E &error() & { return m_val; } - TL_EXPECTED_11_CONSTEXPR E &&error() && { return std::move(m_val); } + Q23_TL_EXPECTED_11_CONSTEXPR E &error() & { return m_val; } + Q23_TL_EXPECTED_11_CONSTEXPR E &&error() && { return std::move(m_val); } constexpr const E &&error() const && { return std::move(m_val); } private: @@ -245,8 +245,8 @@ static constexpr unexpect_t unexpect{}; namespace detail { template <typename E> -[[noreturn]] TL_EXPECTED_11_CONSTEXPR void throw_exception(E &&e) { -#ifdef TL_EXPECTED_EXCEPTIONS_ENABLED +[[noreturn]] Q23_TL_EXPECTED_11_CONSTEXPR void throw_exception(E &&e) { +#ifdef Q23_TL_EXPECTED_EXCEPTIONS_ENABLED throw std::forward<E>(e); #else (void)e; @@ -258,8 +258,8 @@ template <typename E> #endif } -#ifndef TL_TRAITS_MUTEX -#define TL_TRAITS_MUTEX +#ifndef Q23_TL_TRAITS_MUTEX +#define Q23_TL_TRAITS_MUTEX // C++14-style aliases for brevity template <class T> using remove_const_t = typename std::remove_const<T>::type; template <class T> @@ -278,13 +278,13 @@ struct conjunction<B, Bs...> : std::conditional<bool(B::value), conjunction<Bs...>, B>::type {}; #if defined(_LIBCPP_VERSION) && __cplusplus == 201103L -#define TL_TRAITS_LIBCXX_MEM_FN_WORKAROUND +#define Q23_TL_TRAITS_LIBCXX_MEM_FN_WORKAROUND #endif // In C++11 mode, there's an issue in libc++'s std::mem_fn // which results in a hard-error when using it in a noexcept expression // in some cases. This is a check to workaround the common failing case. -#ifdef TL_TRAITS_LIBCXX_MEM_FN_WORKAROUND +#ifdef Q23_TL_TRAITS_LIBCXX_MEM_FN_WORKAROUND template <class T> struct is_pointer_to_non_const_member_func : std::false_type {}; template <class T, class Ret, class... Args> @@ -315,7 +315,7 @@ template <class T> struct is_const_or_const_ref<T const> : std::true_type {}; // https://stackoverflow.com/questions/38288042/c11-14-invoke-workaround template < typename Fn, typename... Args, -#ifdef TL_TRAITS_LIBCXX_MEM_FN_WORKAROUND +#ifdef Q23_TL_TRAITS_LIBCXX_MEM_FN_WORKAROUND typename = enable_if_t<!(is_pointer_to_non_const_member_func<Fn>::value && is_const_or_const_ref<Args...>::value)>, #endif @@ -574,7 +574,7 @@ template <class T, class E> struct expected_storage_base<T, E, true, true> { // T is trivial, E is not. template <class T, class E> struct expected_storage_base<T, E, true, false> { constexpr expected_storage_base() : m_val(T{}), m_has_val(true) {} - TL_EXPECTED_MSVC2015_CONSTEXPR expected_storage_base(no_init_t) + Q23_TL_EXPECTED_MSVC2015_CONSTEXPR expected_storage_base(no_init_t) : m_no_init(), m_has_val(false) {} template <class... Args, @@ -675,7 +675,7 @@ template <class E> struct expected_storage_base<void, E, false, true> { #if __GNUC__ <= 5 //no constexpr for GCC 4/5 bug #else - TL_EXPECTED_MSVC2015_CONSTEXPR + Q23_TL_EXPECTED_MSVC2015_CONSTEXPR #endif expected_storage_base() : m_has_val(true) {} @@ -770,7 +770,7 @@ struct expected_operations_base : expected_storage_base<T, E> { this->m_has_val = false; } -#ifdef TL_EXPECTED_EXCEPTIONS_ENABLED +#ifdef Q23_TL_EXPECTED_EXCEPTIONS_ENABLED // These assign overloads ensure that the most efficient assignment // implementation is used while maintaining the strong exception guarantee. @@ -820,7 +820,7 @@ struct expected_operations_base : expected_storage_base<T, E> { auto tmp = std::move(geterr()); geterr().~unexpected<E>(); -#ifdef TL_EXPECTED_EXCEPTIONS_ENABLED +#ifdef Q23_TL_EXPECTED_EXCEPTIONS_ENABLED try { construct(rhs.get()); } catch (...) { @@ -855,7 +855,7 @@ struct expected_operations_base : expected_storage_base<T, E> { if (!this->m_has_val && rhs.m_has_val) { auto tmp = std::move(geterr()); geterr().~unexpected<E>(); -#ifdef TL_EXPECTED_EXCEPTIONS_ENABLED +#ifdef Q23_TL_EXPECTED_EXCEPTIONS_ENABLED try { construct(std::move(rhs).get()); } catch (...) { @@ -911,27 +911,27 @@ struct expected_operations_base : expected_storage_base<T, E> { bool has_value() const { return this->m_has_val; } - TL_EXPECTED_11_CONSTEXPR T &get() & { return this->m_val; } + Q23_TL_EXPECTED_11_CONSTEXPR T &get() & { return this->m_val; } constexpr const T &get() const & { return this->m_val; } - TL_EXPECTED_11_CONSTEXPR T &&get() && { return std::move(this->m_val); } -#ifndef TL_EXPECTED_NO_CONSTRR + Q23_TL_EXPECTED_11_CONSTEXPR T &&get() && { return std::move(this->m_val); } +#ifndef Q23_TL_EXPECTED_NO_CONSTRR constexpr const T &&get() const && { return std::move(this->m_val); } #endif - TL_EXPECTED_11_CONSTEXPR unexpected<E> &geterr() & { + Q23_TL_EXPECTED_11_CONSTEXPR unexpected<E> &geterr() & { return this->m_unexpect; } constexpr const unexpected<E> &geterr() const & { return this->m_unexpect; } - TL_EXPECTED_11_CONSTEXPR unexpected<E> &&geterr() && { + Q23_TL_EXPECTED_11_CONSTEXPR unexpected<E> &&geterr() && { return std::move(this->m_unexpect); } -#ifndef TL_EXPECTED_NO_CONSTRR +#ifndef Q23_TL_EXPECTED_NO_CONSTRR constexpr const unexpected<E> &&geterr() const && { return std::move(this->m_unexpect); } #endif - TL_EXPECTED_11_CONSTEXPR void destroy_val() { get().~T(); } + Q23_TL_EXPECTED_11_CONSTEXPR void destroy_val() { get().~T(); } }; // This base class provides some handy member functions which can be used in @@ -971,20 +971,20 @@ struct expected_operations_base<void, E> : expected_storage_base<void, E> { bool has_value() const { return this->m_has_val; } - TL_EXPECTED_11_CONSTEXPR unexpected<E> &geterr() & { + Q23_TL_EXPECTED_11_CONSTEXPR unexpected<E> &geterr() & { return this->m_unexpect; } constexpr const unexpected<E> &geterr() const & { return this->m_unexpect; } - TL_EXPECTED_11_CONSTEXPR unexpected<E> &&geterr() && { + Q23_TL_EXPECTED_11_CONSTEXPR unexpected<E> &&geterr() && { return std::move(this->m_unexpect); } -#ifndef TL_EXPECTED_NO_CONSTRR +#ifndef Q23_TL_EXPECTED_NO_CONSTRR constexpr const unexpected<E> &&geterr() const && { return std::move(this->m_unexpect); } #endif - TL_EXPECTED_11_CONSTEXPR void destroy_val() { + Q23_TL_EXPECTED_11_CONSTEXPR void destroy_val() { // no-op } }; @@ -992,8 +992,8 @@ struct expected_operations_base<void, E> : expected_storage_base<void, E> { // This class manages conditionally having a trivial copy constructor // This specialization is for when T and E are trivially copy constructible template <class T, class E, - bool = is_void_or<T, TL_EXPECTED_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(T)>:: - value &&TL_EXPECTED_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(E)::value, + bool = is_void_or<T, Q23_TL_EXPECTED_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(T)>:: + value &&Q23_TL_EXPECTED_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(E)::value, bool = (is_copy_constructible_or_void<T>::value && std::is_copy_constructible<E>::value)> struct expected_copy_base : expected_operations_base<T, E> { @@ -1025,7 +1025,7 @@ struct expected_copy_base<T, E, false, true> : expected_operations_base<T, E> { // doesn't implement an analogue to std::is_trivially_move_constructible. We // have to make do with a non-trivial move constructor even if T is trivially // move constructible -#ifndef TL_EXPECTED_GCC49 +#ifndef Q23_TL_EXPECTED_GCC49 template <class T, class E, bool = is_void_or<T, std::is_trivially_move_constructible<T>>::value &&std::is_trivially_move_constructible<E>::value> @@ -1058,12 +1058,12 @@ struct expected_move_base<T, E, false> : expected_copy_base<T, E> { // This class manages conditionally having a trivial copy assignment operator template <class T, class E, bool = is_void_or< - T, conjunction<TL_EXPECTED_IS_TRIVIALLY_COPY_ASSIGNABLE(T), - TL_EXPECTED_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(T), - TL_EXPECTED_IS_TRIVIALLY_DESTRUCTIBLE(T)>>::value - &&TL_EXPECTED_IS_TRIVIALLY_COPY_ASSIGNABLE(E)::value - &&TL_EXPECTED_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(E)::value - &&TL_EXPECTED_IS_TRIVIALLY_DESTRUCTIBLE(E)::value, + T, conjunction<Q23_TL_EXPECTED_IS_TRIVIALLY_COPY_ASSIGNABLE(T), + Q23_TL_EXPECTED_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(T), + Q23_TL_EXPECTED_IS_TRIVIALLY_DESTRUCTIBLE(T)>>::value + &&Q23_TL_EXPECTED_IS_TRIVIALLY_COPY_ASSIGNABLE(E)::value + &&Q23_TL_EXPECTED_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(E)::value + &&Q23_TL_EXPECTED_IS_TRIVIALLY_DESTRUCTIBLE(E)::value, bool = (is_copy_constructible_or_void<T>::value && std::is_copy_constructible<E>::value && is_copy_assignable_or_void<T>::value && @@ -1093,7 +1093,7 @@ struct expected_copy_assign_base<T, E, false, true> : expected_move_base<T, E> { // doesn't implement an analogue to std::is_trivially_move_assignable. We have // to make do with a non-trivial move assignment operator even if T is trivially // move assignable -#ifndef TL_EXPECTED_GCC49 +#ifndef Q23_TL_EXPECTED_GCC49 template <class T, class E, bool = is_void_or<T, conjunction<std::is_trivially_destructible<T>, @@ -1330,10 +1330,10 @@ class expected : private detail::expected_move_assign_base<T, E>, template <class U = T, detail::enable_if_t<!std::is_void<U>::value> * = nullptr> - TL_EXPECTED_11_CONSTEXPR U &val() { + Q23_TL_EXPECTED_11_CONSTEXPR U &val() { return this->m_val; } - TL_EXPECTED_11_CONSTEXPR unexpected<E> &err() { return this->m_unexpect; } + Q23_TL_EXPECTED_11_CONSTEXPR unexpected<E> &err() { return this->m_unexpect; } template <class U = T, detail::enable_if_t<!std::is_void<U>::value> * = nullptr> @@ -1350,19 +1350,19 @@ public: typedef E error_type; typedef unexpected<E> unexpected_type; -#if defined(TL_EXPECTED_CXX14) && !defined(TL_EXPECTED_GCC49) && \ - !defined(TL_EXPECTED_GCC54) && !defined(TL_EXPECTED_GCC55) - template <class F> TL_EXPECTED_11_CONSTEXPR auto and_then(F &&f) & { +#if defined(Q23_TL_EXPECTED_CXX14) && !defined(Q23_TL_EXPECTED_GCC49) && \ + !defined(Q23_TL_EXPECTED_GCC54) && !defined(Q23_TL_EXPECTED_GCC55) + template <class F> Q23_TL_EXPECTED_11_CONSTEXPR auto and_then(F &&f) & { return and_then_impl(*this, std::forward<F>(f)); } - template <class F> TL_EXPECTED_11_CONSTEXPR auto and_then(F &&f) && { + template <class F> Q23_TL_EXPECTED_11_CONSTEXPR auto and_then(F &&f) && { return and_then_impl(std::move(*this), std::forward<F>(f)); } template <class F> constexpr auto and_then(F &&f) const & { return and_then_impl(*this, std::forward<F>(f)); } -#ifndef TL_EXPECTED_NO_CONSTRR +#ifndef Q23_TL_EXPECTED_NO_CONSTRR template <class F> constexpr auto and_then(F &&f) const && { return and_then_impl(std::move(*this), std::forward<F>(f)); } @@ -1370,13 +1370,13 @@ public: #else template <class F> - TL_EXPECTED_11_CONSTEXPR auto + Q23_TL_EXPECTED_11_CONSTEXPR auto and_then(F &&f) & -> decltype(and_then_impl(std::declval<expected &>(), std::forward<F>(f))) { return and_then_impl(*this, std::forward<F>(f)); } template <class F> - TL_EXPECTED_11_CONSTEXPR auto + Q23_TL_EXPECTED_11_CONSTEXPR auto and_then(F &&f) && -> decltype(and_then_impl(std::declval<expected &&>(), std::forward<F>(f))) { return and_then_impl(std::move(*this), std::forward<F>(f)); @@ -1387,7 +1387,7 @@ public: return and_then_impl(*this, std::forward<F>(f)); } -#ifndef TL_EXPECTED_NO_CONSTRR +#ifndef Q23_TL_EXPECTED_NO_CONSTRR template <class F> constexpr auto and_then(F &&f) const && -> decltype(and_then_impl( std::declval<expected const &&>(), std::forward<F>(f))) { @@ -1396,12 +1396,12 @@ public: #endif #endif -#if defined(TL_EXPECTED_CXX14) && !defined(TL_EXPECTED_GCC49) && \ - !defined(TL_EXPECTED_GCC54) && !defined(TL_EXPECTED_GCC55) - template <class F> TL_EXPECTED_11_CONSTEXPR auto map(F &&f) & { +#if defined(Q23_TL_EXPECTED_CXX14) && !defined(Q23_TL_EXPECTED_GCC49) && \ + !defined(Q23_TL_EXPECTED_GCC54) && !defined(Q23_TL_EXPECTED_GCC55) + template <class F> Q23_TL_EXPECTED_11_CONSTEXPR auto map(F &&f) & { return expected_map_impl(*this, std::forward<F>(f)); } - template <class F> TL_EXPECTED_11_CONSTEXPR auto map(F &&f) && { + template <class F> Q23_TL_EXPECTED_11_CONSTEXPR auto map(F &&f) && { return expected_map_impl(std::move(*this), std::forward<F>(f)); } template <class F> constexpr auto map(F &&f) const & { @@ -1412,13 +1412,13 @@ public: } #else template <class F> - TL_EXPECTED_11_CONSTEXPR decltype(expected_map_impl( + Q23_TL_EXPECTED_11_CONSTEXPR decltype(expected_map_impl( std::declval<expected &>(), std::declval<F &&>())) map(F &&f) & { return expected_map_impl(*this, std::forward<F>(f)); } template <class F> - TL_EXPECTED_11_CONSTEXPR decltype(expected_map_impl(std::declval<expected>(), + Q23_TL_EXPECTED_11_CONSTEXPR decltype(expected_map_impl(std::declval<expected>(), std::declval<F &&>())) map(F &&f) && { return expected_map_impl(std::move(*this), std::forward<F>(f)); @@ -1430,7 +1430,7 @@ public: return expected_map_impl(*this, std::forward<F>(f)); } -#ifndef TL_EXPECTED_NO_CONSTRR +#ifndef Q23_TL_EXPECTED_NO_CONSTRR template <class F> constexpr decltype(expected_map_impl(std::declval<const expected &&>(), std::declval<F &&>())) @@ -1440,12 +1440,12 @@ public: #endif #endif -#if defined(TL_EXPECTED_CXX14) && !defined(TL_EXPECTED_GCC49) && \ - !defined(TL_EXPECTED_GCC54) && !defined(TL_EXPECTED_GCC55) - template <class F> TL_EXPECTED_11_CONSTEXPR auto transform(F &&f) & { +#if defined(Q23_TL_EXPECTED_CXX14) && !defined(Q23_TL_EXPECTED_GCC49) && \ + !defined(Q23_TL_EXPECTED_GCC54) && !defined(Q23_TL_EXPECTED_GCC55) + template <class F> Q23_TL_EXPECTED_11_CONSTEXPR auto transform(F &&f) & { return expected_map_impl(*this, std::forward<F>(f)); } - template <class F> TL_EXPECTED_11_CONSTEXPR auto transform(F &&f) && { + template <class F> Q23_TL_EXPECTED_11_CONSTEXPR auto transform(F &&f) && { return expected_map_impl(std::move(*this), std::forward<F>(f)); } template <class F> constexpr auto transform(F &&f) const & { @@ -1456,13 +1456,13 @@ public: } #else template <class F> - TL_EXPECTED_11_CONSTEXPR decltype(expected_map_impl( + Q23_TL_EXPECTED_11_CONSTEXPR decltype(expected_map_impl( std::declval<expected &>(), std::declval<F &&>())) transform(F &&f) & { return expected_map_impl(*this, std::forward<F>(f)); } template <class F> - TL_EXPECTED_11_CONSTEXPR decltype(expected_map_impl(std::declval<expected>(), + Q23_TL_EXPECTED_11_CONSTEXPR decltype(expected_map_impl(std::declval<expected>(), std::declval<F &&>())) transform(F &&f) && { return expected_map_impl(std::move(*this), std::forward<F>(f)); @@ -1474,7 +1474,7 @@ public: return expected_map_impl(*this, std::forward<F>(f)); } -#ifndef TL_EXPECTED_NO_CONSTRR +#ifndef Q23_TL_EXPECTED_NO_CONSTRR template <class F> constexpr decltype(expected_map_impl(std::declval<const expected &&>(), std::declval<F &&>())) @@ -1484,12 +1484,12 @@ public: #endif #endif -#if defined(TL_EXPECTED_CXX14) && !defined(TL_EXPECTED_GCC49) && \ - !defined(TL_EXPECTED_GCC54) && !defined(TL_EXPECTED_GCC55) - template <class F> TL_EXPECTED_11_CONSTEXPR auto map_error(F &&f) & { +#if defined(Q23_TL_EXPECTED_CXX14) && !defined(Q23_TL_EXPECTED_GCC49) && \ + !defined(Q23_TL_EXPECTED_GCC54) && !defined(Q23_TL_EXPECTED_GCC55) + template <class F> Q23_TL_EXPECTED_11_CONSTEXPR auto map_error(F &&f) & { return map_error_impl(*this, std::forward<F>(f)); } - template <class F> TL_EXPECTED_11_CONSTEXPR auto map_error(F &&f) && { + template <class F> Q23_TL_EXPECTED_11_CONSTEXPR auto map_error(F &&f) && { return map_error_impl(std::move(*this), std::forward<F>(f)); } template <class F> constexpr auto map_error(F &&f) const & { @@ -1500,13 +1500,13 @@ public: } #else template <class F> - TL_EXPECTED_11_CONSTEXPR decltype(map_error_impl(std::declval<expected &>(), + Q23_TL_EXPECTED_11_CONSTEXPR decltype(map_error_impl(std::declval<expected &>(), std::declval<F &&>())) map_error(F &&f) & { return map_error_impl(*this, std::forward<F>(f)); } template <class F> - TL_EXPECTED_11_CONSTEXPR decltype(map_error_impl(std::declval<expected &&>(), + Q23_TL_EXPECTED_11_CONSTEXPR decltype(map_error_impl(std::declval<expected &&>(), std::declval<F &&>())) map_error(F &&f) && { return map_error_impl(std::move(*this), std::forward<F>(f)); @@ -1518,7 +1518,7 @@ public: return map_error_impl(*this, std::forward<F>(f)); } -#ifndef TL_EXPECTED_NO_CONSTRR +#ifndef Q23_TL_EXPECTED_NO_CONSTRR template <class F> constexpr decltype(map_error_impl(std::declval<const expected &&>(), std::declval<F &&>())) @@ -1527,12 +1527,12 @@ public: } #endif #endif -#if defined(TL_EXPECTED_CXX14) && !defined(TL_EXPECTED_GCC49) && \ - !defined(TL_EXPECTED_GCC54) && !defined(TL_EXPECTED_GCC55) - template <class F> TL_EXPECTED_11_CONSTEXPR auto transform_error(F &&f) & { +#if defined(Q23_TL_EXPECTED_CXX14) && !defined(Q23_TL_EXPECTED_GCC49) && \ + !defined(Q23_TL_EXPECTED_GCC54) && !defined(Q23_TL_EXPECTED_GCC55) + template <class F> Q23_TL_EXPECTED_11_CONSTEXPR auto transform_error(F &&f) & { return map_error_impl(*this, std::forward<F>(f)); } - template <class F> TL_EXPECTED_11_CONSTEXPR auto transform_error(F &&f) && { + template <class F> Q23_TL_EXPECTED_11_CONSTEXPR auto transform_error(F &&f) && { return map_error_impl(std::move(*this), std::forward<F>(f)); } template <class F> constexpr auto transform_error(F &&f) const & { @@ -1543,13 +1543,13 @@ public: } #else template <class F> - TL_EXPECTED_11_CONSTEXPR decltype(map_error_impl(std::declval<expected &>(), + Q23_TL_EXPECTED_11_CONSTEXPR decltype(map_error_impl(std::declval<expected &>(), std::declval<F &&>())) transform_error(F &&f) & { return map_error_impl(*this, std::forward<F>(f)); } template <class F> - TL_EXPECTED_11_CONSTEXPR decltype(map_error_impl(std::declval<expected &&>(), + Q23_TL_EXPECTED_11_CONSTEXPR decltype(map_error_impl(std::declval<expected &&>(), std::declval<F &&>())) transform_error(F &&f) && { return map_error_impl(std::move(*this), std::forward<F>(f)); @@ -1561,7 +1561,7 @@ public: return map_error_impl(*this, std::forward<F>(f)); } -#ifndef TL_EXPECTED_NO_CONSTRR +#ifndef Q23_TL_EXPECTED_NO_CONSTRR template <class F> constexpr decltype(map_error_impl(std::declval<const expected &&>(), std::declval<F &&>())) @@ -1570,11 +1570,11 @@ public: } #endif #endif - template <class F> expected TL_EXPECTED_11_CONSTEXPR or_else(F &&f) & { + template <class F> expected Q23_TL_EXPECTED_11_CONSTEXPR or_else(F &&f) & { return or_else_impl(*this, std::forward<F>(f)); } - template <class F> expected TL_EXPECTED_11_CONSTEXPR or_else(F &&f) && { + template <class F> expected Q23_TL_EXPECTED_11_CONSTEXPR or_else(F &&f) && { return or_else_impl(std::move(*this), std::forward<F>(f)); } @@ -1582,7 +1582,7 @@ public: return or_else_impl(*this, std::forward<F>(f)); } -#ifndef TL_EXPECTED_NO_CONSTRR +#ifndef Q23_TL_EXPECTED_NO_CONSTRR template <class F> expected constexpr or_else(F &&f) const && { return or_else_impl(std::move(*this), std::forward<F>(f)); } @@ -1664,7 +1664,7 @@ public: nullptr, detail::expected_enable_from_other<T, E, U, G, const U &, const G &> * = nullptr> - explicit TL_EXPECTED_11_CONSTEXPR expected(const expected<U, G> &rhs) + explicit Q23_TL_EXPECTED_11_CONSTEXPR expected(const expected<U, G> &rhs) : ctor_base(detail::default_constructor_tag{}) { if (rhs.has_value()) { this->construct(*rhs); @@ -1679,7 +1679,7 @@ public: nullptr, detail::expected_enable_from_other<T, E, U, G, const U &, const G &> * = nullptr> - TL_EXPECTED_11_CONSTEXPR expected(const expected<U, G> &rhs) + Q23_TL_EXPECTED_11_CONSTEXPR expected(const expected<U, G> &rhs) : ctor_base(detail::default_constructor_tag{}) { if (rhs.has_value()) { this->construct(*rhs); @@ -1693,7 +1693,7 @@ public: detail::enable_if_t<!(std::is_convertible<U &&, T>::value && std::is_convertible<G &&, E>::value)> * = nullptr, detail::expected_enable_from_other<T, E, U, G, U &&, G &&> * = nullptr> - explicit TL_EXPECTED_11_CONSTEXPR expected(expected<U, G> &&rhs) + explicit Q23_TL_EXPECTED_11_CONSTEXPR expected(expected<U, G> &&rhs) : ctor_base(detail::default_constructor_tag{}) { if (rhs.has_value()) { this->construct(std::move(*rhs)); @@ -1707,7 +1707,7 @@ public: detail::enable_if_t<(std::is_convertible<U &&, T>::value && std::is_convertible<G &&, E>::value)> * = nullptr, detail::expected_enable_from_other<T, E, U, G, U &&, G &&> * = nullptr> - TL_EXPECTED_11_CONSTEXPR expected(expected<U, G> &&rhs) + Q23_TL_EXPECTED_11_CONSTEXPR expected(expected<U, G> &&rhs) : ctor_base(detail::default_constructor_tag{}) { if (rhs.has_value()) { this->construct(std::move(*rhs)); @@ -1720,14 +1720,14 @@ public: class U = T, detail::enable_if_t<!std::is_convertible<U &&, T>::value> * = nullptr, detail::expected_enable_forward_value<T, E, U> * = nullptr> - explicit TL_EXPECTED_MSVC2015_CONSTEXPR expected(U &&v) + explicit Q23_TL_EXPECTED_MSVC2015_CONSTEXPR expected(U &&v) : expected(in_place, std::forward<U>(v)) {} template < class U = T, detail::enable_if_t<std::is_convertible<U &&, T>::value> * = nullptr, detail::expected_enable_forward_value<T, E, U> * = nullptr> - TL_EXPECTED_MSVC2015_CONSTEXPR expected(U &&v) + Q23_TL_EXPECTED_MSVC2015_CONSTEXPR expected(U &&v) : expected(in_place, std::forward<U>(v)) {} template < @@ -1773,7 +1773,7 @@ public: auto tmp = std::move(err()); err().~unexpected<E>(); -#ifdef TL_EXPECTED_EXCEPTIONS_ENABLED +#ifdef Q23_TL_EXPECTED_EXCEPTIONS_ENABLED try { ::new (valptr()) T(std::forward<U>(v)); this->m_has_val = true; @@ -1842,7 +1842,7 @@ public: auto tmp = std::move(err()); err().~unexpected<E>(); -#ifdef TL_EXPECTED_EXCEPTIONS_ENABLED +#ifdef Q23_TL_EXPECTED_EXCEPTIONS_ENABLED try { ::new (valptr()) T(std::forward<Args>(args)...); this->m_has_val = true; @@ -1882,7 +1882,7 @@ public: auto tmp = std::move(err()); err().~unexpected<E>(); -#ifdef TL_EXPECTED_EXCEPTIONS_ENABLED +#ifdef Q23_TL_EXPECTED_EXCEPTIONS_ENABLED try { ::new (valptr()) T(il, std::forward<Args>(args)...); this->m_has_val = true; @@ -1943,7 +1943,7 @@ private: move_constructing_e_can_throw) { auto temp = std::move(val()); val().~T(); -#ifdef TL_EXPECTED_EXCEPTIONS_ENABLED +#ifdef Q23_TL_EXPECTED_EXCEPTIONS_ENABLED try { ::new (errptr()) unexpected_type(std::move(rhs.err())); rhs.err().~unexpected_type(); @@ -1966,7 +1966,7 @@ private: e_is_nothrow_move_constructible) { auto temp = std::move(rhs.err()); rhs.err().~unexpected_type(); -#ifdef TL_EXPECTED_EXCEPTIONS_ENABLED +#ifdef Q23_TL_EXPECTED_EXCEPTIONS_ENABLED try { ::new (rhs.valptr()) T(std::move(val())); val().~T(); @@ -2008,36 +2008,36 @@ public: } constexpr const T *operator->() const { - TL_ASSERT(has_value()); + Q23_TL_ASSERT(has_value()); return valptr(); } - TL_EXPECTED_11_CONSTEXPR T *operator->() { - TL_ASSERT(has_value()); + Q23_TL_EXPECTED_11_CONSTEXPR T *operator->() { + Q23_TL_ASSERT(has_value()); return valptr(); } template <class U = T, detail::enable_if_t<!std::is_void<U>::value> * = nullptr> constexpr const U &operator*() const & { - TL_ASSERT(has_value()); + Q23_TL_ASSERT(has_value()); return val(); } template <class U = T, detail::enable_if_t<!std::is_void<U>::value> * = nullptr> - TL_EXPECTED_11_CONSTEXPR U &operator*() & { - TL_ASSERT(has_value()); + Q23_TL_EXPECTED_11_CONSTEXPR U &operator*() & { + Q23_TL_ASSERT(has_value()); return val(); } template <class U = T, detail::enable_if_t<!std::is_void<U>::value> * = nullptr> constexpr const U &&operator*() const && { - TL_ASSERT(has_value()); + Q23_TL_ASSERT(has_value()); return std::move(val()); } template <class U = T, detail::enable_if_t<!std::is_void<U>::value> * = nullptr> - TL_EXPECTED_11_CONSTEXPR U &&operator*() && { - TL_ASSERT(has_value()); + Q23_TL_EXPECTED_11_CONSTEXPR U &&operator*() && { + Q23_TL_ASSERT(has_value()); return std::move(val()); } @@ -2046,47 +2046,47 @@ public: template <class U = T, detail::enable_if_t<!std::is_void<U>::value> * = nullptr> - TL_EXPECTED_11_CONSTEXPR const U &value() const & { + Q23_TL_EXPECTED_11_CONSTEXPR const U &value() const & { if (!has_value()) detail::throw_exception(bad_expected_access<E>(err().error())); return val(); } template <class U = T, detail::enable_if_t<!std::is_void<U>::value> * = nullptr> - TL_EXPECTED_11_CONSTEXPR U &value() & { + Q23_TL_EXPECTED_11_CONSTEXPR U &value() & { if (!has_value()) detail::throw_exception(bad_expected_access<E>(err().error())); return val(); } template <class U = T, detail::enable_if_t<!std::is_void<U>::value> * = nullptr> - TL_EXPECTED_11_CONSTEXPR const U &&value() const && { + Q23_TL_EXPECTED_11_CONSTEXPR const U &&value() const && { if (!has_value()) detail::throw_exception(bad_expected_access<E>(std::move(err()).error())); return std::move(val()); } template <class U = T, detail::enable_if_t<!std::is_void<U>::value> * = nullptr> - TL_EXPECTED_11_CONSTEXPR U &&value() && { + Q23_TL_EXPECTED_11_CONSTEXPR U &&value() && { if (!has_value()) detail::throw_exception(bad_expected_access<E>(std::move(err()).error())); return std::move(val()); } constexpr const E &error() const & { - TL_ASSERT(!has_value()); + Q23_TL_ASSERT(!has_value()); return err().error(); } - TL_EXPECTED_11_CONSTEXPR E &error() & { - TL_ASSERT(!has_value()); + Q23_TL_EXPECTED_11_CONSTEXPR E &error() & { + Q23_TL_ASSERT(!has_value()); return err().error(); } constexpr const E &&error() const && { - TL_ASSERT(!has_value()); + Q23_TL_ASSERT(!has_value()); return std::move(err().error()); } - TL_EXPECTED_11_CONSTEXPR E &&error() && { - TL_ASSERT(!has_value()); + Q23_TL_EXPECTED_11_CONSTEXPR E &&error() && { + Q23_TL_ASSERT(!has_value()); return std::move(err().error()); } @@ -2096,7 +2096,7 @@ public: "T must be copy-constructible and convertible to from U&&"); return bool(*this) ? **this : static_cast<T>(std::forward<U>(v)); } - template <class U> TL_EXPECTED_11_CONSTEXPR T value_or(U &&v) && { + template <class U> Q23_TL_EXPECTED_11_CONSTEXPR T value_or(U &&v) && { static_assert(std::is_move_constructible<T>::value && std::is_convertible<U &&, T>::value, "T must be move-constructible and convertible to from U&&"); @@ -2109,7 +2109,7 @@ template <class Exp> using exp_t = typename detail::decay_t<Exp>::value_type; template <class Exp> using err_t = typename detail::decay_t<Exp>::error_type; template <class Exp, class Ret> using ret_t = expected<Ret, err_t<Exp>>; -#ifdef TL_EXPECTED_CXX14 +#ifdef Q23_TL_EXPECTED_CXX14 template <class Exp, class F, detail::enable_if_t<!std::is_void<exp_t<Exp>>::value> * = nullptr, class Ret = decltype(detail::invoke(std::declval<F>(), @@ -2156,7 +2156,7 @@ constexpr auto and_then_impl(Exp &&exp, F &&f) -> Ret { } #endif -#ifdef TL_EXPECTED_CXX14 +#ifdef Q23_TL_EXPECTED_CXX14 template <class Exp, class F, detail::enable_if_t<!std::is_void<exp_t<Exp>>::value> * = nullptr, class Ret = decltype(detail::invoke(std::declval<F>(), @@ -2266,8 +2266,8 @@ auto expected_map_impl(Exp &&exp, F &&f) -> expected<void, err_t<Exp>> { } #endif -#if defined(TL_EXPECTED_CXX14) && !defined(TL_EXPECTED_GCC49) && \ - !defined(TL_EXPECTED_GCC54) && !defined(TL_EXPECTED_GCC55) +#if defined(Q23_TL_EXPECTED_CXX14) && !defined(Q23_TL_EXPECTED_GCC49) && \ + !defined(Q23_TL_EXPECTED_GCC54) && !defined(Q23_TL_EXPECTED_GCC55) template <class Exp, class F, detail::enable_if_t<!std::is_void<exp_t<Exp>>::value> * = nullptr, class Ret = decltype(detail::invoke(std::declval<F>(), @@ -2382,7 +2382,7 @@ auto map_error_impl(Exp &&exp, F &&f) -> expected<exp_t<Exp>, monostate> { } #endif -#ifdef TL_EXPECTED_CXX14 +#ifdef Q23_TL_EXPECTED_CXX14 template <class Exp, class F, class Ret = decltype(detail::invoke(std::declval<F>(), std::declval<Exp>().error())), diff --git a/src/corelib/global/qnumeric.h b/src/corelib/global/qnumeric.h index 48e736ff124..db32ae73556 100644 --- a/src/corelib/global/qnumeric.h +++ b/src/corelib/global/qnumeric.h @@ -627,6 +627,27 @@ QT_WARNING_DISABLE_FLOAT_COMPARE QT_WARNING_POP +namespace QtPrivate { +/* + A version of qFuzzyCompare that works for all values (qFuzzyCompare() + requires that neither argument is numerically 0). + + It's private because we need a fix for the many qFuzzyCompare() uses that + ignore the precondition, even for older branches. + + See QTBUG-142020 for discussion of a longer-term solution. +*/ +template <typename T, typename S> +[[nodiscard]] constexpr bool fuzzyCompare(const T &lhs, const S &rhs) noexcept +{ + static_assert(noexcept(qIsNull(lhs) && qIsNull(rhs) && qFuzzyIsNull(lhs - rhs) && qFuzzyCompare(lhs, rhs)), + "The operations qIsNull(), qFuzzyIsNull() and qFuzzyCompare() must be noexcept " + "for both argument types!"); + return qIsNull(lhs) || qIsNull(rhs) ? qFuzzyIsNull(lhs - rhs) : qFuzzyCompare(lhs, rhs); +} +} // namespace QtPrivate + + inline int qIntCast(double f) { return int(f); } inline int qIntCast(float f) { return int(f); } diff --git a/src/corelib/global/qoperatingsystemversion.cpp b/src/corelib/global/qoperatingsystemversion.cpp index d27a71526d8..32364fa8eb4 100644 --- a/src/corelib/global/qoperatingsystemversion.cpp +++ b/src/corelib/global/qoperatingsystemversion.cpp @@ -486,6 +486,12 @@ const QOperatingSystemVersionBase QOperatingSystemVersion::Windows11_22H2; */ /*! + \variable QOperatingSystemVersion::Windows11_25H2 + \brief a version corresponding to Windows 11 Version 25H2 (version 10.0.26200). + \since 6.11 + */ + +/*! \variable QOperatingSystemVersion::OSXMavericks \brief a version corresponding to OS X Mavericks (version 10.9). \since 5.9 diff --git a/src/corelib/global/qoperatingsystemversion.h b/src/corelib/global/qoperatingsystemversion.h index 75801a7ddcf..99866692f8c 100644 --- a/src/corelib/global/qoperatingsystemversion.h +++ b/src/corelib/global/qoperatingsystemversion.h @@ -148,6 +148,7 @@ public: static constexpr QOperatingSystemVersionBase Android14 { QOperatingSystemVersionBase::Android, 14, 0 }; static constexpr QOperatingSystemVersionBase Windows11_23H2 { QOperatingSystemVersionBase::Windows, 10, 0, 22631 }; static constexpr QOperatingSystemVersionBase Windows11_24H2 { QOperatingSystemVersionBase::Windows, 10, 0, 26100 }; + static constexpr QOperatingSystemVersionBase Windows11_25H2 { QOperatingSystemVersionBase::Windows, 10, 0, 26200 }; #if QT_VERSION < QT_VERSION_CHECK(7, 0, 0) && !defined(QT_BOOTSTRAPPED) && !defined(Q_QDOC) }; diff --git a/src/corelib/global/qttranslation.qdoc b/src/corelib/global/qttranslation.qdoc index 191b3777db6..c8b3764614e 100644 --- a/src/corelib/global/qttranslation.qdoc +++ b/src/corelib/global/qttranslation.qdoc @@ -179,7 +179,7 @@ \fn QString qTrId(const char *id, int n = -1) \relates <QtTranslation> \reentrant - \since 6.9 + \since 6.11 \brief The qTrId function is an alias for qtTrId. diff --git a/src/corelib/io/qfsfileengine.cpp b/src/corelib/io/qfsfileengine.cpp index 0771c15584b..d3c398f0860 100644 --- a/src/corelib/io/qfsfileengine.cpp +++ b/src/corelib/io/qfsfileengine.cpp @@ -249,6 +249,12 @@ bool QFSFileEngine::open(QIODevice::OpenMode openMode, FILE *fh, QFile::FileHand } /*! + \class QFSFileEnginePrivate + \inmodule QtCore + \internal +*/ + +/*! Opens the file handle \a fh using the open mode \a flags. */ bool QFSFileEnginePrivate::openFh(QIODevice::OpenMode openMode, FILE *fh) diff --git a/src/corelib/io/qiooperation_p.h b/src/corelib/io/qiooperation_p.h index 56845167ede..1486719a7e8 100644 --- a/src/corelib/io/qiooperation_p.h +++ b/src/corelib/io/qiooperation_p.h @@ -72,6 +72,9 @@ protected: Q_DECLARE_PRIVATE(QIOOperation) friend class QRandomAccessAsyncFilePrivate; + friend class QRandomAccessAsyncFileBackend; + friend class QRandomAccessAsyncFileNativeBackend; + friend class QRandomAccessAsyncFileThreadPoolBackend; }; class Q_CORE_EXPORT QIOReadWriteOperationBase : public QIOOperation @@ -86,6 +89,11 @@ protected: QIOReadWriteOperationBase() = delete; Q_DISABLE_COPY_MOVE(QIOReadWriteOperationBase) explicit QIOReadWriteOperationBase(QIOOperationPrivate &dd, QObject *parent = nullptr); + + friend class QRandomAccessAsyncFilePrivate; + friend class QRandomAccessAsyncFileBackend; + friend class QRandomAccessAsyncFileNativeBackend; + friend class QRandomAccessAsyncFileThreadPoolBackend; }; class Q_CORE_EXPORT QIOReadOperation : public QIOReadWriteOperationBase @@ -101,6 +109,9 @@ protected: explicit QIOReadOperation(QIOOperationPrivate &dd, QObject *parent = nullptr); friend class QRandomAccessAsyncFilePrivate; + friend class QRandomAccessAsyncFileBackend; + friend class QRandomAccessAsyncFileNativeBackend; + friend class QRandomAccessAsyncFileThreadPoolBackend; }; class Q_CORE_EXPORT QIOWriteOperation : public QIOReadWriteOperationBase @@ -116,6 +127,9 @@ protected: explicit QIOWriteOperation(QIOOperationPrivate &dd, QObject *parent = nullptr); friend class QRandomAccessAsyncFilePrivate; + friend class QRandomAccessAsyncFileBackend; + friend class QRandomAccessAsyncFileNativeBackend; + friend class QRandomAccessAsyncFileThreadPoolBackend; }; class Q_CORE_EXPORT QIOVectoredReadOperation : public QIOReadWriteOperationBase @@ -131,6 +145,9 @@ protected: explicit QIOVectoredReadOperation(QIOOperationPrivate &dd, QObject *parent = nullptr); friend class QRandomAccessAsyncFilePrivate; + friend class QRandomAccessAsyncFileBackend; + friend class QRandomAccessAsyncFileNativeBackend; + friend class QRandomAccessAsyncFileThreadPoolBackend; }; class Q_CORE_EXPORT QIOVectoredWriteOperation : public QIOReadWriteOperationBase @@ -146,6 +163,9 @@ protected: explicit QIOVectoredWriteOperation(QIOOperationPrivate &dd, QObject *parent = nullptr); friend class QRandomAccessAsyncFilePrivate; + friend class QRandomAccessAsyncFileBackend; + friend class QRandomAccessAsyncFileNativeBackend; + friend class QRandomAccessAsyncFileThreadPoolBackend; }; QT_END_NAMESPACE diff --git a/src/corelib/io/qioring.cpp b/src/corelib/io/qioring.cpp index 28849b49b04..2eb013e24fc 100644 --- a/src/corelib/io/qioring.cpp +++ b/src/corelib/io/qioring.cpp @@ -8,6 +8,20 @@ QT_BEGIN_NAMESPACE Q_LOGGING_CATEGORY(lcQIORing, "qt.core.ioring", QtCriticalMsg) +QIORing *QIORing::sharedInstance() +{ + thread_local QIORing instance; + if (!instance.initializeIORing()) + return nullptr; + return &instance; +} + +QIORing::QIORing(quint32 submissionQueueSize, quint32 completionQueueSize) + : sqEntries(submissionQueueSize), cqEntries(completionQueueSize) +{ + // Destructor in respective _<platform>.cpp +} + auto QIORing::queueRequestInternal(GenericRequestType &request) -> QueuedRequestStatus { if (!ensureInitialized() || preparingRequests) { // preparingRequests protects against recursing @@ -65,12 +79,20 @@ template <typename T> constexpr bool HasResultMember = qxp::is_detected_v<DetectResult, T>; } +void QIORing::setFileErrorResult(QIORing::GenericRequestType &req, QFileDevice::FileError error) +{ + invokeOnOp(req, [error](auto *concreteRequest) { + if constexpr (QtPrivate::HasResultMember<decltype(*concreteRequest)>) + setFileErrorResult(*concreteRequest, error); + }); +} + void QIORing::finishRequestWithError(QIORing::GenericRequestType &req, QFileDevice::FileError error) { - invokeOnOp(req, [error](auto *req) { - if constexpr (QtPrivate::HasResultMember<decltype(*req)>) - req->result.template emplace<QFileDevice::FileError>(error); - invokeCallback(*req); + invokeOnOp(req, [error](auto *concreteRequest) { + if constexpr (QtPrivate::HasResultMember<decltype(*concreteRequest)>) + setFileErrorResult(*concreteRequest, error); + invokeCallback(*concreteRequest); }); } diff --git a/src/corelib/io/qioring_linux.cpp b/src/corelib/io/qioring_linux.cpp index b296b916c81..2b5865f3c2d 100644 --- a/src/corelib/io/qioring_linux.cpp +++ b/src/corelib/io/qioring_linux.cpp @@ -35,19 +35,6 @@ static io_uring_op toUringOp(QIORing::Operation op); static void prepareFileReadWrite(io_uring_sqe *sqe, const QIORingRequestOffsetFdBase &request, const void *address, qsizetype size); - -QIORing *QIORing::sharedInstance() -{ - thread_local QIORing instance; - if (!instance.initializeIORing()) - return nullptr; - return &instance; -} - -QIORing::QIORing(quint32 submissionQueueSize, quint32 completionQueueSize) - : sqEntries(submissionQueueSize), cqEntries(completionQueueSize) -{ -} QIORing::~QIORing() { if (eventDescriptor != -1) diff --git a/src/corelib/io/qioring_p.h b/src/corelib/io/qioring_p.h index d4c4308122e..0db832bc6bf 100644 --- a/src/corelib/io/qioring_p.h +++ b/src/corelib/io/qioring_p.h @@ -22,7 +22,6 @@ #include <QtCore/qspan.h> #include <QtCore/qhash.h> #include <QtCore/qfiledevice.h> -#include <QtCore/qwineventnotifier.h> #include <QtCore/qloggingcategory.h> #include <QtCore/qdeadlinetimer.h> @@ -30,10 +29,15 @@ # include <QtCore/qsocketnotifier.h> struct io_uring_sqe; struct io_uring_cqe; +#elif defined(Q_OS_WIN) +# include <QtCore/qwineventnotifier.h> +# include <qt_windows.h> +# include <ioringapi.h> #endif #include <algorithm> #include <filesystem> +#include <QtCore/qxpfunctional.h> #include <variant> #include <optional> #include <type_traits> @@ -162,6 +166,12 @@ private: template <typename Fun> static auto invokeOnOp(GenericRequestType &req, Fun fn); + template <Operation Op> + static void setFileErrorResult(QIORingRequest<Op> &req, QFileDevice::FileError error) + { + req.result.template emplace<QFileDevice::FileError>(error); + } + static void setFileErrorResult(GenericRequestType &req, QFileDevice::FileError error); static void finishRequestWithError(GenericRequestType &req, QFileDevice::FileError error); static bool verifyFd(GenericRequestType &req); @@ -205,6 +215,28 @@ private: ReadWriteStatus handleReadCompletion(const io_uring_cqe *cqe, GenericRequestType *request); template <Operation Op> ReadWriteStatus handleWriteCompletion(const io_uring_cqe *cqe, GenericRequestType *request); +#elif defined(Q_OS_WIN) + // We use UINT32 because that's the type used for size parameters in their API. + static constexpr qsizetype MaxReadWriteLen = std::numeric_limits<UINT32>::max(); + std::optional<QWinEventNotifier> notifier; + HIORING ioRingHandle = nullptr; + HANDLE eventHandle = INVALID_HANDLE_VALUE; + + bool initialized = false; + bool queueWasFull = false; + [[nodiscard]] + RequestPrepResult prepareRequest(GenericRequestType &request); + QIORing::ReadWriteStatus handleReadCompletion( + HRESULT result, quintptr information, QSpan<std::byte> *destinations, void *voidExtra, + qxp::function_ref<qint64(std::variant<QFileDevice::FileError, qint64>)> setResult); + template <Operation Op> + ReadWriteStatus handleReadCompletion(const IORING_CQE *cqe, GenericRequestType *request); + ReadWriteStatus handleWriteCompletion( + HRESULT result, quintptr information, const QSpan<const std::byte> *sources, + void *voidExtra, + qxp::function_ref<qint64(std::variant<QFileDevice::FileError, qint64>)> setResult); + template <Operation Op> + ReadWriteStatus handleWriteCompletion(const IORING_CQE *cqe, GenericRequestType *request); #endif }; @@ -243,6 +275,7 @@ struct QIORingRequestBase : Base template <> struct QIORingResult<QtPrivate::Operation::Open> { + // On Windows this is a HANDLE qintptr fd; }; template <> @@ -260,6 +293,7 @@ template <> struct QIORingRequest<QtPrivate::Operation::Close> final : QIORingRequestBase<QtPrivate::Operation::Close, QIORingRequestEmptyBase> { + // On Windows this is a HANDLE qintptr fd; }; @@ -318,6 +352,7 @@ struct QIORingResult<QtPrivate::Operation::Flush> final template <> struct QIORingRequest<QtPrivate::Operation::Flush> final : QIORingRequestBase<QtPrivate::Operation::Flush, QIORingRequestEmptyBase> { + // On Windows this is a HANDLE qintptr fd; }; @@ -330,6 +365,7 @@ template <> struct QIORingRequest<QtPrivate::Operation::Stat> final : QIORingRequestBase<QtPrivate::Operation::Stat, QIORingRequestEmptyBase> { + // On Windows this is a HANDLE qintptr fd; }; @@ -473,6 +509,7 @@ namespace QtPrivate { // The 'extra' struct for Read/Write operations that must be split up struct ReadWriteExtra { + qint64 totalProcessed = 0; qsizetype spanIndex = 0; qsizetype spanOffset = 0; qsizetype numSpans = 1; diff --git a/src/corelib/io/qioring_win.cpp b/src/corelib/io/qioring_win.cpp new file mode 100644 index 00000000000..42c51f428d6 --- /dev/null +++ b/src/corelib/io/qioring_win.cpp @@ -0,0 +1,754 @@ +// Copyright (C) 2025 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only +// Qt-Security score:significant reason:default + +#include "qioring_p.h" + +QT_REQUIRE_CONFIG(windows_ioring); + +#include <QtCore/qcompilerdetection.h> +#include <QtCore/qobject.h> +#include <QtCore/qscopedvaluerollback.h> + +#include <qt_windows.h> +#include <ioringapi.h> + +#include <QtCore/q26numeric.h> + +QT_BEGIN_NAMESPACE + +// We don't really build for 32-bit windows anymore, but this code is definitely wrong if someone +// does. +static_assert(sizeof(qsizetype) > sizeof(UINT32), + "This code is written with assuming 64-bit Windows."); + +using namespace Qt::StringLiterals; + +static HRESULT buildReadOperation(HIORING ioRingHandle, qintptr fd, QSpan<std::byte> destination, + quint64 offset, quintptr userData) +{ + // NOLINTNEXTLINE(performance-no-int-to-ptr) + const IORING_HANDLE_REF fileRef((HANDLE(fd))); + const IORING_BUFFER_REF bufferRef(destination.data()); + const auto maxSize = q26::saturate_cast<UINT32>(destination.size()); + Q_ASSERT(maxSize == destination.size()); + return BuildIoRingReadFile(ioRingHandle, fileRef, bufferRef, maxSize, offset, userData, + IOSQE_FLAGS_NONE); +} + +static HRESULT buildWriteOperation(HIORING ioRingHandle, qintptr fd, QSpan<const std::byte> source, + quint64 offset, quintptr userData) +{ + // NOLINTNEXTLINE(performance-no-int-to-ptr) + const IORING_HANDLE_REF fileRef((HANDLE(fd))); + const IORING_BUFFER_REF bufferRef(const_cast<std::byte *>(source.data())); + const auto maxSize = q26::saturate_cast<UINT32>(source.size()); + Q_ASSERT(maxSize == source.size()); + // @todo: FILE_WRITE_FLAGS can be set to write-through, could be used for Unbuffered mode. + return BuildIoRingWriteFile(ioRingHandle, fileRef, bufferRef, maxSize, offset, + FILE_WRITE_FLAGS_NONE, userData, IOSQE_FLAGS_NONE); +} + +QIORing::~QIORing() +{ + if (initialized) { + CloseHandle(eventHandle); + CloseIoRing(ioRingHandle); + } +} + +bool QIORing::initializeIORing() +{ + if (initialized) + return true; + + IORING_CAPABILITIES capabilities; + QueryIoRingCapabilities(&capabilities); + if (capabilities.MaxVersion < IORING_VERSION_3) // 3 adds write, flush and drain + return false; + if ((capabilities.FeatureFlags & IORING_FEATURE_SET_COMPLETION_EVENT) == 0) + return false; // We currently require the SET_COMPLETION_EVENT feature + + qCDebug(lcQIORing) << "Creating QIORing, requesting space for" << sqEntries + << "submission queue entries, and" << cqEntries + << "completion queue entries"; + + IORING_CREATE_FLAGS flags; + memset(&flags, 0, sizeof(flags)); + HRESULT hr = CreateIoRing(IORING_VERSION_3, flags, sqEntries, cqEntries, &ioRingHandle); + if (FAILED(hr)) { + qErrnoWarning(hr, "failed to initialize QIORing"); + return false; + } + auto earlyExitCleanup = qScopeGuard([this]() { + if (eventHandle != INVALID_HANDLE_VALUE) + CloseHandle(eventHandle); + CloseIoRing(ioRingHandle); + }); + eventHandle = CreateEvent(nullptr, TRUE, FALSE, nullptr); + if (eventHandle == INVALID_HANDLE_VALUE) { + qErrnoWarning("Failed to create event handle"); + return false; + } + notifier.emplace(eventHandle); + hr = SetIoRingCompletionEvent(ioRingHandle, eventHandle); + if (FAILED(hr)) { + qErrnoWarning(hr, "Failed to assign the event handle to QIORing"); + return false; + } + IORING_INFO info; + if (SUCCEEDED(GetIoRingInfo(ioRingHandle, &info))) { + sqEntries = info.SubmissionQueueSize; + cqEntries = info.CompletionQueueSize; + qCDebug(lcQIORing) << "QIORing configured with capacity for" << sqEntries + << "submissions, and" << cqEntries << "completions."; + } + QObject::connect(std::addressof(*notifier), &QWinEventNotifier::activated, + std::addressof(*notifier), [this]() { completionReady(); }); + initialized = true; + earlyExitCleanup.dismiss(); + return true; +} + +QIORing::ReadWriteStatus QIORing::handleReadCompletion( + HRESULT result, quintptr information, QSpan<std::byte> *destinations, void *voidExtra, + qxp::function_ref<qint64(std::variant<QFileDevice::FileError, qint64>)> setResultFn) +{ + if (FAILED(result)) { + if (result == HRESULT_FROM_WIN32(ERROR_HANDLE_EOF)) + return ReadWriteStatus::Finished; + + if (result == E_ABORT) + setResultFn(QFileDevice::AbortError); + else + setResultFn(QFileDevice::ReadError); + } else if (auto *extra = static_cast<QtPrivate::ReadWriteExtra *>(voidExtra)) { + const qsizetype bytesRead = q26::saturate_cast<decltype(MaxReadWriteLen)>(information); + qCDebug(lcQIORing) << "Partial read of" << bytesRead << "bytes completed"; + extra->totalProcessed = setResultFn(bytesRead); + extra->spanOffset += bytesRead; + qCDebug(lcQIORing) << "Read operation progress: span" << extra->spanIndex << "offset" + << extra->spanOffset << "of" << destinations[extra->spanIndex].size() + << "bytes. Total read:" << extra->totalProcessed << "bytes"; + // The while loop is in case there is an empty span, we skip over it: + while (extra->spanOffset == destinations[extra->spanIndex].size()) { + // Move to next span + if (++extra->spanIndex == extra->numSpans) + return ReadWriteStatus::Finished; + extra->spanOffset = 0; + } + return ReadWriteStatus::MoreToDo; + } else { + setResultFn(q26::saturate_cast<decltype(MaxReadWriteLen)>(information)); + } + return ReadWriteStatus::Finished; +} + +template <QIORing::Operation Op> +Q_ALWAYS_INLINE QIORing::ReadWriteStatus QIORing::handleReadCompletion(const IORING_CQE *cqe, + GenericRequestType *request) +{ + static_assert(Op == Operation::Read || Op == Operation::VectoredRead); + QIORingRequest<Op> *readRequest = request->requestData<Op>(); + Q_ASSERT(readRequest); + auto *destinations = [&readRequest]() { + if constexpr (Op == Operation::Read) + return &readRequest->destination; + else + return &readRequest->destinations[0]; + }(); + auto setResult = [readRequest](const std::variant<QFileDevice::FileError, qint64> &result) { + if (result.index() == 0) { // error + QIORing::setFileErrorResult(*readRequest, *std::get_if<QFileDevice::FileError>(&result)); + return 0ll; + } + // else: success + auto &readResult = [&readRequest]() -> QIORingResult<Op> & { + if (auto *result = std::get_if<QIORingResult<Op>>(&readRequest->result)) + return *result; + return readRequest->result.template emplace<QIORingResult<Op>>(); + }(); + qint64 bytesRead = *std::get_if<qint64>(&result); + readResult.bytesRead += bytesRead; + return readResult.bytesRead; + }; + QIORing::ReadWriteStatus rwstatus = handleReadCompletion( + cqe->ResultCode, cqe->Information, destinations, request->getExtra<void>(), setResult); + switch (rwstatus) { + case ReadWriteStatus::Finished: + if (request->getExtra<void>()) + --ongoingSplitOperations; + break; + case ReadWriteStatus::MoreToDo: { + // Move the request such that it is next in the list to be processed: + auto &it = addrItMap[request]; + const auto where = lastUnqueuedIterator.value_or(pendingRequests.end()); + pendingRequests.splice(where, pendingRequests, it); + it = std::prev(where); + lastUnqueuedIterator = it; + break; + } + } + return rwstatus; +} + +QIORing::ReadWriteStatus QIORing::handleWriteCompletion( + HRESULT result, quintptr information, const QSpan<const std::byte> *sources, void *voidExtra, + qxp::function_ref<qint64(std::variant<QFileDevice::FileError, qint64>)> setResultFn) +{ + if (FAILED(result)) { + if (result == E_ABORT) + setResultFn(QFileDevice::AbortError); + else + setResultFn(QFileDevice::WriteError); + } else if (auto *extra = static_cast<QtPrivate::ReadWriteExtra *>(voidExtra)) { + const qsizetype bytesWritten = q26::saturate_cast<decltype(MaxReadWriteLen)>(information); + qCDebug(lcQIORing) << "Partial write of" << bytesWritten << "bytes completed"; + extra->totalProcessed = setResultFn(bytesWritten); + extra->spanOffset += bytesWritten; + qCDebug(lcQIORing) << "Write operation progress: span" << extra->spanIndex << "offset" + << extra->spanOffset << "of" << sources[extra->spanIndex].size() + << "bytes. Total written:" << extra->totalProcessed << "bytes"; + // The while loop is in case there is an empty span, we skip over it: + while (extra->spanOffset == sources[extra->spanIndex].size()) { + // Move to next span + if (++extra->spanIndex == extra->numSpans) + return ReadWriteStatus::Finished; + extra->spanOffset = 0; + } + return ReadWriteStatus::MoreToDo; + } else { + setResultFn(q26::saturate_cast<decltype(MaxReadWriteLen)>(information)); + } + return ReadWriteStatus::Finished; +} + +template <QIORing::Operation Op> +Q_ALWAYS_INLINE QIORing::ReadWriteStatus QIORing::handleWriteCompletion(const IORING_CQE *cqe, + GenericRequestType *request) +{ + static_assert(Op == Operation::Write || Op == Operation::VectoredWrite); + QIORingRequest<Op> *writeRequest = request->requestData<Op>(); + auto *sources = [&writeRequest]() { + if constexpr (Op == Operation::Write) + return &writeRequest->source; + else + return &writeRequest->sources[0]; + }(); + auto setResult = [writeRequest](const std::variant<QFileDevice::FileError, qint64> &result) { + if (result.index() == 0) { // error + QIORing::setFileErrorResult(*writeRequest, *std::get_if<QFileDevice::FileError>(&result)); + return 0ll; + } + // else: success + auto &writeResult = [&writeRequest]() -> QIORingResult<Op> & { + if (auto *result = std::get_if<QIORingResult<Op>>(&writeRequest->result)) + return *result; + return writeRequest->result.template emplace<QIORingResult<Op>>(); + }(); + qint64 bytesWritten = *std::get_if<qint64>(&result); + writeResult.bytesWritten += bytesWritten; + return writeResult.bytesWritten; + }; + QIORing::ReadWriteStatus rwstatus = handleWriteCompletion( + cqe->ResultCode, cqe->Information, sources, request->getExtra<void>(), setResult); + switch (rwstatus) { + case ReadWriteStatus::Finished: + if (request->getExtra<void>()) + --ongoingSplitOperations; + break; + case ReadWriteStatus::MoreToDo: { + // Move the request such that it is next in the list to be processed: + auto &it = addrItMap[request]; + const auto where = lastUnqueuedIterator.value_or(pendingRequests.end()); + pendingRequests.splice(where, pendingRequests, it); + it = std::prev(where); + lastUnqueuedIterator = it; + break; + } + } + return rwstatus; +} + +void QIORing::completionReady() +{ + ResetEvent(eventHandle); + IORING_CQE entry; + while (PopIoRingCompletion(ioRingHandle, &entry) == S_OK) { + // NOLINTNEXTLINE(performance-no-int-to-ptr) + auto *request = reinterpret_cast<GenericRequestType *>(entry.UserData); + if (!addrItMap.contains(request)) { + qCDebug(lcQIORing) << "Got completed entry, but cannot find it in the map. Likely " + "deleted, ignoring. UserData pointer:" + << request; + continue; + } + qCDebug(lcQIORing) << "Got completed entry. Operation:" << request->operation() + << "- UserData pointer:" << request + << "- Result:" << qt_error_string(entry.ResultCode) << '(' + << QByteArray("0x"_ba + QByteArray::number(entry.ResultCode, 16)).data() + << ')'; + switch (request->operation()) { + case Operation::Open: // Synchronously finishes + Q_UNREACHABLE_RETURN(); + case Operation::Close: { + auto closeRequest = request->takeRequestData<Operation::Close>(); + // We ignore the result of the flush, we are closing the handle anyway. + // NOLINTNEXTLINE(performance-no-int-to-ptr) + if (CloseHandle(HANDLE(closeRequest.fd))) + closeRequest.result.emplace<QIORingResult<Operation::Close>>(); + else + closeRequest.result.emplace<QFileDevice::FileError>(QFileDevice::OpenError); + invokeCallback(closeRequest); + break; + } + case Operation::Read: { + const ReadWriteStatus status = handleReadCompletion<Operation::Read>(&entry, request); + if (status == ReadWriteStatus::MoreToDo) + continue; + auto readRequest = request->takeRequestData<Operation::Read>(); + invokeCallback(readRequest); + break; + } + case Operation::Write: { + const ReadWriteStatus status = handleWriteCompletion<Operation::Write>(&entry, request); + if (status == ReadWriteStatus::MoreToDo) + continue; + auto writeRequest = request->takeRequestData<Operation::Write>(); + invokeCallback(writeRequest); + break; + } + case Operation::VectoredRead: { + const ReadWriteStatus status = handleReadCompletion<Operation::VectoredRead>(&entry, + request); + if (status == ReadWriteStatus::MoreToDo) + continue; + auto vectoredReadRequest = request->takeRequestData<Operation::VectoredRead>(); + invokeCallback(vectoredReadRequest); + break; + } + case Operation::VectoredWrite: { + const ReadWriteStatus status = handleWriteCompletion<Operation::VectoredWrite>(&entry, + request); + if (status == ReadWriteStatus::MoreToDo) + continue; + auto vectoredWriteRequest = request->takeRequestData<Operation::VectoredWrite>(); + invokeCallback(vectoredWriteRequest); + break; + } + case Operation::Flush: { + auto flushRequest = request->takeRequestData<Operation::Flush>(); + if (FAILED(entry.ResultCode)) { + qErrnoWarning(entry.ResultCode, "Flush operation failed"); + // @todo any FlushError? + flushRequest.result.emplace<QFileDevice::FileError>( + QFileDevice::FileError::WriteError); + } else { + flushRequest.result.emplace<QIORingResult<Operation::Flush>>(); + } + invokeCallback(flushRequest); + break; + } + case QtPrivate::Operation::Cancel: { + auto cancelRequest = request->takeRequestData<Operation::Cancel>(); + invokeCallback(cancelRequest); + break; + } + case QtPrivate::Operation::Stat: + Q_UNREACHABLE_RETURN(); // Completes synchronously + break; + case Operation::NumOperations: + Q_UNREACHABLE_RETURN(); + break; + } + auto it = addrItMap.take(request); + pendingRequests.erase(it); + --inFlightRequests; + queueWasFull = false; + } + prepareRequests(); + if (unstagedRequests > 0) + submitRequests(); +} + +bool QIORing::waitForCompletions(QDeadlineTimer deadline) +{ + notifier->setEnabled(false); + auto reactivateNotifier = qScopeGuard([this]() { + notifier->setEnabled(true); + }); + + while (!deadline.hasExpired()) { + DWORD timeout = 0; + if (deadline.isForever()) { + timeout = INFINITE; + } else { + timeout = q26::saturate_cast<DWORD>(deadline.remainingTime()); + if (timeout == INFINITE) + --timeout; + } + if (WaitForSingleObject(eventHandle, timeout) == WAIT_OBJECT_0) + return true; + } + return false; +} + +static HANDLE openFile(const QIORingRequest<QIORing::Operation::Open> &openRequest) +{ + DWORD access = 0; + if (openRequest.flags.testFlag(QIODevice::ReadOnly)) + access |= GENERIC_READ; + if (openRequest.flags.testFlag(QIODevice::WriteOnly)) + access |= GENERIC_WRITE; + + DWORD disposition = 0; + if (openRequest.flags.testFlag(QIODevice::Append)) { + qCWarning(lcQIORing, "Opening file with Append not supported for random access file"); + return INVALID_HANDLE_VALUE; + } + if (openRequest.flags.testFlag(QIODevice::NewOnly)) { + disposition = CREATE_NEW; + } else { + // If Write is specified we _may_ create a file. + // See qfsfileengine_p.h openModeCanCreate. + disposition = openRequest.flags.testFlag(QIODeviceBase::WriteOnly) + && !openRequest.flags.testFlags(QIODeviceBase::ExistingOnly) + ? OPEN_ALWAYS + : OPEN_EXISTING; + } + const DWORD shareMode = FILE_SHARE_READ | FILE_SHARE_WRITE; + const DWORD flagsAndAttribs = FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED; + HANDLE h = CreateFile(openRequest.path.native().c_str(), access, shareMode, nullptr, + disposition, flagsAndAttribs, nullptr); + if (h != INVALID_HANDLE_VALUE && openRequest.flags.testFlag(QIODeviceBase::Truncate)) { + FILE_END_OF_FILE_INFO info; + memset(&info, 0, sizeof(info)); + SetFileInformationByHandle(h, FileEndOfFileInfo, &info, sizeof(info)); + } + return h; +} + +bool QIORing::supportsOperation(Operation op) +{ + switch (op) { + case QtPrivate::Operation::Open: + case QtPrivate::Operation::Close: + case QtPrivate::Operation::Read: + case QtPrivate::Operation::Write: + case QtPrivate::Operation::Flush: + case QtPrivate::Operation::Cancel: + case QtPrivate::Operation::Stat: + case QtPrivate::Operation::VectoredRead: + case QtPrivate::Operation::VectoredWrite: + return true; + case QtPrivate::Operation::NumOperations: + return false; + } + return false; // Not unreachable, we could allow more for io_uring +} + +void QIORing::submitRequests() +{ + stagePending = false; + if (unstagedRequests == 0) + return; + + // We perform a miniscule wait - to see if anything already in the queue is already completed - + // if we have been told the queue is full. Then we can try queuing more things right away + const bool shouldTryWait = std::exchange(queueWasFull, false); + const auto submitToRing = [this, &shouldTryWait] { + quint32 submittedEntries = 0; + HRESULT hr = SubmitIoRing(ioRingHandle, shouldTryWait ? 1 : 0, 1, &submittedEntries); + qCDebug(lcQIORing) << "Submitted" << submittedEntries << "requests"; + unstagedRequests -= submittedEntries; + if (FAILED(hr)) { + // Too noisy, not a real problem + // qErrnoWarning(hr, "Failed to submit QIORing request: %u", submittedEntries); + return false; + } + return submittedEntries > 0; + }; + if (submitToRing() && shouldTryWait) { + // We try to prepare some more request and submit more if able + prepareRequests(); + if (unstagedRequests > 0) + submitToRing(); + } +} + +void QIORing::prepareRequests() +{ + if (!lastUnqueuedIterator) + return; + Q_ASSERT(!preparingRequests); + QScopedValueRollback<bool> prepareGuard(preparingRequests, true); + + auto it = *lastUnqueuedIterator; + lastUnqueuedIterator.reset(); + const auto end = pendingRequests.end(); + while (!queueWasFull && it != end) { + auto &request = *it; + switch (prepareRequest(request)) { + case RequestPrepResult::Ok: + ++unstagedRequests; + ++inFlightRequests; + break; + case RequestPrepResult::QueueFull: + qCDebug(lcQIORing) << "Queue was reported as full, in flight requests:" + << inFlightRequests << "submission queue size:" << sqEntries + << "completion queue size:" << cqEntries; + queueWasFull = true; + lastUnqueuedIterator = it; + return; + case RequestPrepResult::Defer: + qCDebug(lcQIORing) << "Request for" << request.operation() + << "had to be deferred, will not queue any more requests at the " + "moment."; + lastUnqueuedIterator = it; + return; // + case RequestPrepResult::RequestCompleted: + // Used for requests that immediately finish. So we erase it: + qCDebug(lcQIORing) << "Request for" << request.operation() + << "completed synchronously."; + addrItMap.remove(&request); + it = pendingRequests.erase(it); + continue; // Don't increment iterator again + } + ++it; + } +} + +namespace QtPrivate { +template <typename T> +using DetectHasFd = decltype(std::declval<const T &>().fd); + +template <typename T> +constexpr bool OperationHasFd_v = qxp::is_detected_v<DetectHasFd, T>; +} // namespace QtPrivate + +auto QIORing::prepareRequest(GenericRequestType &request) -> RequestPrepResult +{ + qCDebug(lcQIORing) << "Preparing a request with operation" << request.operation(); + HRESULT hr = -1; + + if (!verifyFd(request)) { + finishRequestWithError(request, QFileDevice::OpenError); + return RequestPrepResult::RequestCompleted; + } + + switch (request.operation()) { + case Operation::Open: { + QIORingRequest<Operation::Open> openRequest = request.takeRequestData<Operation::Open>(); + HANDLE fileDescriptor = openFile(openRequest); + if (fileDescriptor == INVALID_HANDLE_VALUE) { + openRequest.result.emplace<QFileDevice::FileError>(QFileDevice::FileError::OpenError); + } else { + auto &result = openRequest.result.emplace<QIORingResult<Operation::Open>>(); + result.fd = qintptr(fileDescriptor); + } + invokeCallback(openRequest); + return RequestPrepResult::RequestCompleted; + } + case Operation::Close: { + if (ongoingSplitOperations > 0) + return RequestPrepResult::Defer; + + // We need to wait until all previous OPS are done before we close the request. + // There is no no-op request in the Windows QIORing, so we issue a flush. + auto *closeRequest = request.requestData<Operation::Close>(); + // NOLINTNEXTLINE(performance-no-int-to-ptr) + const IORING_HANDLE_REF fileRef(HANDLE(closeRequest->fd)); + hr = BuildIoRingFlushFile(ioRingHandle, fileRef, FILE_FLUSH_MIN_METADATA, + quintptr(std::addressof(request)), + IOSQE_FLAGS_DRAIN_PRECEDING_OPS); + break; + } + case Operation::Read: { + auto *readRequest = request.requestData<Operation::Read>(); + auto span = readRequest->destination; + auto offset = readRequest->offset; + if (span.size() > MaxReadWriteLen) { + qCDebug(lcQIORing) << "Requested Read of size" << span.size() << "has to be split"; + auto *extra = request.getOrInitializeExtra<QtPrivate::ReadWriteExtra>(); + if (extra->spanOffset == 0) + ++ongoingSplitOperations; + const qsizetype remaining = span.size() - extra->spanOffset; + span.slice(extra->spanOffset, std::min(remaining, MaxReadWriteLen)); + offset += extra->totalProcessed; + } + hr = buildReadOperation(ioRingHandle, readRequest->fd, span, offset, + quintptr(std::addressof(request))); + break; + } + case Operation::VectoredRead: { + auto *vectoredReadRequest = request.requestData<Operation::VectoredRead>(); + auto span = vectoredReadRequest->destinations.front(); + auto offset = vectoredReadRequest->offset; + if (Q_LIKELY(vectoredReadRequest->destinations.size() > 1 + || span.size() > MaxReadWriteLen)) { + auto *extra = request.getOrInitializeExtra<QtPrivate::ReadWriteExtra>(); + if (extra->spanOffset == 0 && extra->spanIndex == 0) + ++ongoingSplitOperations; + extra->numSpans = vectoredReadRequest->destinations.size(); + + span = vectoredReadRequest->destinations[extra->spanIndex]; + + const qsizetype remaining = span.size() - extra->spanOffset; + span.slice(extra->spanOffset, std::min(remaining, MaxReadWriteLen)); + offset += extra->totalProcessed; + } + hr = buildReadOperation(ioRingHandle, vectoredReadRequest->fd, span, + offset, quintptr(std::addressof(request))); + break; + } + case Operation::Write: { + auto *writeRequest = request.requestData<Operation::Write>(); + auto span = writeRequest->source; + auto offset = writeRequest->offset; + if (span.size() > MaxReadWriteLen) { + qCDebug(lcQIORing) << "Requested Write of size" << span.size() << "has to be split"; + auto *extra = request.getOrInitializeExtra<QtPrivate::ReadWriteExtra>(); + if (extra->spanOffset == 0) + ++ongoingSplitOperations; + const qsizetype remaining = span.size() - extra->spanOffset; + span.slice(extra->spanOffset, std::min(remaining, MaxReadWriteLen)); + offset += extra->totalProcessed; + } + hr = buildWriteOperation(ioRingHandle, writeRequest->fd, span, offset, + quintptr(std::addressof(request))); + break; + } + case Operation::VectoredWrite: { + auto *vectoredWriteRequest = request.requestData<Operation::VectoredWrite>(); + auto span = vectoredWriteRequest->sources.front(); + auto offset = vectoredWriteRequest->offset; + if (Q_LIKELY(vectoredWriteRequest->sources.size() > 1 + || span.size() > MaxReadWriteLen)) { + auto *extra = request.getOrInitializeExtra<QtPrivate::ReadWriteExtra>(); + if (extra->spanOffset == 0 && extra->spanIndex == 0) + ++ongoingSplitOperations; + extra->numSpans = vectoredWriteRequest->sources.size(); + + span = vectoredWriteRequest->sources[extra->spanIndex]; + + const qsizetype remaining = span.size() - extra->spanOffset; + span.slice(extra->spanOffset, std::min(remaining, MaxReadWriteLen)); + offset += extra->totalProcessed; + } + hr = buildWriteOperation(ioRingHandle, vectoredWriteRequest->fd, span, + offset, quintptr(std::addressof(request))); + break; + } + case Operation::Flush: { + if (ongoingSplitOperations > 0) + return RequestPrepResult::Defer; + auto *flushRequest = request.requestData<Operation::Flush>(); + // NOLINTNEXTLINE(performance-no-int-to-ptr) + const IORING_HANDLE_REF fileRef(HANDLE(flushRequest->fd)); + hr = BuildIoRingFlushFile(ioRingHandle, fileRef, FILE_FLUSH_DEFAULT, + quintptr(std::addressof(request)), + IOSQE_FLAGS_DRAIN_PRECEDING_OPS); + break; + } + case QtPrivate::Operation::Stat: { + auto statRequest = request.takeRequestData<Operation::Stat>(); + FILE_STANDARD_INFO info; + // NOLINTNEXTLINE(performance-no-int-to-ptr) + if (!GetFileInformationByHandleEx(HANDLE(statRequest.fd), FileStandardInfo, &info, + sizeof(info))) { + DWORD winErr = GetLastError(); + QFileDevice::FileError error = QFileDevice::UnspecifiedError; + if (winErr == ERROR_FILE_NOT_FOUND || winErr == ERROR_INVALID_HANDLE) + error = QFileDevice::OpenError; + else if (winErr == ERROR_ACCESS_DENIED) + error = QFileDevice::PermissionsError; + statRequest.result.emplace<QFileDevice::FileError>(error); + } else { + auto &result = statRequest.result.emplace<QIORingResult<Operation::Stat>>(); + result.size = info.EndOfFile.QuadPart; + } + invokeCallback(statRequest); + return RequestPrepResult::RequestCompleted; + } + case Operation::Cancel: { + auto *cancelRequest = request.requestData<Operation::Cancel>(); + auto *otherOperation = reinterpret_cast<GenericRequestType *>(cancelRequest->handle); + if (!otherOperation || !addrItMap.contains(otherOperation)) { + qCDebug(lcQIORing, "Invalid cancel for non-existant operation"); + invokeCallback(*cancelRequest); + return RequestPrepResult::RequestCompleted; + } + qCDebug(lcQIORing) << "Cancelling operation of type" << otherOperation->operation() + << "which was" + << (otherOperation->wasQueued() ? "queued" : "not queued"); + Q_ASSERT(&request != otherOperation); + if (!otherOperation->wasQueued()) { + // The request hasn't been queued yet, so we can just drop it from + // the pending requests and call the callback. + auto it = addrItMap.take(otherOperation); + finishRequestWithError(*otherOperation, QFileDevice::AbortError); + pendingRequests.erase(it); // otherOperation is deleted + invokeCallback(*cancelRequest); + return RequestPrepResult::RequestCompleted; + } + qintptr fd = -1; + invokeOnOp(*otherOperation, [&fd](auto *request) { + if constexpr (QtPrivate::OperationHasFd_v<decltype(*request)>) + fd = request->fd; + }); + if (fd == -1) { + qCDebug(lcQIORing, "Invalid cancel for non-existant fd"); + invokeCallback(*cancelRequest); + return RequestPrepResult::RequestCompleted; + } + // NOLINTNEXTLINE(performance-no-int-to-ptr) + const IORING_HANDLE_REF fileRef((HANDLE(fd))); + hr = BuildIoRingCancelRequest(ioRingHandle, fileRef, quintptr(otherOperation), + quintptr(std::addressof(request))); + break; + } + case Operation::NumOperations: + Q_UNREACHABLE_RETURN(RequestPrepResult::RequestCompleted); + break; + } + if (hr == IORING_E_SUBMISSION_QUEUE_FULL) + return RequestPrepResult::QueueFull; + if (FAILED(hr)) { + finishRequestWithError(request, QFileDevice::UnspecifiedError); + return RequestPrepResult::RequestCompleted; + } + request.setQueued(true); + return RequestPrepResult::Ok; +} + +bool QIORing::verifyFd(GenericRequestType &req) +{ + bool result = true; + invokeOnOp(req, [&](auto *request) { + if constexpr (QtPrivate::OperationHasFd_v<decltype(*request)>) { + result = quintptr(request->fd) > 0 && quintptr(request->fd) != quintptr(INVALID_HANDLE_VALUE); + } + }); + return result; +} + +void QIORing::GenericRequestType::cleanupExtra(Operation op, void *extra) +{ + switch (op) { + case QtPrivate::Operation::Read: + case QtPrivate::Operation::VectoredRead: + case QtPrivate::Operation::Write: + case QtPrivate::Operation::VectoredWrite: + delete static_cast<QtPrivate::ReadWriteExtra *>(extra); + break; + case QtPrivate::Operation::Open: + case QtPrivate::Operation::Close: + case QtPrivate::Operation::Flush: + case QtPrivate::Operation::Stat: + case QtPrivate::Operation::Cancel: + case QtPrivate::Operation::NumOperations: + break; + } +} + +QT_END_NAMESPACE diff --git a/src/corelib/io/qrandomaccessasyncfile.cpp b/src/corelib/io/qrandomaccessasyncfile.cpp index c544585de9d..34e216efe27 100644 --- a/src/corelib/io/qrandomaccessasyncfile.cpp +++ b/src/corelib/io/qrandomaccessasyncfile.cpp @@ -7,6 +7,31 @@ QT_BEGIN_NAMESPACE +QRandomAccessAsyncFileBackend::QRandomAccessAsyncFileBackend(QRandomAccessAsyncFile *owner) + : m_owner(owner) +{ +} +QRandomAccessAsyncFileBackend::~QRandomAccessAsyncFileBackend() = default; +QRandomAccessAsyncFilePrivate::QRandomAccessAsyncFilePrivate() = default; +QRandomAccessAsyncFilePrivate::~QRandomAccessAsyncFilePrivate() = default; + +void QRandomAccessAsyncFilePrivate::init() +{ + Q_Q(QRandomAccessAsyncFile); + +#if defined(QT_RANDOMACCESSASYNCFILE_QIORING) || defined(Q_OS_DARWIN) + m_backend = std::make_unique<QRandomAccessAsyncFileNativeBackend>(q); +#endif + if (!m_backend || !m_backend->init()) { +#if QT_CONFIG(thread) && QT_CONFIG(future) + m_backend = std::make_unique<QRandomAccessAsyncFileThreadPoolBackend>(q); + [[maybe_unused]] + bool result = m_backend->init(); + Q_ASSERT(result); // it always succeeds +#endif + } +} + QRandomAccessAsyncFile::QRandomAccessAsyncFile(QObject *parent) : QObject{*new QRandomAccessAsyncFilePrivate, parent} { diff --git a/src/corelib/io/qrandomaccessasyncfile_darwin.mm b/src/corelib/io/qrandomaccessasyncfile_darwin.mm index 7231d12fe7d..80c1affa642 100644 --- a/src/corelib/io/qrandomaccessasyncfile_darwin.mm +++ b/src/corelib/io/qrandomaccessasyncfile_darwin.mm @@ -27,14 +27,14 @@ static bool isBarrierOperation(QIOOperation::Type type) // only! template <typename Operation, typename ...Args> Operation * -QRandomAccessAsyncFilePrivate::addOperation(QIOOperation::Type type, qint64 offset, Args &&...args) +QRandomAccessAsyncFileNativeBackend::addOperation(QIOOperation::Type type, qint64 offset, Args &&...args) { auto dataStorage = new QtPrivate::QIOOperationDataStorage(std::forward<Args>(args)...); auto *priv = new QIOOperationPrivate(dataStorage); priv->offset = offset; priv->type = type; - Operation *op = new Operation(*priv, q_ptr); + Operation *op = new Operation(*priv, m_owner); auto opId = getNextId(); m_operations.push_back(OperationInfo(opId, op)); startOperationsUntilBarrier(); @@ -42,19 +42,20 @@ QRandomAccessAsyncFilePrivate::addOperation(QIOOperation::Type type, qint64 offs return op; } -QRandomAccessAsyncFilePrivate::QRandomAccessAsyncFilePrivate() - : QObjectPrivate() +QRandomAccessAsyncFileNativeBackend::QRandomAccessAsyncFileNativeBackend(QRandomAccessAsyncFile *owner) + : QRandomAccessAsyncFileBackend(owner) { } -QRandomAccessAsyncFilePrivate::~QRandomAccessAsyncFilePrivate() +QRandomAccessAsyncFileNativeBackend::~QRandomAccessAsyncFileNativeBackend() = default; -void QRandomAccessAsyncFilePrivate::init() +bool QRandomAccessAsyncFileNativeBackend::init() { + return true; } -void QRandomAccessAsyncFilePrivate::cancelAndWait(QIOOperation *op) +void QRandomAccessAsyncFileNativeBackend::cancelAndWait(QIOOperation *op) { auto it = std::find_if(m_operations.cbegin(), m_operations.cend(), [op](const auto &opInfo) { @@ -87,7 +88,7 @@ void QRandomAccessAsyncFilePrivate::cancelAndWait(QIOOperation *op) startOperationsUntilBarrier(); } -void QRandomAccessAsyncFilePrivate::close() +void QRandomAccessAsyncFileNativeBackend::close() { if (m_fileState == FileState::Closed) return; @@ -95,10 +96,8 @@ void QRandomAccessAsyncFilePrivate::close() // cancel all operations m_mutex.lock(); m_opToCancel = kAllOperationIds; - m_numChannelsToClose = m_ioChannel ? 1 : 0; for (const auto &op : m_operations) { if (op.channel) { - ++m_numChannelsToClose; closeIoChannel(op.channel); } } @@ -127,7 +126,7 @@ void QRandomAccessAsyncFilePrivate::close() m_fileState = FileState::Closed; } -qint64 QRandomAccessAsyncFilePrivate::size() const +qint64 QRandomAccessAsyncFileNativeBackend::size() const { if (m_fileState != FileState::Opened) return -1; @@ -140,7 +139,7 @@ qint64 QRandomAccessAsyncFilePrivate::size() const } QIOOperation * -QRandomAccessAsyncFilePrivate::open(const QString &path, QIODeviceBase::OpenMode mode) +QRandomAccessAsyncFileNativeBackend::open(const QString &path, QIODeviceBase::OpenMode mode) { if (m_fileState == FileState::Closed) { m_filePath = path; @@ -153,44 +152,44 @@ QRandomAccessAsyncFilePrivate::open(const QString &path, QIODeviceBase::OpenMode return addOperation<QIOOperation>(QIOOperation::Type::Open, 0); } -QIOOperation *QRandomAccessAsyncFilePrivate::flush() +QIOOperation *QRandomAccessAsyncFileNativeBackend::flush() { return addOperation<QIOOperation>(QIOOperation::Type::Flush, 0); } -QIOReadOperation *QRandomAccessAsyncFilePrivate::read(qint64 offset, qint64 maxSize) +QIOReadOperation *QRandomAccessAsyncFileNativeBackend::read(qint64 offset, qint64 maxSize) { QByteArray array(maxSize, Qt::Uninitialized); return addOperation<QIOReadOperation>(QIOOperation::Type::Read, offset, std::move(array)); } -QIOWriteOperation *QRandomAccessAsyncFilePrivate::write(qint64 offset, const QByteArray &data) +QIOWriteOperation *QRandomAccessAsyncFileNativeBackend::write(qint64 offset, const QByteArray &data) { QByteArray copy = data; return write(offset, std::move(copy)); } -QIOWriteOperation *QRandomAccessAsyncFilePrivate::write(qint64 offset, QByteArray &&data) +QIOWriteOperation *QRandomAccessAsyncFileNativeBackend::write(qint64 offset, QByteArray &&data) { return addOperation<QIOWriteOperation>(QIOOperation::Type::Write, offset, std::move(data)); } QIOVectoredReadOperation * -QRandomAccessAsyncFilePrivate::readInto(qint64 offset, QSpan<std::byte> buffer) +QRandomAccessAsyncFileNativeBackend::readInto(qint64 offset, QSpan<std::byte> buffer) { return addOperation<QIOVectoredReadOperation>(QIOOperation::Type::Read, offset, QSpan<const QSpan<std::byte>>{buffer}); } QIOVectoredWriteOperation * -QRandomAccessAsyncFilePrivate::writeFrom(qint64 offset, QSpan<const std::byte> buffer) +QRandomAccessAsyncFileNativeBackend::writeFrom(qint64 offset, QSpan<const std::byte> buffer) { return addOperation<QIOVectoredWriteOperation>(QIOOperation::Type::Write, offset, QSpan<const QSpan<const std::byte>>{buffer}); } QIOVectoredReadOperation * -QRandomAccessAsyncFilePrivate::readInto(qint64 offset, QSpan<const QSpan<std::byte>> buffers) +QRandomAccessAsyncFileNativeBackend::readInto(qint64 offset, QSpan<const QSpan<std::byte>> buffers) { // GCD implementation does not have vectored read. Spawning several read // operations (each with an updated offset), is not ideal, because some @@ -204,32 +203,40 @@ QRandomAccessAsyncFilePrivate::readInto(qint64 offset, QSpan<const QSpan<std::by } QIOVectoredWriteOperation * -QRandomAccessAsyncFilePrivate::writeFrom(qint64 offset, QSpan<const QSpan<const std::byte>> buffers) +QRandomAccessAsyncFileNativeBackend::writeFrom(qint64 offset, QSpan<const QSpan<const std::byte>> buffers) { return addOperation<QIOVectoredWriteOperation>(QIOOperation::Type::Write, offset, buffers); } -void QRandomAccessAsyncFilePrivate::notifyIfOperationsAreCompleted() +void QRandomAccessAsyncFileNativeBackend::notifyIfOperationsAreCompleted() { QMutexLocker locker(&m_mutex); + --m_numChannelsToClose; if (m_opToCancel == kAllOperationIds) { - --m_numChannelsToClose; if (m_numChannelsToClose == 0 && m_runningOps.isEmpty()) m_cancellationCondition.wakeOne(); } } -dispatch_io_t QRandomAccessAsyncFilePrivate::createMainChannel(int fd) +dispatch_io_t QRandomAccessAsyncFileNativeBackend::createMainChannel(int fd) { auto sharedThis = this; - return dispatch_io_create(DISPATCH_IO_RANDOM, fd, - dispatch_get_global_queue(QOS_CLASS_USER_INTERACTIVE, 0), - ^(int /*error*/) { - sharedThis->notifyIfOperationsAreCompleted(); - }); + auto channel = + dispatch_io_create(DISPATCH_IO_RANDOM, fd, + dispatch_get_global_queue(QOS_CLASS_USER_INTERACTIVE, 0), + ^(int /*error*/) { + // main I/O channel uses kInvalidOperationId + // as its identifier + sharedThis->notifyIfOperationsAreCompleted(); + }); + if (channel) { + QMutexLocker locker(&m_mutex); + ++m_numChannelsToClose; + } + return channel; } -dispatch_io_t QRandomAccessAsyncFilePrivate::duplicateIoChannel(OperationId opId) +dispatch_io_t QRandomAccessAsyncFileNativeBackend::duplicateIoChannel(OperationId opId) { if (!m_ioChannel) return nullptr; @@ -247,17 +254,18 @@ dispatch_io_t QRandomAccessAsyncFilePrivate::duplicateIoChannel(OperationId opId if (channel) { QMutexLocker locker(&m_mutex); m_runningOps.insert(opId); + ++m_numChannelsToClose; } return channel; } -void QRandomAccessAsyncFilePrivate::closeIoChannel(dispatch_io_t channel) +void QRandomAccessAsyncFileNativeBackend::closeIoChannel(dispatch_io_t channel) { if (channel) dispatch_io_close(channel, DISPATCH_IO_STOP); } -void QRandomAccessAsyncFilePrivate::releaseIoChannel(dispatch_io_t channel) +void QRandomAccessAsyncFileNativeBackend::releaseIoChannel(dispatch_io_t channel) { if (channel) { dispatch_release(channel); @@ -265,7 +273,7 @@ void QRandomAccessAsyncFilePrivate::releaseIoChannel(dispatch_io_t channel) } } -void QRandomAccessAsyncFilePrivate::handleOperationComplete(const OperationResult &opResult) +void QRandomAccessAsyncFileNativeBackend::handleOperationComplete(const OperationResult &opResult) { // try to start next operations on return auto onReturn = qScopeGuard([this] { @@ -372,15 +380,15 @@ void QRandomAccessAsyncFilePrivate::handleOperationComplete(const OperationResul } } -void QRandomAccessAsyncFilePrivate::queueCompletion(OperationId opId, int error) +void QRandomAccessAsyncFileNativeBackend::queueCompletion(OperationId opId, int error) { const OperationResult res = { opId, 0LL, error }; - QMetaObject::invokeMethod(q_ptr, [this, res] { + QMetaObject::invokeMethod(m_owner, [this, res] { handleOperationComplete(res); }, Qt::QueuedConnection); } -void QRandomAccessAsyncFilePrivate::startOperationsUntilBarrier() +void QRandomAccessAsyncFileNativeBackend::startOperationsUntilBarrier() { // starts all operations until barrier, or a barrier operation if it's the // first one @@ -414,7 +422,7 @@ void QRandomAccessAsyncFilePrivate::startOperationsUntilBarrier() } } -void QRandomAccessAsyncFilePrivate::executeRead(OperationInfo &opInfo) +void QRandomAccessAsyncFileNativeBackend::executeRead(OperationInfo &opInfo) { opInfo.channel = duplicateIoChannel(opInfo.opId); if (!opInfo.channel) { @@ -445,7 +453,7 @@ void QRandomAccessAsyncFilePrivate::executeRead(OperationInfo &opInfo) } } -void QRandomAccessAsyncFilePrivate::executeWrite(OperationInfo &opInfo) +void QRandomAccessAsyncFileNativeBackend::executeWrite(OperationInfo &opInfo) { opInfo.channel = duplicateIoChannel(opInfo.opId); if (!opInfo.channel) { @@ -494,7 +502,7 @@ void QRandomAccessAsyncFilePrivate::executeWrite(OperationInfo &opInfo) } } -void QRandomAccessAsyncFilePrivate::executeFlush(OperationInfo &opInfo) +void QRandomAccessAsyncFileNativeBackend::executeFlush(OperationInfo &opInfo) { opInfo.channel = duplicateIoChannel(opInfo.opId); if (!opInfo.channel) { @@ -523,7 +531,7 @@ void QRandomAccessAsyncFilePrivate::executeFlush(OperationInfo &opInfo) } } } else { - auto context = sharedThis->q_ptr; + auto context = sharedThis->m_owner; const OperationResult res = { opId, 0LL, err }; QMetaObject::invokeMethod(context, [sharedThis](const OperationResult &r) { sharedThis->handleOperationComplete(r); @@ -555,7 +563,7 @@ static inline int openModeToOpenFlags(QIODevice::OpenMode mode) return oflags; } -void QRandomAccessAsyncFilePrivate::executeOpen(OperationInfo &opInfo) +void QRandomAccessAsyncFileNativeBackend::executeOpen(OperationInfo &opInfo) { if (m_fileState != FileState::OpenPending) { queueCompletion(opInfo.opId, EINVAL); @@ -593,9 +601,10 @@ void QRandomAccessAsyncFilePrivate::executeOpen(OperationInfo &opInfo) // So we need to notify the condition variable in // both cases. Q_ASSERT(sharedThis->m_runningOps.isEmpty()); + Q_ASSERT(sharedThis->m_numChannelsToClose == 0); sharedThis->m_cancellationCondition.wakeOne(); } else { - auto context = sharedThis->q_ptr; + auto context = sharedThis->m_owner; const OperationResult res = { opId, qint64(fd), err }; QMetaObject::invokeMethod(context, [sharedThis](const OperationResult &r) { @@ -605,7 +614,7 @@ void QRandomAccessAsyncFilePrivate::executeOpen(OperationInfo &opInfo) }); } -void QRandomAccessAsyncFilePrivate::readOneBuffer(OperationId opId, qsizetype bufferIdx, +void QRandomAccessAsyncFileNativeBackend::readOneBuffer(OperationId opId, qsizetype bufferIdx, qint64 alreadyRead) { // we need to lookup the operation again, because it could have beed removed @@ -642,7 +651,7 @@ void QRandomAccessAsyncFilePrivate::readOneBuffer(OperationId opId, qsizetype bu readBuffers.size(), alreadyRead); } -void QRandomAccessAsyncFilePrivate::readOneBufferHelper(OperationId opId, dispatch_io_t channel, +void QRandomAccessAsyncFileNativeBackend::readOneBufferHelper(OperationId opId, dispatch_io_t channel, qint64 offset, void *bytesPtr, qint64 maxSize, qsizetype currentBufferIdx, qsizetype totalBuffers, qint64 alreadyRead) @@ -690,7 +699,7 @@ void QRandomAccessAsyncFilePrivate::readOneBufferHelper(OperationId opId, dispat } } else { sharedThis->m_runningOps.remove(opId); - auto context = sharedThis->q_ptr; + auto context = sharedThis->m_owner; // if error, or last buffer, or read less than expected, // report operation completion qint64 totalRead = qint64(readFromBuffer) + alreadyRead; @@ -713,7 +722,7 @@ void QRandomAccessAsyncFilePrivate::readOneBufferHelper(OperationId opId, dispat }); } -void QRandomAccessAsyncFilePrivate::writeHelper(OperationId opId, dispatch_io_t channel, +void QRandomAccessAsyncFileNativeBackend::writeHelper(OperationId opId, dispatch_io_t channel, qint64 offset, dispatch_data_t dataToWrite, qint64 dataSize) { @@ -752,7 +761,7 @@ void QRandomAccessAsyncFilePrivate::writeHelper(OperationId opId, dispatch_io_t const size_t written = dataSize - toBeWritten; [dataToWrite release]; - auto context = sharedThis->q_ptr; + auto context = sharedThis->m_owner; const OperationResult res = { opId, qint64(written), error }; QMetaObject::invokeMethod(context, [sharedThis](const OperationResult &r) { @@ -762,7 +771,7 @@ void QRandomAccessAsyncFilePrivate::writeHelper(OperationId opId, dispatch_io_t }); } -QRandomAccessAsyncFilePrivate::OperationId QRandomAccessAsyncFilePrivate::getNextId() +QRandomAccessAsyncFileNativeBackend::OperationId QRandomAccessAsyncFileNativeBackend::getNextId() { // never return reserved values static OperationId opId = kInvalidOperationId; diff --git a/src/corelib/io/qrandomaccessasyncfile_p_p.h b/src/corelib/io/qrandomaccessasyncfile_p_p.h index 11ad788c884..2eb53058780 100644 --- a/src/corelib/io/qrandomaccessasyncfile_p_p.h +++ b/src/corelib/io/qrandomaccessasyncfile_p_p.h @@ -22,7 +22,7 @@ #include <QtCore/qstring.h> -#ifdef QT_RANDOMACCESSASYNCFILE_THREAD +#if QT_CONFIG(future) && QT_CONFIG(thread) #include <QtCore/private/qfsfileengine_p.h> @@ -30,7 +30,7 @@ #include <QtCore/qmutex.h> #include <QtCore/qqueue.h> -#endif // QT_RANDOMACCESSASYNCFILE_THREAD +#endif // future && thread #ifdef Q_OS_DARWIN @@ -50,41 +50,36 @@ QT_BEGIN_NAMESPACE -class QRandomAccessAsyncFilePrivate : public QObjectPrivate +class QRandomAccessAsyncFileBackend { - Q_DECLARE_PUBLIC(QRandomAccessAsyncFile) - Q_DISABLE_COPY_MOVE(QRandomAccessAsyncFilePrivate) + Q_DISABLE_COPY_MOVE(QRandomAccessAsyncFileBackend) public: - QRandomAccessAsyncFilePrivate(); - ~QRandomAccessAsyncFilePrivate() override; - - static QRandomAccessAsyncFilePrivate *get(QRandomAccessAsyncFile *file) - { return file->d_func(); } + explicit QRandomAccessAsyncFileBackend(QRandomAccessAsyncFile *owner); + virtual ~QRandomAccessAsyncFileBackend(); - void init(); - void cancelAndWait(QIOOperation *op); + virtual bool init() = 0; + virtual void cancelAndWait(QIOOperation *op) = 0; - void close(); - qint64 size() const; + virtual void close() = 0; + virtual qint64 size() const = 0; - [[nodiscard]] QIOOperation *open(const QString &path, QIODeviceBase::OpenMode mode); - [[nodiscard]] QIOOperation *flush(); + [[nodiscard]] virtual QIOOperation *open(const QString &path, QIODeviceBase::OpenMode mode) = 0; + [[nodiscard]] virtual QIOOperation *flush() = 0; - [[nodiscard]] QIOReadOperation *read(qint64 offset, qint64 maxSize); - [[nodiscard]] QIOWriteOperation *write(qint64 offset, const QByteArray &data); - [[nodiscard]] QIOWriteOperation *write(qint64 offset, QByteArray &&data); + [[nodiscard]] virtual QIOReadOperation *read(qint64 offset, qint64 maxSize) = 0; + [[nodiscard]] virtual QIOWriteOperation *write(qint64 offset, const QByteArray &data) = 0; + [[nodiscard]] virtual QIOWriteOperation *write(qint64 offset, QByteArray &&data) = 0; - [[nodiscard]] QIOVectoredReadOperation * - readInto(qint64 offset, QSpan<std::byte> buffer); - [[nodiscard]] QIOVectoredWriteOperation * - writeFrom(qint64 offset, QSpan<const std::byte> buffer); - - [[nodiscard]] QIOVectoredReadOperation * - readInto(qint64 offset, QSpan<const QSpan<std::byte>> buffers); - [[nodiscard]] QIOVectoredWriteOperation * - writeFrom(qint64 offset, QSpan<const QSpan<const std::byte>> buffers); + [[nodiscard]] virtual QIOVectoredReadOperation * + readInto(qint64 offset, QSpan<std::byte> buffer) = 0; + [[nodiscard]] virtual QIOVectoredWriteOperation * + writeFrom(qint64 offset, QSpan<const std::byte> buffer) = 0; -private: + [[nodiscard]] virtual QIOVectoredReadOperation * + readInto(qint64 offset, QSpan<const QSpan<std::byte>> buffers) = 0; + [[nodiscard]] virtual QIOVectoredWriteOperation * + writeFrom(qint64 offset, QSpan<const QSpan<const std::byte>> buffers) = 0; +protected: // common for all backends enum class FileState : quint8 { @@ -94,32 +89,131 @@ private: }; QString m_filePath; + QRandomAccessAsyncFile *m_owner = nullptr; QIODeviceBase::OpenMode m_openMode; FileState m_fileState = FileState::Closed; +}; -#ifdef QT_RANDOMACCESSASYNCFILE_THREAD +class QRandomAccessAsyncFilePrivate : public QObjectPrivate +{ + Q_DECLARE_PUBLIC(QRandomAccessAsyncFile) + Q_DISABLE_COPY_MOVE(QRandomAccessAsyncFilePrivate) public: - struct OperationResult + QRandomAccessAsyncFilePrivate(); + ~QRandomAccessAsyncFilePrivate() override; + + static QRandomAccessAsyncFilePrivate *get(QRandomAccessAsyncFile *file) + { return file->d_func(); } + + void init(); + void cancelAndWait(QIOOperation *op) { - qint64 bytesProcessed; // either read or written - QIOOperation::Error error; - }; + checkValid(); + m_backend->cancelAndWait(op); + } + + void close() + { + checkValid(); + m_backend->close(); + } + qint64 size() const + { + checkValid(); + return m_backend->size(); + } + + [[nodiscard]] QIOOperation *open(const QString &path, QIODeviceBase::OpenMode mode) + { + checkValid(); + return m_backend->open(path, mode); + } + [[nodiscard]] QIOOperation *flush() + { + checkValid(); + return m_backend->flush(); + } + + [[nodiscard]] QIOReadOperation *read(qint64 offset, qint64 maxSize) + { + checkValid(); + return m_backend->read(offset, maxSize); + } + [[nodiscard]] QIOWriteOperation *write(qint64 offset, const QByteArray &data) + { + checkValid(); + return m_backend->write(offset, data); + } + [[nodiscard]] QIOWriteOperation *write(qint64 offset, QByteArray &&data) + { + checkValid(); + return m_backend->write(offset, std::move(data)); + } + + [[nodiscard]] QIOVectoredReadOperation *readInto(qint64 offset, QSpan<std::byte> buffer) + { + checkValid(); + return m_backend->readInto(offset, buffer); + } + [[nodiscard]] QIOVectoredWriteOperation *writeFrom(qint64 offset, QSpan<const std::byte> buffer) + { + checkValid(); + return m_backend->writeFrom(offset, buffer); + } + + [[nodiscard]] QIOVectoredReadOperation *readInto(qint64 offset, + QSpan<const QSpan<std::byte>> buffers) + { + checkValid(); + return m_backend->readInto(offset, buffers); + } + [[nodiscard]] QIOVectoredWriteOperation *writeFrom(qint64 offset, + QSpan<const QSpan<const std::byte>> buffers) + { + checkValid(); + return m_backend->writeFrom(offset, buffers); + } private: - mutable QBasicMutex m_engineMutex; - std::unique_ptr<QFSFileEngine> m_engine; - QFutureWatcher<OperationResult> m_watcher; + void checkValid() const { Q_ASSERT(m_backend); } + std::unique_ptr<QRandomAccessAsyncFileBackend> m_backend; - QQueue<QPointer<QIOOperation>> m_operations; - QPointer<QIOOperation> m_currentOperation; - qsizetype numProcessedBuffers = 0; +}; - void executeNextOperation(); - void processBufferAt(qsizetype idx); - void processFlush(); - void processOpen(); - void operationComplete(); -#elif defined(QT_RANDOMACCESSASYNCFILE_QIORING) + +#if defined(QT_RANDOMACCESSASYNCFILE_QIORING) || defined(Q_OS_DARWIN) +class QRandomAccessAsyncFileNativeBackend final : public QRandomAccessAsyncFileBackend +{ + Q_DISABLE_COPY_MOVE(QRandomAccessAsyncFileNativeBackend) +public: + explicit QRandomAccessAsyncFileNativeBackend(QRandomAccessAsyncFile *owner); + ~QRandomAccessAsyncFileNativeBackend(); + + bool init() override; + void cancelAndWait(QIOOperation *op) override; + + void close() override; + qint64 size() const override; + + [[nodiscard]] QIOOperation *open(const QString &path, QIODeviceBase::OpenMode mode) override; + [[nodiscard]] QIOOperation *flush() override; + + [[nodiscard]] QIOReadOperation *read(qint64 offset, qint64 maxSize) override; + [[nodiscard]] QIOWriteOperation *write(qint64 offset, const QByteArray &data) override; + [[nodiscard]] QIOWriteOperation *write(qint64 offset, QByteArray &&data) override; + + [[nodiscard]] QIOVectoredReadOperation * + readInto(qint64 offset, QSpan<std::byte> buffer) override; + [[nodiscard]] QIOVectoredWriteOperation * + writeFrom(qint64 offset, QSpan<const std::byte> buffer) override; + + [[nodiscard]] QIOVectoredReadOperation * + readInto(qint64 offset, QSpan<const QSpan<std::byte>> buffers) override; + [[nodiscard]] QIOVectoredWriteOperation * + writeFrom(qint64 offset, QSpan<const QSpan<const std::byte>> buffers) override; + +private: +#if defined(QT_RANDOMACCESSASYNCFILE_QIORING) void queueCompletion(QIOOperationPrivate *priv, QIOOperation::Error error); void startReadIntoSingle(QIOOperation *op, const QSpan<std::byte> &to); void startWriteFromSingle(QIOOperation *op, const QSpan<const std::byte> &from); @@ -210,6 +304,61 @@ private: dispatch_data_t dataToWrite, qint64 dataSize); #endif }; +#endif // QIORing || macOS + +#if QT_CONFIG(future) && QT_CONFIG(thread) +class QRandomAccessAsyncFileThreadPoolBackend : public QRandomAccessAsyncFileBackend +{ + Q_DISABLE_COPY_MOVE(QRandomAccessAsyncFileThreadPoolBackend) +public: + explicit QRandomAccessAsyncFileThreadPoolBackend(QRandomAccessAsyncFile *owner); + ~QRandomAccessAsyncFileThreadPoolBackend(); + + bool init() override; + void cancelAndWait(QIOOperation *op) override; + + void close() override; + qint64 size() const override; + + [[nodiscard]] QIOOperation *open(const QString &path, QIODeviceBase::OpenMode mode) override; + [[nodiscard]] QIOOperation *flush() override; + + [[nodiscard]] QIOReadOperation *read(qint64 offset, qint64 maxSize) override; + [[nodiscard]] QIOWriteOperation *write(qint64 offset, const QByteArray &data) override; + [[nodiscard]] QIOWriteOperation *write(qint64 offset, QByteArray &&data) override; + + [[nodiscard]] QIOVectoredReadOperation * + readInto(qint64 offset, QSpan<std::byte> buffer) override; + [[nodiscard]] QIOVectoredWriteOperation * + writeFrom(qint64 offset, QSpan<const std::byte> buffer) override; + + [[nodiscard]] QIOVectoredReadOperation * + readInto(qint64 offset, QSpan<const QSpan<std::byte>> buffers) override; + [[nodiscard]] QIOVectoredWriteOperation * + writeFrom(qint64 offset, QSpan<const QSpan<const std::byte>> buffers) override; + + struct OperationResult + { + qint64 bytesProcessed; // either read or written + QIOOperation::Error error; + }; +private: + + mutable QBasicMutex m_engineMutex; + std::unique_ptr<QFSFileEngine> m_engine; + QFutureWatcher<OperationResult> m_watcher; + + QQueue<QPointer<QIOOperation>> m_operations; + QPointer<QIOOperation> m_currentOperation; + qsizetype numProcessedBuffers = 0; + + void executeNextOperation(); + void processBufferAt(qsizetype idx); + void processFlush(); + void processOpen(); + void operationComplete(); +}; +#endif // future && thread QT_END_NAMESPACE diff --git a/src/corelib/io/qrandomaccessasyncfile_qioring.cpp b/src/corelib/io/qrandomaccessasyncfile_qioring.cpp index c9783ea2856..8e1145325d8 100644 --- a/src/corelib/io/qrandomaccessasyncfile_qioring.cpp +++ b/src/corelib/io/qrandomaccessasyncfile_qioring.cpp @@ -18,18 +18,21 @@ QT_BEGIN_NAMESPACE Q_STATIC_LOGGING_CATEGORY(lcQRandomAccessIORing, "qt.core.qrandomaccessasyncfile.ioring", QtCriticalMsg); -QRandomAccessAsyncFilePrivate::QRandomAccessAsyncFilePrivate() = default; +QRandomAccessAsyncFileNativeBackend::QRandomAccessAsyncFileNativeBackend(QRandomAccessAsyncFile *owner) + : QRandomAccessAsyncFileBackend(owner) +{} -QRandomAccessAsyncFilePrivate::~QRandomAccessAsyncFilePrivate() = default; +QRandomAccessAsyncFileNativeBackend::~QRandomAccessAsyncFileNativeBackend() = default; -void QRandomAccessAsyncFilePrivate::init() +bool QRandomAccessAsyncFileNativeBackend::init() { m_ioring = QIORing::sharedInstance(); if (!m_ioring) - qCCritical(lcQRandomAccessIORing, "QRandomAccessAsyncFile: ioring failed to initialize"); + qCWarning(lcQRandomAccessIORing, "QRandomAccessAsyncFile: ioring failed to initialize"); + return m_ioring != nullptr; } -QIORing::RequestHandle QRandomAccessAsyncFilePrivate::cancel(QIORing::RequestHandle handle) +QIORing::RequestHandle QRandomAccessAsyncFileNativeBackend::cancel(QIORing::RequestHandle handle) { if (handle) { QIORingRequest<QIORing::Operation::Cancel> cancelRequest; @@ -39,7 +42,7 @@ QIORing::RequestHandle QRandomAccessAsyncFilePrivate::cancel(QIORing::RequestHan return nullptr; } -void QRandomAccessAsyncFilePrivate::cancelAndWait(QIOOperation *op) +void QRandomAccessAsyncFileNativeBackend::cancelAndWait(QIOOperation *op) { auto *opHandle = m_opHandleMap.value(op); if (auto *handle = cancel(opHandle)) { @@ -48,7 +51,7 @@ void QRandomAccessAsyncFilePrivate::cancelAndWait(QIOOperation *op) } } -void QRandomAccessAsyncFilePrivate::queueCompletion(QIOOperationPrivate *priv, QIOOperation::Error error) +void QRandomAccessAsyncFileNativeBackend::queueCompletion(QIOOperationPrivate *priv, QIOOperation::Error error) { // Remove the handle now in case the user cancels or deletes the io-operation // before operationComplete is called - the null-handle will protect from @@ -61,14 +64,14 @@ void QRandomAccessAsyncFilePrivate::queueCompletion(QIOOperationPrivate *priv, Q }, Qt::QueuedConnection); } -QIOOperation *QRandomAccessAsyncFilePrivate::open(const QString &path, QIODeviceBase::OpenMode mode) +QIOOperation *QRandomAccessAsyncFileNativeBackend::open(const QString &path, QIODeviceBase::OpenMode mode) { auto *dataStorage = new QtPrivate::QIOOperationDataStorage(); auto *priv = new QIOOperationPrivate(dataStorage); priv->type = QIOOperation::Type::Open; - auto *op = new QIOOperation(*priv, q_ptr); + auto *op = new QIOOperation(*priv, m_owner); if (m_fileState != FileState::Closed) { queueCompletion(priv, QIOOperation::Error::Open); return op; @@ -115,7 +118,7 @@ QIOOperation *QRandomAccessAsyncFilePrivate::open(const QString &path, QIODevice return op; } -void QRandomAccessAsyncFilePrivate::close() +void QRandomAccessAsyncFileNativeBackend::close() { // all the operations should be aborted const auto ops = std::exchange(m_operations, {}); @@ -123,7 +126,7 @@ void QRandomAccessAsyncFilePrivate::close() // Request to cancel all of the in-flight operations: for (const auto &op : ops) { if (op) { - op->d_func()->error = QIOOperation::Error::Aborted; + QIOOperationPrivate::get(op)->error = QIOOperation::Error::Aborted; if (auto *opHandle = m_opHandleMap.value(op)) { tasksToAwait.append(cancel(opHandle)); tasksToAwait.append(opHandle); @@ -142,7 +145,7 @@ void QRandomAccessAsyncFilePrivate::close() m_fd = -1; } -qint64 QRandomAccessAsyncFilePrivate::size() const +qint64 QRandomAccessAsyncFileNativeBackend::size() const { QIORingRequest<QIORing::Operation::Stat> statRequest; statRequest.fd = m_fd; @@ -161,14 +164,14 @@ qint64 QRandomAccessAsyncFilePrivate::size() const return finalSize; } -QIOOperation *QRandomAccessAsyncFilePrivate::flush() +QIOOperation *QRandomAccessAsyncFileNativeBackend::flush() { auto *dataStorage = new QtPrivate::QIOOperationDataStorage(); auto *priv = new QIOOperationPrivate(dataStorage); priv->type = QIOOperation::Type::Flush; - auto *op = new QIOOperation(*priv, q_ptr); + auto *op = new QIOOperation(*priv, m_owner); m_operations.append(op); QIORingRequest<QIORing::Operation::Flush> flushRequest; @@ -192,7 +195,7 @@ QIOOperation *QRandomAccessAsyncFilePrivate::flush() return op; } -void QRandomAccessAsyncFilePrivate::startReadIntoSingle(QIOOperation *op, +void QRandomAccessAsyncFileNativeBackend::startReadIntoSingle(QIOOperation *op, const QSpan<std::byte> &to) { QIORingRequest<QIORing::Operation::Read> readRequest; @@ -231,7 +234,7 @@ void QRandomAccessAsyncFilePrivate::startReadIntoSingle(QIOOperation *op, m_opHandleMap.insert(priv->q_func(), m_ioring->queueRequest(std::move(readRequest))); } -QIOReadOperation *QRandomAccessAsyncFilePrivate::read(qint64 offset, qint64 maxSize) +QIOReadOperation *QRandomAccessAsyncFileNativeBackend::read(qint64 offset, qint64 maxSize) { QByteArray array; array.resizeForOverwrite(maxSize); @@ -241,7 +244,7 @@ QIOReadOperation *QRandomAccessAsyncFilePrivate::read(qint64 offset, qint64 maxS priv->offset = offset; priv->type = QIOOperation::Type::Read; - auto *op = new QIOReadOperation(*priv, q_ptr); + auto *op = new QIOReadOperation(*priv, m_owner); m_operations.append(op); startReadIntoSingle(op, as_writable_bytes(QSpan(dataStorage->getByteArray()))); @@ -249,12 +252,12 @@ QIOReadOperation *QRandomAccessAsyncFilePrivate::read(qint64 offset, qint64 maxS return op; } -QIOWriteOperation *QRandomAccessAsyncFilePrivate::write(qint64 offset, const QByteArray &data) +QIOWriteOperation *QRandomAccessAsyncFileNativeBackend::write(qint64 offset, const QByteArray &data) { return write(offset, QByteArray(data)); } -void QRandomAccessAsyncFilePrivate::startWriteFromSingle(QIOOperation *op, +void QRandomAccessAsyncFileNativeBackend::startWriteFromSingle(QIOOperation *op, const QSpan<const std::byte> &from) { QIORingRequest<QIORing::Operation::Write> writeRequest; @@ -288,7 +291,7 @@ void QRandomAccessAsyncFilePrivate::startWriteFromSingle(QIOOperation *op, m_opHandleMap.insert(priv->q_func(), m_ioring->queueRequest(std::move(writeRequest))); } -QIOWriteOperation *QRandomAccessAsyncFilePrivate::write(qint64 offset, QByteArray &&data) +QIOWriteOperation *QRandomAccessAsyncFileNativeBackend::write(qint64 offset, QByteArray &&data) { auto *dataStorage = new QtPrivate::QIOOperationDataStorage(std::move(data)); @@ -296,7 +299,7 @@ QIOWriteOperation *QRandomAccessAsyncFilePrivate::write(qint64 offset, QByteArra priv->offset = offset; priv->type = QIOOperation::Type::Write; - auto *op = new QIOWriteOperation(*priv, q_ptr); + auto *op = new QIOWriteOperation(*priv, m_owner); m_operations.append(op); startWriteFromSingle(op, as_bytes(QSpan(dataStorage->getByteArray()))); @@ -304,7 +307,7 @@ QIOWriteOperation *QRandomAccessAsyncFilePrivate::write(qint64 offset, QByteArra return op; } -QIOVectoredReadOperation *QRandomAccessAsyncFilePrivate::readInto(qint64 offset, +QIOVectoredReadOperation *QRandomAccessAsyncFileNativeBackend::readInto(qint64 offset, QSpan<std::byte> buffer) { auto *dataStorage = new QtPrivate::QIOOperationDataStorage( @@ -314,7 +317,7 @@ QIOVectoredReadOperation *QRandomAccessAsyncFilePrivate::readInto(qint64 offset, priv->offset = offset; priv->type = QIOOperation::Type::Read; - auto *op = new QIOVectoredReadOperation(*priv, q_ptr); + auto *op = new QIOVectoredReadOperation(*priv, m_owner); m_operations.append(op); startReadIntoSingle(op, dataStorage->getReadSpans().first()); @@ -322,7 +325,7 @@ QIOVectoredReadOperation *QRandomAccessAsyncFilePrivate::readInto(qint64 offset, return op; } -QIOVectoredWriteOperation *QRandomAccessAsyncFilePrivate::writeFrom(qint64 offset, +QIOVectoredWriteOperation *QRandomAccessAsyncFileNativeBackend::writeFrom(qint64 offset, QSpan<const std::byte> buffer) { auto *dataStorage = new QtPrivate::QIOOperationDataStorage( @@ -332,7 +335,7 @@ QIOVectoredWriteOperation *QRandomAccessAsyncFilePrivate::writeFrom(qint64 offse priv->offset = offset; priv->type = QIOOperation::Type::Write; - auto *op = new QIOVectoredWriteOperation(*priv, q_ptr); + auto *op = new QIOVectoredWriteOperation(*priv, m_owner); m_operations.append(op); startWriteFromSingle(op, dataStorage->getWriteSpans().first()); @@ -341,7 +344,7 @@ QIOVectoredWriteOperation *QRandomAccessAsyncFilePrivate::writeFrom(qint64 offse } QIOVectoredReadOperation * -QRandomAccessAsyncFilePrivate::readInto(qint64 offset, QSpan<const QSpan<std::byte>> buffers) +QRandomAccessAsyncFileNativeBackend::readInto(qint64 offset, QSpan<const QSpan<std::byte>> buffers) { if (!QIORing::supportsOperation(QtPrivate::Operation::VectoredRead)) return nullptr; @@ -351,7 +354,7 @@ QRandomAccessAsyncFilePrivate::readInto(qint64 offset, QSpan<const QSpan<std::by priv->offset = offset; priv->type = QIOOperation::Type::Read; - auto *op = new QIOVectoredReadOperation(*priv, q_ptr); + auto *op = new QIOVectoredReadOperation(*priv, m_owner); if (priv->offset < 0) { // The QIORing offset is unsigned, so error out now queueCompletion(priv, QIOOperation::Error::IncorrectOffset); return op; @@ -393,7 +396,7 @@ QRandomAccessAsyncFilePrivate::readInto(qint64 offset, QSpan<const QSpan<std::by } QIOVectoredWriteOperation * -QRandomAccessAsyncFilePrivate::writeFrom(qint64 offset, QSpan<const QSpan<const std::byte>> buffers) +QRandomAccessAsyncFileNativeBackend::writeFrom(qint64 offset, QSpan<const QSpan<const std::byte>> buffers) { if (!QIORing::supportsOperation(QtPrivate::Operation::VectoredWrite)) return nullptr; @@ -403,7 +406,7 @@ QRandomAccessAsyncFilePrivate::writeFrom(qint64 offset, QSpan<const QSpan<const priv->offset = offset; priv->type = QIOOperation::Type::Write; - auto *op = new QIOVectoredWriteOperation(*priv, q_ptr); + auto *op = new QIOVectoredWriteOperation(*priv, m_owner); if (priv->offset < 0) { // The QIORing offset is unsigned, so error out now queueCompletion(priv, QIOOperation::Error::IncorrectOffset); return op; diff --git a/src/corelib/io/qrandomaccessasyncfile_threadpool.cpp b/src/corelib/io/qrandomaccessasyncfile_threadpool.cpp index 4ebcf554655..774fbadc5ad 100644 --- a/src/corelib/io/qrandomaccessasyncfile_threadpool.cpp +++ b/src/corelib/io/qrandomaccessasyncfile_threadpool.cpp @@ -64,28 +64,29 @@ static SharedThreadPool asyncFileThreadPool; } // anonymous namespace -QRandomAccessAsyncFilePrivate::QRandomAccessAsyncFilePrivate() : - QObjectPrivate() +QRandomAccessAsyncFileThreadPoolBackend::QRandomAccessAsyncFileThreadPoolBackend(QRandomAccessAsyncFile *owner) : + QRandomAccessAsyncFileBackend(owner) { asyncFileThreadPool.ref(); } -QRandomAccessAsyncFilePrivate::~QRandomAccessAsyncFilePrivate() +QRandomAccessAsyncFileThreadPoolBackend::~QRandomAccessAsyncFileThreadPoolBackend() { asyncFileThreadPool.deref(); } -void QRandomAccessAsyncFilePrivate::init() +bool QRandomAccessAsyncFileThreadPoolBackend::init() { - QObject::connect(&m_watcher, &QFutureWatcherBase::finished, q_ptr, [this]{ + QObject::connect(&m_watcher, &QFutureWatcherBase::finished, m_owner, [this]{ operationComplete(); }); - QObject::connect(&m_watcher, &QFutureWatcherBase::canceled, q_ptr, [this]{ + QObject::connect(&m_watcher, &QFutureWatcherBase::canceled, m_owner, [this]{ operationComplete(); }); + return true; } -void QRandomAccessAsyncFilePrivate::cancelAndWait(QIOOperation *op) +void QRandomAccessAsyncFileThreadPoolBackend::cancelAndWait(QIOOperation *op) { if (op == m_currentOperation) { m_currentOperation = nullptr; // to discard the result @@ -97,7 +98,7 @@ void QRandomAccessAsyncFilePrivate::cancelAndWait(QIOOperation *op) } QIOOperation * -QRandomAccessAsyncFilePrivate::open(const QString &path, QIODeviceBase::OpenMode mode) +QRandomAccessAsyncFileThreadPoolBackend::open(const QString &path, QIODeviceBase::OpenMode mode) { // We generate the command in any case. But if the file is already opened, // it will finish with an error @@ -112,14 +113,14 @@ QRandomAccessAsyncFilePrivate::open(const QString &path, QIODeviceBase::OpenMode auto *priv = new QIOOperationPrivate(dataStorage); priv->type = QIOOperation::Type::Open; - auto *op = new QIOOperation(*priv, q_ptr); + auto *op = new QIOOperation(*priv, m_owner); m_operations.append(op); executeNextOperation(); return op; } -void QRandomAccessAsyncFilePrivate::close() +void QRandomAccessAsyncFileThreadPoolBackend::close() { // all the operations should be aborted for (const auto &op : std::as_const(m_operations)) { @@ -148,7 +149,7 @@ void QRandomAccessAsyncFilePrivate::close() m_fileState = FileState::Closed; } -qint64 QRandomAccessAsyncFilePrivate::size() const +qint64 QRandomAccessAsyncFileThreadPoolBackend::size() const { QMutexLocker locker(&m_engineMutex); if (m_engine) @@ -156,20 +157,20 @@ qint64 QRandomAccessAsyncFilePrivate::size() const return -1; } -QIOOperation *QRandomAccessAsyncFilePrivate::flush() +QIOOperation *QRandomAccessAsyncFileThreadPoolBackend::flush() { auto *dataStorage = new QtPrivate::QIOOperationDataStorage(); auto *priv = new QIOOperationPrivate(dataStorage); priv->type = QIOOperation::Type::Flush; - auto *op = new QIOOperation(*priv, q_ptr); + auto *op = new QIOOperation(*priv, m_owner); m_operations.append(op); executeNextOperation(); return op; } -QIOReadOperation *QRandomAccessAsyncFilePrivate::read(qint64 offset, qint64 maxSize) +QIOReadOperation *QRandomAccessAsyncFileThreadPoolBackend::read(qint64 offset, qint64 maxSize) { QByteArray array; array.resizeForOverwrite(maxSize); @@ -179,14 +180,14 @@ QIOReadOperation *QRandomAccessAsyncFilePrivate::read(qint64 offset, qint64 maxS priv->offset = offset; priv->type = QIOOperation::Type::Read; - auto *op = new QIOReadOperation(*priv, q_ptr); + auto *op = new QIOReadOperation(*priv, m_owner); m_operations.append(op); executeNextOperation(); return op; } QIOWriteOperation * -QRandomAccessAsyncFilePrivate::write(qint64 offset, const QByteArray &data) +QRandomAccessAsyncFileThreadPoolBackend::write(qint64 offset, const QByteArray &data) { auto *dataStorage = new QtPrivate::QIOOperationDataStorage(data); @@ -194,14 +195,14 @@ QRandomAccessAsyncFilePrivate::write(qint64 offset, const QByteArray &data) priv->offset = offset; priv->type = QIOOperation::Type::Write; - auto *op = new QIOWriteOperation(*priv, q_ptr); + auto *op = new QIOWriteOperation(*priv, m_owner); m_operations.append(op); executeNextOperation(); return op; } QIOWriteOperation * -QRandomAccessAsyncFilePrivate::write(qint64 offset, QByteArray &&data) +QRandomAccessAsyncFileThreadPoolBackend::write(qint64 offset, QByteArray &&data) { auto *dataStorage = new QtPrivate::QIOOperationDataStorage(std::move(data)); @@ -209,14 +210,14 @@ QRandomAccessAsyncFilePrivate::write(qint64 offset, QByteArray &&data) priv->offset = offset; priv->type = QIOOperation::Type::Write; - auto *op = new QIOWriteOperation(*priv, q_ptr); + auto *op = new QIOWriteOperation(*priv, m_owner); m_operations.append(op); executeNextOperation(); return op; } QIOVectoredReadOperation * -QRandomAccessAsyncFilePrivate::readInto(qint64 offset, QSpan<std::byte> buffer) +QRandomAccessAsyncFileThreadPoolBackend::readInto(qint64 offset, QSpan<std::byte> buffer) { auto *dataStorage = new QtPrivate::QIOOperationDataStorage(QSpan<const QSpan<std::byte>>{buffer}); @@ -225,14 +226,14 @@ QRandomAccessAsyncFilePrivate::readInto(qint64 offset, QSpan<std::byte> buffer) priv->offset = offset; priv->type = QIOOperation::Type::Read; - auto *op = new QIOVectoredReadOperation(*priv, q_ptr); + auto *op = new QIOVectoredReadOperation(*priv, m_owner); m_operations.append(op); executeNextOperation(); return op; } QIOVectoredWriteOperation * -QRandomAccessAsyncFilePrivate::writeFrom(qint64 offset, QSpan<const std::byte> buffer) +QRandomAccessAsyncFileThreadPoolBackend::writeFrom(qint64 offset, QSpan<const std::byte> buffer) { auto *dataStorage = new QtPrivate::QIOOperationDataStorage(QSpan<const QSpan<const std::byte>>{buffer}); @@ -241,14 +242,14 @@ QRandomAccessAsyncFilePrivate::writeFrom(qint64 offset, QSpan<const std::byte> b priv->offset = offset; priv->type = QIOOperation::Type::Write; - auto *op = new QIOVectoredWriteOperation(*priv, q_ptr); + auto *op = new QIOVectoredWriteOperation(*priv, m_owner); m_operations.append(op); executeNextOperation(); return op; } QIOVectoredReadOperation * -QRandomAccessAsyncFilePrivate::readInto(qint64 offset, QSpan<const QSpan<std::byte>> buffers) +QRandomAccessAsyncFileThreadPoolBackend::readInto(qint64 offset, QSpan<const QSpan<std::byte>> buffers) { auto *dataStorage = new QtPrivate::QIOOperationDataStorage(buffers); @@ -256,14 +257,14 @@ QRandomAccessAsyncFilePrivate::readInto(qint64 offset, QSpan<const QSpan<std::by priv->offset = offset; priv->type = QIOOperation::Type::Read; - auto *op = new QIOVectoredReadOperation(*priv, q_ptr); + auto *op = new QIOVectoredReadOperation(*priv, m_owner); m_operations.append(op); executeNextOperation(); return op; } QIOVectoredWriteOperation * -QRandomAccessAsyncFilePrivate::writeFrom(qint64 offset, QSpan<const QSpan<const std::byte>> buffers) +QRandomAccessAsyncFileThreadPoolBackend::writeFrom(qint64 offset, QSpan<const QSpan<const std::byte>> buffers) { auto *dataStorage = new QtPrivate::QIOOperationDataStorage(buffers); @@ -271,16 +272,16 @@ QRandomAccessAsyncFilePrivate::writeFrom(qint64 offset, QSpan<const QSpan<const priv->offset = offset; priv->type = QIOOperation::Type::Write; - auto *op = new QIOVectoredWriteOperation(*priv, q_ptr); + auto *op = new QIOVectoredWriteOperation(*priv, m_owner); m_operations.append(op); executeNextOperation(); return op; } -static QRandomAccessAsyncFilePrivate::OperationResult +static QRandomAccessAsyncFileThreadPoolBackend::OperationResult executeRead(QFSFileEngine *engine, QBasicMutex *mutex, qint64 offset, char *buffer, qint64 maxSize) { - QRandomAccessAsyncFilePrivate::OperationResult result{0, QIOOperation::Error::None}; + QRandomAccessAsyncFileThreadPoolBackend::OperationResult result{0, QIOOperation::Error::None}; QMutexLocker locker(mutex); if (engine) { @@ -299,11 +300,11 @@ executeRead(QFSFileEngine *engine, QBasicMutex *mutex, qint64 offset, char *buff return result; } -static QRandomAccessAsyncFilePrivate::OperationResult +static QRandomAccessAsyncFileThreadPoolBackend::OperationResult executeWrite(QFSFileEngine *engine, QBasicMutex *mutex, qint64 offset, const char *buffer, qint64 size) { - QRandomAccessAsyncFilePrivate::OperationResult result{0, QIOOperation::Error::None}; + QRandomAccessAsyncFileThreadPoolBackend::OperationResult result{0, QIOOperation::Error::None}; QMutexLocker locker(mutex); if (engine) { @@ -322,7 +323,7 @@ executeWrite(QFSFileEngine *engine, QBasicMutex *mutex, qint64 offset, return result; } -void QRandomAccessAsyncFilePrivate::executeNextOperation() +void QRandomAccessAsyncFileThreadPoolBackend::executeNextOperation() { if (m_currentOperation.isNull()) { // start next @@ -351,7 +352,7 @@ void QRandomAccessAsyncFilePrivate::executeNextOperation() } } -void QRandomAccessAsyncFilePrivate::processBufferAt(qsizetype idx) +void QRandomAccessAsyncFileThreadPoolBackend::processBufferAt(qsizetype idx) { Q_ASSERT(!m_currentOperation.isNull()); auto *priv = QIOOperationPrivate::get(m_currentOperation.get()); @@ -417,7 +418,7 @@ void QRandomAccessAsyncFilePrivate::processBufferAt(qsizetype idx) } } -void QRandomAccessAsyncFilePrivate::processFlush() +void QRandomAccessAsyncFileThreadPoolBackend::processFlush() { Q_ASSERT(!m_currentOperation.isNull()); auto *priv = QIOOperationPrivate::get(m_currentOperation.get()); @@ -427,7 +428,7 @@ void QRandomAccessAsyncFilePrivate::processFlush() QBasicMutex *mutexPtr = &m_engineMutex; auto op = [engine = m_engine.get(), mutexPtr] { QMutexLocker locker(mutexPtr); - QRandomAccessAsyncFilePrivate::OperationResult result{0, QIOOperation::Error::None}; + QRandomAccessAsyncFileThreadPoolBackend::OperationResult result{0, QIOOperation::Error::None}; if (engine) { if (!engine->flush()) result.error = QIOOperation::Error::Flush; @@ -442,7 +443,7 @@ void QRandomAccessAsyncFilePrivate::processFlush() m_watcher.setFuture(f); } -void QRandomAccessAsyncFilePrivate::processOpen() +void QRandomAccessAsyncFileThreadPoolBackend::processOpen() { Q_ASSERT(!m_currentOperation.isNull()); auto *priv = QIOOperationPrivate::get(m_currentOperation.get()); @@ -457,7 +458,7 @@ void QRandomAccessAsyncFilePrivate::processOpen() m_engineMutex.unlock(); QBasicMutex *mutexPtr = &m_engineMutex; auto op = [engine = m_engine.get(), mutexPtr, mode = m_openMode] { - QRandomAccessAsyncFilePrivate::OperationResult result{0, QIOOperation::Error::None}; + QRandomAccessAsyncFileThreadPoolBackend::OperationResult result{0, QIOOperation::Error::None}; QMutexLocker locker(mutexPtr); const bool res = engine && engine->open(mode | QIODeviceBase::Unbuffered, std::nullopt); @@ -468,13 +469,13 @@ void QRandomAccessAsyncFilePrivate::processOpen() f = QtFuture::makeReadyVoidFuture().then(asyncFileThreadPool(), op); } else { f = QtFuture::makeReadyVoidFuture().then(asyncFileThreadPool(), [] { - return QRandomAccessAsyncFilePrivate::OperationResult{0, QIOOperation::Error::Open}; + return QRandomAccessAsyncFileThreadPoolBackend::OperationResult{0, QIOOperation::Error::Open}; }); } m_watcher.setFuture(f); } -void QRandomAccessAsyncFilePrivate::operationComplete() +void QRandomAccessAsyncFileThreadPoolBackend::operationComplete() { // TODO: if one of the buffers was read/written with an error, // stop processing immediately diff --git a/src/corelib/io/qwindowspipereader.cpp b/src/corelib/io/qwindowspipereader.cpp index 456a209af37..6edf4395887 100644 --- a/src/corelib/io/qwindowspipereader.cpp +++ b/src/corelib/io/qwindowspipereader.cpp @@ -14,6 +14,11 @@ using namespace Qt::StringLiterals; static const DWORD minReadBufferSize = 4096; +/*! + \class QWindowsPipeReader + \inmodule QtCore + \internal +*/ QWindowsPipeReader::QWindowsPipeReader(QObject *parent) : QObject(parent), handle(INVALID_HANDLE_VALUE), diff --git a/src/corelib/itemmodels/qrangemodel_impl.h b/src/corelib/itemmodels/qrangemodel_impl.h index 0233727f848..7eca3094a66 100644 --- a/src/corelib/itemmodels/qrangemodel_impl.h +++ b/src/corelib/itemmodels/qrangemodel_impl.h @@ -239,17 +239,15 @@ namespace QRangeModelDetails : std::true_type {}; - // we use std::rotate in moveRows/Columns, which requires std::swap and the - // iterators to be at least a forward iterator - template <typename It, typename = void> - struct test_rotate : std::false_type {}; - + // we use std::rotate in moveRows/Columns, which requires the values (which + // might be const if we only get a const iterator) to be swappable, and the + // iterator type to be at least a forward iterator template <typename It> - struct test_rotate<It, std::void_t<decltype(std::swap(*std::declval<It>(), - *std::declval<It>()))>> - : std::is_base_of<std::forward_iterator_tag, - typename std::iterator_traits<It>::iterator_category> - {}; + using test_rotate = std::conjunction< + std::is_swappable<decltype(*std::declval<It>())>, + std::is_base_of<std::forward_iterator_tag, + typename std::iterator_traits<It>::iterator_category> + >; template <typename C, typename = void> struct test_splice : std::false_type {}; @@ -1373,25 +1371,34 @@ public: using multi_role = QRangeModelDetails::is_multi_role<value_type>; auto setRangeModelDataRole = [&target, &data]{ - auto &targetRef = QRangeModelDetails::refTo(target); constexpr auto targetMetaType = QMetaType::fromType<value_type>(); const auto dataMetaType = data.metaType(); constexpr bool isWrapped = QRangeModelDetails::is_wrapped<value_type>(); if constexpr (!std::is_copy_assignable_v<wrapped_value_type>) { // we don't support replacing objects that are stored as raw pointers, // as this makes object ownership very messy. But we can replace objects - // stored in smart pointers. - if constexpr (isWrapped && !std::is_pointer_v<value_type> - && std::is_copy_assignable_v<value_type>) { - if (data.canConvert(targetMetaType)) { - target = data.value<value_type>(); - return true; + // stored in smart pointers, and we can initialize raw nullptr objects. + if constexpr (isWrapped) { + constexpr bool is_raw_pointer = std::is_pointer_v<value_type>; + if constexpr (!is_raw_pointer && std::is_copy_assignable_v<value_type>) { + if (data.canConvert(targetMetaType)) { + target = data.value<value_type>(); + return true; + } + } else if constexpr (is_raw_pointer) { + if (!QRangeModelDetails::isValid(target) && data.canConvert(targetMetaType)) { + target = data.value<value_type>(); + return true; + } + } else { + Q_UNUSED(target); } } // Otherwise we have a move-only or polymorph type. fall through to // error handling. } else if constexpr (isWrapped) { if (QRangeModelDetails::isValid(target)) { + auto &targetRef = QRangeModelDetails::refTo(target); // we need to get a wrapped value type out of the QVariant, which // might carry a pointer. We have to try all alternatives. if (const auto mt = QMetaType::fromType<wrapped_value_type>(); @@ -1405,10 +1412,10 @@ public: } } } else if (targetMetaType == dataMetaType) { - targetRef = data.value<value_type>(); + QRangeModelDetails::refTo(target) = data.value<value_type>(); return true; } else if (dataMetaType.flags() & QMetaType::PointerToGadget) { - targetRef = *data.value<value_type *>(); + QRangeModelDetails::refTo(target) = *data.value<value_type *>(); return true; } #ifndef QT_NO_DEBUG @@ -1463,6 +1470,13 @@ public: }; success = writeAt(index, writeData); + + if constexpr (itemsAreQObjects) { + if (success && isRangeModelRole(role) && this->autoConnectPolicy() == AutoConnectPolicy::Full) { + if (QObject *item = data.value<QObject *>()) + Self::connectProperties(index, item, m_data.context, m_data.properties); + } + } } return success; } diff --git a/src/corelib/itemmodels/qrangemodeladapter.h b/src/corelib/itemmodels/qrangemodeladapter.h index bd3342e6185..0234c402248 100644 --- a/src/corelib/itemmodels/qrangemodeladapter.h +++ b/src/corelib/itemmodels/qrangemodeladapter.h @@ -19,13 +19,11 @@ class QT_TECH_PREVIEW_API QRangeModelAdapter #ifdef Q_QDOC using range_type = Range; - using const_row_reference = typename std::iterator_traits<Range>::const_reference; - using row_reference = typename std::iterator_traits<Range>::reference; #else using range_type = QRangeModelDetails::wrapped_t<Range>; +#endif using const_row_reference = typename Impl::const_row_reference; using row_reference = typename Impl::row_reference; -#endif using range_features = typename QRangeModelDetails::range_traits<range_type>; using row_type = std::remove_reference_t<row_reference>; using row_features = QRangeModelDetails::range_traits<typename Impl::wrapped_row_type>; @@ -78,16 +76,22 @@ class QT_TECH_PREVIEW_API QRangeModelAdapter template <typename C> using if_compatible_row_range = std::enable_if_t<is_compatible_row_range<C>, bool>; template <typename Data> - static constexpr bool is_compatible_data = true; - // std::is_convertible_v<Data, decltype(*std::begin(std::declval<const_row_reference>()))>; + static constexpr bool is_compatible_data = std::is_convertible_v<Data, data_type>; template <typename Data> using if_compatible_data = std::enable_if_t<is_compatible_data<Data>, bool>; template <typename C> static constexpr bool is_compatible_data_range = is_compatible_data< + typename QRangeModelDetails::data_type< + typename QRangeModelDetails::row_traits< decltype(*std::begin(std::declval<C&>())) - >; + >::item_type + >::type + >; template <typename C> - using if_compatible_data_range = std::enable_if_t<is_compatible_data_range<C>, bool>; + using if_compatible_column_data = std::enable_if_t<is_compatible_data<C> + || is_compatible_data_range<C>, bool>; + template <typename C> + using if_compatible_column_range = std::enable_if_t<is_compatible_data_range<C>, bool>; template <typename R> using if_assignable_range = std::enable_if_t<std::is_assignable_v<range_type, R>, bool>; @@ -141,6 +145,7 @@ public: } DataReference(const DataReference &other) = default; + DataReference(DataReference &&other) = default; // reference (not std::reference_wrapper) semantics DataReference &operator=(const DataReference &other) @@ -149,29 +154,23 @@ public: return *this; } + DataReference &operator=(DataReference &&other) + { + *this = other.get(); + return *this; + } + ~DataReference() = default; DataReference &operator=(const value_type &value) { - constexpr Qt::ItemDataRole dataRole = Qt::RangeModelAdapterRole; + assign(value); + return *this; + } - if (m_index.isValid()) { - auto model = const_cast<QAbstractItemModel *>(m_index.model()); - [[maybe_unused]] bool couldWrite = false; - if constexpr (std::is_same_v<q20::remove_cvref_t<value_type>, QVariant>) - couldWrite = model->setData(m_index, value, dataRole); - else - couldWrite = model->setData(m_index, QVariant::fromValue(value), dataRole); -#ifndef QT_NO_DEBUG - if (!couldWrite) { - qWarning() << "Writing value of type" << QMetaType::fromType<value_type>().name() - << "to role" << dataRole << "at index" << m_index - << "of the model failed"; - } - } else { - qCritical("Data reference for invalid index, can't write to model"); -#endif - } + DataReference &operator=(value_type &&value) + { + assign(std::move(value)); return *this; } @@ -196,6 +195,33 @@ public: private: QModelIndex m_index; + template <typename Value> + void assign(Value &&value) + { + constexpr Qt::ItemDataRole dataRole = Qt::RangeModelAdapterRole; + + if (m_index.isValid()) { + auto model = const_cast<QAbstractItemModel *>(m_index.model()); + [[maybe_unused]] bool couldWrite = false; + if constexpr (std::is_same_v<q20::remove_cvref_t<Value>, QVariant>) { + couldWrite = model->setData(m_index, value, dataRole); + } else { + couldWrite = model->setData(m_index, + QVariant::fromValue(std::forward<Value>(value)), + dataRole); + } +#ifndef QT_NO_DEBUG + if (!couldWrite) { + qWarning() << "Writing value of type" + << QMetaType::fromType<q20::remove_cvref_t<Value>>().name() + << "to role" << dataRole << "at index" << m_index << "failed"; + } + } else { + qCritical("Data reference for invalid index, can't write to model"); +#endif + } + } + friend inline bool comparesEqual(const DataReference &lhs, const DataReference &rhs) { return lhs.m_index == rhs.m_index @@ -1020,60 +1046,55 @@ public: template <typename NewRange = range_type, if_assignable_range<NewRange> = true> void setRange(NewRange &&newRange) { - using namespace QRangeModelDetails; - - auto *impl = storage.implementation(); - const QModelIndex root = storage.root(); - const qsizetype newLastRow = qsizetype(Impl::size(refTo(newRange))) - 1; - auto *oldRange = impl->childRange(root); - const qsizetype oldLastRow = qsizetype(Impl::size(oldRange)) - 1; - - if (!root.isValid()) { - impl->beginResetModel(); - impl->deleteOwnedRows(); - } else if constexpr (is_tree<Impl>) { - if (oldLastRow > 0) { - impl->beginRemoveRows(root, 0, model()->rowCount(root) - 1); - impl->deleteRemovedRows(refTo(oldRange)); - impl->endRemoveRows(); - } - if (newLastRow > 0) - impl->beginInsertRows(root, 0, newLastRow); - } else { - Q_ASSERT_X(false, "QRangeModelAdapter::setRange", - "Internal error: The root index in a table or list must be invalid."); - } - refTo(oldRange) = std::forward<NewRange>(newRange); - if (!root.isValid()) { - impl->endResetModel(); - } else if constexpr (is_tree<Impl>) { - if (newLastRow > 0) { - Q_ASSERT(model()->hasChildren(root)); - // if it was moved, then newRange is now likely to be empty. Get - // the inserted row. - impl->setParentRow(refTo(impl->childRange(storage.root())), - pointerTo(impl->rowData(root))); - impl->endInsertRows(); - } - } - if constexpr (Impl::itemsAreQObjects) { - if (model()->autoConnectPolicy() == QRangeModel::AutoConnectPolicy::Full) { - const auto begin = QRangeModelDetails::begin(refTo(oldRange)); - const auto end = QRangeModelDetails::end(refTo(oldRange)); - int rowIndex = 0; - for (auto it = begin; it != end; ++it, ++rowIndex) - impl->autoConnectPropertiesInRow(*it, rowIndex, root); - } - } + setRangeImpl(qsizetype(Impl::size(QRangeModelDetails::refTo(newRange))) - 1, + [&newRange](auto &oldRange) { + oldRange = std::forward<NewRange>(newRange); + }); } - template <typename NewRange = range_type, if_assignable_range<NewRange> = true> + template <typename NewRange = range_type, if_assignable_range<NewRange> = true, + unless_adapter<NewRange> = true> QRangeModelAdapter &operator=(NewRange &&newRange) { setRange(std::forward<NewRange>(newRange)); return *this; } + template <typename Row, if_assignable_range<std::initializer_list<Row>> = true> + void setRange(std::initializer_list<Row> newRange) + { + setRangeImpl(qsizetype(newRange.size() - 1), [&newRange](auto &oldRange) { + oldRange = newRange; + }); + } + + template <typename Row, if_assignable_range<std::initializer_list<Row>> = true> + QRangeModelAdapter &operator=(std::initializer_list<Row> newRange) + { + setRange(newRange); + return *this; + } + + template <typename Row, if_assignable_range<std::initializer_list<Row>> = true> + void assign(std::initializer_list<Row> newRange) + { + setRange(newRange); + } + + template <typename InputIterator, typename Sentinel, typename I = Impl, if_writable<I> = true> + void setRange(InputIterator first, Sentinel last) + { + setRangeImpl(qsizetype(std::distance(first, last) - 1), [first, last](auto &oldRange) { + oldRange.assign(first, last); + }); + } + + template <typename InputIterator, typename Sentinel, typename I = Impl, if_writable<I> = true> + void assign(InputIterator first, Sentinel last) + { + setRange(first, last); + } + // iterator API ConstRowIterator cbegin() const { @@ -1245,12 +1266,12 @@ public: decltype(auto) operator[](int row) const { return at(row); } template <typename I = Impl, if_table<I> = true, if_writable<I> = true> - decltype(auto) at(int row) + auto at(int row) { return RowReference{index(row, 0), this}; } template <typename I = Impl, if_table<I> = true, if_writable<I> = true> - decltype(auto) operator[](int row) { return at(row); } + auto operator[](int row) { return at(row); } // at/operator[int, int] for table: returns value at row/column template <typename I = Impl, unless_list<I> = true> @@ -1428,15 +1449,15 @@ public: return storage.m_model->insertColumn(before); } - template <typename D = row_type, typename I = Impl, - if_canInsertColumns<I> = true, if_compatible_data<D> = true> + template <typename D, typename I = Impl, + if_canInsertColumns<I> = true, if_compatible_column_data<D> = true> bool insertColumn(int before, D &&data) { return insertColumnImpl(before, storage.root(), std::forward<D>(data)); } template <typename C, typename I = Impl, - if_canInsertColumns<I> = true, if_compatible_data_range<C> = true> + if_canInsertColumns<I> = true, if_compatible_column_range<C> = true> bool insertColumns(int before, C &&data) { return insertColumnsImpl(before, storage.root(), std::forward<C>(data)); @@ -1503,6 +1524,65 @@ private: Q_EMIT storage.implementation()->dataChanged(topLeft, bottomRight, {}); } + void beginSetRangeImpl(Impl *impl, range_type *oldRange, qsizetype newLastRow) + { + const QModelIndex root = storage.root(); + const qsizetype oldLastRow = qsizetype(Impl::size(oldRange)) - 1; + + if (!root.isValid()) { + impl->beginResetModel(); + impl->deleteOwnedRows(); + } else if constexpr (is_tree<Impl>) { + if (oldLastRow > 0) { + impl->beginRemoveRows(root, 0, model()->rowCount(root) - 1); + impl->deleteRemovedRows(QRangeModelDetails::refTo(oldRange)); + impl->endRemoveRows(); + } + if (newLastRow > 0) + impl->beginInsertRows(root, 0, newLastRow); + } else { + Q_ASSERT_X(false, "QRangeModelAdapter::setRange", + "Internal error: The root index in a table or list must be invalid."); + } + } + + void endSetRangeImpl(Impl *impl, qsizetype newLastRow) + { + const QModelIndex root = storage.root(); + if (!root.isValid()) { + impl->endResetModel(); + } else if constexpr (is_tree<Impl>) { + if (newLastRow > 0) { + Q_ASSERT(model()->hasChildren(root)); + // if it was moved, then newRange is now likely to be empty. Get + // the inserted row. + impl->setParentRow(QRangeModelDetails::refTo(impl->childRange(root)), + QRangeModelDetails::pointerTo(impl->rowData(root))); + impl->endInsertRows(); + } + } + } + + template <typename Assigner> + void setRangeImpl(qsizetype newLastRow, Assigner &&assigner) + { + auto *impl = storage.implementation(); + auto *oldRange = impl->childRange(storage.root()); + beginSetRangeImpl(impl, oldRange, newLastRow); + assigner(QRangeModelDetails::refTo(oldRange)); + endSetRangeImpl(impl, newLastRow); + + if constexpr (Impl::itemsAreQObjects) { + if (model()->autoConnectPolicy() == QRangeModel::AutoConnectPolicy::Full) { + const auto begin = QRangeModelDetails::begin(QRangeModelDetails::refTo(oldRange)); + const auto end = QRangeModelDetails::end(QRangeModelDetails::refTo(oldRange)); + int rowIndex = 0; + for (auto it = begin; it != end; ++it, ++rowIndex) + impl->autoConnectPropertiesInRow(*it, rowIndex, storage.root()); + } + } + } + template <typename P> static auto setParentRow(P protocol, row_type &newRow, row_ptr parentRow) -> decltype(protocol.setParentRow(std::declval<row_type&>(), std::declval<row_ptr>())) diff --git a/src/corelib/itemmodels/qrangemodeladapter.qdoc b/src/corelib/itemmodels/qrangemodeladapter.qdoc index 263bff0dd0c..88872589299 100644 --- a/src/corelib/itemmodels/qrangemodeladapter.qdoc +++ b/src/corelib/itemmodels/qrangemodeladapter.qdoc @@ -277,14 +277,6 @@ */ /*! - \typedef QRangeModelAdapter::const_row_reference -*/ - -/*! - \typedef QRangeModelAdapter::row_reference -*/ - -/*! \typedef QRangeModelAdapter::range_type */ @@ -329,16 +321,39 @@ /*! \fn template <typename Range, typename Protocol, typename Model> template <typename NewRange, QRangeModelAdapter<Range, Protocol, Model>::if_assignable_range<NewRange>> void QRangeModelAdapter<Range, Protocol, Model>::setRange(NewRange &&newRange) - \fn template <typename Range, typename Protocol, typename Model> template <typename NewRange, QRangeModelAdapter<Range, Protocol, Model>::if_assignable_range<NewRange>> QRangeModelAdapter &QRangeModelAdapter<Range, Protocol, Model>::operator=(NewRange &&newRange) + \fn template <typename Range, typename Protocol, typename Model> template <typename NewRange, QRangeModelAdapter<Range, Protocol, Model>::if_assignable_range<NewRange>, QRangeModelAdapter<Range, Protocol, Model>::unless_adapter<NewRange>> QRangeModelAdapter &QRangeModelAdapter<Range, Protocol, Model>::operator=(NewRange &&newRange) + + Replaces the contents of the model with the rows in \a newRange, possibly + using move semantics. - Assigns \a newRange to the stored range, possibly using move semantics. This function makes the model() emit the \l{QAbstractItemModel::}{modelAboutToBeReset()} and \l{QAbstractItemModel::}{modelReset()} signals. \constraints \c Range is mutable, and \a newRange is of a type that can be - assigned to \c Range. + assigned to \c Range, but not a QRangeModelAdapter. + + \sa range(), at(), model(), assign() +*/ + +/*! + \fn template <typename Range, typename Protocol, typename Model> template <typename Row, QRangeModelAdapter<Range, Protocol, Model>::if_assignable_range<std::initializer_list<Row>>> void QRangeModelAdapter<Range, Protocol, Model>::setRange(std::initializer_list<Row> newRange) + \fn template <typename Range, typename Protocol, typename Model> template <typename Row, QRangeModelAdapter<Range, Protocol, Model>::if_assignable_range<std::initializer_list<Row>>> QRangeModelAdapter &QRangeModelAdapter<Range, Protocol, Model>::assign(std::initializer_list<Row> newRange) + \fn template <typename Range, typename Protocol, typename Model> template <typename Row, QRangeModelAdapter<Range, Protocol, Model>::if_assignable_range<std::initializer_list<Row>>> QRangeModelAdapter &QRangeModelAdapter<Range, Protocol, Model>::operator=(std::initializer_list<Row> newRange) + + Replaces the contents of the model with the rows in \a newRange. - \sa range(), at(), model() + \constraints \c Range is mutable, and \a newRange can be assigned to \c Range. + + \sa range(), at, model() +*/ + +/*! + \fn template <typename Range, typename Protocol, typename Model> template <typename InputIterator, typename Sentinel, typename I = Impl, QRangeModelAdapter<Range, Protocol, Model>::if_writable<I>> void QRangeModelAdapter<Range, Protocol, Model>::setRange(InputIterator first, Sentinel last) + \fn template <typename Range, typename Protocol, typename Model> template <typename InputIterator, typename Sentinel, typename I = Impl, QRangeModelAdapter<Range, Protocol, Model>::if_writable<I>> void QRangeModelAdapter<Range, Protocol, Model>::assign(InputIterator first, Sentinel last) + + Replaces the contents of the models with the rows in the range [\a first, \a last). + + \sa range(), at, model() */ /*! @@ -508,8 +523,8 @@ */ /*! - \fn template <typename Range, typename Protocol, typename Model> template <typename I, QRangeModelAdapter<Range, Protocol, Model>::unless_list<I>> QRangeModelAdapter<Range, Protocol, Model>::const_row_reference QRangeModelAdapter<Range, Protocol, Model>::at(int row) const - \fn template <typename Range, typename Protocol, typename Model> template <typename I, QRangeModelAdapter<Range, Protocol, Model>::unless_list<I>> QRangeModelAdapter<Range, Protocol, Model>::const_row_reference QRangeModelAdapter<Range, Protocol, Model>::operator[](int row) const + \fn template <typename Range, typename Protocol, typename Model> template <typename I, QRangeModelAdapter<Range, Protocol, Model>::unless_list<I>> decltype(auto) QRangeModelAdapter<Range, Protocol, Model>::at(int row) const + \fn template <typename Range, typename Protocol, typename Model> template <typename I, QRangeModelAdapter<Range, Protocol, Model>::unless_list<I>> decltype(auto) QRangeModelAdapter<Range, Protocol, Model>::operator[](int row) const \return a constant reference to the row at \a row, as stored in \c Range. @@ -517,8 +532,8 @@ */ /*! - \fn template <typename Range, typename Protocol, typename Model> template <typename I, QRangeModelAdapter<Range, Protocol, Model>::if_table<I>, QRangeModelAdapter<Range, Protocol, Model>::if_writable<I>> QRangeModelAdapter<Range, Protocol, Model>::row_reference QRangeModelAdapter<Range, Protocol, Model>::at(int row) - \fn template <typename Range, typename Protocol, typename Model> template <typename I, QRangeModelAdapter<Range, Protocol, Model>::if_table<I>, QRangeModelAdapter<Range, Protocol, Model>::if_writable<I>> QRangeModelAdapter<Range, Protocol, Model>::row_reference QRangeModelAdapter<Range, Protocol, Model>::operator[](int row) + \fn template <typename Range, typename Protocol, typename Model> template <typename I, QRangeModelAdapter<Range, Protocol, Model>::if_table<I>, QRangeModelAdapter<Range, Protocol, Model>::if_writable<I>> auto QRangeModelAdapter<Range, Protocol, Model>::at(int row) + \fn template <typename Range, typename Protocol, typename Model> template <typename I, QRangeModelAdapter<Range, Protocol, Model>::if_table<I>, QRangeModelAdapter<Range, Protocol, Model>::if_writable<I>> auto QRangeModelAdapter<Range, Protocol, Model>::operator[](int row) \return a mutable reference to the row at \a row, as stored in \c Range. @@ -574,6 +589,16 @@ */ /*! + \fn template <typename Range, typename Protocol, typename Model> template <typename I, QRangeModelAdapter<Range, Protocol, Model>::if_tree<I>> decltype(auto) QRangeModelAdapter<Range, Protocol, Model>::at(QSpan<const int> path) const + \fn template <typename Range, typename Protocol, typename Model> template <typename I, QRangeModelAdapter<Range, Protocol, Model>::if_tree<I>> decltype(auto) QRangeModelAdapter<Range, Protocol, Model>::operator[](QSpan<const int> path) const + + \return a constant reference to the row specified by \a path, as stored in + \c Range. + + \constraints \c Range is a tree. +*/ + +/*! \fn template <typename Range, typename Protocol, typename Model> template <typename I, QRangeModelAdapter<Range, Protocol, Model>::if_tree<I>, QRangeModelAdapter<Range, Protocol, Model>::if_writable<I>> auto QRangeModelAdapter<Range, Protocol, Model>::at(QSpan<const int> path) \fn template <typename Range, typename Protocol, typename Model> template <typename I, QRangeModelAdapter<Range, Protocol, Model>::if_tree<I>, QRangeModelAdapter<Range, Protocol, Model>::if_writable<I>> auto QRangeModelAdapter<Range, Protocol, Model>::operator[](QSpan<const int> path) @@ -810,7 +835,7 @@ */ /*! - \fn template <typename Range, typename Protocol, typename Model> template <typename D, typename I, QRangeModelAdapter<Range, Protocol, Model>::if_canInsertColumns<I>, QRangeModelAdapter<Range, Protocol, Model>::if_compatible_data<D>> bool QRangeModelAdapter<Range, Protocol, Model>::insertColumn(int before, D &&data) + \fn template <typename Range, typename Protocol, typename Model> template <typename D, typename I, QRangeModelAdapter<Range, Protocol, Model>::if_canInsertColumns<I>, QRangeModelAdapter<Range, Protocol, Model>::if_compatible_column_data<D>> bool QRangeModelAdapter<Range, Protocol, Model>::insertColumn(int before, D &&data) \overload Inserts a single column constructed from \a data before the column specified @@ -838,7 +863,7 @@ */ /*! - \fn template <typename Range, typename Protocol, typename Model> template <typename C, typename I, QRangeModelAdapter<Range, Protocol, Model>::if_canInsertColumns<I>, QRangeModelAdapter<Range, Protocol, Model>::if_compatible_data_range<C>> bool QRangeModelAdapter<Range, Protocol, Model>::insertColumns(int before, C &&data) + \fn template <typename Range, typename Protocol, typename Model> template <typename C, typename I, QRangeModelAdapter<Range, Protocol, Model>::if_canInsertColumns<I>, QRangeModelAdapter<Range, Protocol, Model>::if_compatible_column_range<C>> bool QRangeModelAdapter<Range, Protocol, Model>::insertColumns(int before, C &&data) Inserts columns constructed from the elements in \a data before the column specified by \a before into all rows, and returns whether the insertion was diff --git a/src/corelib/kernel/qassociativeiterable.h b/src/corelib/kernel/qassociativeiterable.h index 39f66d45fa0..4f9bbf67bfb 100644 --- a/src/corelib/kernel/qassociativeiterable.h +++ b/src/corelib/kernel/qassociativeiterable.h @@ -156,10 +156,12 @@ inline QVariantRef<QAssociativeIterator>::operator QVariant() const if (!metaType.isValid()) return m_pointer->key(); + return [&] { QVariant v(metaType); metaAssociation.mappedAtIterator(m_pointer->constIterator(), metaType == QMetaType::fromType<QVariant>() ? &v : v.data()); return v; + }(); } template<> diff --git a/src/corelib/kernel/qcore_mac.mm b/src/corelib/kernel/qcore_mac.mm index 687fc7e85fa..f5f1f4a8cb8 100644 --- a/src/corelib/kernel/qcore_mac.mm +++ b/src/corelib/kernel/qcore_mac.mm @@ -22,6 +22,10 @@ #include <spawn.h> #include <qdebug.h> +#include <qpoint.h> +#include <qsize.h> +#include <qrect.h> +#include <qmargins.h> #include "qendian.h" #include "qhash.h" @@ -222,6 +226,34 @@ QDebug operator<<(QDebug dbg, CFStringRef stringRef) return dbg; } +QDebug operator<<(QDebug dbg, CGPoint point) +{ + dbg << QPointF::fromCGPoint(point); + return dbg; +} + +QDebug operator<<(QDebug dbg, CGSize size) +{ + dbg << QSizeF::fromCGSize(size); + return dbg; +} + +QDebug operator<<(QDebug dbg, CGRect rect) +{ + dbg << QRectF::fromCGRect(rect); + return dbg; +} + +#if defined(Q_OS_MACOS) +QDebug operator<<(QDebug dbg, NSEdgeInsets insets) +#else +QDebug operator<<(QDebug dbg, UIEdgeInsets insets) +#endif +{ + dbg << QMargins(insets.left, insets.top, insets.right, insets.bottom); + return dbg; +} + // Prevents breaking the ODR in case we introduce support for more types // later on, and lets the user override our default QDebug operators. #define QT_DECLARE_WEAK_QDEBUG_OPERATOR_FOR_CF_TYPE(CFType) \ diff --git a/src/corelib/kernel/qcore_mac_p.h b/src/corelib/kernel/qcore_mac_p.h index 2c4b4c02c55..1e57ee01e1d 100644 --- a/src/corelib/kernel/qcore_mac_p.h +++ b/src/corelib/kernel/qcore_mac_p.h @@ -78,6 +78,15 @@ kern_return_t IOObjectRelease(io_object_t object); Q_FORWARD_DECLARE_OBJC_CLASS(NSObject); Q_FORWARD_DECLARE_OBJC_CLASS(NSString); +struct CGPoint; +struct CGSize; +struct CGRect; +#if defined(Q_OS_MACOS) +struct NSEdgeInsets; +#else +struct UIEdgeInsets; +#endif + // @compatibility_alias doesn't work with categories or their methods #define QtExtras QT_MANGLE_NAMESPACE(QtExtras) @@ -225,6 +234,14 @@ Q_AUTOTEST_EXPORT void qt_mac_ensureResponsible(); #ifndef QT_NO_DEBUG_STREAM Q_CORE_EXPORT QDebug operator<<(QDebug debug, const QMacAutoReleasePool *pool); Q_CORE_EXPORT QDebug operator<<(QDebug debug, const QCFString &string); +Q_CORE_EXPORT QDebug operator<<(QDebug, CGPoint); +Q_CORE_EXPORT QDebug operator<<(QDebug, CGSize); +Q_CORE_EXPORT QDebug operator<<(QDebug, CGRect); +#if defined(Q_OS_MACOS) +Q_CORE_EXPORT QDebug operator<<(QDebug, NSEdgeInsets); +#else +Q_CORE_EXPORT QDebug operator<<(QDebug, UIEdgeInsets); +#endif #endif Q_CORE_EXPORT bool qt_apple_isApplicationExtension(); diff --git a/src/corelib/kernel/qeventdispatcher_cf.mm b/src/corelib/kernel/qeventdispatcher_cf.mm index 2dddf147f85..d57c057a21a 100644 --- a/src/corelib/kernel/qeventdispatcher_cf.mm +++ b/src/corelib/kernel/qeventdispatcher_cf.mm @@ -207,6 +207,7 @@ QEventLoop *QEventDispatcherCoreFoundation::currentEventLoop() const } /*! + \internal Processes all pending events that match \a flags until there are no more events to process. Returns \c true if pending events were handled; otherwise returns \c false. diff --git a/src/corelib/kernel/qiterable.h b/src/corelib/kernel/qiterable.h index baab2897967..494cec73a3f 100644 --- a/src/corelib/kernel/qiterable.h +++ b/src/corelib/kernel/qiterable.h @@ -502,7 +502,10 @@ public: if (m_metaContainer.hasSize()) return m_metaContainer.size(container); - // ### Qt7: Return -1 here. We shouldn't second-guess the underlying container +#if QT_VERSION >= QT_VERSION_CHECK(7, 0, 0) + // We shouldn't second-guess the underlying container, so we're not synthesizing a size. + return -1; +#else QtPrivate::warnSynthesizedAccess( "size() called on an iterable without native size accessor. This is slow"); @@ -515,6 +518,7 @@ public: m_metaContainer.destroyConstIterator(begin); m_metaContainer.destroyConstIterator(end); return size; +#endif } void clear() diff --git a/src/corelib/kernel/qjniobject.cpp b/src/corelib/kernel/qjniobject.cpp index abef9fdd663..21bfe5af448 100644 --- a/src/corelib/kernel/qjniobject.cpp +++ b/src/corelib/kernel/qjniobject.cpp @@ -27,7 +27,8 @@ using namespace Qt::StringLiterals; garbage-collected and providing access to most \c JNIEnv method calls (member, static) and fields (setter, getter). It eliminates much boiler-plate that would normally be needed, with direct JNI access, for - every operation, including exception-handling. + every operation. Exceptions thrown by called Java methods are cleared by + default, but can since Qt 6.11 also be handled by the caller. \note This API has been designed and tested for use with Android. It has not been tested for other platforms. @@ -129,12 +130,46 @@ using namespace Qt::StringLiterals; Note that while the first template parameter specifies the return type of the Java function, the method will still return a QJniObject. - \section1 Handling Java Exception + \section1 Handling Java Exceptions 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. + QJniObject functions can handle exceptions internally by reporting and + clearing them. This includes JNI exceptions, for instance when trying to + call a method that doesn't exist, or with bad parameters; and exceptions + are thrown by the method as a way of reporting errors or returning failure + information. + + From Qt 6.11 on, client code can opt in to handle exceptions explicitly in + each call. To do so, use \c{std::expected} from C++ 23 as the return type, + with the value type as the expected, and \c{jthrowable} as the error type. + For instance, trying to read a setting value via the + \c{android.provider.Settings.Secure} type might throw an exception if the + setting does not exist. + + \code + Q_DECLARE_JNI_CLASS(SettingsSecure, "android/provider/Settings$Secure") + using namespace QtJniTypes; + + QString enabledInputMethods() + { + ContentResolver resolver; + SettingsSecure settings; + + auto defaultInputMethods = settings.callMethod<std::expected<QString, jthrowable>>( + "getString", resolver, u"enabled_input_methods"_s + ); + if (defaultInputMethods) + return defaultInputMethods.value(); + QStringList stackTrace = QJniEnvironment::stackTrace(defaultInputMethods.error()); + } + \endcode + + You can use any other type that behaves like \c{std::expected}, so handling + exceptions explicitly is possible without using C++23. The only + requirements are that the type declares three nested types \c{value_type}, + \c{error_type}, and \c{unexpected_type}, can be constructed from the value + type, and from its \c{unexpected_type} holding a \c{jthrowable}. \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 @@ -921,7 +956,10 @@ QByteArray QJniObject::className() const jint size = myJavaString.callMethod<jint>("length"); \endcode - The method signature is deduced at compile time from \c Ret and the types of \a args. + The method signature is deduced at compile time from \c Ret and the types + of \a args. \c Ret can be a \c{std::expected}-compatible type that returns + a value, or \l{Handling Java Exceptions}{any Java exception thrown} by the + called method. */ /*! @@ -952,7 +990,10 @@ QByteArray QJniObject::className() const jint value = QJniObject::callStaticMethod<jint>("MyClass", "staticMethod"); \endcode - The method signature is deduced at compile time from \c Ret and the types of \a args. + The method signature is deduced at compile time from \c Ret and the types + of \a args. \c Ret can be a \c{std::expected}-compatible type that returns + a value, or \l{Handling Java Exceptions}{any Java exception thrown} by the + called method. */ /*! @@ -1009,7 +1050,10 @@ QByteArray QJniObject::className() const jdouble randNr = QJniObject::callStaticMethod<jdouble>(javaMathClass, "random"); \endcode - The method signature is deduced at compile time from \c Ret and the types of \a args. + The method signature is deduced at compile time from \c Ret and the types + of \a args. \c Ret can be a \c{std::expected}-compatible type that returns + a value, or \l{Handling Java Exceptions}{any Java exception thrown} by the + called method. */ /*! @@ -1020,8 +1064,12 @@ QByteArray QJniObject::className() const \c Ret (unless \c Ret is \c void). If \c Ret is a jobject type, then the returned value will be a QJniObject. - The method signature is deduced at compile time from \c Ret and the types of \a args. - \c Klass needs to be a C++ type with a registered type mapping to a Java type. + The method signature is deduced at compile time from \c Ret and the types + of \a args. \c Klass needs to be a C++ type with a registered type mapping + to a Java type. \c Ret can be a \c{std::expected}-compatible type that + returns a value, or \l{Handling Java Exceptions}{any Java exception thrown} + by the called method. + */ /*! @@ -1150,7 +1198,10 @@ QJniObject QJniObject::callStaticObjectMethod(jclass clazz, jmethodID methodId, QJniObject myJavaString2 = myJavaString1.callObjectMethod<jstring>("toString"); \endcode - The method signature is deduced at compile time from \c Ret and the types of \a args. + The method signature is deduced at compile time from \c Ret and the types + of \a args. \c Ret can be a \c{std::expected}-compatible type that returns + a value, or \l{Handling Java Exceptions}{any Java exception thrown} by the + called method. */ /*! @@ -1164,7 +1215,10 @@ QJniObject QJniObject::callStaticObjectMethod(jclass clazz, jmethodID methodId, QJniObject string = QJniObject::callStaticObjectMethod<jstring>("CustomClass", "getClassName"); \endcode - The method signature is deduced at compile time from \c Ret and the types of \a args. + The method signature is deduced at compile time from \c Ret and the types + of \a args. \c Ret can be a \c{std::expected}-compatible type that returns + a value, or \l{Handling Java Exceptions}{any Java exception thrown} by the + called method. */ /*! diff --git a/src/corelib/kernel/qmetaassociation.h b/src/corelib/kernel/qmetaassociation.h index 6d8de13e90a..d481ae91079 100644 --- a/src/corelib/kernel/qmetaassociation.h +++ b/src/corelib/kernel/qmetaassociation.h @@ -25,8 +25,8 @@ public: using reference = QVariant::Reference<AssociativeIterator>; using pointer = QVariant::Pointer<AssociativeIterator>; - static constexpr bool canNoexceptAssignQVariant = false; - static constexpr bool canNoexceptConvertToQVariant = false; + static constexpr bool CanNoexceptAssignQVariant = false; + static constexpr bool CanNoexceptConvertToQVariant = false; AssociativeIterator(QIterator &&it) : QIterator(std::move(it)) {} @@ -51,7 +51,7 @@ public: using reference = QVariant::ConstReference<AssociativeConstIterator>; using pointer = QVariant::ConstPointer<AssociativeConstIterator>; - static constexpr bool canNoexceptConvertToQVariant = false; + static constexpr bool CanNoexceptConvertToQVariant = false; AssociativeConstIterator(QConstIterator &&it) : QConstIterator(std::move(it)) {} diff --git a/src/corelib/kernel/qmetaobject.cpp b/src/corelib/kernel/qmetaobject.cpp index 24cc58829c8..c7e50788b45 100644 --- a/src/corelib/kernel/qmetaobject.cpp +++ b/src/corelib/kernel/qmetaobject.cpp @@ -469,6 +469,33 @@ QMetaType QMetaObject::metaType() const } } +static inline QByteArrayView objectMetaObjectHash(const QMetaObject *m) +{ + // metaObjectHash didn't exist before revision 14 + if (QT_VERSION < QT_VERSION_CHECK(7, 0, 0) && priv(m->d.data)->revision < 14) + return {}; + const auto index = priv(m->d.data)->metaObjectHashIndex; + if (index == -1) + return {}; + return stringDataView(m, index); +} + +/*! + \since 6.11 + + Returns the revisioned hash of the contents of this QMetaObject or nullptr. + + The hash has the following format <hash_revision>$<hash_b64>, where + hash_revision is an integer and hash_b64 is the base64 encoding of the + hash. + + Note that only hashes of the same revision should be compared. +*/ +const char *QMetaObject::metaObjectHash() const +{ + return objectMetaObjectHash(this).constData(); +} + /*! Returns the method offset for this class; i.e. the index position of this class's first member function. diff --git a/src/corelib/kernel/qmetaobject_p.h b/src/corelib/kernel/qmetaobject_p.h index bfda30fda28..7264d2a956f 100644 --- a/src/corelib/kernel/qmetaobject_p.h +++ b/src/corelib/kernel/qmetaobject_p.h @@ -124,6 +124,7 @@ struct QMetaObjectPrivate int constructorCount, constructorData; int flags; int signalCount; + int metaObjectHashIndex; static inline const QMetaObjectPrivate *get(const QMetaObject *metaobject) { return reinterpret_cast<const QMetaObjectPrivate*>(metaobject->d.data); } diff --git a/src/corelib/kernel/qmetaobjectbuilder.cpp b/src/corelib/kernel/qmetaobjectbuilder.cpp index 6065bf2baea..9af6de73680 100644 --- a/src/corelib/kernel/qmetaobjectbuilder.cpp +++ b/src/corelib/kernel/qmetaobjectbuilder.cpp @@ -558,6 +558,8 @@ QMetaPropertyBuilder QMetaObjectBuilder::addProperty(const QMetaProperty &protot property.setEnumOrFlag(prototype.isEnumType()); property.setConstant(prototype.isConstant()); property.setFinal(prototype.isFinal()); + property.setVirtual(prototype.isVirtual()); + property.setOverride(prototype.isOverride()); property.setRevision(prototype.revision()); if (prototype.hasNotifySignal()) { // Find an existing method for the notify signal, or add a new one. @@ -1177,10 +1179,11 @@ static int buildMetaObject(QMetaObjectBuilderPrivate *d, char *buf, int methodParametersDataSize = aggregateParameterCount(d->methods) + aggregateParameterCount(d->constructors); if constexpr (mode == Construct) { - static_assert(QMetaObjectPrivate::OutputRevision == 13, "QMetaObjectBuilder should generate the same version as moc"); + static_assert(QMetaObjectPrivate::OutputRevision == 14, "QMetaObjectBuilder should generate the same version as moc"); pmeta->revision = QMetaObjectPrivate::OutputRevision; pmeta->flags = d->flags.toInt() | AllocatedMetaObject; pmeta->className = 0; // Class name is always the first string. + pmeta->metaObjectHashIndex = -1; // TODO support hash in the builder too //pmeta->signalCount is handled in the "output method loop" as an optimization. pmeta->classInfoCount = d->classInfoNames.size(); @@ -2068,6 +2071,32 @@ bool QMetaPropertyBuilder::isFinal() const } /*! + Returns \c true if the property is virtual; otherwise returns \c false. + The default value is false. +*/ +bool QMetaPropertyBuilder::isVirtual() const +{ + QMetaPropertyBuilderPrivate *d = d_func(); + if (d) + return d->flag(Virtual); + else + return false; +} + +/*! + Returns \c true if the property does override; otherwise returns \c false. + The default value is false. +*/ +bool QMetaPropertyBuilder::isOverride() const +{ + QMetaPropertyBuilderPrivate *d = d_func(); + if (d) + return d->flag(Override); + else + return false; +} + +/*! * Returns \c true if the property is an alias. * The default value is false */ @@ -2239,6 +2268,30 @@ void QMetaPropertyBuilder::setFinal(bool value) } /*! + Sets the \c VIRTUAL flag on this property to \a value. + + \sa isFinal() +*/ +void QMetaPropertyBuilder::setVirtual(bool value) +{ + QMetaPropertyBuilderPrivate *d = d_func(); + if (d) + d->setFlag(Virtual, value); +} + +/*! + Sets the \c OVERRIDE flag on this property to \a value. + + \sa isOverride() +*/ +void QMetaPropertyBuilder::setOverride(bool value) +{ + QMetaPropertyBuilderPrivate *d = d_func(); + if (d) + d->setFlag(Override, value); +} + +/*! Sets the \c ALIAS flag on this property to \a value */ void QMetaPropertyBuilder::setAlias(bool value) diff --git a/src/corelib/kernel/qmetaobjectbuilder_p.h b/src/corelib/kernel/qmetaobjectbuilder_p.h index 563704d60e6..9591944602a 100644 --- a/src/corelib/kernel/qmetaobjectbuilder_p.h +++ b/src/corelib/kernel/qmetaobjectbuilder_p.h @@ -214,6 +214,8 @@ public: bool isEnumOrFlag() const; bool isConstant() const; bool isFinal() const; + bool isVirtual() const; + bool isOverride() const; bool isAlias() const; bool isBindable() const; bool isRequired() const; @@ -229,6 +231,8 @@ public: void setEnumOrFlag(bool value); void setConstant(bool value); void setFinal(bool value); + void setVirtual(bool value); + void setOverride(bool value); void setAlias(bool value); void setBindable(bool value); void setRequired(bool value); diff --git a/src/corelib/kernel/qmetasequence.h b/src/corelib/kernel/qmetasequence.h index e9505054159..26156e7924f 100644 --- a/src/corelib/kernel/qmetasequence.h +++ b/src/corelib/kernel/qmetasequence.h @@ -24,8 +24,8 @@ public: using reference = QVariant::Reference<SequentialIterator>; using pointer = QVariant::Pointer<SequentialIterator>; - static constexpr bool canNoexceptAssignQVariant = false; - static constexpr bool canNoexceptConvertToQVariant = false; + static constexpr bool CanNoexceptAssignQVariant = false; + static constexpr bool CanNoexceptConvertToQVariant = false; SequentialIterator(QIterator &&it) : QIterator(std::move(it)) {} @@ -41,7 +41,7 @@ public: using reference = QVariant::ConstReference<SequentialConstIterator>; using pointer = QVariant::ConstPointer<SequentialConstIterator>; - static constexpr bool canNoexceptConvertToQVariant = false; + static constexpr bool CanNoexceptConvertToQVariant = false; SequentialConstIterator(QConstIterator &&it) : QConstIterator(std::move(it)) {} @@ -184,13 +184,15 @@ public: return; } - // ### Qt7: Drop this code. We shouldn't second-guess the underlying container +#if QT_VERSION < QT_VERSION_CHECK(7, 0, 0) + // We shouldn't second-guess the underlying container. QtPrivate::warnSynthesizedAccess( "at() called on an iterable without native indexed accessors. This is slow"); void *it = meta.constBegin(m_iterable.constPointer()); meta.advanceConstIterator(it, idx); meta.valueAtConstIterator(it, dataPtr); meta.destroyConstIterator(it); +#endif }); } @@ -204,13 +206,15 @@ public: return; } - // ### Qt7: Drop this code. We shouldn't second-guess the underlying container +#if QT_VERSION < QT_VERSION_CHECK(7, 0, 0) + // We shouldn't second-guess the underlying container QtPrivate::warnSynthesizedAccess( "set() called on an iterable without native indexed accessors. This is slow"); void *it = meta.begin(m_iterable.mutablePointer()); meta.advanceIterator(it, idx); meta.setValueAtIterator(it, dataPtr); meta.destroyIterator(it); +#endif } void append(const QVariant &value) @@ -248,55 +252,14 @@ public: Unspecified, AtBegin, AtEnd }; - QT_DEPRECATED_VERSION_X_6_11("Use append() or prepend() instead.") void addValue(const QVariant &value, Position position = Unspecified) - { - const QMetaSequence meta = metaContainer(); - QtPrivate::QVariantTypeCoercer coercer; - const void *valuePtr = coercer.coerce(value, meta.valueMetaType()); - - switch (position) { - case AtBegin: - if (meta.canAddValueAtBegin()) - meta.addValueAtBegin(mutableIterable(), valuePtr); - break; - case AtEnd: - if (meta.canAddValueAtEnd()) - meta.addValueAtEnd(mutableIterable(), valuePtr); - break; - case Unspecified: - if (meta.canAddValue()) - meta.addValue(mutableIterable(), valuePtr); - break; - } - } + Q_DECL_EQ_DELETE_X("Use append() or prepend() instead."); - QT_DEPRECATED_VERSION_X_6_11("Use removeLast() or removeFirst() instead.") void removeValue(Position position = Unspecified) - { - const QMetaSequence meta = metaContainer(); + Q_DECL_EQ_DELETE_X("Use removeLast() or removeFirst() instead."); - switch (position) { - case AtBegin: - if (meta.canRemoveValueAtBegin()) - meta.removeValueAtBegin(mutableIterable()); - break; - case AtEnd: - if (meta.canRemoveValueAtEnd()) - meta.removeValueAtEnd(mutableIterable()); - break; - case Unspecified: - if (meta.canRemoveValue()) - meta.removeValue(mutableIterable()); - break; - } - } - - QT_DEPRECATED_VERSION_X_6_11("Use QMetaSequence::valueMetaType() instead.") QMetaType valueMetaType() const - { - return metaContainer().valueMetaType(); - } + Q_DECL_EQ_DELETE_X("Use QMetaSequence::valueMetaType() instead."); QT_WARNING_POP #endif // QT_DEPRECATED_SINCE(6, 11) diff --git a/src/corelib/kernel/qobjectdefs.h b/src/corelib/kernel/qobjectdefs.h index 848102cc57a..d3e761982f5 100644 --- a/src/corelib/kernel/qobjectdefs.h +++ b/src/corelib/kernel/qobjectdefs.h @@ -247,6 +247,8 @@ struct Q_CORE_EXPORT QMetaObject QMetaType metaType() const; + const char *metaObjectHash() const; + int methodOffset() const; int enumeratorOffset() const; int propertyOffset() const; diff --git a/src/corelib/kernel/qpermissions.cpp b/src/corelib/kernel/qpermissions.cpp index 6767917e176..69383dafdd9 100644 --- a/src/corelib/kernel/qpermissions.cpp +++ b/src/corelib/kernel/qpermissions.cpp @@ -119,7 +119,7 @@ Q_LOGGING_CATEGORY(lcPermissions, "qt.permissions", QtWarningMsg); To ensure the relevant permission backend is included with your application, please \l {QT_ANDROID_PACKAGE_SOURCE_DIR} {point the build system to your custom \c AndroidManifest.xml} - or use \l {qt_add_android_permission()}. + or use \l {qt_add_android_permission}(). The relevant permission names are described in the documentation for each permission type. diff --git a/src/corelib/kernel/qproperty.cpp b/src/corelib/kernel/qproperty.cpp index d538ed7b4e9..9141a8f8bad 100644 --- a/src/corelib/kernel/qproperty.cpp +++ b/src/corelib/kernel/qproperty.cpp @@ -402,6 +402,16 @@ QPropertyBindingPrivate::NotificationState QPropertyBindingPrivate::notifyNonRec } /*! + \class QUntypedPropertyBinding + \inmodule QtCore + \since 6.0 + \ingroup tools + \brief Represents a type-erased property binding. + + \sa QUntypedBindable +*/ + +/*! Constructs a null QUntypedPropertyBinding. \sa isNull() @@ -409,8 +419,8 @@ QPropertyBindingPrivate::NotificationState QPropertyBindingPrivate::notifyNonRec QUntypedPropertyBinding::QUntypedPropertyBinding() = default; /*! - \fn template<typename Functor> - QUntypedPropertyBinding(QMetaType metaType, Functor &&f, const QPropertyBindingSourceLocation &location) + \fn template<typename Functor> QUntypedPropertyBinding( + QMetaType metaType, Functor &&f, const QPropertyBindingSourceLocation &location) \internal */ @@ -448,7 +458,6 @@ QUntypedPropertyBinding::QUntypedPropertyBinding(const QUntypedPropertyBinding & : d(other.d) { } - /*! Copy-assigns \a other to this QUntypedPropertyBinding. */ @@ -1183,7 +1192,7 @@ QString QPropertyBindingError::description() const \return \c true when the binding was successfully set. - //! \sa QUntypedPropertyBinding::valueMetaType() + \sa QUntypedPropertyBinding::valueMetaType() */ /*! @@ -1199,8 +1208,7 @@ QString QPropertyBindingError::description() const Returns the metatype of the property from which the QUntypedBindable was created. If the bindable is invalid, an invalid metatype will be returned. - \sa isValid() - //! \sa QUntypedPropertyBinding::valueMetaType() + \sa isValid(), QUntypedPropertyBinding::valueMetaType() */ /*! diff --git a/src/corelib/kernel/qsequentialiterable.h b/src/corelib/kernel/qsequentialiterable.h index 92252cb19dd..76908bdae4b 100644 --- a/src/corelib/kernel/qsequentialiterable.h +++ b/src/corelib/kernel/qsequentialiterable.h @@ -142,10 +142,13 @@ inline QVariantRef<QSequentialIterator>::operator QVariant() const if (m_pointer == nullptr) return QVariant(); const QMetaType metaType(m_pointer->metaContainer().valueMetaType()); + + return [&] { QVariant v(metaType); void *dataPtr = metaType == QMetaType::fromType<QVariant>() ? &v : v.data(); m_pointer->metaContainer().valueAtIterator(m_pointer->constIterator(), dataPtr); return v; + }(); } template<> diff --git a/src/corelib/kernel/qtmocconstants.h b/src/corelib/kernel/qtmocconstants.h index 260ac2fa5f8..822e02e6c8e 100644 --- a/src/corelib/kernel/qtmocconstants.h +++ b/src/corelib/kernel/qtmocconstants.h @@ -30,7 +30,8 @@ namespace QtMocConstants { // revision 11 is Qt 6.5: The metatype for void is stored in the metatypes array // revision 12 is Qt 6.6: It adds the metatype for enums // revision 13 is Qt 6.9: Adds support for 64-bit QFlags and moves the method revision -enum { OutputRevision = 13 }; // Used by moc, qmetaobjectbuilder and qdbus +// revision 14 is Qt 6.11: Adds a hash of meta object contents +enum { OutputRevision = 14 }; // Used by moc, qmetaobjectbuilder and qdbus enum PropertyFlags : uint { Invalid = 0x00000000, diff --git a/src/corelib/kernel/qtmochelpers.h b/src/corelib/kernel/qtmochelpers.h index 4c549e78ad5..3d2b59d2a73 100644 --- a/src/corelib/kernel/qtmochelpers.h +++ b/src/corelib/kernel/qtmochelpers.h @@ -511,7 +511,8 @@ template <typename ObjectType, typename Unique, typename Strings, typename Constructors = UintData<>, typename ClassInfo = detail::UintDataBlock<0, 0>> constexpr auto metaObjectData(uint flags, const Strings &strings, const Methods &methods, const Properties &properties, - const Enums &enums, const Constructors &constructors = {}, + const Enums &enums, int qt_metaObjectHashIndex = -1, + const Constructors &constructors = {}, const ClassInfo &classInfo = {}) { constexpr uint MetaTypeCount = Properties::metaTypeCount() @@ -520,7 +521,7 @@ constexpr auto metaObjectData(uint flags, const Strings &strings, + Methods::metaTypeCount() + Constructors::metaTypeCount(); - constexpr uint HeaderSize = 14; + constexpr uint HeaderSize = 15; constexpr uint TotalSize = HeaderSize + Properties::dataSize() + Enums::dataSize() @@ -582,6 +583,8 @@ constexpr auto metaObjectData(uint flags, const Strings &strings, } } + data[14] = qt_metaObjectHashIndex; + return result; } diff --git a/src/corelib/kernel/qtranslator.cpp b/src/corelib/kernel/qtranslator.cpp index 9fdac89f775..6000edaa177 100644 --- a/src/corelib/kernel/qtranslator.cpp +++ b/src/corelib/kernel/qtranslator.cpp @@ -392,8 +392,8 @@ public: translation files may contain misleading or malicious translations. \sa QCoreApplication::installTranslator(), QCoreApplication::removeTranslator(), - QObject::tr(), QCoreApplication::translate(), {I18N Example}, - {Hello tr() Example}, {Arrow Pad Example}, {Troll Print Example} + QObject::tr(), QCoreApplication::translate(), + {Localized Clock Example}, {Arrow Pad Example}, {Troll Print Example} */ /*! diff --git a/src/corelib/kernel/qvariant.h b/src/corelib/kernel/qvariant.h index 82eec0693d6..19cd1fea7fb 100644 --- a/src/corelib/kernel/qvariant.h +++ b/src/corelib/kernel/qvariant.h @@ -263,7 +263,7 @@ public: ConstReference &operator=(ConstReference &&value) = delete; // To be specialized for each Referred - operator QVariant() const noexcept(Referred::canNoexceptConvertToQVariant); + operator QVariant() const noexcept(Referred::CanNoexceptConvertToQVariant); }; template<typename Referred> @@ -288,18 +288,18 @@ public: ~Reference() = default; Reference &operator=(const Reference &value) - noexcept(Referred::canNoexceptAssignQVariant) + noexcept(Referred::CanNoexceptAssignQVariant) { return operator=(QVariant(value)); } Reference &operator=(Reference &&value) - noexcept(Referred::canNoexceptAssignQVariant) + noexcept(Referred::CanNoexceptAssignQVariant) { return operator=(QVariant(value)); } - operator QVariant() const noexcept(Referred::canNoexceptConvertToQVariant) + operator QVariant() const noexcept(Referred::CanNoexceptConvertToQVariant) { return ConstReference(m_referred); } @@ -313,7 +313,7 @@ public: } // To be specialized for each Referred - Reference &operator=(const QVariant &value) noexcept(Referred::canNoexceptAssignQVariant); + Reference &operator=(const QVariant &value) noexcept(Referred::CanNoexceptAssignQVariant); }; template<typename Pointed> diff --git a/src/corelib/mimetypes/qmimeprovider.cpp b/src/corelib/mimetypes/qmimeprovider.cpp index 9c26de94b6d..ecd1ac77779 100644 --- a/src/corelib/mimetypes/qmimeprovider.cpp +++ b/src/corelib/mimetypes/qmimeprovider.cpp @@ -517,13 +517,15 @@ QMimeBinaryProvider::loadMimeTypeExtra(const QString &mimeName) // load comment and globPatterns // shared-mime-info since 1.3 lowercases the xml files - QString mimeFile = m_directory + u'/' + mimeName.toLower() + ".xml"_L1; - if (!QFile::exists(mimeFile)) - mimeFile = m_directory + u'/' + mimeName + ".xml"_L1; // pre-1.3 - - QFile qfile(mimeFile); - if (!qfile.open(QFile::ReadOnly)) - return it; + QFile qfile; + const QString mimeFile = m_directory + u'/' + mimeName.toLower() + ".xml"_L1; + qfile.setFileName(mimeFile); + if (!qfile.open(QFile::ReadOnly)) { + const QString fallbackMimeFile = m_directory + u'/' + mimeName + ".xml"_L1; // pre-1.3 + qfile.setFileName(fallbackMimeFile); + if (!qfile.open(QFile::ReadOnly)) + return it; + } MimeTypeExtra &extra = it->second; QString mainPattern; diff --git a/src/corelib/platform/windows/qbstr_p.h b/src/corelib/platform/windows/qbstr_p.h index 21eecfe2df7..32b1aace76f 100644 --- a/src/corelib/platform/windows/qbstr_p.h +++ b/src/corelib/platform/windows/qbstr_p.h @@ -1,5 +1,6 @@ // Copyright (C) 2024 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only +// Qt-Security score:significant reason:default #ifndef QBSTR_P_H #define QBSTR_P_H @@ -141,4 +142,4 @@ QT_END_NAMESPACE #endif // Q_OS_WIN -#endif // QCOMPTR_P_H +#endif // QBSTR_P_H diff --git a/src/corelib/platform/windows/qcomobject_p.h b/src/corelib/platform/windows/qcomobject_p.h index bd6f81d1c28..b7a9c56555d 100644 --- a/src/corelib/platform/windows/qcomobject_p.h +++ b/src/corelib/platform/windows/qcomobject_p.h @@ -1,5 +1,6 @@ // Copyright (C) 2023 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only +// Qt-Security score:significant reason:default #ifndef QCOMOBJECT_P_H #define QCOMOBJECT_P_H diff --git a/src/corelib/platform/windows/qcomptr_p.h b/src/corelib/platform/windows/qcomptr_p.h index 2a69e7b6038..e640528d3c2 100644 --- a/src/corelib/platform/windows/qcomptr_p.h +++ b/src/corelib/platform/windows/qcomptr_p.h @@ -1,5 +1,6 @@ // Copyright (C) 2024 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only +// Qt-Security score:significant reason:default #ifndef QCOMPTR_P_H #define QCOMPTR_P_H diff --git a/src/corelib/platform/windows/qcomvariant_p.h b/src/corelib/platform/windows/qcomvariant_p.h index 34ce5f179ce..cc4ad104106 100644 --- a/src/corelib/platform/windows/qcomvariant_p.h +++ b/src/corelib/platform/windows/qcomvariant_p.h @@ -1,5 +1,6 @@ // Copyright (C) 2024 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only +// Qt-Security score:significant reason:default #ifndef QCOMVARIANT_P_H #define QCOMVARIANT_P_H diff --git a/src/corelib/platform/windows/qfactorycacheregistration.cpp b/src/corelib/platform/windows/qfactorycacheregistration.cpp index 6bd69c66d14..04c81b7b665 100644 --- a/src/corelib/platform/windows/qfactorycacheregistration.cpp +++ b/src/corelib/platform/windows/qfactorycacheregistration.cpp @@ -1,5 +1,6 @@ // Copyright (C) 2022 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only +// Qt-Security score:significant reason:default #include "qfactorycacheregistration_p.h" diff --git a/src/corelib/platform/windows/qfactorycacheregistration_p.h b/src/corelib/platform/windows/qfactorycacheregistration_p.h index d0b19b995b4..f6e7d9eb064 100644 --- a/src/corelib/platform/windows/qfactorycacheregistration_p.h +++ b/src/corelib/platform/windows/qfactorycacheregistration_p.h @@ -1,5 +1,6 @@ // Copyright (C) 2022 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only +// Qt-Security score:significant reason:default #ifndef QFACTORYCACHEREGISTRATION_P_H #define QFACTORYCACHEREGISTRATION_P_H diff --git a/src/corelib/platform/windows/qt_winrtbase_p.h b/src/corelib/platform/windows/qt_winrtbase_p.h index 79c2bdf6b1c..69a602a59e0 100644 --- a/src/corelib/platform/windows/qt_winrtbase_p.h +++ b/src/corelib/platform/windows/qt_winrtbase_p.h @@ -1,5 +1,6 @@ // Copyright (C) 2022 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only +// Qt-Security score:significant reason:default #ifndef QT_WINRTBASE_P_H #define QT_WINRTBASE_P_H diff --git a/src/corelib/serialization/qcborvalue.cpp b/src/corelib/serialization/qcborvalue.cpp index 13d74e591d5..0aeb2b07f47 100644 --- a/src/corelib/serialization/qcborvalue.cpp +++ b/src/corelib/serialization/qcborvalue.cpp @@ -975,6 +975,7 @@ QCborContainerPrivate *QCborContainerPrivate::detach(QCborContainerPrivate *d, q } /*! + \internal Prepare for an insertion at position \a index Detaches and ensures there are at least index entries in the array, padding diff --git a/src/corelib/serialization/qdatastream.cpp b/src/corelib/serialization/qdatastream.cpp index ae3bed5b751..fd2f7faeee5 100644 --- a/src/corelib/serialization/qdatastream.cpp +++ b/src/corelib/serialization/qdatastream.cpp @@ -552,6 +552,7 @@ void QDataStream::setByteOrder(ByteOrder bo) \value Qt_6_9 \value Qt_6_10 \value Qt_6_11 + \value Qt_6_12 \omitvalue Qt_DefaultCompiledVersion \sa setVersion(), version() diff --git a/src/corelib/serialization/qdatastream.h b/src/corelib/serialization/qdatastream.h index 04373fe9c8a..d6fcf17efcd 100644 --- a/src/corelib/serialization/qdatastream.h +++ b/src/corelib/serialization/qdatastream.h @@ -96,8 +96,9 @@ public: Qt_6_9 = Qt_6_7, Qt_6_10 = 23, Qt_6_11 = 24, - Qt_DefaultCompiledVersion = Qt_6_11 -#if QT_VERSION >= QT_VERSION_CHECK(6, 12, 0) + Qt_6_12 = Qt_6_11, + Qt_DefaultCompiledVersion = Qt_6_12 +#if QT_VERSION >= QT_VERSION_CHECK(6, 13, 0) #error Add the datastream version for this Qt version and update Qt_DefaultCompiledVersion #endif }; diff --git a/src/corelib/serialization/qxmlstream.cpp b/src/corelib/serialization/qxmlstream.cpp index 9cd90fa9d65..ff70289013a 100644 --- a/src/corelib/serialization/qxmlstream.cpp +++ b/src/corelib/serialization/qxmlstream.cpp @@ -3678,7 +3678,7 @@ void QXmlStreamWriter::setStopWritingOnError(bool stop) The error status is never reset. Writes happening after the error occurred may be ignored, even if the error condition is cleared. - \sa error(), errorString(), raiseError(const QString &message), + \sa error(), errorString(), raiseError() */ bool QXmlStreamWriter::hasError() const { @@ -3692,7 +3692,7 @@ bool QXmlStreamWriter::hasError() const QXmlStreamWriter::Error::None. \since 6.10 - \sa errorString(), raiseError(const QString &message), hasError() + \sa errorString(), raiseError(), hasError() */ QXmlStreamWriter::Error QXmlStreamWriter::error() const { @@ -3708,7 +3708,7 @@ QXmlStreamWriter::Error QXmlStreamWriter::error() const a null string. \since 6.10 - \sa error(), raiseError(const QString &message), hasError() + \sa error(), raiseError(), hasError() */ QString QXmlStreamWriter::errorString() const { diff --git a/src/corelib/text/qlocale.cpp b/src/corelib/text/qlocale.cpp index fed09aee45e..18007cacae6 100644 --- a/src/corelib/text/qlocale.cpp +++ b/src/corelib/text/qlocale.cpp @@ -301,6 +301,7 @@ bool operator<(LikelyPair lhs, LikelyPair rhs) } // anonymous namespace /*! + \internal Fill in blank fields of a locale ID. An ID in which some fields are zero stands for any locale that agrees with diff --git a/src/corelib/text/qstringconverter.cpp b/src/corelib/text/qstringconverter.cpp index bf6e776ee0e..896142b4837 100644 --- a/src/corelib/text/qstringconverter.cpp +++ b/src/corelib/text/qstringconverter.cpp @@ -2739,7 +2739,7 @@ QStringList QStringConverter::availableCodecs() May also provide data from residual content that was pending decoding. When there is no residual data to account for, the return's \c error - field will be set to \l {QCharConverter::FinalizeResult::Error::} + field will be set to \l {QStringConverter::FinalizeResultChar::error} {NoError}. If \a out is supplied and non-null, it must have space in which up to @@ -2793,7 +2793,7 @@ auto QStringDecoder::finalize(char16_t *out, qsizetype maxlen) -> FinalizeResult May also provide data from residual content that was pending decoding. When there is no residual data to account for, the return's \c error - field will be set to \l {QCharConverter::FinalizeResult::Error::} + field will be set to \l {QStringConverter::FinalizeResultChar::error} {NoError}. If \a out is supplied and non-null, it must have space in which up to diff --git a/src/corelib/thread/qfuture_impl.h b/src/corelib/thread/qfuture_impl.h index 371ee524d72..d6c185cd704 100644 --- a/src/corelib/thread/qfuture_impl.h +++ b/src/corelib/thread/qfuture_impl.h @@ -845,7 +845,7 @@ struct UnwrapHandler using NestedType = typename QtPrivate::Future<ResultType>::type; QFutureInterface<NestedType> promise(QFutureInterfaceBase::State::Pending); - outer->then([promise](const QFuture<ResultType> &outerFuture) mutable { + auto chain = outer->then([promise](const QFuture<ResultType> &outerFuture) mutable { // We use the .then([](QFuture<ResultType> outerFuture) {...}) version // (where outerFuture == *outer), to propagate the exception if the // outer future has failed. @@ -883,6 +883,13 @@ struct UnwrapHandler promise.reportCanceled(); promise.reportFinished(); }); + + // Inject the promise into the chain. + // We use a fake function as a continuation, since the promise is + // managed by the outer future + chain.d.setContinuation(ContinuationWrapper(std::move([](const QFutureInterfaceBase &) {})), + promise.d, QFutureInterfaceBase::ContinuationType::Then); + return promise.future(); } }; diff --git a/src/corelib/thread/qfutureinterface.h b/src/corelib/thread/qfutureinterface.h index 0b88013800e..ff17560d3a1 100644 --- a/src/corelib/thread/qfutureinterface.h +++ b/src/corelib/thread/qfutureinterface.h @@ -42,6 +42,8 @@ template<class Function, class ResultType> class FailureHandler; #endif +struct UnwrapHandler; + #if QT_CORE_REMOVED_SINCE(6, 10) void Q_CORE_EXPORT watchContinuationImpl(const QObject *context, QtPrivate::QSlotObjectBase *slotObj, @@ -187,6 +189,8 @@ private: friend class QtPrivate::FailureHandler; #endif + friend struct QtPrivate::UnwrapHandler; + #if QT_CORE_REMOVED_SINCE(6, 10) friend Q_CORE_EXPORT void QtPrivate::watchContinuationImpl( const QObject *context, QtPrivate::QSlotObjectBase *slotObj, QFutureInterfaceBase &fi); diff --git a/src/corelib/thread/qreadwritelock.cpp b/src/corelib/thread/qreadwritelock.cpp index 96e35dcb965..2a1af2315ca 100644 --- a/src/corelib/thread/qreadwritelock.cpp +++ b/src/corelib/thread/qreadwritelock.cpp @@ -234,14 +234,14 @@ QBasicReadWriteLock::contendedTryLockForRead(QDeadlineTimer timeout, void *dd) return d->recursiveLockForRead(timeout); auto lock = qt_unique_lock(d->mutex); - if (d != d_ptr.loadRelaxed()) { + if (QReadWriteLockPrivate *dd = d_ptr.loadAcquire(); d != dd) { // d_ptr has changed: this QReadWriteLock was unlocked before we had // time to lock d->mutex. // We are holding a lock to a mutex within a QReadWriteLockPrivate // that is already released (or even is already re-used). That's ok // because the QFreeList never frees them. // Just unlock d->mutex (at the end of the scope) and retry. - d = d_ptr.loadAcquire(); + d = dd; continue; } return d->lockForRead(lock, timeout); @@ -340,11 +340,11 @@ QBasicReadWriteLock::contendedTryLockForWrite(QDeadlineTimer timeout, void *dd) return d->recursiveLockForWrite(timeout); auto lock = qt_unique_lock(d->mutex); - if (d != d_ptr.loadRelaxed()) { + if (QReadWriteLockPrivate *dd = d_ptr.loadAcquire(); d != dd) { // The mutex was unlocked before we had time to lock the mutex. // We are holding to a mutex within a QReadWriteLockPrivate that is already released // (or even is already re-used) but that's ok because the QFreeList never frees them. - d = d_ptr.loadAcquire(); + d = dd; continue; } return d->lockForWrite(lock, timeout); diff --git a/src/corelib/time/qdatetimeparser.cpp b/src/corelib/time/qdatetimeparser.cpp index 10882738a39..05ba3d2beae 100644 --- a/src/corelib/time/qdatetimeparser.cpp +++ b/src/corelib/time/qdatetimeparser.cpp @@ -2389,6 +2389,7 @@ bool operator==(QDateTimeParser::SectionNode s1, QDateTimeParser::SectionNode s2 } /*! + \internal Sets \a cal as the calendar to use. The default is Gregorian. */ diff --git a/src/corelib/tools/qarraydata.cpp b/src/corelib/tools/qarraydata.cpp index 1c0371e463e..d173d824e88 100644 --- a/src/corelib/tools/qarraydata.cpp +++ b/src/corelib/tools/qarraydata.cpp @@ -162,7 +162,7 @@ allocateHelper(QArrayData **dptr, qsizetype objectSize, qsizetype alignment, qsi QArrayData::AllocationOption option) noexcept { *dptr = nullptr; - if (capacity == 0) + if (capacity <= 0) return {}; const qsizetype headerSize = calculateHeaderSize(alignment); diff --git a/src/corelib/tools/qeasingcurve.cpp b/src/corelib/tools/qeasingcurve.cpp index de68a0042ac..1e647a83dc0 100644 --- a/src/corelib/tools/qeasingcurve.cpp +++ b/src/corelib/tools/qeasingcurve.cpp @@ -332,15 +332,13 @@ struct TCBPoint qreal _c; qreal _b; - TCBPoint() {} - TCBPoint(QPointF point, qreal t, qreal c, qreal b) : _point(point), _t(t), _c(c), _b(b) {} - bool operator==(const TCBPoint &other) const + friend bool operator==(const TCBPoint &lhs, const TCBPoint &rhs) noexcept { - return _point == other._point && - qFuzzyCompare(_t, other._t) && - qFuzzyCompare(_c, other._c) && - qFuzzyCompare(_b, other._b); + return qFuzzyCompare(lhs._point, rhs._point) + && QtPrivate::fuzzyCompare(lhs._t, rhs._t) + && QtPrivate::fuzzyCompare(lhs._c, rhs._c) + && QtPrivate::fuzzyCompare(lhs._b, rhs._b); } }; Q_DECLARE_TYPEINFO(TCBPoint, Q_PRIMITIVE_TYPE); @@ -1381,7 +1379,7 @@ void QEasingCurve::addTCBSegment(const QPointF &nextPoint, qreal t, qreal c, qre if (!d_ptr->config) d_ptr->config = curveToFunctionObject(d_ptr->type); - d_ptr->config->_tcbPoints.append(TCBPoint(nextPoint, t, c, b)); + d_ptr->config->_tcbPoints.append(TCBPoint{nextPoint, t, c, b}); if (nextPoint == QPointF(1.0, 1.0)) { d_ptr->config->_bezierCurves = tcbToBezier(d_ptr->config->_tcbPoints); diff --git a/src/corelib/tools/qmargins.h b/src/corelib/tools/qmargins.h index f833a338b16..cbdb093adc8 100644 --- a/src/corelib/tools/qmargins.h +++ b/src/corelib/tools/qmargins.h @@ -333,20 +333,13 @@ private: qreal m_right; qreal m_bottom; - QT_WARNING_PUSH - QT_WARNING_DISABLE_FLOAT_COMPARE friend constexpr bool qFuzzyCompare(const QMarginsF &lhs, const QMarginsF &rhs) noexcept { - return ((!lhs.m_left || !rhs.m_left) ? qFuzzyIsNull(lhs.m_left - rhs.m_left) - : qFuzzyCompare(lhs.m_left, rhs.m_left)) - && ((!lhs.m_top || !rhs.m_top) ? qFuzzyIsNull(lhs.m_top - rhs.m_top) - : qFuzzyCompare(lhs.m_top, rhs.m_top)) - && ((!lhs.m_right || !rhs.m_right) ? qFuzzyIsNull(lhs.m_right - rhs.m_right) - : qFuzzyCompare(lhs.m_right, rhs.m_right)) - && ((!lhs.m_bottom || !rhs.m_bottom) ? qFuzzyIsNull(lhs.m_bottom - rhs.m_bottom) - : qFuzzyCompare(lhs.m_bottom, rhs.m_bottom)); + return QtPrivate::fuzzyCompare(lhs.m_left, rhs.m_left) + && QtPrivate::fuzzyCompare(lhs.m_top, rhs.m_top) + && QtPrivate::fuzzyCompare(lhs.m_right, rhs.m_right) + && QtPrivate::fuzzyCompare(lhs.m_bottom, rhs.m_bottom); } - QT_WARNING_POP friend constexpr bool qFuzzyIsNull(const QMarginsF &m) noexcept { return qFuzzyIsNull(m.m_left) && qFuzzyIsNull(m.m_top) diff --git a/src/corelib/tools/qpoint.h b/src/corelib/tools/qpoint.h index ae896ba7079..1b767324058 100644 --- a/src/corelib/tools/qpoint.h +++ b/src/corelib/tools/qpoint.h @@ -259,14 +259,11 @@ public: } private: - QT_WARNING_PUSH - QT_WARNING_DISABLE_FLOAT_COMPARE friend constexpr bool qFuzzyCompare(const QPointF &p1, const QPointF &p2) noexcept { - return ((!p1.xp || !p2.xp) ? qFuzzyIsNull(p1.xp - p2.xp) : qFuzzyCompare(p1.xp, p2.xp)) - && ((!p1.yp || !p2.yp) ? qFuzzyIsNull(p1.yp - p2.yp) : qFuzzyCompare(p1.yp, p2.yp)); + return QtPrivate::fuzzyCompare(p1.xp, p2.xp) + && QtPrivate::fuzzyCompare(p1.yp, p2.yp); } - QT_WARNING_POP friend constexpr bool qFuzzyIsNull(const QPointF &point) noexcept { return qFuzzyIsNull(point.xp) && qFuzzyIsNull(point.yp); diff --git a/src/corelib/tools/qsize.h b/src/corelib/tools/qsize.h index 86509cb6483..680bf2812d3 100644 --- a/src/corelib/tools/qsize.h +++ b/src/corelib/tools/qsize.h @@ -254,16 +254,11 @@ public: inline QSizeF &operator/=(qreal c); private: - QT_WARNING_PUSH - QT_WARNING_DISABLE_FLOAT_COMPARE friend constexpr bool qFuzzyCompare(const QSizeF &s1, const QSizeF &s2) noexcept { - // Cannot use qFuzzyCompare(), because it will give incorrect results - // if one of the arguments is 0.0. - return ((!s1.wd || !s2.wd) ? qFuzzyIsNull(s1.wd - s2.wd) : qFuzzyCompare(s1.wd, s2.wd)) - && ((!s1.ht || !s2.ht) ? qFuzzyIsNull(s1.ht - s2.ht) : qFuzzyCompare(s1.ht, s2.ht)); + return QtPrivate::fuzzyCompare(s1.wd, s2.wd) + && QtPrivate::fuzzyCompare(s1.ht, s2.ht); } - QT_WARNING_POP friend constexpr bool qFuzzyIsNull(const QSizeF &size) noexcept { return qFuzzyIsNull(size.wd) && qFuzzyIsNull(size.ht); } friend constexpr bool comparesEqual(const QSizeF &lhs, const QSizeF &rhs) noexcept diff --git a/src/dbus/qdbusmetaobject.cpp b/src/dbus/qdbusmetaobject.cpp index 149392f9c3c..a4ffd7a64dd 100644 --- a/src/dbus/qdbusmetaobject.cpp +++ b/src/dbus/qdbusmetaobject.cpp @@ -383,9 +383,10 @@ void QDBusMetaObjectGenerator::write(QDBusMetaObject *obj) - methods.size(); // ditto QDBusMetaObjectPrivate *header = reinterpret_cast<QDBusMetaObjectPrivate *>(idata.data()); - static_assert(QMetaObjectPrivate::OutputRevision == 13, "QtDBus meta-object generator should generate the same version as moc"); + static_assert(QMetaObjectPrivate::OutputRevision == 14, "QtDBus meta-object generator should generate the same version as moc"); header->revision = QMetaObjectPrivate::OutputRevision; header->className = 0; + header->metaObjectHashIndex = -1; // TODO support hash in dbus metaobject too header->classInfoCount = 0; header->classInfoData = 0; header->methodCount = int(signals_.size() + methods.size()); diff --git a/src/gui/accessible/qaccessible_base.h b/src/gui/accessible/qaccessible_base.h index 3881c6346a0..04efeddf06f 100644 --- a/src/gui/accessible/qaccessible_base.h +++ b/src/gui/accessible/qaccessible_base.h @@ -175,11 +175,15 @@ public: // quint64 alertMedium : 1; // quint64 alertHigh : 1; + Q_DECL_UNUSED_MEMBER quint64 qt_reserved : 27; + State() { std::memset(this, 0, sizeof(State)); } friend inline bool operator==(const QAccessible::State &first, const QAccessible::State &second) { + static_assert(std::has_unique_object_representations_v<State>, + "memcmp() cannot be used on types with padding"); return std::memcmp(&first, &second, sizeof(QAccessible::State)) == 0; } }; diff --git a/src/gui/accessible/qaccessiblecache.cpp b/src/gui/accessible/qaccessiblecache.cpp index 311b53aeaa3..b5dcdca6270 100644 --- a/src/gui/accessible/qaccessiblecache.cpp +++ b/src/gui/accessible/qaccessiblecache.cpp @@ -19,6 +19,7 @@ Q_STATIC_LOGGING_CATEGORY(lcAccessibilityCache, "qt.accessibility.cache"); */ static QAccessibleCache *accessibleCache = nullptr; +static bool inCacheDestructor = false; static void cleanupAccessibleCache() { @@ -32,6 +33,8 @@ QAccessibleObjectDestroyedEvent::~QAccessibleObjectDestroyedEvent() QAccessibleCache::~QAccessibleCache() { + inCacheDestructor = true; + for (QAccessible::Id id: idToInterface.keys()) deleteInterface(id); } @@ -188,10 +191,9 @@ void QAccessibleCache::deleteInterface(QAccessible::Id id, QObject *obj) return; } - // QObjects sends this from their destructor, but - // the object less interfaces calls deleteInterface - // directly - if (!obj && !iface->object()) { + // QObjects send this from their destructors, but the interfaces + // with no associated object call deleteInterface directly. + if (!inCacheDestructor && !obj && !iface->object()) { if (QGuiApplicationPrivate::is_app_running && !QGuiApplicationPrivate::is_app_closing && QAccessible::isActive()) { QAccessibleObjectDestroyedEvent event(id); QAccessible::updateAccessibility(&event); diff --git a/src/gui/doc/images/coordinatesystem-transformations.png b/src/gui/doc/images/coordinatesystem-transformations.png Binary files differdeleted file mode 100644 index 2736213c072..00000000000 --- a/src/gui/doc/images/coordinatesystem-transformations.png +++ /dev/null diff --git a/src/gui/doc/images/coordinatesystem-transformations.svg b/src/gui/doc/images/coordinatesystem-transformations.svg new file mode 100644 index 00000000000..a3bc17af3ef --- /dev/null +++ b/src/gui/doc/images/coordinatesystem-transformations.svg @@ -0,0 +1,148 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<svg width="760" height="300" + viewBox="0 0 760 300" + xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.2" baseProfile="tiny"> + +<style> + svg .line-style { stroke: black; fill: none; stroke-width: 2 } + svg .text-style { font: 14px arial; fill: black } + svg .mono-text-style { font: 14px monospace; fill: black } + svg .heading-style { font: 14px arial; fill: black; font-weight: bold } + svg .shape-style { stroke: none; fill: black } + svg .dash-style { stroke-dasharray: 4,3; stroke-dashoffset: 0; fill: none; + stroke: black } + + svg .window-line-style { stroke: red; fill: none; stroke-width: 2 } + svg .window-text-style { font: 14px arial; fill: red } + svg .window-shape-style { stroke: none; fill: red } + svg .window-dash-style { stroke-dasharray: 4,3; stroke-dashoffset: 0; fill: none; + stroke: red } + + svg.dark .line-style { stroke: #f2f2f2; fill: none; stroke-width: 2 } + svg.dark .text-style { font: 14px arial; fill: #f2f2f2 } + svg.dark .mono-text-style { font: 14px monospace; fill: #f2f2f2 } + svg.dark .heading-style { font: 14px arial; fill: #f2f2f2; font-weight: bold } + svg.dark .shape-style { stroke: none; fill: #f2f2f2 } + svg.dark .dash-style { stroke-dasharray: 4,3; stroke-dashoffset: 0; fill: none; + stroke: #f2f2f2 } + svg.dark .window-line-style { stroke: yellow; fill: none; stroke-width: 2 } + svg.dark .window-text-style { font: 14px arial; fill: yellow } + svg.dark .window-shape-style { stroke: none; fill: yellow } + svg.dark .window-dash-style { stroke-dasharray: 4,3; stroke-dashoffset: 0; fill: none; + stroke: yellow } + + [data-theme="dark"] svg .line-style { stroke: #f2f2f2; fill: none; stroke-width: 2 } + [data-theme="dark"] svg .text-style { font: 14px arial; fill: #f2f2f2 } + [data-theme="dark"] svg .mono-text-style { font: 14px monospace; fill: #f2f2f2 } + [data-theme="dark"] svg .heading-style { font: 14px arial; fill: #f2f2f2; font-weight: bold } + [data-theme="dark"] svg .shape-style { stroke: none; fill: #f2f2f2 } + [data-theme="dark"] svg .dash-style { stroke-dasharray: 4,3; stroke-dashoffset: 0; fill: none; + stroke: #f2f2f2 } + [data-theme="dark"] svg .window-line-style { stroke: yellow; fill: none; stroke-width: 2 } + [data-theme="dark"] svg .window-text-style { font: 14px arial; fill: yellow } + [data-theme="dark"] svg .window-shape-style { stroke: none; fill: yellow } + [data-theme="dark"] svg .window-dash-style { stroke-dasharray: 4,3; stroke-dashoffset: 0; fill: none; + stroke: yellow } + + [data-theme="light"] svg .line-style { stroke: black; fill: none; stroke-width: 2 } + [data-theme="light"] svg .text-style { font: 14px arial; fill: black } + [data-theme="light"] svg .mono-text-style { font: 14px monospace; fill: black } + [data-theme="light"] svg .heading-style { font: 14px arial; fill: black; font-weight: bold } + [data-theme="light"] svg .shape-style { stroke: none; fill: black } + [data-theme="light"] svg .dash-style { stroke-dasharray: 4,3; stroke-dashoffset: 0; fill: none; + stroke: black } + [data-theme="light"] svg .window-line-style { stroke: red; fill: none; stroke-width: 2 } + [data-theme="light"] svg .window-text-style { font: 14px arial; fill: red } + [data-theme="light"] svg .window-shape-style { stroke: none; fill: red } + [data-theme="light"] svg .window-dash-style { stroke-dasharray: 4,3; stroke-dashoffset: 0; fill: none; + stroke: red } +</style> + +<text x="50" y="20" fill="black" font-size="14px" font-family="arial" + font-weight="bold" + class="heading-style">World coordinates</text> + +<g transform="translate(20, 30)"> + <rect x="0" y="0" width="200" height="200" stroke="black" fill="none" stroke-dasharray="4,3" stroke-dashoffset="0" + class="dash-style" /> + + <path d="M5,5 L16,10 L10,16 L5,5" class="shape-style" /> + <polyline points="5,5 20,20" stroke="black" stroke-width="2" fill="none" class="line-style" /> + <text x="25" y="30" fill="black" font-size="14px" font-family="arial" + class="text-style">(0, 0)</text> + + <path d="M195,195 L184,190 L190,184 L195,195" class="shape-style" /> + <polyline points="195,195 180,180" stroke="black" stroke-width="2" fill="none" class="line-style" /> + <text x="115" y="175" fill="black" font-size="14px" font-family="arial" + class="text-style">(100, 100)</text> + + <polyline points="-3,-3 3,3" stroke="black" stroke-width="2" class="line-style" /> + <polyline points="-3,3 3,-3" stroke="black" stroke-width="2" class="line-style" /> +</g> + +<g transform="translate(45,235)"> + <path d="M 0,0 c 0,25 10,35 25,35" stroke="black" stroke-width="2" fill="none" class="line-style" /> + <path d="M 25,30 l 10,5 l -10,5 z" class="shape-style" /> + <text x="40" y="40" fill="black" font-size="14px" font-family="monospace" + class="mono-text-style">setWindow(-50, -50, 100, 100)</text> + <path d="M 290,35 c 15,0 25,-10 25,-35" stroke="black" stroke-width="2" fill="none" class="line-style" /> + <path d="M 315,0 l 5,10 l -10,0 z" class="shape-style" /> +</g> + +<text x="300" y="20" fill="black" font-size="14px" font-family="arial" + font-weight="bold" + class="heading-style">"Window" coordinates</text> + +<g transform="translate(280, 30)"> + + <rect x="0" y="0" width="200" height="200" stroke="red" fill="none" stroke-dasharray="4,3" stroke-dashoffset="0" + class="window-dash-style" /> + + <path d="M5,5 L16,10 L10,16 L5,5" fill="red" class="window-shape-style" /> + <polyline points="5,5 20,20" stroke="red" stroke-width="2" fill="none" class="window-line-style" /> + <text x="25" y="30" fill="red" font-size="14px" font-family="arial" + class="window-text-style">(-50, -50)</text> + + <path d="M195,195 L184,190 L190,184 L195,195" fill="red" class="window-shape-style" /> + <polyline points="195,195 180,180" stroke="red" stroke-width="2" fill="none" class="window-line-style" /> + <text x="130" y="175" fill="red" font-size="14px" font-family="arial" + class="window-text-style">(50, 50)</text> + + <polyline points="97,97 103,103" stroke="red" stroke-width="2" class="window-line-style" /> + <polyline points="97,103 103,97" stroke="red" stroke-width="2" class="window-line-style" /> +</g> + +<g transform="translate(395,235)"> + <path d="M 0,0 c 0,25 10,35 25,35" stroke="black" stroke-width="2" fill="none" class="line-style" /> + <path d="M 25,30 l 10,5 l -10,5 z" class="shape-style" /> + <text x="40" y="40" fill="black" font-size="14px" font-family="monospace" + class="mono-text-style">setViewport(45, 25, 50, 50)</text> + <path d="M 270,35 c 15,0 25,-10 25,-35" stroke="black" stroke-width="2" fill="none" class="line-style" /> + <path d="M 295,0 l 5,10 l -10,0 z" class="shape-style" /> +</g> + +<text x="570" y="20" fill="black" font-size="14px" font-family="arial" + font-weight="bold" + class="heading-style">Device coordinates</text> + +<g transform="translate(540, 30)"> + + <rect x="0" y="0" width="200" height="200" stroke="black" stroke-width="2" fill="none" class="line-style" /> + + <path d="M5,5 L16,10 L10,16 L5,5" class="shape-style" /> + <polyline points="5,5 20,20" stroke="black" stroke-width="2" fill="none" class="line-style" /> + <text x="25" y="30" fill="black" font-size="14px" font-family="arial" + class="text-style">(0, 0)</text> + + <path d="M195,195 L184,190 L190,184 L195,195" class="shape-style" /> + <polyline points="195,195 180,180" stroke="black" stroke-width="2" fill="none" class="line-style" /> + <text x="115" y="175" fill="black" font-size="14px" font-family="arial" + class="text-style">(100, 100)</text> + + <rect x="90" y="50" width="100" height="100" stroke="red" fill="none" stroke-dasharray="4,3" stroke-dashoffset="0" + class="window-dash-style" /> + <polyline points="137,97 143,103" stroke="red" stroke-width="2" class="window-line-style" /> + <polyline points="137,103 143,97" stroke="red" stroke-width="2" class="window-line-style" /> +</g> + +</svg> diff --git a/src/gui/doc/qtgui.qdocconf b/src/gui/doc/qtgui.qdocconf index 24d9d522735..8b7569c1296 100644 --- a/src/gui/doc/qtgui.qdocconf +++ b/src/gui/doc/qtgui.qdocconf @@ -50,7 +50,8 @@ depends += \ qtshadertools \ qttestlib \ qtplatformintegration \ - qthelp + qthelp \ + qtquickcontrols headerdirs += .. diff --git a/src/gui/doc/src/coordsys.qdoc b/src/gui/doc/src/coordsys.qdoc index 3dd064c19bc..22e14121af6 100644 --- a/src/gui/doc/src/coordsys.qdoc +++ b/src/gui/doc/src/coordsys.qdoc @@ -321,7 +321,7 @@ still transformed to the viewport using the same linear algebraic approach. - \image coordinatesystem-transformations.png {Illustration showing + \image coordinatesystem-transformations.svg {Illustration showing how coordinates are mapped using viewport, "window" and transformation matrix} diff --git a/src/gui/doc/src/external-resources.qdoc b/src/gui/doc/src/external-resources.qdoc index 0f356dd5046..14ed0817e62 100644 --- a/src/gui/doc/src/external-resources.qdoc +++ b/src/gui/doc/src/external-resources.qdoc @@ -36,6 +36,7 @@ \externalpage https://specifications.freedesktop.org/icon-naming-spec/icon-naming-spec-latest.html \title Freedesktop Icon Naming Specification */ + /*! \externalpage https://standards.freedesktop.org/icon-theme-spec/icon-theme-spec-latest.html#directory_layout \title Icon Theme Specification - Directory Layout diff --git a/src/gui/image/qabstractfileiconprovider.cpp b/src/gui/image/qabstractfileiconprovider.cpp index 78777ec115a..ad646a6b89a 100644 --- a/src/gui/image/qabstractfileiconprovider.cpp +++ b/src/gui/image/qabstractfileiconprovider.cpp @@ -288,3 +288,5 @@ QString QAbstractFileIconProvider::type(const QFileInfo &info) const } QT_END_NAMESPACE + +#include "moc_qabstractfileiconprovider.cpp" diff --git a/src/gui/itemmodels/qfileinfogatherer.cpp b/src/gui/itemmodels/qfileinfogatherer.cpp index b7ab3dbbc46..ea19db0d20f 100644 --- a/src/gui/itemmodels/qfileinfogatherer.cpp +++ b/src/gui/itemmodels/qfileinfogatherer.cpp @@ -46,6 +46,12 @@ static QString translateDriveName(const QFileInfo &drive) } /*! + \class QFileInfoGatherer + \inmodule QtGui + \internal +*/ + +/*! Creates thread */ QFileInfoGatherer::QFileInfoGatherer(QObject *parent) diff --git a/src/gui/kernel/qguiapplication.cpp b/src/gui/kernel/qguiapplication.cpp index 741b089306e..e8df40f21b2 100644 --- a/src/gui/kernel/qguiapplication.cpp +++ b/src/gui/kernel/qguiapplication.cpp @@ -490,6 +490,12 @@ static QWindowGeometrySpecification windowGeometrySpecification = Q_WINDOW_GEOME */ /*! + \class QGuiApplicationPrivate + \inmodule QtGui + \internal +*/ + +/*! Initializes the window system and constructs an application object with \a argc command line arguments in \a argv. diff --git a/src/gui/kernel/qguiapplication_p.h b/src/gui/kernel/qguiapplication_p.h index cb4702b5f7e..633ae7895ba 100644 --- a/src/gui/kernel/qguiapplication_p.h +++ b/src/gui/kernel/qguiapplication_p.h @@ -228,8 +228,8 @@ public: // to use single-point precision. friend constexpr bool operator==(const QLastCursorPosition &p1, const QPointF &p2) noexcept { - return qFuzzyCompare(float(p1.x()), float(p2.x())) - && qFuzzyCompare(float(p1.y()), float(p2.y())); + return QtPrivate::fuzzyCompare(float(p1.x()), float(p2.x())) + && QtPrivate::fuzzyCompare(float(p1.y()), float(p2.y())); } friend constexpr bool operator!=(const QLastCursorPosition &p1, const QPointF &p2) noexcept { diff --git a/src/gui/kernel/qplatformintegrationfactory.cpp b/src/gui/kernel/qplatformintegrationfactory.cpp index d0a5e8871f8..d113e86090d 100644 --- a/src/gui/kernel/qplatformintegrationfactory.cpp +++ b/src/gui/kernel/qplatformintegrationfactory.cpp @@ -24,6 +24,7 @@ QPlatformIntegration *QPlatformIntegrationFactory::create(const QString &platfor } /*! + \internal Returns the list of valid keys, i.e. the keys this factory can create styles for. diff --git a/src/gui/kernel/qplatformthemefactory.cpp b/src/gui/kernel/qplatformthemefactory.cpp index beefa1c2942..3ac462598fd 100644 --- a/src/gui/kernel/qplatformthemefactory.cpp +++ b/src/gui/kernel/qplatformthemefactory.cpp @@ -31,6 +31,7 @@ QPlatformTheme *QPlatformThemeFactory::create(const QString& key, const QString } /*! + \internal Returns the list of valid keys, i.e. the keys this factory can create styles for. diff --git a/src/gui/kernel/qsurface.cpp b/src/gui/kernel/qsurface.cpp index e2bafa73988..f361ec86c2d 100644 --- a/src/gui/kernel/qsurface.cpp +++ b/src/gui/kernel/qsurface.cpp @@ -68,7 +68,10 @@ QT_IMPL_METATYPE_EXTERN_TAGGED(QSurface*, QSurface_ptr) bool QSurface::supportsOpenGL() const { - return surfaceType() == OpenGLSurface; + static bool openGLOnRasterSurfaceSupported = + QGuiApplicationPrivate::instance()->platformIntegration()->hasCapability(QPlatformIntegration::OpenGLOnRasterSurface); + return surfaceType() == OpenGLSurface + || (surfaceType() == RasterSurface && openGLOnRasterSurfaceSupported); } /*! diff --git a/src/gui/math3d/qmatrix4x4.cpp b/src/gui/math3d/qmatrix4x4.cpp index f6a06fd47ca..ec6e696ba00 100644 --- a/src/gui/math3d/qmatrix4x4.cpp +++ b/src/gui/math3d/qmatrix4x4.cpp @@ -294,6 +294,7 @@ double QMatrix4x4::determinant() const if ((flagBits & ~(Translation | Rotation2D | Rotation)) == Identity) return 1.0; + Q_DECL_UNINITIALIZED double mm[4][4]; copyToDoubles(m, mm); if (flagBits < Rotation2D) @@ -355,8 +356,10 @@ QMatrix4x4 QMatrix4x4::inverted(bool *invertible) const *invertible = true; return orthonormalInverse(); } else if (flagBits < Perspective) { + Q_DECL_UNINITIALIZED QMatrix4x4 inv(Qt::Uninitialized); + Q_DECL_UNINITIALIZED double mm[4][4]; copyToDoubles(m, mm); @@ -391,8 +394,10 @@ QMatrix4x4 QMatrix4x4::inverted(bool *invertible) const return inv; } + Q_DECL_UNINITIALIZED QMatrix4x4 inv(Qt::Uninitialized); + Q_DECL_UNINITIALIZED double mm[4][4]; copyToDoubles(m, mm); @@ -465,6 +470,7 @@ QMatrix3x3 QMatrix4x4::normalMatrix() const return inv; } + Q_DECL_UNINITIALIZED double mm[4][4]; copyToDoubles(m, mm); double det = matrixDet3(mm, 0, 1, 2, 0, 1, 2); @@ -493,6 +499,7 @@ QMatrix3x3 QMatrix4x4::normalMatrix() const */ QMatrix4x4 QMatrix4x4::transposed() const { + Q_DECL_UNINITIALIZED QMatrix4x4 result(Qt::Uninitialized); for (int row = 0; row < 4; ++row) { for (int col = 0; col < 4; ++col) { @@ -709,6 +716,7 @@ QMatrix4x4& QMatrix4x4::operator/=(float divisor) */ QMatrix4x4 operator/(const QMatrix4x4& matrix, float divisor) { + Q_DECL_UNINITIALIZED QMatrix4x4 m(Qt::Uninitialized); m.m[0][0] = matrix.m[0][0] / divisor; m.m[0][1] = matrix.m[0][1] / divisor; @@ -738,22 +746,22 @@ QMatrix4x4 operator/(const QMatrix4x4& matrix, float divisor) */ bool qFuzzyCompare(const QMatrix4x4& m1, const QMatrix4x4& m2) noexcept { - return qFuzzyCompare(m1.m[0][0], m2.m[0][0]) && - qFuzzyCompare(m1.m[0][1], m2.m[0][1]) && - qFuzzyCompare(m1.m[0][2], m2.m[0][2]) && - qFuzzyCompare(m1.m[0][3], m2.m[0][3]) && - qFuzzyCompare(m1.m[1][0], m2.m[1][0]) && - qFuzzyCompare(m1.m[1][1], m2.m[1][1]) && - qFuzzyCompare(m1.m[1][2], m2.m[1][2]) && - qFuzzyCompare(m1.m[1][3], m2.m[1][3]) && - qFuzzyCompare(m1.m[2][0], m2.m[2][0]) && - qFuzzyCompare(m1.m[2][1], m2.m[2][1]) && - qFuzzyCompare(m1.m[2][2], m2.m[2][2]) && - qFuzzyCompare(m1.m[2][3], m2.m[2][3]) && - qFuzzyCompare(m1.m[3][0], m2.m[3][0]) && - qFuzzyCompare(m1.m[3][1], m2.m[3][1]) && - qFuzzyCompare(m1.m[3][2], m2.m[3][2]) && - qFuzzyCompare(m1.m[3][3], m2.m[3][3]); + return QtPrivate::fuzzyCompare(m1.m[0][0], m2.m[0][0]) + && QtPrivate::fuzzyCompare(m1.m[0][1], m2.m[0][1]) + && QtPrivate::fuzzyCompare(m1.m[0][2], m2.m[0][2]) + && QtPrivate::fuzzyCompare(m1.m[0][3], m2.m[0][3]) + && QtPrivate::fuzzyCompare(m1.m[1][0], m2.m[1][0]) + && QtPrivate::fuzzyCompare(m1.m[1][1], m2.m[1][1]) + && QtPrivate::fuzzyCompare(m1.m[1][2], m2.m[1][2]) + && QtPrivate::fuzzyCompare(m1.m[1][3], m2.m[1][3]) + && QtPrivate::fuzzyCompare(m1.m[2][0], m2.m[2][0]) + && QtPrivate::fuzzyCompare(m1.m[2][1], m2.m[2][1]) + && QtPrivate::fuzzyCompare(m1.m[2][2], m2.m[2][2]) + && QtPrivate::fuzzyCompare(m1.m[2][3], m2.m[2][3]) + && QtPrivate::fuzzyCompare(m1.m[3][0], m2.m[3][0]) + && QtPrivate::fuzzyCompare(m1.m[3][1], m2.m[3][1]) + && QtPrivate::fuzzyCompare(m1.m[3][2], m2.m[3][2]) + && QtPrivate::fuzzyCompare(m1.m[3][3], m2.m[3][3]); } @@ -1141,6 +1149,7 @@ void QMatrix4x4::rotate(float angle, float x, float y, float z) z = float(double(z) / len); } float ic = 1.0f - c; + Q_DECL_UNINITIALIZED QMatrix4x4 rot(Qt::Uninitialized); rot.m[0][0] = x * x * ic + c; rot.m[1][0] = x * y * ic - z * s; @@ -1244,6 +1253,7 @@ void QMatrix4x4::projectedRotate(float angle, float x, float y, float z, float d z = float(double(z) / len); } const float ic = 1.0f - c; + Q_DECL_UNINITIALIZED QMatrix4x4 rot(Qt::Uninitialized); rot.m[0][0] = x * x * ic + c; rot.m[1][0] = x * y * ic - z * s; @@ -1306,6 +1316,7 @@ void QMatrix4x4::rotate(const QQuaternion& quaternion) // Algorithm from: // http://www.j3d.org/matrix_faq/matrfaq_latest.html#Q54 + Q_DECL_UNINITIALIZED QMatrix4x4 m(Qt::Uninitialized); const float f2x = quaternion.x() + quaternion.x(); @@ -1393,6 +1404,7 @@ void QMatrix4x4::ortho(float left, float right, float bottom, float top, float n const float width = right - left; const float invheight = top - bottom; const float clip = farPlane - nearPlane; + Q_DECL_UNINITIALIZED QMatrix4x4 m(Qt::Uninitialized); m.m[0][0] = 2.0f / width; m.m[1][0] = 0.0f; @@ -1431,6 +1443,7 @@ void QMatrix4x4::frustum(float left, float right, float bottom, float top, float return; // Construct the projection. + Q_DECL_UNINITIALIZED QMatrix4x4 m(Qt::Uninitialized); const float width = right - left; const float invheight = top - bottom; @@ -1474,6 +1487,7 @@ void QMatrix4x4::perspective(float verticalAngle, float aspectRatio, float nearP return; // Construct the projection. + Q_DECL_UNINITIALIZED QMatrix4x4 m(Qt::Uninitialized); const float radians = qDegreesToRadians(verticalAngle / 2.0f); const float sine = std::sin(radians); @@ -1524,6 +1538,7 @@ void QMatrix4x4::lookAt(const QVector3D& eye, const QVector3D& center, const QVe QVector3D side = QVector3D::crossProduct(forward, up).normalized(); QVector3D upVector = QVector3D::crossProduct(side, forward); + Q_DECL_UNINITIALIZED QMatrix4x4 m(Qt::Uninitialized); m.m[0][0] = side.x(); m.m[1][0] = side.y(); @@ -1573,6 +1588,7 @@ void QMatrix4x4::viewport(float left, float bottom, float width, float height, f const float w2 = width / 2.0f; const float h2 = height / 2.0f; + Q_DECL_UNINITIALIZED QMatrix4x4 m(Qt::Uninitialized); m.m[0][0] = w2; m.m[1][0] = 0.0f; @@ -1871,6 +1887,7 @@ QRectF QMatrix4x4::mapRect(const QRectF& rect) const // of just rotations and translations. QMatrix4x4 QMatrix4x4::orthonormalInverse() const { + Q_DECL_UNINITIALIZED QMatrix4x4 result(Qt::Uninitialized); result.m[0][0] = m[0][0]; @@ -1943,6 +1960,7 @@ void QMatrix4x4::optimize() flagBits &= ~Scale; } else { // If the columns are orthonormal and form a right-handed system, then there is no scale. + Q_DECL_UNINITIALIZED double mm[4][4]; copyToDoubles(m, mm); double det = matrixDet2(mm, 0, 1, 0, 1); @@ -1957,6 +1975,7 @@ void QMatrix4x4::optimize() } } else { // If the columns are orthonormal and form a right-handed system, then there is no scale. + Q_DECL_UNINITIALIZED double mm[4][4]; copyToDoubles(m, mm); double det = matrixDet3(mm, 0, 1, 2, 0, 1, 2); diff --git a/src/gui/math3d/qmatrix4x4.h b/src/gui/math3d/qmatrix4x4.h index 2ba274d4517..2a801905ce0 100644 --- a/src/gui/math3d/qmatrix4x4.h +++ b/src/gui/math3d/qmatrix4x4.h @@ -564,6 +564,7 @@ inline bool QMatrix4x4::operator!=(const QMatrix4x4& other) const inline QMatrix4x4 operator+(const QMatrix4x4& m1, const QMatrix4x4& m2) { + Q_DECL_UNINITIALIZED QMatrix4x4 m(Qt::Uninitialized); m.m[0][0] = m1.m[0][0] + m2.m[0][0]; m.m[0][1] = m1.m[0][1] + m2.m[0][1]; @@ -586,6 +587,7 @@ inline QMatrix4x4 operator+(const QMatrix4x4& m1, const QMatrix4x4& m2) inline QMatrix4x4 operator-(const QMatrix4x4& m1, const QMatrix4x4& m2) { + Q_DECL_UNINITIALIZED QMatrix4x4 m(Qt::Uninitialized); m.m[0][0] = m1.m[0][0] - m2.m[0][0]; m.m[0][1] = m1.m[0][1] - m2.m[0][1]; @@ -608,21 +610,34 @@ inline QMatrix4x4 operator-(const QMatrix4x4& m1, const QMatrix4x4& m2) inline QMatrix4x4 operator*(const QMatrix4x4& m1, const QMatrix4x4& m2) { + Q_DECL_UNINITIALIZED + QMatrix4x4 m(Qt::Uninitialized); QMatrix4x4::Flags flagBits = m1.flagBits | m2.flagBits; if (flagBits.toInt() < QMatrix4x4::Rotation2D) { - QMatrix4x4 m = m1; - m.m[3][0] += m.m[0][0] * m2.m[3][0]; - m.m[3][1] += m.m[1][1] * m2.m[3][1]; - m.m[3][2] += m.m[2][2] * m2.m[3][2]; - - m.m[0][0] *= m2.m[0][0]; - m.m[1][1] *= m2.m[1][1]; - m.m[2][2] *= m2.m[2][2]; + // Scale | Translation + m.m[0][0] = m1.m[0][0] * m2.m[0][0]; + m.m[0][1] = 0.0f; + m.m[0][2] = 0.0f; + m.m[0][3] = 0.0f; + + m.m[1][0] = 0.0f; + m.m[1][1] = m1.m[1][1] * m2.m[1][1]; + m.m[1][2] = 0.0f; + m.m[1][3] = 0.0f; + + m.m[2][0] = 0.0f; + m.m[2][1] = 0.0f; + m.m[2][2] = m1.m[2][2] * m2.m[2][2]; + m.m[2][3] = 0.0f; + + m.m[3][0] = m1.m[3][0] + m1.m[0][0] * m2.m[3][0]; + m.m[3][1] = m1.m[3][1] + m1.m[1][1] * m2.m[3][1]; + m.m[3][2] = m1.m[3][2] + m1.m[2][2] * m2.m[3][2]; + m.m[3][3] = 1.0f; m.flagBits = flagBits; return m; } - QMatrix4x4 m(Qt::Uninitialized); m.m[0][0] = m1.m[0][0] * m2.m[0][0] + m1.m[1][0] * m2.m[0][1] + m1.m[2][0] * m2.m[0][2] @@ -843,6 +858,7 @@ inline QPointF operator*(const QMatrix4x4& matrix, const QPointF& point) inline QMatrix4x4 operator-(const QMatrix4x4& matrix) { + Q_DECL_UNINITIALIZED QMatrix4x4 m(Qt::Uninitialized); m.m[0][0] = -matrix.m[0][0]; m.m[0][1] = -matrix.m[0][1]; @@ -865,6 +881,7 @@ inline QMatrix4x4 operator-(const QMatrix4x4& matrix) inline QMatrix4x4 operator*(float factor, const QMatrix4x4& matrix) { + Q_DECL_UNINITIALIZED QMatrix4x4 m(Qt::Uninitialized); m.m[0][0] = matrix.m[0][0] * factor; m.m[0][1] = matrix.m[0][1] * factor; @@ -887,6 +904,7 @@ inline QMatrix4x4 operator*(float factor, const QMatrix4x4& matrix) inline QMatrix4x4 operator*(const QMatrix4x4& matrix, float factor) { + Q_DECL_UNINITIALIZED QMatrix4x4 m(Qt::Uninitialized); m.m[0][0] = matrix.m[0][0] * factor; m.m[0][1] = matrix.m[0][1] * factor; diff --git a/src/gui/math3d/qquaternion.h b/src/gui/math3d/qquaternion.h index a7b1d432df7..c92e7177199 100644 --- a/src/gui/math3d/qquaternion.h +++ b/src/gui/math3d/qquaternion.h @@ -305,10 +305,10 @@ constexpr QQuaternion operator/(const QQuaternion &quaternion, float divisor) constexpr bool qFuzzyCompare(const QQuaternion &q1, const QQuaternion &q2) noexcept { - return qFuzzyCompare(q1.wp, q2.wp) && - qFuzzyCompare(q1.xp, q2.xp) && - qFuzzyCompare(q1.yp, q2.yp) && - qFuzzyCompare(q1.zp, q2.zp); + return QtPrivate::fuzzyCompare(q1.wp, q2.wp) + && QtPrivate::fuzzyCompare(q1.xp, q2.xp) + && QtPrivate::fuzzyCompare(q1.yp, q2.yp) + && QtPrivate::fuzzyCompare(q1.zp, q2.zp); } #if QT_GUI_INLINE_IMPL_SINCE(6, 11) diff --git a/src/gui/math3d/qvectornd.cpp b/src/gui/math3d/qvectornd.cpp index ec836cfa56e..ee070b2b5be 100644 --- a/src/gui/math3d/qvectornd.cpp +++ b/src/gui/math3d/qvectornd.cpp @@ -375,7 +375,8 @@ QT_BEGIN_NAMESPACE */ bool qFuzzyCompare(QVector2D v1, QVector2D v2) noexcept { - return qFuzzyCompare(v1.v[0], v2.v[0]) && qFuzzyCompare(v1.v[1], v2.v[1]); + return QtPrivate::fuzzyCompare(v1.v[0], v2.v[0]) + && QtPrivate::fuzzyCompare(v1.v[1], v2.v[1]); } #ifndef QT_NO_VECTOR3D @@ -979,9 +980,9 @@ QVector3D QVector3D::unproject(const QMatrix4x4 &modelView, const QMatrix4x4 &pr */ bool qFuzzyCompare(QVector3D v1, QVector3D v2) noexcept { - return qFuzzyCompare(v1.v[0], v2.v[0]) && - qFuzzyCompare(v1.v[1], v2.v[1]) && - qFuzzyCompare(v1.v[2], v2.v[2]); + return QtPrivate::fuzzyCompare(v1.v[0], v2.v[0]) + && QtPrivate::fuzzyCompare(v1.v[1], v2.v[1]) + && QtPrivate::fuzzyCompare(v1.v[2], v2.v[2]); } #ifndef QT_NO_VECTOR2D @@ -1501,10 +1502,10 @@ QDataStream &operator>>(QDataStream &stream, QVector3D &vector) */ bool qFuzzyCompare(QVector4D v1, QVector4D v2) noexcept { - return qFuzzyCompare(v1.v[0], v2.v[0]) && - qFuzzyCompare(v1.v[1], v2.v[1]) && - qFuzzyCompare(v1.v[2], v2.v[2]) && - qFuzzyCompare(v1.v[3], v2.v[3]); + return QtPrivate::fuzzyCompare(v1.v[0], v2.v[0]) + && QtPrivate::fuzzyCompare(v1.v[1], v2.v[1]) + && QtPrivate::fuzzyCompare(v1.v[2], v2.v[2]) + && QtPrivate::fuzzyCompare(v1.v[3], v2.v[3]); } #ifndef QT_NO_VECTOR2D diff --git a/src/gui/opengl/platform/egl/qeglplatformcontext.cpp b/src/gui/opengl/platform/egl/qeglplatformcontext.cpp index e56504833d1..350968b87d4 100644 --- a/src/gui/opengl/platform/egl/qeglplatformcontext.cpp +++ b/src/gui/opengl/platform/egl/qeglplatformcontext.cpp @@ -104,6 +104,14 @@ QT_BEGIN_NAMESPACE #define GL_LOSE_CONTEXT_ON_RESET 0x8252 #endif +// Constants from GL_EXT_robustness. +#ifndef GL_RESET_NOTIFICATION_STRATEGY_EXT +#define GL_RESET_NOTIFICATION_STRATEGY_EXT 0x8256 +#endif +#ifndef GL_LOSE_CONTEXT_ON_RESET_EXT +#define GL_LOSE_CONTEXT_ON_RESET_EXT 0x8252 +#endif + // Constants from EGL_NV_robustness_video_memory_purge #ifndef EGL_GENERATE_RESET_ON_VIDEO_MEMORY_PURGE_NV #define EGL_GENERATE_RESET_ON_VIDEO_MEMORY_PURGE_NV 0x334C @@ -452,11 +460,16 @@ void QEGLPlatformContext::updateFormatFromGL() } } } - if (hasExtension("GL_ARB_robustness")) { + if (m_format.renderableType() == QSurfaceFormat::OpenGL && hasExtension("GL_ARB_robustness")) { GLint value = 0; glGetIntegerv(GL_RESET_NOTIFICATION_STRATEGY, &value); if (value == GL_LOSE_CONTEXT_ON_RESET) m_format.setOption(QSurfaceFormat::ResetNotification); + } else if (m_format.renderableType() == QSurfaceFormat::OpenGLES && hasExtension("GL_EXT_robustness")) { + GLint value = 0; + glGetIntegerv(GL_RESET_NOTIFICATION_STRATEGY_EXT, &value); + if (value == GL_LOSE_CONTEXT_ON_RESET_EXT) + m_format.setOption(QSurfaceFormat::ResetNotification); } } runGLChecks(); diff --git a/src/gui/opengl/qopenglfunctions.cpp b/src/gui/opengl/qopenglfunctions.cpp index 6b45d26fb4c..cb927d7c296 100644 --- a/src/gui/opengl/qopenglfunctions.cpp +++ b/src/gui/opengl/qopenglfunctions.cpp @@ -192,6 +192,12 @@ QOpenGLFunctions::QOpenGLFunctions(QOpenGLContext *context) qWarning("QOpenGLFunctions created with non-current context"); } +/*! + \class QOpenGLExtensions + \inmodule QtGui + \internal +*/ + QOpenGLExtensions::QOpenGLExtensions() { } diff --git a/src/gui/painting/qdrawhelper.cpp b/src/gui/painting/qdrawhelper.cpp index 04433c7703a..697ede42d92 100644 --- a/src/gui/painting/qdrawhelper.cpp +++ b/src/gui/painting/qdrawhelper.cpp @@ -930,7 +930,7 @@ static inline bool canUseFastMatrixPath(const qreal cx, const qreal cy, const qs minc = std::min(minc, std::min(fx, fy)); maxc = std::max(maxc, std::max(fx, fy)); - return minc >= std::numeric_limits<int>::min() && maxc <= std::numeric_limits<int>::max(); + return minc >= std::numeric_limits<int>::min() && maxc <= qreal(std::numeric_limits<int>::max()); } template<TextureBlendType blendType, QPixelLayout::BPP bpp, typename T> @@ -5179,7 +5179,7 @@ static inline bool calculate_fixed_gradient_factors(int count, const QT_FT_Span const int gss = GRADIENT_STOPTABLE_SIZE - 1; qreal ryinc = linear.dy * data->m22 * gss * FIXPT_SIZE; qreal roff = (linear.dy * (data->m22 * qreal(0.5) + data->dy) + linear.off) * gss * FIXPT_SIZE; - const int limit = std::numeric_limits<int>::max() - FIXPT_SIZE; + const qreal limit = qreal(std::numeric_limits<int>::max() - FIXPT_SIZE); if (count && (std::fabs(ryinc) < limit) && (std::fabs(roff) < limit) && (std::fabs(ryinc * spans->y + roff) < limit) && (std::fabs(ryinc * (spans + count - 1)->y + roff) < limit)) { diff --git a/src/gui/painting/qpagelayout.cpp b/src/gui/painting/qpagelayout.cpp index e6f346dcdf2..17fb6491b75 100644 --- a/src/gui/painting/qpagelayout.cpp +++ b/src/gui/painting/qpagelayout.cpp @@ -606,7 +606,7 @@ bool QPageLayout::setLeftMargin(qreal leftMargin, OutOfBoundsPolicy outOfBoundsP if (d->m_mode == StandardMode && outOfBoundsPolicy == OutOfBoundsPolicy::Clamp) leftMargin = qBound(d->m_minMargins.left(), leftMargin, d->m_maxMargins.left()); - if (qFuzzyCompare(leftMargin, d->m_margins.left())) + if (QtPrivate::fuzzyCompare(leftMargin, d->m_margins.left())) return true; if (d->m_mode == FullPageMode @@ -637,7 +637,7 @@ bool QPageLayout::setRightMargin(qreal rightMargin, OutOfBoundsPolicy outOfBound if (d->m_mode == StandardMode && outOfBoundsPolicy == OutOfBoundsPolicy::Clamp) rightMargin = qBound(d->m_minMargins.right(), rightMargin, d->m_maxMargins.right()); - if (qFuzzyCompare(rightMargin, d->m_margins.right())) + if (QtPrivate::fuzzyCompare(rightMargin, d->m_margins.right())) return true; if (d->m_mode == FullPageMode @@ -668,7 +668,7 @@ bool QPageLayout::setTopMargin(qreal topMargin, OutOfBoundsPolicy outOfBoundsPol if (d->m_mode == StandardMode && outOfBoundsPolicy == OutOfBoundsPolicy::Clamp) topMargin = qBound(d->m_minMargins.top(), topMargin, d->m_maxMargins.top()); - if (qFuzzyCompare(topMargin, d->m_margins.top())) + if (QtPrivate::fuzzyCompare(topMargin, d->m_margins.top())) return true; if (d->m_mode == FullPageMode @@ -699,7 +699,7 @@ bool QPageLayout::setBottomMargin(qreal bottomMargin, OutOfBoundsPolicy outOfBou if (d->m_mode == StandardMode && outOfBoundsPolicy == OutOfBoundsPolicy::Clamp) bottomMargin = qBound(d->m_minMargins.bottom(), bottomMargin, d->m_maxMargins.bottom()); - if (qFuzzyCompare(bottomMargin, d->m_margins.bottom())) + if (QtPrivate::fuzzyCompare(bottomMargin, d->m_margins.bottom())) return true; if (d->m_mode == FullPageMode diff --git a/src/gui/painting/qpaintengine_raster.cpp b/src/gui/painting/qpaintengine_raster.cpp index 74321705ff5..8e815394849 100644 --- a/src/gui/painting/qpaintengine_raster.cpp +++ b/src/gui/painting/qpaintengine_raster.cpp @@ -2881,6 +2881,7 @@ bool QRasterPaintEngine::drawCachedGlyphs(int numGlyphs, const glyph_t *glyphs, /*! + * \internal * Returns \c true if the rectangle is completely within the current clip * state of the paint engine. */ @@ -2952,9 +2953,9 @@ inline bool QRasterPaintEnginePrivate::isUnclipped(const QRectF &rect, int penWidth) const { const QRectF norm = rect.normalized(); - if (norm.left() <= INT_MIN || norm.top() <= INT_MIN - || norm.right() > INT_MAX || norm.bottom() > INT_MAX - || norm.width() > INT_MAX || norm.height() > INT_MAX) + if (norm.left() <= qreal(INT_MIN) || norm.top() <= qreal(INT_MIN) + || norm.right() > qreal(INT_MAX) || norm.bottom() > qreal(INT_MAX) + || norm.width() > qreal(INT_MAX) || norm.height() > qreal(INT_MAX)) return false; return isUnclipped(norm.toAlignedRect(), penWidth); } diff --git a/src/gui/painting/qpainterpath.h b/src/gui/painting/qpainterpath.h index cc80641ded9..ab9e7fe312a 100644 --- a/src/gui/painting/qpainterpath.h +++ b/src/gui/painting/qpainterpath.h @@ -6,9 +6,11 @@ #include <QtGui/qtguiglobal.h> #include <QtGui/qtransform.h> + #include <QtCore/qglobal.h> #include <QtCore/qline.h> #include <QtCore/qlist.h> +#include <QtCore/qpoint.h> #include <QtCore/qrect.h> QT_BEGIN_NAMESPACE diff --git a/src/gui/painting/qpdf.cpp b/src/gui/painting/qpdf.cpp index 908051a477c..b1ea3f240f1 100644 --- a/src/gui/painting/qpdf.cpp +++ b/src/gui/painting/qpdf.cpp @@ -3192,6 +3192,7 @@ static inline bool is_monochrome(const QList<QRgb> &colorTable) } /*! + * \internal * Adds an image to the pdf and return the pdf-object id. Returns -1 if adding the image failed. */ int QPdfEnginePrivate::addImage(const QImage &img, bool *bitmap, bool lossless, qint64 serial_no) diff --git a/src/gui/painting/qstroker.cpp b/src/gui/painting/qstroker.cpp index 0d435c95048..08128c30a70 100644 --- a/src/gui/painting/qstroker.cpp +++ b/src/gui/painting/qstroker.cpp @@ -145,6 +145,11 @@ static inline qreal adapted_angle_on_x(const QLineF &line) return QLineF(0, 0, 1, 0).angleTo(line); } +/*! + \class QStrokerOps + \inmodule QtGui + \internal +*/ QStrokerOps::QStrokerOps() : m_elements(0) , m_curveThreshold(qt_real_to_fixed(0.25)) @@ -373,6 +378,7 @@ QStroker::LineJoinMode QStroker::joinModeForJoin(Qt::PenJoinStyle joinStyle) /*! + \internal This function is called to stroke the currently built up subpath. The subpath is cleared when the function completes. */ diff --git a/src/gui/platform/unix/dbusmenu/qdbusplatformmenu.cpp b/src/gui/platform/unix/dbusmenu/qdbusplatformmenu.cpp index fe050461260..00c3192e00a 100644 --- a/src/gui/platform/unix/dbusmenu/qdbusplatformmenu.cpp +++ b/src/gui/platform/unix/dbusmenu/qdbusplatformmenu.cpp @@ -48,6 +48,7 @@ void QDBusPlatformMenuItem::setIcon(const QIcon &icon) } /*! + \internal Set a submenu under this menu item. */ void QDBusPlatformMenuItem::setMenu(QPlatformMenu *menu) diff --git a/src/gui/rhi/qrhimetal.mm b/src/gui/rhi/qrhimetal.mm index 7fa05f69232..0d4ce909daa 100644 --- a/src/gui/rhi/qrhimetal.mm +++ b/src/gui/rhi/qrhimetal.mm @@ -26,6 +26,8 @@ #include <Metal/Metal.h> +#include <utility> // for std::pair + QT_BEGIN_NAMESPACE /* @@ -1674,12 +1676,12 @@ void QRhiMetal::setShaderResources(QRhiCommandBuffer *cb, QRhiShaderResourceBind if (needsBufferSizeBuffer) { QMetalBuffer *bufD = nullptr; - QVarLengthArray<QPair<QMetalShader *, QRhiShaderResourceBinding::StageFlag>, 4> shaders; + QVarLengthArray<std::pair<QMetalShader *, QRhiShaderResourceBinding::StageFlag>, 4> shaders; if (compPsD) { bufD = compPsD->d->bufferSizeBuffer; Q_ASSERT(compPsD->d->cs.nativeShaderInfo.extraBufferBindings.contains(QShaderPrivate::MslBufferSizeBufferBinding)); - shaders.append(qMakePair(&compPsD->d->cs, QRhiShaderResourceBinding::StageFlag::ComputeStage)); + shaders.append({&compPsD->d->cs, QRhiShaderResourceBinding::StageFlag::ComputeStage}); } else { bufD = gfxPsD->d->bufferSizeBuffer; if (gfxPsD->d->tess.enabled) { @@ -1706,24 +1708,24 @@ void QRhiMetal::setShaderResources(QRhiCommandBuffer *cb, QRhiShaderResourceBind == gfxPsD->d->tess.compVs[2].nativeShaderInfo.extraBufferBindings[QShaderPrivate::MslBufferSizeBufferBinding]); if (gfxPsD->d->tess.compVs[0].nativeShaderInfo.extraBufferBindings.contains(QShaderPrivate::MslBufferSizeBufferBinding)) - shaders.append(qMakePair(&gfxPsD->d->tess.compVs[0], QRhiShaderResourceBinding::StageFlag::VertexStage)); + shaders.append({&gfxPsD->d->tess.compVs[0], QRhiShaderResourceBinding::StageFlag::VertexStage}); if (gfxPsD->d->tess.compTesc.nativeShaderInfo.extraBufferBindings.contains(QShaderPrivate::MslBufferSizeBufferBinding)) - shaders.append(qMakePair(&gfxPsD->d->tess.compTesc, QRhiShaderResourceBinding::StageFlag::TessellationControlStage)); + shaders.append({&gfxPsD->d->tess.compTesc, QRhiShaderResourceBinding::StageFlag::TessellationControlStage}); if (gfxPsD->d->tess.vertTese.nativeShaderInfo.extraBufferBindings.contains(QShaderPrivate::MslBufferSizeBufferBinding)) - shaders.append(qMakePair(&gfxPsD->d->tess.vertTese, QRhiShaderResourceBinding::StageFlag::TessellationEvaluationStage)); + shaders.append({&gfxPsD->d->tess.vertTese, QRhiShaderResourceBinding::StageFlag::TessellationEvaluationStage}); } else { if (gfxPsD->d->vs.nativeShaderInfo.extraBufferBindings.contains(QShaderPrivate::MslBufferSizeBufferBinding)) - shaders.append(qMakePair(&gfxPsD->d->vs, QRhiShaderResourceBinding::StageFlag::VertexStage)); + shaders.append({&gfxPsD->d->vs, QRhiShaderResourceBinding::StageFlag::VertexStage}); } if (gfxPsD->d->fs.nativeShaderInfo.extraBufferBindings.contains(QShaderPrivate::MslBufferSizeBufferBinding)) - shaders.append(qMakePair(&gfxPsD->d->fs, QRhiShaderResourceBinding::StageFlag::FragmentStage)); + shaders.append({&gfxPsD->d->fs, QRhiShaderResourceBinding::StageFlag::FragmentStage}); } quint32 offset = 0; - for (const QPair<QMetalShader *, QRhiShaderResourceBinding::StageFlag> &shader : shaders) { + for (const auto &shader : shaders) { const int binding = shader.first->nativeShaderInfo.extraBufferBindings[QShaderPrivate::MslBufferSizeBufferBinding]; @@ -6030,7 +6032,7 @@ bool QMetalGraphicsPipeline::create() for (QMetalShader *shader : shaders) { if (shader->nativeShaderInfo.extraBufferBindings.contains(QShaderPrivate::MslBufferSizeBufferBinding)) { const int binding = shader->nativeShaderInfo.extraBufferBindings[QShaderPrivate::MslBufferSizeBufferBinding]; - shader->nativeResourceBindingMap[binding] = qMakePair(binding, -1); + shader->nativeResourceBindingMap[binding] = {binding, -1}; int maxNativeBinding = 0; for (const QShaderDescription::StorageBlock &block : shader->desc.storageBlocks()) maxNativeBinding = qMax(maxNativeBinding, shader->nativeResourceBindingMap[block.binding].first); @@ -6148,7 +6150,7 @@ bool QMetalComputePipeline::create() // SPIRV-Cross buffer size buffers if (d->cs.nativeShaderInfo.extraBufferBindings.contains(QShaderPrivate::MslBufferSizeBufferBinding)) { const int binding = d->cs.nativeShaderInfo.extraBufferBindings[QShaderPrivate::MslBufferSizeBufferBinding]; - d->cs.nativeResourceBindingMap[binding] = qMakePair(binding, -1); + d->cs.nativeResourceBindingMap[binding] = {binding, -1}; } if (rhiD->d->shaderCache.count() >= QRhiMetal::MAX_SHADER_CACHE_ENTRIES) { diff --git a/src/gui/rhi/qrhivulkan.cpp b/src/gui/rhi/qrhivulkan.cpp index 33e35ba6694..202e28263c2 100644 --- a/src/gui/rhi/qrhivulkan.cpp +++ b/src/gui/rhi/qrhivulkan.cpp @@ -682,6 +682,12 @@ bool QRhiVulkan::create(QRhi::Flags flags) if (devExts.contains("VK_KHR_fragment_shading_rate")) addToChain(&physDevFeaturesChainable, &fragmentShadingRateFeatures); #endif +#ifdef VK_EXT_device_fault + VkPhysicalDeviceFaultFeaturesEXT deviceFaultFeatures = {}; + deviceFaultFeatures.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FAULT_FEATURES_EXT; + if (devExts.contains(VK_EXT_DEVICE_FAULT_EXTENSION_NAME)) + addToChain(&physDevFeaturesChainable, &deviceFaultFeatures); +#endif #endif // Vulkan >=1.2 headers at build time, >=1.2 implementation at run time @@ -825,6 +831,13 @@ bool QRhiVulkan::create(QRhi::Flags flags) requestedDevExts.append(VK_KHR_FRAGMENT_SHADING_RATE_EXTENSION_NAME); #endif +#ifdef VK_EXT_device_fault + if (devExts.contains(VK_EXT_DEVICE_FAULT_EXTENSION_NAME)) { + requestedDevExts.append(VK_EXT_DEVICE_FAULT_EXTENSION_NAME); + caps.deviceFault = true; + } +#endif + for (const QByteArray &ext : requestedDeviceExtensions) { if (!ext.isEmpty() && !requestedDevExts.contains(ext)) { if (devExts.contains(ext)) { @@ -910,6 +923,7 @@ bool QRhiVulkan::create(QRhi::Flags flags) // Here we have no way to tell if the extensions got enabled or not. // Pretend it's all there and supported. If getProcAddress fails, we'll // handle that gracefully. + caps.deviceFault = true; caps.vertexAttribDivisor = true; caps.renderPass2KHR = true; caps.depthStencilResolveKHR = true; @@ -1126,6 +1140,12 @@ bool QRhiVulkan::create(QRhi::Flags flags) } #endif +#ifdef VK_EXT_device_fault + if (caps.deviceFault) { + vkGetDeviceFaultInfoEXT = reinterpret_cast<PFN_vkGetDeviceFaultInfoEXT>(f->vkGetDeviceProcAddr(dev, "vkGetDeviceFaultInfoEXT")); + } +#endif + deviceLost = false; nativeHandlesStruct.physDev = physDev; @@ -2643,6 +2663,7 @@ QRhi::FrameOpResult QRhiVulkan::beginFrame(QRhiSwapChain *swapChain, QRhi::Begin } else { if (err == VK_ERROR_DEVICE_LOST) { qWarning("Device loss detected in vkAcquireNextImageKHR()"); + printExtraErrorInfo(err); deviceLost = true; return QRhi::FrameOpDeviceLost; } @@ -2803,6 +2824,7 @@ QRhi::FrameOpResult QRhiVulkan::endFrame(QRhiSwapChain *swapChain, QRhi::EndFram } else if (err != VK_SUBOPTIMAL_KHR) { if (err == VK_ERROR_DEVICE_LOST) { qWarning("Device loss detected in vkQueuePresentKHR()"); + printExtraErrorInfo(err); deviceLost = true; return QRhi::FrameOpDeviceLost; } @@ -2862,6 +2884,7 @@ QRhi::FrameOpResult QRhiVulkan::startPrimaryCommandBuffer(VkCommandBuffer *cb) if (err != VK_SUCCESS) { if (err == VK_ERROR_DEVICE_LOST) { qWarning("Device loss detected in vkAllocateCommandBuffers()"); + printExtraErrorInfo(err); deviceLost = true; return QRhi::FrameOpDeviceLost; } @@ -2877,6 +2900,7 @@ QRhi::FrameOpResult QRhiVulkan::startPrimaryCommandBuffer(VkCommandBuffer *cb) if (err != VK_SUCCESS) { if (err == VK_ERROR_DEVICE_LOST) { qWarning("Device loss detected in vkBeginCommandBuffer()"); + printExtraErrorInfo(err); deviceLost = true; return QRhi::FrameOpDeviceLost; } @@ -2894,6 +2918,7 @@ QRhi::FrameOpResult QRhiVulkan::endAndSubmitPrimaryCommandBuffer(VkCommandBuffer if (err != VK_SUCCESS) { if (err == VK_ERROR_DEVICE_LOST) { qWarning("Device loss detected in vkEndCommandBuffer()"); + printExtraErrorInfo(err); deviceLost = true; return QRhi::FrameOpDeviceLost; } @@ -2930,6 +2955,7 @@ QRhi::FrameOpResult QRhiVulkan::endAndSubmitPrimaryCommandBuffer(VkCommandBuffer if (err != VK_SUCCESS) { if (err == VK_ERROR_DEVICE_LOST) { qWarning("Device loss detected in vkQueueSubmit()"); + printExtraErrorInfo(err); deviceLost = true; return QRhi::FrameOpDeviceLost; } @@ -2951,6 +2977,7 @@ QRhi::FrameOpResult QRhiVulkan::waitCommandCompletion(int frameSlot) if (err != VK_SUCCESS) { if (err == VK_ERROR_DEVICE_LOST) { qWarning("Device loss detected in vkWaitForFences()"); + printExtraErrorInfo(err); deviceLost = true; return QRhi::FrameOpDeviceLost; } @@ -4079,10 +4106,87 @@ void QRhiVulkan::prepareUploadSubres(QVkTexture *texD, int layer, int level, void QRhiVulkan::printExtraErrorInfo(VkResult err) { + if (err == VK_ERROR_DEVICE_LOST) + printDeviceLossErrorInfo(); if (err == VK_ERROR_OUT_OF_DEVICE_MEMORY) qWarning() << "Out of device memory, current allocator statistics are" << statistics(); } +void QRhiVulkan::printDeviceLossErrorInfo() const +{ +#ifdef VK_EXT_device_fault + if (!dev || !caps.deviceFault || !vkGetDeviceFaultInfoEXT) + return; + + VkDeviceFaultCountsEXT faultCounts{}; + faultCounts.sType = VK_STRUCTURE_TYPE_DEVICE_FAULT_COUNTS_EXT; + faultCounts.pNext = nullptr; + + VkResult result = vkGetDeviceFaultInfoEXT(dev, &faultCounts, nullptr); + if (result != VK_SUCCESS && result != VK_INCOMPLETE) { + qWarning("vkGetDeviceFaultInfoEXT failed with %d", result); + return; + } + faultCounts.vendorBinarySize = 0; + + QVarLengthArray<VkDeviceFaultAddressInfoEXT> addressInfos; + addressInfos.resize(faultCounts.addressInfoCount); + + QVarLengthArray<VkDeviceFaultVendorInfoEXT> vendorInfos; + vendorInfos.resize(faultCounts.vendorInfoCount); + + VkDeviceFaultInfoEXT info{}; + info.sType = VK_STRUCTURE_TYPE_DEVICE_FAULT_INFO_EXT; + info.pNext = nullptr; + info.pAddressInfos = addressInfos.isEmpty() ? nullptr : addressInfos.data(); + info.pVendorInfos = vendorInfos.isEmpty() ? nullptr : vendorInfos.data(); + info.pVendorBinaryData = nullptr; + + result = vkGetDeviceFaultInfoEXT(dev, &faultCounts, &info); + if (result != VK_SUCCESS && result != VK_INCOMPLETE) { + qWarning("vkGetDeviceFaultInfoEXT failed with %d", result); + return; + } + + const char *desc = info.description[0] ? info.description : "n/a"; + qWarning("VK_ERROR_DEVICE_LOST (VK_EXT_device_fault): %u address infos, %u vendor infos, %llu bytes vendor binary: %s", + faultCounts.addressInfoCount, + faultCounts.vendorInfoCount, + (unsigned long long)faultCounts.vendorBinarySize, + desc); + + for (uint32_t i = 0; i < faultCounts.addressInfoCount; ++i) { + const auto &a = addressInfos[i]; + auto addressTypeString = [](const VkDeviceFaultAddressTypeEXT type) { + switch (type) { + case VK_DEVICE_FAULT_ADDRESS_TYPE_NONE_EXT: return "NONE"; + case VK_DEVICE_FAULT_ADDRESS_TYPE_READ_INVALID_EXT: return "READ_INVALID"; + case VK_DEVICE_FAULT_ADDRESS_TYPE_WRITE_INVALID_EXT: return "WRITE_INVALID"; + case VK_DEVICE_FAULT_ADDRESS_TYPE_EXECUTE_INVALID_EXT: return "EXECUTE_INVALID"; + case VK_DEVICE_FAULT_ADDRESS_TYPE_INSTRUCTION_POINTER_UNKNOWN_EXT: return "INSTRUCTION_POINTER_UNKNOWN"; + case VK_DEVICE_FAULT_ADDRESS_TYPE_INSTRUCTION_POINTER_INVALID_EXT: return "INSTRUCTION_POINTER_INVALID"; + case VK_DEVICE_FAULT_ADDRESS_TYPE_INSTRUCTION_POINTER_FAULT_EXT: return "INSTRUCTION_POINTER_FAULT"; + default: return "UNKNOWN"; + }; + }; + qWarning(" AddressInfo[%02u]: type=%s addr=0x%llx precision=%llu", + i, + addressTypeString(a.addressType), + (unsigned long long)a.reportedAddress, + (unsigned long long)a.addressPrecision); + } + + for (uint32_t i = 0; i < faultCounts.vendorInfoCount; ++i) { + const auto &v = vendorInfos[i]; + qWarning(" VendorInfo[%02u]: code=%llu data=%llu desc=%s", + i, + (unsigned long long)v.vendorFaultCode, + (unsigned long long)v.vendorFaultData, + v.description); + } +#endif // VK_EXT_device_fault +} + void QRhiVulkan::enqueueResourceUpdates(QVkCommandBuffer *cbD, QRhiResourceUpdateBatch *resourceUpdates) { QRhiResourceUpdateBatchPrivate *ud = QRhiResourceUpdateBatchPrivate::get(resourceUpdates); diff --git a/src/gui/rhi/qrhivulkan_p.h b/src/gui/rhi/qrhivulkan_p.h index 21044545ad2..eb07d8be448 100644 --- a/src/gui/rhi/qrhivulkan_p.h +++ b/src/gui/rhi/qrhivulkan_p.h @@ -882,6 +882,7 @@ public: void ensureCommandPoolForNewFrame(); double elapsedSecondsFromTimestamp(quint64 timestamp[2], bool *ok); void printExtraErrorInfo(VkResult err); + void printDeviceLossErrorInfo() const; QVulkanInstance *inst = nullptr; QWindow *maybeWindow = nullptr; @@ -942,11 +943,16 @@ public: PFN_vkCreateRenderPass2KHR vkCreateRenderPass2KHR = nullptr; #endif +#ifdef VK_EXT_device_fault + PFN_vkGetDeviceFaultInfoEXT vkGetDeviceFaultInfoEXT = nullptr; +#endif + struct { bool compute = false; bool depthClamp = false; bool wideLines = false; bool debugUtils = false; + bool deviceFault = false; bool vertexAttribDivisor = false; bool texture3DSliceAs2D = false; bool tessellation = false; diff --git a/src/gui/text/freetype/qfontengine_ft.cpp b/src/gui/text/freetype/qfontengine_ft.cpp index 63d9c2893dc..e331a4cc815 100644 --- a/src/gui/text/freetype/qfontengine_ft.cpp +++ b/src/gui/text/freetype/qfontengine_ft.cpp @@ -1225,7 +1225,7 @@ static inline QTransform FTAffineToQTransform(const FT_Affine23 &matrix) } bool QFontEngineFT::traverseColr1(FT_OpaquePaint opaquePaint, - QSet<QPair<FT_Byte *, FT_Bool> > *loops, + QSet<std::pair<FT_Byte *, FT_Bool> > *loops, QColor foregroundColor, FT_Color *palette, ushort paletteCount, @@ -1233,7 +1233,7 @@ bool QFontEngineFT::traverseColr1(FT_OpaquePaint opaquePaint, { FT_Face face = freetype->face; - auto key = qMakePair(opaquePaint.p, opaquePaint.insert_root_transform); + auto key = std::pair{opaquePaint.p, opaquePaint.insert_root_transform}; if (loops->contains(key)) { qCWarning(lcColrv1) << "Cycle detected in COLRv1 graph"; return false; @@ -1680,7 +1680,7 @@ QFontEngineFT::Glyph *QFontEngineFT::loadColrv1Glyph(QGlyphSet *set, // Do a pass over the graph to find the bounds QColrPaintGraphRenderer boundingRectCalculator; boundingRectCalculator.beginCalculateBoundingBox(); - QSet<QPair<FT_Byte *, FT_Bool> > loops; + QSet<std::pair<FT_Byte *, FT_Bool> > loops; if (traverseColr1(opaquePaint, &loops, QColor{}, @@ -1735,7 +1735,7 @@ QFontEngineFT::Glyph *QFontEngineFT::loadColrv1Glyph(QGlyphSet *set, originalXform); // Render - QSet<QPair<FT_Byte *, FT_Bool> > loops; + QSet<std::pair<FT_Byte *, FT_Bool> > loops; if (!traverseColr1(opaquePaint, &loops, foregroundColor, diff --git a/src/gui/text/freetype/qfontengine_ft_p.h b/src/gui/text/freetype/qfontengine_ft_p.h index fc07693ef6a..13cd1bf2bfa 100644 --- a/src/gui/text/freetype/qfontengine_ft_p.h +++ b/src/gui/text/freetype/qfontengine_ft_p.h @@ -34,6 +34,8 @@ #include <string.h> #include <qpainterpath.h> +#include <utility> // for std::pair + QT_BEGIN_NAMESPACE class QFontEngineFTRawFont; @@ -333,7 +335,7 @@ private: bool fetchMetricsOnly) const; bool traverseColr1(FT_OpaquePaint paint, - QSet<QPair<FT_Byte *, FT_Bool> > *loops, + QSet<std::pair<FT_Byte *, FT_Bool> > *loops, QColor foregroundColor, FT_Color *palette, ushort paletteCount, diff --git a/src/gui/text/qcolrpaintgraphrenderer.cpp b/src/gui/text/qcolrpaintgraphrenderer.cpp index 9041e804753..bc439021eb1 100644 --- a/src/gui/text/qcolrpaintgraphrenderer.cpp +++ b/src/gui/text/qcolrpaintgraphrenderer.cpp @@ -180,7 +180,7 @@ void QColrPaintGraphRenderer::setConicalGradient(QPointF center, adaptedStops.reserve(gradientStops.size()); for (const QGradientStop &gradientStop : gradientStops) - adaptedStops.append(qMakePair(gradientStop.first * multiplier, gradientStop.second)); + adaptedStops.append({gradientStop.first * multiplier, gradientStop.second}); conicalGradient.setStops(adaptedStops); conicalGradient.setCoordinateMode(QGradient::LogicalMode); diff --git a/src/gui/text/qfont.cpp b/src/gui/text/qfont.cpp index c144820fa24..ba49d538c2c 100644 --- a/src/gui/text/qfont.cpp +++ b/src/gui/text/qfont.cpp @@ -31,6 +31,7 @@ #include <QtCore/QMutexLocker> #include <QtCore/QMutex> +#include <algorithm> #include <array> // #define QFONTCACHE_DEBUG @@ -1853,35 +1854,13 @@ bool QFont::operator<(const QFont &f) const int f2attrs = (d->underline << 3) + (d->overline << 2) + (d->strikeOut<<1) + d->kerning; if (f1attrs != f2attrs) return f1attrs < f2attrs; - if (d->features.size() != f.d->features.size()) - return f.d->features.size() < d->features.size(); - - { - auto it = d->features.constBegin(); - auto jt = f.d->features.constBegin(); - for (; it != d->features.constEnd(); ++it, ++jt) { - if (it.key() != jt.key()) - return jt.key() < it.key(); - if (it.value() != jt.value()) - return jt.value() < it.value(); - } - } - - if (r1.variableAxisValues.size() != r2.variableAxisValues.size()) - return r1.variableAxisValues.size() < r2.variableAxisValues.size(); - - { - auto it = r1.variableAxisValues.constBegin(); - auto jt = r2.variableAxisValues.constBegin(); - for (; it != r1.variableAxisValues.constEnd(); ++it, ++jt) { - if (it.key() != jt.key()) - return jt.key() < it.key(); - if (it.value() != jt.value()) - return jt.value() < it.value(); - } + if (d->features != f.d->features) { + return std::lexicographical_compare(f.d->features.keyValueBegin(), f.d->features.keyValueEnd(), + d->features.keyValueBegin(), d->features.keyValueEnd()); } - return false; + return std::lexicographical_compare(r1.variableAxisValues.keyValueBegin(), r1.variableAxisValues.keyValueEnd(), + r2.variableAxisValues.keyValueBegin(), r2.variableAxisValues.keyValueEnd()); } diff --git a/src/gui/text/qfontengine.cpp b/src/gui/text/qfontengine.cpp index 0ad45b1f280..4df55d5b89c 100644 --- a/src/gui/text/qfontengine.cpp +++ b/src/gui/text/qfontengine.cpp @@ -402,7 +402,7 @@ bool QFontEngine::processHheaTable() const const qreal unitsPerEm = emSquareSize().toReal(); // Bail out if values are too large for QFixed - const auto limitForQFixed = std::numeric_limits<int>::max() / (fontDef.pixelSize * 64); + const auto limitForQFixed = qreal(std::numeric_limits<int>::max() >> 6) / fontDef.pixelSize; if (ascent > limitForQFixed || descent > limitForQFixed || leading > limitForQFixed) return false; m_ascent = QFixed::fromReal(ascent * fontDef.pixelSize / unitsPerEm); @@ -470,7 +470,7 @@ bool QFontEngine::processOS2Table() const if (typoAscent == 0 && typoDescent == 0) return false; // Bail out if values are too large for QFixed - const auto limitForQFixed = std::numeric_limits<int>::max() / (fontDef.pixelSize * 64); + const auto limitForQFixed = qreal(std::numeric_limits<int>::max() >> 6) / fontDef.pixelSize; if (typoAscent > limitForQFixed || typoDescent > limitForQFixed || typoLineGap > limitForQFixed) return false; @@ -481,7 +481,7 @@ bool QFontEngine::processOS2Table() const // Some fonts may have invalid OS/2 data. We detect this and bail out. if (winAscent == 0 && winDescent == 0) return false; - const auto limitForQFixed = std::numeric_limits<int>::max() / (fontDef.pixelSize * 64); + const auto limitForQFixed = qreal(std::numeric_limits<int>::max() >> 6) / fontDef.pixelSize; if (winAscent > limitForQFixed || winDescent > limitForQFixed) return false; m_ascent = QFixed::fromReal(winAscent * fontDef.pixelSize / unitsPerEm); @@ -1059,6 +1059,7 @@ void QFontEngine::getUnscaledGlyph(glyph_t glyph, QPainterPath *path, glyph_metr } /*! + \internal Returns \c true if the font table idetified by \a tag exists in the font; returns \c false otherwise. diff --git a/src/gui/text/qrawfont.cpp b/src/gui/text/qrawfont.cpp index 7acc3c5218c..5bd9799ca7d 100644 --- a/src/gui/text/qrawfont.cpp +++ b/src/gui/text/qrawfont.cpp @@ -224,7 +224,7 @@ void QRawFont::loadFromData(const QByteArray &fontData, \since 6.11 */ -int QRawFont::glyphCount() const +quint32 QRawFont::glyphCount() const { return d->isValid() ? d->fontEngine->glyphCount() : 0; } diff --git a/src/gui/text/qrawfont.h b/src/gui/text/qrawfont.h index f13f04ebe37..a1522aa8048 100644 --- a/src/gui/text/qrawfont.h +++ b/src/gui/text/qrawfont.h @@ -55,7 +55,7 @@ public: inline bool operator!=(const QRawFont &other) const { return !operator==(other); } - int glyphCount() const; + quint32 glyphCount() const; QString familyName() const; QString styleName() const; diff --git a/src/gui/text/qtextdocument.cpp b/src/gui/text/qtextdocument.cpp index eb0f6c3710c..d519cd5a5d3 100644 --- a/src/gui/text/qtextdocument.cpp +++ b/src/gui/text/qtextdocument.cpp @@ -1976,7 +1976,7 @@ void QTextDocument::print(QPagedPaintDevice *printer) const return; bool documentPaginated = d->pageSize.isValid() && !d->pageSize.isNull() - && d->pageSize.height() != INT_MAX; + && d->pageSize.height() != qreal(INT_MAX); // ### set page size to paginated size? QMarginsF m = printer->pageLayout().margins(QPageLayout::Millimeter); @@ -2385,6 +2385,11 @@ static QString colorValue(QColor color) return result; } +/*! + \class QTextHtmlExporter + \inmodule QtGui + \internal +*/ QTextHtmlExporter::QTextHtmlExporter(const QTextDocument *_doc) : doc(_doc), fragmentMarkers(false) { diff --git a/src/gui/text/qtextdocument_p.cpp b/src/gui/text/qtextdocument_p.cpp index 227cbae2952..85a74d366ac 100644 --- a/src/gui/text/qtextdocument_p.cpp +++ b/src/gui/text/qtextdocument_p.cpp @@ -1006,6 +1006,7 @@ int QTextDocumentPrivate::undoRedo(bool undo) } /*! + \internal Appends a custom undo \a item to the undo stack. */ void QTextDocumentPrivate::appendUndoItem(QAbstractUndoItem *item) diff --git a/src/gui/text/qtexttable.cpp b/src/gui/text/qtexttable.cpp index ff8644b5302..337228ff170 100644 --- a/src/gui/text/qtexttable.cpp +++ b/src/gui/text/qtexttable.cpp @@ -394,11 +394,9 @@ void QTextTablePrivate::fragmentRemoved(QChar type, uint fragment) } /*! - /fn void QTextTablePrivate::update() const - + \internal This function is usually called when the table is "dirty". It seems to update all kind of table information. - */ void QTextTablePrivate::update() const { diff --git a/src/gui/text/windows/qwindowsfontdatabase.cpp b/src/gui/text/windows/qwindowsfontdatabase.cpp index 93af1a9600b..0c6d9a31316 100644 --- a/src/gui/text/windows/qwindowsfontdatabase.cpp +++ b/src/gui/text/windows/qwindowsfontdatabase.cpp @@ -1113,17 +1113,21 @@ void QWindowsFontDatabase::removeApplicationFonts() m_eudcFonts.clear(); } +#if QT_CONFIG(directwrite) QWindowsFontDatabase::FontHandle::FontHandle(IDWriteFontFace *face, const QString &name) : fontFace(face), faceName(name) { fontFace->AddRef(); } +#endif // !QT_NO_DIRECTWRITE QWindowsFontDatabase::FontHandle::~FontHandle() { +#if QT_CONFIG(directwrite) if (fontFace != nullptr) fontFace->Release(); +#endif // !QT_NO_DIRECTWRITE } void QWindowsFontDatabase::releaseHandle(void *handle) diff --git a/src/gui/text/windows/qwindowsfontdatabase_p.h b/src/gui/text/windows/qwindowsfontdatabase_p.h index 92e3f04f968..856a5593722 100644 --- a/src/gui/text/windows/qwindowsfontdatabase_p.h +++ b/src/gui/text/windows/qwindowsfontdatabase_p.h @@ -78,10 +78,14 @@ public: struct FontHandle { FontHandle(const QString &name) : faceName(name) {} +#if QT_CONFIG(directwrite) FontHandle(IDWriteFontFace *face, const QString &name); +#endif // !QT_NO_DIRECTWRITE ~FontHandle(); +#if QT_CONFIG(directwrite) IDWriteFontFace *fontFace = nullptr; +#endif // !QT_NO_DIRECTWRITE QString faceName; }; diff --git a/src/gui/text/windows/qwindowsfontdatabasebase.cpp b/src/gui/text/windows/qwindowsfontdatabasebase.cpp index 990f20fa447..055e616dbb2 100644 --- a/src/gui/text/windows/qwindowsfontdatabasebase.cpp +++ b/src/gui/text/windows/qwindowsfontdatabasebase.cpp @@ -18,6 +18,8 @@ # include "qwindowsfontenginedirectwrite_p.h" #endif +#include <utility> // for std::pair + QT_BEGIN_NAMESPACE using namespace Qt::StringLiterals; @@ -363,7 +365,7 @@ namespace { inline void addKey(const QByteArray &fontData, const QString &filename) { if (!m_fontDatas.contains(fontData.data())) - m_fontDatas.insert(fontData.data(), qMakePair(fontData, filename)); + m_fontDatas.insert(fontData.data(), {fontData, filename}); } HRESULT STDMETHODCALLTYPE GetFilePathLengthFromKey(void const* fontFileReferenceKey, @@ -433,7 +435,7 @@ namespace { private: ULONG m_referenceCount; - QHash<const void *, QPair<QByteArray, QString> > m_fontDatas; + QHash<const void *, std::pair<QByteArray, QString> > m_fontDatas; }; HRESULT STDMETHODCALLTYPE DirectWriteFontFileLoader::QueryInterface(const IID &iid, diff --git a/src/gui/text/windows/qwindowsfontenginedirectwrite.cpp b/src/gui/text/windows/qwindowsfontenginedirectwrite.cpp index 3e10cdad44f..2f0ce3449d9 100644 --- a/src/gui/text/windows/qwindowsfontenginedirectwrite.cpp +++ b/src/gui/text/windows/qwindowsfontenginedirectwrite.cpp @@ -1074,7 +1074,7 @@ bool QWindowsFontEngineDirectWrite::traverseColr1(IDWritePaintReader *paintReade for (int i = 0; i < stopCount; ++i) { const D2D1_GRADIENT_STOP &stop = stops[i]; QColor color = QColor::fromRgbF(stop.color.r, stop.color.g, stop.color.b, stop.color.a); - ret.append(qMakePair(stop.position, color)); + ret.append({stop.position, color}); } return ret; diff --git a/src/gui/util/qlayoutpolicy.cpp b/src/gui/util/qlayoutpolicy.cpp index 4d81a426835..0c0651c1f39 100644 --- a/src/gui/util/qlayoutpolicy.cpp +++ b/src/gui/util/qlayoutpolicy.cpp @@ -8,6 +8,11 @@ QT_BEGIN_NAMESPACE +/*! + \class QLayoutPolicy + \inmodule QtGui + \internal +*/ void QLayoutPolicy::setControlType(ControlType type) { /* diff --git a/src/network/access/qbytedatabuffer_p.h b/src/network/access/qbytedatabuffer_p.h index a119093cf7e..036b562d06a 100644 --- a/src/network/access/qbytedatabuffer_p.h +++ b/src/network/access/qbytedatabuffer_p.h @@ -280,6 +280,8 @@ public: } return false; } + + const QByteArray &last() const { return buffers.last(); } }; QT_END_NAMESPACE diff --git a/src/network/access/qformdatabuilder.cpp b/src/network/access/qformdatabuilder.cpp index 470e285c6ad..debefd24634 100644 --- a/src/network/access/qformdatabuilder.cpp +++ b/src/network/access/qformdatabuilder.cpp @@ -9,6 +9,7 @@ #include "QtCore/qmimedatabase.h" #endif +#include <variant> #include <vector> QT_BEGIN_NAMESPACE diff --git a/src/network/access/qformdatabuilder.h b/src/network/access/qformdatabuilder.h index 7d667aea21c..3992776161d 100644 --- a/src/network/access/qformdatabuilder.h +++ b/src/network/access/qformdatabuilder.h @@ -15,7 +15,6 @@ #include <QtCore/qstring.h> #include <memory> -#include <variant> #ifndef Q_OS_WASM QT_REQUIRE_CONFIG(http); diff --git a/src/network/access/qhttp2connection.cpp b/src/network/access/qhttp2connection.cpp index 2895e8335d2..c0b07ddd652 100644 --- a/src/network/access/qhttp2connection.cpp +++ b/src/network/access/qhttp2connection.cpp @@ -34,8 +34,38 @@ using namespace Http2; \sa QHttp2Connection */ -QHttp2Stream::QHttp2Stream(QHttp2Connection *connection, quint32 streamID) noexcept - : QObject(connection), m_streamID(streamID) +/*! + \struct QHttp2Stream::Configuration + \inmodule QtNetwork + \internal + + \brief Configuration options for a QHttp2Stream. + + The Configuration struct holds options that control stream behavior. + + \sa QHttp2Connection::createStream() +*/ + +/*! + \variable QHttp2Stream::Configuration::useDownloadBuffer + + Controls whether incoming DATA frames, from QHttp2Stream::dataReceived(), + are buffered. The default is \c true. + + You may disable buffering for client-initiated streams when the + application processes DATA immediately. + + Buffering must remain enabled for pushed streams. A pushed stream can + receive DATA before the application becomes aware of them and the buffered + DATA is required to deliver the pushed response. + + \sa QHttp2Stream::downloadBuffer(), QHttp2Stream::takeDownloadBuffer(), + QHttp2Configuration::serverPushEnabled(), QHttp2Stream::dataReceived() +*/ + +QHttp2Stream::QHttp2Stream(QHttp2Connection *connection, quint32 streamID, + Configuration configuration) noexcept + : QObject(connection), m_streamID(streamID), m_configuration(configuration) { Q_ASSERT(connection); Q_ASSERT(streamID); // stream id 0 is reserved for connection control messages @@ -213,6 +243,12 @@ QHttp2Stream::~QHttp2Stream() noexcept { Returns the buffer containing the data received from the remote peer. */ +/*! + \fn QHttp2Stream::Configuration QHttp2Stream::configuration() const + + Returns the configuration of this stream. +*/ + void QHttp2Stream::finishWithError(Http2::Http2Error errorCode, const QString &message) { qCDebug(qHttp2ConnectionLog, "[%p] stream %u finished with error: %ls (error code: %u)", @@ -697,8 +733,14 @@ void QHttp2Stream::handleDATA(const Frame &inboundFrame) inboundFrame.dataSize()); if (endStream) transitionState(StateTransition::CloseRemote); - emit dataReceived(fragment, endStream); - m_downloadBuffer.append(std::move(fragment)); + const auto shouldBuffer = m_configuration.useDownloadBuffer && !fragment.isEmpty(); + if (shouldBuffer) { + // Only non-empty fragments get appended! + m_downloadBuffer.append(std::move(fragment)); + emit dataReceived(m_downloadBuffer.last(), endStream); + } else { + emit dataReceived(fragment, endStream); + } } if (!endStream && m_recvWindow < connection->streamInitialReceiveWindowSize / 2) { @@ -885,23 +927,35 @@ QHttp2Connection *QHttp2Connection::createDirectServerConnection(QIODevice *sock } /*! - Creates a stream on this connection. + \fn QH2Expected<QHttp2Stream *, QHttp2Connection::CreateStreamError> QHttp2Connection::createStream() + Creates a stream on this connection, using the default QHttp2Stream::Configuration. + +//! [createStream] Automatically picks the next available stream ID and returns a pointer to the new stream, if possible. Otherwise returns an error. \sa QHttp2Connection::CreateStreamError, QHttp2Stream +//! [createStream] + \sa createStream(QHttp2Stream::Configuration) +*/ + +/*! + Creates a stream with \a configuration on this connection. + + \include qhttp2connection.cpp createStream */ -QH2Expected<QHttp2Stream *, QHttp2Connection::CreateStreamError> QHttp2Connection::createStream() +QH2Expected<QHttp2Stream *, QHttp2Connection::CreateStreamError> +QHttp2Connection::createStream(QHttp2Stream::Configuration configuration) { Q_ASSERT(m_connectionType == Type::Client); // This overload is just for clients if (m_nextStreamID > lastValidStreamID) return { QHttp2Connection::CreateStreamError::StreamIdsExhausted }; - return createLocalStreamInternal(); + return createLocalStreamInternal(configuration); } QH2Expected<QHttp2Stream *, QHttp2Connection::CreateStreamError> -QHttp2Connection::createLocalStreamInternal() +QHttp2Connection::createLocalStreamInternal(QHttp2Stream::Configuration conf) { if (m_goingAway) return { QHttp2Connection::CreateStreamError::ReceivedGOAWAY }; @@ -909,7 +963,7 @@ QHttp2Connection::createLocalStreamInternal() if (size_t(m_peerMaxConcurrentStreams) <= size_t(numActiveLocalStreams())) return { QHttp2Connection::CreateStreamError::MaxConcurrentStreamsReached }; - if (QHttp2Stream *ptr = createStreamInternal_impl(streamID)) { + if (QHttp2Stream *ptr = createStreamInternal_impl(streamID, conf)) { m_nextStreamID += 2; return {ptr}; } @@ -917,7 +971,8 @@ QHttp2Connection::createLocalStreamInternal() return { QHttp2Connection::CreateStreamError::UnknownError }; } -QHttp2Stream *QHttp2Connection::createStreamInternal_impl(quint32 streamID) +QHttp2Stream *QHttp2Connection::createStreamInternal_impl(quint32 streamID, + QHttp2Stream::Configuration conf) { Q_ASSERT(streamID > m_lastIncomingStreamID || streamID >= m_nextStreamID); @@ -930,7 +985,7 @@ QHttp2Stream *QHttp2Connection::createStreamInternal_impl(quint32 streamID) if (!result.inserted) return nullptr; QPointer<QHttp2Stream> &stream = result.iterator.value(); - stream = new QHttp2Stream(this, streamID); + stream = new QHttp2Stream(this, streamID, conf); stream->m_recvWindow = streamInitialReceiveWindowSize; stream->m_sendWindow = streamInitialSendWindowSize; diff --git a/src/network/access/qhttp2connection_p.h b/src/network/access/qhttp2connection_p.h index f3f14145278..e2af7d7ab33 100644 --- a/src/network/access/qhttp2connection_p.h +++ b/src/network/access/qhttp2connection_p.h @@ -101,6 +101,11 @@ public: Q_ENUM(State) constexpr static quint8 DefaultPriority = 127; + struct Configuration + { + bool useDownloadBuffer = true; + }; + ~QHttp2Stream() noexcept; // HTTP2 things @@ -124,6 +129,8 @@ public: QByteDataBuffer takeDownloadBuffer() noexcept { return std::exchange(m_downloadBuffer, {}); } void clearDownloadBuffer() { m_downloadBuffer.clear(); } + Configuration configuration() const { return m_configuration; } + Q_SIGNALS: void headersReceived(const HPack::HttpHeader &headers, bool endStream); void headersUpdated(); @@ -154,7 +161,8 @@ private Q_SLOTS: private: friend class QHttp2Connection; - QHttp2Stream(QHttp2Connection *connection, quint32 streamID) noexcept; + QHttp2Stream(QHttp2Connection *connection, quint32 streamID, + Configuration configuration) noexcept; [[nodiscard]] QHttp2Connection *getConnection() const { @@ -201,6 +209,8 @@ private: bool m_isReserved = false; bool m_owningByteDevice = false; + const Configuration m_configuration; + friend tst_QHttp2Connection; }; @@ -235,7 +245,12 @@ public: createDirectServerConnection(QIODevice *socket, const QHttp2Configuration &config); ~QHttp2Connection(); - [[nodiscard]] QH2Expected<QHttp2Stream *, CreateStreamError> createStream(); + [[nodiscard]] QH2Expected<QHttp2Stream *, CreateStreamError> createStream() + { + return createStream(QHttp2Stream::Configuration{}); + } + [[nodiscard]] QH2Expected<QHttp2Stream *, CreateStreamError> + createStream(QHttp2Stream::Configuration config); QHttp2Stream *getStream(quint32 streamId) const; QHttp2Stream *promisedStream(const QUrl &streamKey) const @@ -278,8 +293,9 @@ private: friend class QHttp2Stream; [[nodiscard]] QIODevice *getSocket() const { return qobject_cast<QIODevice *>(parent()); } - QH2Expected<QHttp2Stream *, QHttp2Connection::CreateStreamError> createLocalStreamInternal(); - QHttp2Stream *createStreamInternal_impl(quint32 streamID); + QH2Expected<QHttp2Stream *, QHttp2Connection::CreateStreamError> + createLocalStreamInternal(QHttp2Stream::Configuration = {}); + QHttp2Stream *createStreamInternal_impl(quint32 streamID, QHttp2Stream::Configuration = {}); bool isInvalidStream(quint32 streamID) noexcept; bool streamWasResetLocally(quint32 streamID) noexcept; diff --git a/src/network/access/qhttpnetworkconnection_p.h b/src/network/access/qhttpnetworkconnection_p.h index f35b89d1aec..3f60c5b3925 100644 --- a/src/network/access/qhttpnetworkconnection_p.h +++ b/src/network/access/qhttpnetworkconnection_p.h @@ -259,7 +259,7 @@ public: QString peerVerifyName; - QTcpKeepAliveConfiguration tcpKeepAliveConfiguration; + QTcpKeepAliveConfiguration tcpKeepAliveConfiguration = {}; friend class QHttpNetworkConnectionChannel; }; diff --git a/src/network/access/qhttpthreaddelegate_p.h b/src/network/access/qhttpthreaddelegate_p.h index f179d95ac17..5005f8c01c2 100644 --- a/src/network/access/qhttpthreaddelegate_p.h +++ b/src/network/access/qhttpthreaddelegate_p.h @@ -92,7 +92,7 @@ public: QString incomingErrorDetail; QHttp1Configuration http1Parameters; QHttp2Configuration http2Parameters; - QTcpKeepAliveConfiguration tcpKeepAliveParameters; + QTcpKeepAliveConfiguration tcpKeepAliveParameters = {}; protected: // The zerocopy download buffer, if used: diff --git a/src/network/access/qnetworkaccesscache.cpp b/src/network/access/qnetworkaccesscache.cpp index bb1bdd87a8a..6ff70cd546d 100644 --- a/src/network/access/qnetworkaccesscache.cpp +++ b/src/network/access/qnetworkaccesscache.cpp @@ -5,11 +5,6 @@ #include "qnetworkaccesscache_p.h" #include "QtCore/qpointer.h" #include "QtCore/qdeadlinetimer.h" -#include "qnetworkaccessmanager_p.h" -#include "qnetworkreply_p.h" -#include "qnetworkrequest.h" - -#include <vector> //#define DEBUG_ACCESSCACHE diff --git a/src/network/access/qnetworkcookie.cpp b/src/network/access/qnetworkcookie.cpp index c20cd43a2fd..6c44e42dbba 100644 --- a/src/network/access/qnetworkcookie.cpp +++ b/src/network/access/qnetworkcookie.cpp @@ -5,7 +5,6 @@ #include "qnetworkcookie.h" #include "qnetworkcookie_p.h" -#include "qnetworkrequest.h" #include "qnetworkreply.h" #include "QtCore/qbytearray.h" #include "QtCore/qdatetime.h" diff --git a/src/plugins/platforms/cocoa/qcocoaglcontext.mm b/src/plugins/platforms/cocoa/qcocoaglcontext.mm index adad12a4242..be05249c1b8 100644 --- a/src/plugins/platforms/cocoa/qcocoaglcontext.mm +++ b/src/plugins/platforms/cocoa/qcocoaglcontext.mm @@ -265,19 +265,26 @@ void QCocoaGLContext::updateSurfaceFormat() return value; }; - int colorSize = pixelFormatAttribute(NSOpenGLPFAColorSize); - colorSize /= 4; // The attribute includes the alpha component - m_format.setRedBufferSize(colorSize); - m_format.setGreenBufferSize(colorSize); - m_format.setBlueBufferSize(colorSize); + // Resolve color channel bits from GL, rather than NSOpenGLPFAColorSize, + // as the latter is not specific enough (combines all channels). + GLint redBits, greenBits, blueBits; + glGetIntegerv(GL_RED_BITS, &redBits); + glGetIntegerv(GL_GREEN_BITS, &greenBits); + glGetIntegerv(GL_BLUE_BITS, &blueBits); + m_format.setRedBufferSize(redBits); + m_format.setGreenBufferSize(greenBits); + m_format.setBlueBufferSize(blueBits); // Surfaces on macOS always have an alpha channel, but unless the user requested // one via setAlphaBufferSize(), which triggered setting NSOpenGLCPSurfaceOpacity // to make the surface non-opaque, we don't want to report back the actual alpha // size, as that will make the user believe the alpha channel can be used for // something useful, when in reality it can't, due to the surface being opaque. - if (m_format.alphaBufferSize() > 0) - m_format.setAlphaBufferSize(pixelFormatAttribute(NSOpenGLPFAAlphaSize)); + if (m_format.alphaBufferSize() > 0) { + GLint alphaBits; + glGetIntegerv(GL_ALPHA_BITS, &alphaBits); + m_format.setAlphaBufferSize(alphaBits); + } m_format.setDepthBufferSize(pixelFormatAttribute(NSOpenGLPFADepthSize)); m_format.setStencilBufferSize(pixelFormatAttribute(NSOpenGLPFAStencilSize)); diff --git a/src/plugins/platforms/cocoa/qcocoawindow.mm b/src/plugins/platforms/cocoa/qcocoawindow.mm index 0905ba3e644..1b34490e358 100644 --- a/src/plugins/platforms/cocoa/qcocoawindow.mm +++ b/src/plugins/platforms/cocoa/qcocoawindow.mm @@ -330,6 +330,13 @@ QMargins QCocoaWindow::safeAreaMargins() const // merge them. auto screenRect = m_view.window.screen.frame; auto screenInsets = m_view.window.screen.safeAreaInsets; + auto screenSafeArea = QCocoaScreen::mapFromNative(NSMakeRect( + NSMinX(screenRect) + screenInsets.left, + NSMinY(screenRect) + screenInsets.bottom, // Non-flipped + NSWidth(screenRect) - screenInsets.left - screenInsets.right, + NSHeight(screenRect) - screenInsets.top - screenInsets.bottom + )); + auto screenRelativeViewBounds = QCocoaScreen::mapFromNative( [m_view.window convertRectToScreen: [m_view convertRect:m_view.bounds toView:nil]] @@ -339,20 +346,10 @@ QMargins QCocoaWindow::safeAreaMargins() const // Note that we do not want represent the area outside of the // screen as being outside of the safe area. QMarginsF screenSafeAreaMargins = { - screenInsets.left ? - qMax(0.0f, screenInsets.left - screenRelativeViewBounds.left()) - : 0.0f, - screenInsets.top ? - qMax(0.0f, screenInsets.top - screenRelativeViewBounds.top()) - : 0.0f, - screenInsets.right ? - qMax(0.0f, screenInsets.right - - (screenRect.size.width - screenRelativeViewBounds.right())) - : 0.0f, - screenInsets.bottom ? - qMax(0.0f, screenInsets.bottom - - (screenRect.size.height - screenRelativeViewBounds.bottom())) - : 0.0f + qMin(screenSafeArea.left() - screenRelativeViewBounds.left(), screenInsets.left), + qMin(screenSafeArea.top() - screenRelativeViewBounds.top(), screenInsets.top), + qMin(screenRelativeViewBounds.right() - screenSafeArea.right(), screenInsets.right), + qMin(screenRelativeViewBounds.bottom() - screenSafeArea.bottom(), screenInsets.bottom) }; return (screenSafeAreaMargins | viewSafeAreaMargins).toMargins(); diff --git a/src/plugins/platforms/cocoa/qnsview_drawing.mm b/src/plugins/platforms/cocoa/qnsview_drawing.mm index 64c806a087b..b3c22ff051e 100644 --- a/src/plugins/platforms/cocoa/qnsview_drawing.mm +++ b/src/plugins/platforms/cocoa/qnsview_drawing.mm @@ -183,6 +183,9 @@ { qCDebug(lcQpaDrawing) << "Backing properties changed for" << self; + if (!m_platformWindow) + return; + [self propagateBackingProperties]; // Ideally we would plumb this situation through QPA in a way that lets diff --git a/src/plugins/platforms/wasm/qwasmdrag.cpp b/src/plugins/platforms/wasm/qwasmdrag.cpp index 757959e5694..6447d1e399f 100644 --- a/src/plugins/platforms/wasm/qwasmdrag.cpp +++ b/src/plugins/platforms/wasm/qwasmdrag.cpp @@ -94,15 +94,13 @@ Qt::DropAction QWasmDrag::drag(QDrag *drag) return Qt::IgnoreAction; Qt::DropAction dragResult = Qt::IgnoreAction; - if (qstdweb::haveJspi()) { + if (qstdweb::haveAsyncify()) { m_dragState = std::make_unique<DragState>(drag, window, [this]() { QSimpleDrag::cancelDrag(); }); - QSimpleDrag::drag(drag); - dragResult = m_dragState->dropAction; + dragResult = QSimpleDrag::drag(drag); m_dragState.reset(); - } - - if (dragResult == Qt::IgnoreAction) + } else { dragResult = QBasicDrag::drag(drag); + } return dragResult; } @@ -117,6 +115,7 @@ void QWasmDrag::onNativeDragStarted(DragEvent *event) event->cancelDragStart(); return; } + setExecutedDropAction(event->dropAction); // We have our own window if (shapedPixmapWindow()) @@ -145,8 +144,10 @@ void QWasmDrag::onNativeDragOver(DragEvent *event) event->mouseButton, event->modifiers); event->acceptDragOver(); if (dragResponse.isAccepted()) { + setExecutedDropAction(dragResponse.acceptedAction()); event->dataTransfer.setDropAction(dragResponse.acceptedAction()); } else { + setExecutedDropAction(Qt::DropAction::IgnoreAction); event->dataTransfer.setDropAction(Qt::DropAction::IgnoreAction); } } @@ -174,6 +175,7 @@ void QWasmDrag::onNativeDrop(DragEvent *event) // files, but the browser expects that accepted state is set before any // async calls. event->acceptDrop(); + setExecutedDropAction(event->dropAction); std::shared_ptr<DragState> dragState = m_dragState; const auto dropCallback = [dragState, wasmWindow, targetWindowPos, @@ -198,6 +200,7 @@ void QWasmDrag::onNativeDragFinished(DragEvent *event) { event->webEvent.call<void>("preventDefault"); m_dragState->dropAction = event->dropAction; + setExecutedDropAction(event->dropAction); m_dragState->quitEventLoopClosure(); } @@ -213,6 +216,8 @@ void QWasmDrag::onNativeDragEnter(DragEvent *event) if (m_dragState) m_dragState->dropAction = event->dropAction; + setExecutedDropAction(event->dropAction); + QDrag *drag = new QDrag(this); drag->setMimeData(new QMimeData()); drag->exec(Qt::CopyAction | Qt::MoveAction, Qt::CopyAction); @@ -223,6 +228,7 @@ void QWasmDrag::onNativeDragLeave(DragEvent *event) event->webEvent.call<void>("preventDefault"); if (m_dragState) m_dragState->dropAction = event->dropAction; + setExecutedDropAction(event->dropAction); event->dataTransfer.setDropAction(Qt::DropAction::IgnoreAction); } diff --git a/src/plugins/platforms/wayland/qwaylandwindow.cpp b/src/plugins/platforms/wayland/qwaylandwindow.cpp index f27943070d0..2be05625971 100644 --- a/src/plugins/platforms/wayland/qwaylandwindow.cpp +++ b/src/plugins/platforms/wayland/qwaylandwindow.cpp @@ -513,7 +513,6 @@ void QWaylandWindow::setGeometry(const QRect &r) mWindowDecoration->update(); QWindowSystemInterface::handleGeometryChange<QWindowSystemInterface::SynchronousDelivery>(window(), geometry()); - mSentInitialResize = true; } // Wayland has no concept of areas being exposed or not, only the entire window, when our geometry changes, we need to flag the new area as exposed diff --git a/src/plugins/platforms/wayland/qwaylandwindow_p.h b/src/plugins/platforms/wayland/qwaylandwindow_p.h index 9e1bd92af30..7dda16cc776 100644 --- a/src/plugins/platforms/wayland/qwaylandwindow_p.h +++ b/src/plugins/platforms/wayland/qwaylandwindow_p.h @@ -334,7 +334,6 @@ protected: int mFrameCallbackTimeout = 100; QVariantMap m_properties; - bool mSentInitialResize = false; QPoint mOffset; std::optional<qreal> mScale = std::nullopt; diff --git a/src/plugins/platforms/windows/qwindowsscreen.cpp b/src/plugins/platforms/windows/qwindowsscreen.cpp index 0236669d6fb..23bbe409caa 100644 --- a/src/plugins/platforms/windows/qwindowsscreen.cpp +++ b/src/plugins/platforms/windows/qwindowsscreen.cpp @@ -450,6 +450,26 @@ QPixmap QWindowsScreen::grabWindow(WId window, int xIn, int yIn, int width, int hwnd = GetDesktopWindow(); const QRect screenGeometry = geometry(); windowSize = screenGeometry.size(); + // When dpi awareness is not set to PerMonitor, windows reports primary display or dummy + // DPI for all displays, so xIn and yIn and windowSize are calculated with a wrong DPI, + // so we need to recalculate them using the actual screen size we get from + // EnumDisplaySettings api. + const auto dpiAwareness = QWindowsContext::instance()->processDpiAwareness(); + if (dpiAwareness != QtWindows::DpiAwareness::PerMonitor && + dpiAwareness != QtWindows::DpiAwareness::PerMonitorVersion2) { + MONITORINFOEX info = {}; + info.cbSize = sizeof(MONITORINFOEX); + if (GetMonitorInfo(handle(), &info)) { + DEVMODE dm = {}; + dm.dmSize = sizeof(dm); + if (EnumDisplaySettings(info.szDevice, ENUM_CURRENT_SETTINGS, &dm)) { + qreal scale = static_cast<qreal>(dm.dmPelsWidth) / windowSize.width(); + x = static_cast<int>(static_cast<qreal>(x) * scale); + y = static_cast<int>(static_cast<qreal>(y) * scale); + windowSize = QSize(dm.dmPelsWidth, dm.dmPelsHeight); + } + } + } x += screenGeometry.x(); y += screenGeometry.y(); } diff --git a/src/plugins/platforms/windows/qwindowswindow.cpp b/src/plugins/platforms/windows/qwindowswindow.cpp index b77e985c965..2816982b1a8 100644 --- a/src/plugins/platforms/windows/qwindowswindow.cpp +++ b/src/plugins/platforms/windows/qwindowswindow.cpp @@ -1732,8 +1732,10 @@ void QWindowsWindow::destroyWindow() m_surface = nullptr; } #endif + DestroyWindow(m_data.hwndTitlebar); DestroyWindow(m_data.hwnd); context->removeWindow(m_data.hwnd); + m_data.hwndTitlebar = nullptr; m_data.hwnd = nullptr; } } diff --git a/src/plugins/platforms/xcb/qxcbwindow.cpp b/src/plugins/platforms/xcb/qxcbwindow.cpp index 2c56603fef0..7d5f0155960 100644 --- a/src/plugins/platforms/xcb/qxcbwindow.cpp +++ b/src/plugins/platforms/xcb/qxcbwindow.cpp @@ -2573,7 +2573,7 @@ void QXcbWindow::setOpacity(qreal level) if (!m_window) return; - quint32 value = qRound64(qBound(qreal(0), level, qreal(1)) * 0xffffffff); + quint32 value = qRound64(qBound(qreal(0), level, qreal(1)) * qreal(0xffffffff)); xcb_change_property(xcb_connection(), XCB_PROP_MODE_REPLACE, diff --git a/src/plugins/sqldrivers/.cmake.conf b/src/plugins/sqldrivers/.cmake.conf index be788d10f8e..846c4f3b923 100644 --- a/src/plugins/sqldrivers/.cmake.conf +++ b/src/plugins/sqldrivers/.cmake.conf @@ -1 +1 @@ -set(QT_REPO_MODULE_VERSION "6.11.0") +set(QT_REPO_MODULE_VERSION "6.12.0") diff --git a/src/plugins/styles/modernwindows/qwindows11style.cpp b/src/plugins/styles/modernwindows/qwindows11style.cpp index 6fd857828d3..e9b90d787bc 100644 --- a/src/plugins/styles/modernwindows/qwindows11style.cpp +++ b/src/plugins/styles/modernwindows/qwindows11style.cpp @@ -596,7 +596,7 @@ void QWindows11Style::drawComplexControl(ComplexControl control, const QStyleOpt if (sub & SC_ComboBoxArrow) { QRectF rect = proxy()->subControlRect(CC_ComboBox, option, SC_ComboBoxArrow, widget); painter->setFont(d->assetFont); - painter->setPen(controlTextColor(option)); + painter->setPen(controlTextColor(option, true)); painter->drawText(rect, Qt::AlignCenter, fluentIcon(Icon::ChevronDownMed)); } if (state & State_KeyboardFocusChange && hasFocus) { @@ -887,7 +887,7 @@ void QWindows11Style::drawPrimitive(PrimitiveElement element, const QStyleOption if (isOn) { painter->setFont(d->assetFont); - painter->setPen(controlTextColor(option, QPalette::Window)); + painter->setPen(controlTextColor(option)); qreal clipWidth = 1.0; const QString str = fluentIcon(Icon::AcceptMedium); QFontMetrics fm(d->assetFont); @@ -907,19 +907,24 @@ void QWindows11Style::drawPrimitive(PrimitiveElement element, const QStyleOption QFont f(d->assetFont); f.setPointSize(6); painter->setFont(f); - painter->setPen(controlTextColor(option, QPalette::Window)); + painter->setPen(controlTextColor(option)); painter->drawText(rect, Qt::AlignCenter, fluentIcon(Icon::Dash12)); } } break; case PE_IndicatorBranch: { if (option->state & State_Children) { + const QStyleOptionViewItem *vopt = qstyleoption_cast<const QStyleOptionViewItem *>(option); const bool isReverse = option->direction == Qt::RightToLeft; const bool isOpen = option->state & QStyle::State_Open; + const QAbstractItemView *view = qobject_cast<const QAbstractItemView *>(widget); QFont f(d->assetFont); - f.setPointSize(6); + f.setPointSize(8); painter->setFont(f); - painter->setPen(option->palette.color(isOpen ? QPalette::Active : QPalette::Disabled, + if (view && view->alternatingRowColors() && vopt && vopt->state & State_Selected) + painter->setPen(winUI3Color(textOnAccentPrimary)); + else + painter->setPen(option->palette.color(isOpen ? QPalette::Active : QPalette::Disabled, QPalette::WindowText)); const auto ico = isOpen ? Icon::ChevronDownMed : (isReverse ? Icon::ChevronLeftMed @@ -1071,14 +1076,16 @@ void QWindows11Style::drawPrimitive(PrimitiveElement element, const QStyleOption if (option->state & State_Selected && !highContrastTheme) { // keep in sync with CE_ItemViewItem QListView indicator painting - const auto col = option->palette.accent().color(); - painter->setBrush(col); - painter->setPen(col); - const auto xPos = isRtl ? rect.right() - 4.5f : rect.left() + 3.5f; - const auto yOfs = rect.height() / 4.; - QRectF r(QPointF(xPos, rect.y() + yOfs), - QPointF(xPos + 1, rect.y() + rect.height() - yOfs)); - painter->drawRoundedRect(r, 1, 1); + if (!qobject_cast<const QTableView *>(widget)) { + const auto col = option->palette.accent().color(); + painter->setBrush(col); + painter->setPen(col); + const auto xPos = isRtl ? rect.right() - 4.5f : rect.left() + 3.5f; + const auto yOfs = rect.height() / 4.; + QRectF r(QPointF(xPos, rect.y() + yOfs), + QPointF(xPos + 1, rect.y() + rect.height() - yOfs)); + painter->drawRoundedRect(r, 1, 1); + } } const bool isTreeDecoration = vopt->features.testFlag( @@ -1099,7 +1106,7 @@ void QWindows11Style::drawPrimitive(PrimitiveElement element, const QStyleOption } const QAbstractItemView *view = qobject_cast<const QAbstractItemView *>(widget); - painter->setBrush(view->alternatingRowColors() ? vopt->palette.highlight() : WINUI3Colors[colorSchemeIndex][subtleHighlightColor]); + painter->setBrush(view->alternatingRowColors() && state & State_Selected ? calculateAccentColor(option) : WINUI3Colors[colorSchemeIndex][subtleHighlightColor]); painter->setPen(Qt::NoPen); if (isFirst) { QPainterStateGuard psg(painter); @@ -1207,7 +1214,7 @@ void QWindows11Style::drawControl(ControlElement element, const QStyleOption *op case QStyle::CE_ComboBoxLabel: #if QT_CONFIG(combobox) if (const QStyleOptionComboBox *cb = qstyleoption_cast<const QStyleOptionComboBox *>(option)) { - painter->setPen(controlTextColor(option)); + painter->setPen(controlTextColor(option, true)); QStyleOptionComboBox newOption = *cb; newOption.rect.adjust(4,0,-4,0); QCommonStyle::drawControl(element, &newOption, painter, widget); @@ -1753,13 +1760,13 @@ void QWindows11Style::drawControl(ControlElement element, const QStyleOption *op } } const bool highlightCurrent = vopt->state.testAnyFlags(State_Selected | State_MouseOver); + const QAbstractItemView *view = qobject_cast<const QAbstractItemView *>(widget); if (highlightCurrent) { if (highContrastTheme) { painter->setBrush(vopt->palette.highlight()); } else { - const QAbstractItemView *view = qobject_cast<const QAbstractItemView *>(widget); - painter->setBrush(view && view->alternatingRowColors() - ? vopt->palette.highlight() + painter->setBrush(view && view->alternatingRowColors() && vopt->state & State_Selected + ? calculateAccentColor(option) : winUI3Color(subtleHighlightColor)); } } else { @@ -1815,8 +1822,13 @@ void QWindows11Style::drawControl(ControlElement element, const QStyleOption *op vopt->icon.paint(painter, iconRect, vopt->decorationAlignment, mode, state); } - painter->setPen(highlightCurrent && highContrastTheme ? vopt->palette.base().color() - : vopt->palette.text().color()); + if (highlightCurrent && highContrastTheme) { + painter->setPen(vopt->palette.base().color()); + } else if ((view && view->alternatingRowColors() && highlightCurrent && vopt->state & State_Selected)) { + painter->setPen(winUI3Color(textOnAccentPrimary)); + } else { + painter->setPen(vopt->palette.text().color()); + } d->viewItemDrawText(painter, vopt, textRect); // paint a vertical marker for QListView @@ -2725,7 +2737,7 @@ QBrush QWindows11Style::inputFillBrush(const QStyleOption *option, const QWidget return winUI3Color(fillControlDefault); } -QColor QWindows11Style::controlTextColor(const QStyleOption *option, QPalette::ColorRole role) const +QColor QWindows11Style::controlTextColor(const QStyleOption *option, bool ignoreIsChecked) const { using namespace StyleOptionHelper; static constexpr WINUI3Color colorEnums[2][4] = { @@ -2738,12 +2750,9 @@ QColor QWindows11Style::controlTextColor(const QStyleOption *option, QPalette::C if (option->palette.isBrushSet(QPalette::Current, QPalette::ButtonText)) return option->palette.buttonText().color(); - const int colorIndex = isChecked(option) ? 1 : 0; + const int colorIndex = !ignoreIsChecked && isChecked(option) ? 1 : 0; const auto state = calcControlState(option); - const auto alpha = winUI3Color(colorEnums[colorIndex][int(state)]); - QColor col = option->palette.color(role); - col.setAlpha(alpha.alpha()); - return col; + return winUI3Color(colorEnums[colorIndex][int(state)]); } void QWindows11Style::drawLineEditFrame(QPainter *p, const QRectF &rect, const QStyleOption *o, bool isEditable) const diff --git a/src/plugins/styles/modernwindows/qwindows11style_p.h b/src/plugins/styles/modernwindows/qwindows11style_p.h index 96c2c4136e0..9d0cdda3e33 100644 --- a/src/plugins/styles/modernwindows/qwindows11style_p.h +++ b/src/plugins/styles/modernwindows/qwindows11style_p.h @@ -104,8 +104,7 @@ private: QBrush controlFillBrush(const QStyleOption *option, ControlType controlType) const; QBrush inputFillBrush(const QStyleOption *option, const QWidget *widget) const; // ControlType::ControlAlt can be mapped to QPalette directly - QColor controlTextColor(const QStyleOption *option, - QPalette::ColorRole role = QPalette::ButtonText) const; + QColor controlTextColor(const QStyleOption *option, bool ignoreIsChecked = false) const; void drawLineEditFrame(QPainter *p, const QRectF &rect, const QStyleOption *o, bool isEditable = true) const; inline QColor winUI3Color(enum WINUI3Color col) const; diff --git a/src/plugins/styles/modernwindows/qwindowsvistastyle.cpp b/src/plugins/styles/modernwindows/qwindowsvistastyle.cpp index 64ffba2d6f8..36b5d0f0143 100644 --- a/src/plugins/styles/modernwindows/qwindowsvistastyle.cpp +++ b/src/plugins/styles/modernwindows/qwindowsvistastyle.cpp @@ -16,6 +16,9 @@ #include <private/qapplication_p.h> #include <private/qsystemlibrary_p.h> #include <private/qwindowsthemecache_p.h> +#if QT_CONFIG(tooltip) +#include "private/qtooltip_p.h" +#endif #include "qdrawutil.h" // for now #include <qbackingstore.h> @@ -4676,7 +4679,7 @@ void QWindowsVistaStyle::polish(QWidget *widget) widget->setPalette(pal); } else #endif // QT_CONFIG(commandlinkbutton) - if (widget->inherits("QTipLabel")) { + if (qobject_cast<const QTipLabel *>(widget)) { //note that since tooltips are not reused //we do not have to care about unpolishing widget->setContentsMargins(3, 0, 4, 0); diff --git a/src/plugins/tls/openssl/qtlsbackend_openssl.cpp b/src/plugins/tls/openssl/qtlsbackend_openssl.cpp index deb257be01c..d3b7d669ec7 100644 --- a/src/plugins/tls/openssl/qtlsbackend_openssl.cpp +++ b/src/plugins/tls/openssl/qtlsbackend_openssl.cpp @@ -407,8 +407,13 @@ QList<QSslCertificate> systemCaCertificates() for (const QByteArray &directory : directories) { for (const auto &dirEntry : QDirListing(QFile::decodeName(directory), flags)) { // use canonical path here to not load the same certificate twice if symlinked - if (hasMatchingExtension(dirEntry.fileName())) - certFiles.insert(dirEntry.canonicalFilePath()); + if (hasMatchingExtension(dirEntry.fileName())) { + QString canonicalPath = dirEntry.canonicalFilePath(); + // skip broken symlinks to not end up adding "" to the list which will then + // just be rejected by `QSslCertificate::fromFile` + if (!canonicalPath.isEmpty()) + certFiles.insert(canonicalPath); + } } } for (const QString& file : std::as_const(certFiles)) diff --git a/src/testlib/doc/src/qt-webpages.qdoc b/src/testlib/doc/src/qt-webpages.qdoc index 611f3795ba9..b32fd4f750f 100644 --- a/src/testlib/doc/src/qt-webpages.qdoc +++ b/src/testlib/doc/src/qt-webpages.qdoc @@ -15,7 +15,3 @@ \title Googletest Mocking (gMock) Framework */ -/*! - \externalpage https://www.itk.org/Wiki/CMake_Testing_With_CTest - \title CMake/Testing With CTest -*/ diff --git a/src/tools/androidtestrunner/main.cpp b/src/tools/androidtestrunner/main.cpp index b517d85c5fb..161d95db49c 100644 --- a/src/tools/androidtestrunner/main.cpp +++ b/src/tools/androidtestrunner/main.cpp @@ -28,7 +28,16 @@ using namespace Qt::StringLiterals; -#define EXIT_ERROR -1 + +// QTest-based test processes may exit with up to 127 for normal test failures +static constexpr int HIGHEST_QTEST_EXITCODE = 127; +// Something went wrong in androidtestrunner, in general +static constexpr int EXIT_ERROR = 254; +// More specific exit codes for failures in androidtestrunner: +static constexpr int EXIT_NOEXITCODE = 253; // Failed to transfer exit code from device +static constexpr int EXIT_ANR = 252; // Android ANR error (Application Not Responding) +static constexpr int EXIT_NORESULTS = 251; // Failed to transfer result files from device + struct Options { @@ -71,6 +80,13 @@ struct TestInfo static TestInfo g_testInfo; +// QTest-based processes return 0 if all tests PASSed, or the number of FAILs up to 127. +// Other exitcodes signify abnormal termination and are system-dependent. +static bool isTestExitCodeNormal(const int ec) +{ + return (ec >= 0 && ec <= HIGHEST_QTEST_EXITCODE); +} + static bool execCommand(const QString &program, const QStringList &args, QByteArray *output = nullptr, bool verbose = false) { @@ -744,9 +760,9 @@ void printLogcatCrash(const QByteArray &logcat) } if (!crashLogcat.startsWith("********** Crash dump")) - qDebug() << "********** Crash dump: **********"; + qDebug() << "[androidtestrunner] ********** BEGIN crash dump **********"; qDebug().noquote() << crashLogcat.trimmed(); - qDebug() << "********** End crash dump **********"; + qDebug() << "[androidtestrunner] ********** END crash dump **********"; } void analyseLogcat(const QString &timeStamp, int *exitCode) @@ -781,10 +797,13 @@ void analyseLogcat(const QString &timeStamp, int *exitCode) // Check for ANRs const bool anrOccurred = logcat.contains("ANR in %1"_L1.arg(g_options.package).toUtf8()); if (anrOccurred) { - // Treat a found ANR as a test failure. - *exitCode = *exitCode < 1 ? 1 : *exitCode; - qCritical("An ANR has occurred while running the test %s. The logcat will include " - "additional logs from the system_server process.", + // Rather improbable, but if the test managed to return a non-crash exitcode then overwrite + // it to signify that something blew up. Same if we didn't manage to collect an exit code. + // Preserve all other exitcodes, they might be useful crash information from the device. + if (isTestExitCodeNormal(*exitCode) || *exitCode == EXIT_NOEXITCODE) + *exitCode = EXIT_ANR; + qCritical("[androidtestrunner] An ANR has occurred while running the test '%s';" + " consult logcat for additional logs from the system_server process", qPrintable(g_options.package)); } @@ -818,13 +837,14 @@ void analyseLogcat(const QString &timeStamp, int *exitCode) } } - // If we have a crash, attempt to print both logcat and the crash buffer which - // includes the crash stacktrace that is not included in the default logcat. - const bool testCrashed = *exitCode == EXIT_ERROR && !g_testInfo.isTestRunnerInterrupted.load(); + // If we have an unpredictable exitcode, possibly a crash, attempt to print both logcat and the + // crash buffer which includes the crash stacktrace that is not included in the default logcat. + const bool testCrashed = ( !isTestExitCodeNormal(*exitCode) + && !g_testInfo.isTestRunnerInterrupted.load()); if (testCrashed) { - qDebug() << "********** logcat dump **********"; + qDebug() << "[androidtestrunner] ********** BEGIN logcat dump **********"; qDebug().noquote() << testLogcat.join(u'\n').trimmed(); - qDebug() << "********** End logcat dump **********"; + qDebug() << "[androidtestrunner] ********** END logcat dump **********"; if (!crashLogcat.isEmpty()) printLogcatCrash(crashLogcat); @@ -839,7 +859,7 @@ static QString getCurrentTimeString() QStringList dateArgs = { "shell"_L1, "date"_L1, "+'%1'"_L1.arg(timeFormat) }; QByteArray output; if (!execAdbCommand(dateArgs, &output, false)) { - qWarning() << "Date/time adb command failed"; + qWarning() << "[androidtestrunner] ERROR in command: adb shell date"; return {}; } @@ -851,14 +871,15 @@ static int testExitCode() QByteArray exitCodeOutput; const QString exitCodeCmd = "cat files/qtest_last_exit_code 2> /dev/null"_L1; if (!execAdbCommand({ "shell"_L1, runCommandAsUserArgs(exitCodeCmd) }, &exitCodeOutput, false)) { - qCritical() << "Failed to retrieve the test exit code."; - return EXIT_ERROR; + qCritical() << "[androidtestrunner] ERROR in command: adb shell cat files/qtest_last_exit_code"; + return EXIT_NOEXITCODE; } + qDebug() << "[androidtestrunner] Test exitcode: " << exitCodeOutput; bool ok; int exitCode = exitCodeOutput.toInt(&ok); - return ok ? exitCode : EXIT_ERROR; + return ok ? exitCode : EXIT_NOEXITCODE; } static bool uninstallTestPackage() @@ -899,7 +920,7 @@ void sigHandler(int signal) // a main event loop. Since, there's no other alternative to do this, // let's do the cleanup anyway. if (!g_testInfo.isPackageInstalled.load()) - _exit(-1); + _exit(EXIT_ERROR); g_testInfo.isTestRunnerInterrupted.store(true); } @@ -1031,7 +1052,9 @@ int main(int argc, char *argv[]) if (g_options.showLogcatOutput) analyseLogcat(formattedStartTime, &exitCode); - exitCode = pullResults() ? exitCode : EXIT_ERROR; + const bool pullRes = pullResults(); + if (!pullRes && isTestExitCodeNormal(exitCode)) + exitCode = EXIT_NORESULTS; if (!uninstallTestPackage()) return EXIT_ERROR; diff --git a/src/tools/configure.cmake b/src/tools/configure.cmake index 27ea90b89ac..07e11dd935b 100644 --- a/src/tools/configure.cmake +++ b/src/tools/configure.cmake @@ -37,7 +37,7 @@ qt_feature("qmake" PRIVATE QT_FEATURE_datestring AND QT_FEATURE_regularexpression AND QT_FEATURE_temporaryfile) qt_feature("qtwaylandscanner" PRIVATE - CONDITION TARGET Wayland::Scanner + CONDITION TARGET Wayland::Scanner AND NOT INTEGRITY AND NOT ANDROID AND NOT WASM AND NOT IOS AND NOT QNX AND NOT VXWORKS ) qt_configure_add_summary_section(NAME "Core tools") diff --git a/src/tools/moc/generator.cpp b/src/tools/moc/generator.cpp index 6e7077b383e..94c75ae6eb3 100644 --- a/src/tools/moc/generator.cpp +++ b/src/tools/moc/generator.cpp @@ -78,16 +78,18 @@ QT_FOR_EACH_STATIC_TYPE(RETURN_METATYPENAME_STRING) return nullptr; } - Generator::Generator(Moc *moc, ClassDef *classDef, const QList<QByteArray> &metaTypes, + Generator::Generator(Moc *moc, const ClassDef *classDef, const QList<QByteArray> &metaTypes, const QHash<QByteArray, QByteArray> &knownQObjectClasses, - const QHash<QByteArray, QByteArray> &knownGadgets, FILE *outfile, - bool requireCompleteTypes) + const QHash<QByteArray, QByteArray> &knownGadgets, + const QHash<QByteArray, QByteArray> &hashes, + FILE *outfile, bool requireCompleteTypes) : parser(moc), out(outfile), cdef(classDef), metaTypes(metaTypes), knownQObjectClasses(knownQObjectClasses), knownGadgets(knownGadgets), + hashes(hashes), requireCompleteTypes(requireCompleteTypes) { if (cdef->superclassList.size()) @@ -228,28 +230,11 @@ void Generator::generateCode() bool isQObject = (cdef->classname == "QObject"); bool isConstructible = !cdef->constructorList.isEmpty(); - // filter out undeclared enumerators and sets - { - QList<EnumDef> enumList; - for (EnumDef def : std::as_const(cdef->enumList)) { - if (cdef->enumDeclarations.contains(def.name)) { - enumList += def; - } - def.enumName = def.name; - QByteArray alias = cdef->flagAliases.value(def.name); - if (cdef->enumDeclarations.contains(alias)) { - def.name = alias; - def.flags |= cdef->enumDeclarations[alias]; - enumList += def; - } - } - cdef->enumList = enumList; - } - // // Register all strings used in data section // strreg(cdef->qualified); + strreg(hashes[cdef->qualified]); registerClassInfoStrings(); registerFunctionStrings(cdef->signalList); registerFunctionStrings(cdef->slotList); @@ -308,6 +293,8 @@ void Generator::generateCode() addEnums(); fprintf(out, " };\n"); + fprintf(out, " uint qt_metaObjectHashIndex = %d;\n", stridx(hashes[cdef->qualified])); + const char *uintDataParams = ""; if (isConstructible || !cdef->classInfoList.isEmpty()) { if (isConstructible) { @@ -340,7 +327,7 @@ void Generator::generateCode() if (!requireCompleteness) tagType = "qt_meta_tag_" + qualifiedClassNameIdentifier + "_t"; fprintf(out, " return QtMocHelpers::metaObjectData<%s, %s>(%s, qt_stringData,\n" - " qt_methods, qt_properties, qt_enums%s);\n" + " qt_methods, qt_properties, qt_enums, qt_metaObjectHashIndex%s);\n" "}\n", ownType, tagType.constData(), metaObjectFlags, uintDataParams); } diff --git a/src/tools/moc/generator.h b/src/tools/moc/generator.h index 45df0783c2b..77be2fc6714 100644 --- a/src/tools/moc/generator.h +++ b/src/tools/moc/generator.h @@ -12,14 +12,15 @@ class Generator { Moc *parser = nullptr; FILE *out; - ClassDef *cdef; + const ClassDef *cdef; QList<uint> meta_data; public: - Generator(Moc *moc, ClassDef *classDef, const QList<QByteArray> &metaTypes, + Generator(Moc *moc, const ClassDef *classDef, const QList<QByteArray> &metaTypes, const QHash<QByteArray, QByteArray> &knownQObjectClasses, - const QHash<QByteArray, QByteArray> &knownGadgets, FILE *outfile = nullptr, - bool requireCompleteTypes = false); + const QHash<QByteArray, QByteArray> &knownGadgets, + const QHash<QByteArray, QByteArray> &hashes, + FILE *outfile = nullptr, bool requireCompleteTypes = false); void generateCode(); qsizetype registeredStringsCount() { return strings.size(); } @@ -54,6 +55,7 @@ private: QList<QByteArray> metaTypes; QHash<QByteArray, QByteArray> knownQObjectClasses; QHash<QByteArray, QByteArray> knownGadgets; + QHash<QByteArray, QByteArray> hashes; bool requireCompleteTypes; }; diff --git a/src/tools/moc/moc.cpp b/src/tools/moc/moc.cpp index baa6690350d..7f05f34edb6 100644 --- a/src/tools/moc/moc.cpp +++ b/src/tools/moc/moc.cpp @@ -17,6 +17,10 @@ #include <private/qmetaobject_moc_p.h> #include <private/qduplicatetracker_p.h> +// This is a bootstrapped tool, so we can't rely on QCryptographicHash for the +// faster SHA1 implementations from OpenSSL. +#include "../../3rdparty/sha1/sha1.cpp" + QT_BEGIN_NAMESPACE using namespace Qt::StringLiterals; @@ -1191,6 +1195,24 @@ static QByteArrayList requiredQtContainers(const QList<ClassDef> &classes) return required; } +QByteArray classDefJsonObjectHash(const QJsonObject &object) +{ + const QByteArray json = QJsonDocument(object).toJson(QJsonValue::JsonFormat::Compact); + QByteArray hash(20, 0); // SHA1 produces 160 bits of data + + { + Sha1State state; + sha1InitState(&state); + sha1Update(&state, reinterpret_cast<const uchar *>(json.constData()), json.size()); + sha1FinalizeState(&state); + sha1ToHash(&state, reinterpret_cast<uchar *>(hash.data())); + } + + static const char revisionPrefix[] = "0$"; + const QByteArray hashB64 = hash.toBase64(QByteArray::OmitTrailingEquals); + return revisionPrefix + hashB64; +} + void Moc::generate(FILE *out, FILE *jsonOutput) { QByteArrayView fn = strippedFileName(); @@ -1247,14 +1269,40 @@ void Moc::generate(FILE *out, FILE *jsonOutput) "#endif\n\n"); #endif + // filter out undeclared enumerators and sets + for (ClassDef &cdef : classList) { + QList<EnumDef> enumList; + for (EnumDef def : std::as_const(cdef.enumList)) { + if (cdef.enumDeclarations.contains(def.name)) { + enumList += def; + } + def.enumName = def.name; + QByteArray alias = cdef.flagAliases.value(def.name); + if (cdef.enumDeclarations.contains(alias)) { + def.name = alias; + def.flags |= cdef.enumDeclarations[alias]; + enumList += def; + } + } + cdef.enumList = enumList; + } + fprintf(out, "QT_WARNING_PUSH\n"); fprintf(out, "QT_WARNING_DISABLE_DEPRECATED\n"); fprintf(out, "QT_WARNING_DISABLE_GCC(\"-Wuseless-cast\")\n"); + QHash<QByteArray, QJsonObject> classDefJsonObjects; + QHash<QByteArray, QByteArray> metaObjectHashes; + for (const ClassDef &def : std::as_const(classList)) { + const QJsonObject jsonObject = def.toJson(); + classDefJsonObjects.insert(def.qualified, jsonObject); + metaObjectHashes.insert(def.qualified, classDefJsonObjectHash(jsonObject)); + } + fputs("", out); - for (ClassDef &def : classList) { - Generator generator(this, &def, metaTypes, knownQObjectClasses, knownGadgets, out, - requireCompleteTypes); + for (const ClassDef &def : std::as_const(classList)) { + Generator generator(this, &def, metaTypes, knownQObjectClasses, knownGadgets, + metaObjectHashes, out, requireCompleteTypes); generator.generateCode(); // generator.generateCode() should have already registered all strings @@ -1273,13 +1321,20 @@ void Moc::generate(FILE *out, FILE *jsonOutput) mocData["inputFile"_L1] = QLatin1StringView(fn.constData()); QJsonArray classesJsonFormatted; + QJsonObject hashesJsonObject; - for (const ClassDef &cdef: std::as_const(classList)) - classesJsonFormatted.append(cdef.toJson()); + for (const ClassDef &cdef : std::as_const(classList)) { + classesJsonFormatted.append(classDefJsonObjects[cdef.qualified]); + hashesJsonObject.insert(QString::fromLatin1(cdef.qualified), + QString::fromLatin1(metaObjectHashes[cdef.qualified])); + } if (!classesJsonFormatted.isEmpty()) mocData["classes"_L1] = classesJsonFormatted; + if (!hashesJsonObject.isEmpty()) + mocData["hashes"_L1] = hashesJsonObject; + QJsonDocument jsonDoc(mocData); fputs(jsonDoc.toJson().constData(), jsonOutput); } diff --git a/src/widgets/CMakeLists.txt b/src/widgets/CMakeLists.txt index d43b6ec4fb2..c47e3bee13c 100644 --- a/src/widgets/CMakeLists.txt +++ b/src/widgets/CMakeLists.txt @@ -412,7 +412,7 @@ qt_internal_extend_target(Widgets CONDITION QT_FEATURE_shortcut qt_internal_extend_target(Widgets CONDITION QT_FEATURE_tooltip SOURCES - kernel/qtooltip.cpp kernel/qtooltip.h + kernel/qtooltip.cpp kernel/qtooltip.h kernel/qtooltip_p.h ) qt_internal_extend_target(Widgets CONDITION QT_FEATURE_whatsthis diff --git a/src/widgets/accessible/qaccessiblecolorwell.cpp b/src/widgets/accessible/qaccessiblecolorwell.cpp index ca08e511e9a..64fcd2a7fd1 100644 --- a/src/widgets/accessible/qaccessiblecolorwell.cpp +++ b/src/widgets/accessible/qaccessiblecolorwell.cpp @@ -3,6 +3,8 @@ #include "private/qaccessiblecolorwell_p.h" +#include <QtCore/qcoreapplication.h> + QT_REQUIRE_CONFIG(accessibility); #if QT_CONFIG(colordialog) @@ -14,6 +16,7 @@ class QAccessibleColorWellItem : public QAccessibleInterface { QAccessibleColorWell *m_parent; + Q_DECLARE_TR_FUNCTIONS(QAccessibleColorWellItem) public: QAccessibleColorWellItem(QAccessibleColorWell *parent); @@ -79,7 +82,7 @@ QString QAccessibleColorWellItem::text(QAccessible::Text t) const if (t == QAccessible::Name) { QRgb color = m_parent->colorWell()->rgbValues()[m_parent->indexOfChild(this)]; //: Color specified via its 3 RGB components (red, green, blue) - return QObject::tr("RGB %1, %2, %3") + return tr("RGB %1, %2, %3") .arg(QString::number(qRed(color)), QString::number(qGreen(color)), QString::number(qBlue(color))); } diff --git a/src/widgets/dialogs/qcolordialog.cpp b/src/widgets/dialogs/qcolordialog.cpp index d51c408ab5c..ce46170bba5 100644 --- a/src/widgets/dialogs/qcolordialog.cpp +++ b/src/widgets/dialogs/qcolordialog.cpp @@ -662,7 +662,7 @@ private: int val2y(int val); void setVal(int v); - QPixmap *pix; + QPixmap pix; }; @@ -682,14 +682,12 @@ QColorLuminancePicker::QColorLuminancePicker(QWidget* parent) :QWidget(parent) { hue = 100; val = 100; sat = 100; - pix = nullptr; // setAttribute(WA_NoErase, true); setFocusPolicy(Qt::StrongFocus); } QColorLuminancePicker::~QColorLuminancePicker() { - delete pix; } void QColorLuminancePicker::keyPressEvent(QKeyEvent *event) @@ -725,7 +723,7 @@ void QColorLuminancePicker::setVal(int v) if (val == v) return; val = qMax(0, qMin(v,255)); - delete pix; pix=nullptr; + pix = QPixmap(); repaint(); emit newHsv(hue, sat, val); } @@ -744,8 +742,7 @@ void QColorLuminancePicker::paintEvent(QPaintEvent *) QRect r(0, foff, w, height() - 2*foff); int wi = r.width() - 2; int hi = r.height() - 2; - if (!pix || pix->height() != hi || pix->width() != wi) { - delete pix; + if (pix.isNull() || pix.height() != hi || pix.width() != wi) { QImage img(wi, hi, QImage::Format_RGB32); int y; uint *pixel = (uint *) img.scanLine(0); @@ -754,10 +751,10 @@ void QColorLuminancePicker::paintEvent(QPaintEvent *) std::fill(pixel, end, QColor::fromHsv(hue, sat, y2val(y + coff)).rgb()); pixel = end; } - pix = new QPixmap(QPixmap::fromImage(img)); + pix = QPixmap::fromImage(img); } QPainter p(this); - p.drawPixmap(1, coff, *pix); + p.drawPixmap(1, coff, pix); const QPalette &g = palette(); qDrawShadePanel(&p, r, g, true); p.setPen(g.windowText().color()); @@ -773,7 +770,7 @@ void QColorLuminancePicker::setCol(int h, int s , int v) val = v; hue = h; sat = s; - delete pix; pix=nullptr; + pix = QPixmap(); repaint(); } diff --git a/src/widgets/doc/src/external-resources.qdoc b/src/widgets/doc/src/external-resources.qdoc index 96117546a29..0eccc3b19d4 100644 --- a/src/widgets/doc/src/external-resources.qdoc +++ b/src/widgets/doc/src/external-resources.qdoc @@ -1,12 +1,6 @@ // Copyright (C) 2016 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only - -/*! - \externalpage http://developer.apple.com/documentation/UserExperience/Conceptual/OSXHIGuidelines/index.html - \title Apple Human Interface Guidelines -*/ - /*! \externalpage https://rk.nvg.ntnu.no/sinclair/computers/zxspectrum/zxspectrum.htm \title Sinclair Spectrum diff --git a/src/widgets/kernel/qtooltip.cpp b/src/widgets/kernel/qtooltip.cpp index fa17c94a23f..97332cd7d5d 100644 --- a/src/widgets/kernel/qtooltip.cpp +++ b/src/widgets/kernel/qtooltip.cpp @@ -6,15 +6,12 @@ #include <qapplication.h> #include <qevent.h> -#include <qpointer.h> #include <qstyle.h> #include <qstyleoption.h> #include <qstylepainter.h> #if QT_CONFIG(effects) #include <private/qeffects_p.h> #endif -#include <qtextdocument.h> -#include <qdebug.h> #include <qpa/qplatformscreen.h> #include <qpa/qplatformcursor.h> #if QT_CONFIG(style_stylesheet) @@ -23,12 +20,10 @@ #include <qpa/qplatformwindow.h> #include <qpa/qplatformwindow_p.h> -#include <qlabel.h> #include <QtWidgets/private/qlabel_p.h> #include <QtGui/private/qhighdpiscaling_p.h> #include <qtooltip.h> - -#include <QtCore/qbasictimer.h> +#include <QtWidgets/private/qtooltip_p.h> QT_BEGIN_NAMESPACE @@ -93,57 +88,6 @@ using namespace Qt::StringLiterals; \sa QWidget::toolTip, QAction::toolTip */ -class QTipLabel : public QLabel -{ - Q_OBJECT -public: - QTipLabel(const QString &text, const QPoint &pos, QWidget *w, int msecDisplayTime); - ~QTipLabel(); - static QTipLabel *instance; - - void adjustTooltipScreen(const QPoint &pos); - void updateSize(const QPoint &pos); - - bool eventFilter(QObject *, QEvent *) override; - - QBasicTimer hideTimer, expireTimer; - - bool fadingOut; - - void reuseTip(const QString &text, int msecDisplayTime, const QPoint &pos); - void hideTip(); - void hideTipImmediately(); - void setTipRect(QWidget *w, const QRect &r); - void restartExpireTimer(int msecDisplayTime); - bool tipChanged(const QPoint &pos, const QString &text, QObject *o); - void placeTip(const QPoint &pos, QWidget *w); - - static QScreen *getTipScreen(const QPoint &pos, QWidget *w); -protected: - void timerEvent(QTimerEvent *e) override; - void paintEvent(QPaintEvent *e) override; - void mouseMoveEvent(QMouseEvent *e) override; - void resizeEvent(QResizeEvent *e) override; - -#if QT_CONFIG(style_stylesheet) -public slots: - /** \internal - Cleanup the _q_stylesheet_parent property. - */ - void styleSheetParentDestroyed() { - setProperty("_q_stylesheet_parent", QVariant()); - styleSheetParent = nullptr; - } - -private: - QWidget *styleSheetParent; -#endif - -private: - QWidget *widget; - QRect rect; -}; - QTipLabel *QTipLabel::instance = nullptr; QTipLabel::QTipLabel(const QString &text, const QPoint &pos, QWidget *w, int msecDisplayTime) @@ -152,6 +96,7 @@ QTipLabel::QTipLabel(const QString &text, const QPoint &pos, QWidget *w, int mse , styleSheetParent(nullptr) #endif , widget(nullptr) + , fadingOut(false) { delete instance; instance = this; @@ -166,7 +111,6 @@ QTipLabel::QTipLabel(const QString &text, const QPoint &pos, QWidget *w, int mse qApp->installEventFilter(this); setWindowOpacity(style()->styleHint(QStyle::SH_ToolTipLabel_Opacity, nullptr, this) / 255.0); setMouseTracking(true); - fadingOut = false; reuseTip(text, msecDisplayTime, pos); } @@ -240,10 +184,10 @@ void QTipLabel::resizeEvent(QResizeEvent *e) void QTipLabel::mouseMoveEvent(QMouseEvent *e) { if (!rect.isNull()) { - QPoint pos = e->globalPosition().toPoint(); + QPointF pos = e->globalPosition(); if (widget) pos = widget->mapFromGlobal(pos); - if (!rect.contains(pos)) + if (!rect.contains(pos.toPoint())) hideTip(); } QLabel::mouseMoveEvent(e); @@ -433,6 +377,15 @@ bool QTipLabel::tipChanged(const QPoint &pos, const QString &text, QObject *o) return false; } +/** \internal + Cleanup the _q_stylesheet_parent property. + */ +void QTipLabel::styleSheetParentDestroyed() +{ + setProperty("_q_stylesheet_parent", QVariant()); + styleSheetParent = nullptr; +} + /*! Shows \a text as a tool tip, with the global position \a pos as the point of interest. The tool tip will be shown with a platform @@ -594,4 +547,4 @@ void QToolTip::setFont(const QFont &font) QT_END_NAMESPACE -#include "qtooltip.moc" +#include "moc_qtooltip_p.cpp" diff --git a/src/widgets/kernel/qtooltip_p.h b/src/widgets/kernel/qtooltip_p.h new file mode 100644 index 00000000000..51faaf58c34 --- /dev/null +++ b/src/widgets/kernel/qtooltip_p.h @@ -0,0 +1,74 @@ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only +// Qt-Security score:significant reason:default + +#ifndef QTOOLTIP_P_H +#define QTOOLTIP_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of qlayout*.cpp, and qabstractlayout.cpp. This header +// file may change from version to version without notice, or even be removed. +// +// We mean it. +// + +#include <QLabel> +#include <QString> +#include <QRect> +#include <QToolTip> + +QT_REQUIRE_CONFIG(tooltip); +QT_BEGIN_NAMESPACE + +class Q_WIDGETS_EXPORT QTipLabel final : public QLabel +{ + Q_OBJECT +public: + explicit QTipLabel(const QString &text, const QPoint &pos, QWidget *w, int msecDisplayTime); + ~QTipLabel() override; + + void adjustTooltipScreen(const QPoint &pos); + void updateSize(const QPoint &pos); + + bool eventFilter(QObject *, QEvent *) override; + + void reuseTip(const QString &text, int msecDisplayTime, const QPoint &pos); + void hideTip(); + void hideTipImmediately(); + void setTipRect(QWidget *w, const QRect &r); + void restartExpireTimer(int msecDisplayTime); + bool tipChanged(const QPoint &pos, const QString &text, QObject *o); + void placeTip(const QPoint &pos, QWidget *w); + + static QScreen *getTipScreen(const QPoint &pos, QWidget *w); +protected: + void timerEvent(QTimerEvent *e) override; + void paintEvent(QPaintEvent *e) override; + void mouseMoveEvent(QMouseEvent *e) override; + void resizeEvent(QResizeEvent *e) override; + +#if QT_CONFIG(style_stylesheet) +public Q_SLOTS: + void styleSheetParentDestroyed(); + +private: + QWidget *styleSheetParent; +#endif + +private: + friend class QToolTip; + + static QTipLabel *instance; + QBasicTimer hideTimer, expireTimer; + QWidget *widget; + QRect rect; + bool fadingOut; +}; + +QT_END_NAMESPACE + +#endif // QTOOLTIP_P_H diff --git a/src/widgets/kernel/qwidget.cpp b/src/widgets/kernel/qwidget.cpp index 9499c88af12..bd2b5be11aa 100644 --- a/src/widgets/kernel/qwidget.cpp +++ b/src/widgets/kernel/qwidget.cpp @@ -32,7 +32,7 @@ #include "private/qwidgetwindow_p.h" #include "qpainter.h" #if QT_CONFIG(tooltip) -#include "qtooltip.h" +#include "private/qtooltip_p.h" #endif #if QT_CONFIG(whatsthis) #include "qwhatsthis.h" @@ -1435,7 +1435,9 @@ void QWidgetPrivate::createTLSysExtra() if (extra->topextra->opacity != 255 && q->isWindow()) extra->topextra->window->setOpacity(qreal(extra->topextra->opacity) / qreal(255)); - const bool isTipLabel = q->inherits("QTipLabel"); +#if QT_CONFIG(tooltip) + const bool isTipLabel = qobject_cast<const QTipLabel *>(q) != nullptr; +#endif const bool isAlphaWidget = !isTipLabel && q->inherits("QAlphaWidget"); #ifdef Q_OS_WIN // Pass on native parent handle for Widget embedded into Active X. diff --git a/src/widgets/styles/qcommonstyle.cpp b/src/widgets/styles/qcommonstyle.cpp index 90c1cfb4b86..592b70ef8ba 100644 --- a/src/widgets/styles/qcommonstyle.cpp +++ b/src/widgets/styles/qcommonstyle.cpp @@ -1994,12 +1994,9 @@ void QCommonStyle::drawControl(ControlElement element, const QStyleOption *opt, tr = proxy()->subElementRect(SE_TabBarTabText, opt, widget); if (!tab->icon.isNull()) { - QPixmap tabIcon = tab->icon.pixmap(tab->iconSize, QStyleHelper::getDpr(p), - (tab->state & State_Enabled) ? QIcon::Normal - : QIcon::Disabled, - (tab->state & State_Selected) ? QIcon::On - : QIcon::Off); - p->drawPixmap(iconRect.x(), iconRect.y(), tabIcon); + const auto mode = (tab->state & State_Enabled) ? QIcon::Normal : QIcon::Disabled; + const auto state = (tab->state & State_Selected) ? QIcon::On : QIcon::Off; + tab->icon.paint(p, iconRect, Qt::AlignCenter, mode, state); } proxy()->drawItemText(p, tr, alignment, tab->palette, tab->state & State_Enabled, tab->text, diff --git a/src/widgets/styles/qstylesheetstyle.cpp b/src/widgets/styles/qstylesheetstyle.cpp index 25b048db65e..e0fdd56e6d8 100644 --- a/src/widgets/styles/qstylesheetstyle.cpp +++ b/src/widgets/styles/qstylesheetstyle.cpp @@ -35,7 +35,7 @@ #include <qabstractscrollarea.h> #include "private/qabstractscrollarea_p.h" #if QT_CONFIG(tooltip) -#include <qtooltip.h> +#include "private/qtooltip_p.h" #endif #include <qshareddata.h> #if QT_CONFIG(toolbutton) @@ -950,7 +950,7 @@ QRenderRule::QRenderRule(const QList<Declaration> &declarations, const QObject * hasFont = v.extractFont(&font, &adj); #if QT_CONFIG(tooltip) - if (object && qstrcmp(object->metaObject()->className(), "QTipLabel") == 0) + if (qobject_cast<const QTipLabel *>(object) != nullptr) palette = QToolTip::palette(); #endif @@ -1495,7 +1495,7 @@ bool QRenderRule::hasModification() const static inline QObject *parentObject(const QObject *obj) { #if QT_CONFIG(tooltip) - if (qobject_cast<const QLabel *>(obj) && qstrcmp(obj->metaObject()->className(), "QTipLabel") == 0) { + if (qobject_cast<const QTipLabel *>(obj) != nullptr) { QObject *p = qvariant_cast<QObject *>(obj->property("_q_stylesheet_parent")); if (p) return p; @@ -1515,7 +1515,7 @@ public: return QStringList(); const QMetaObject *metaObject = OBJECT_PTR(node)->metaObject(); #if QT_CONFIG(tooltip) - if (qstrcmp(metaObject->className(), "QTipLabel") == 0) + if (metaObject == &QTipLabel::staticMetaObject) return QStringList("QToolTip"_L1); #endif QStringList result; @@ -1581,7 +1581,7 @@ public: return false; const QMetaObject *metaObject = OBJECT_PTR(node)->metaObject(); #if QT_CONFIG(tooltip) - if (qstrcmp(metaObject->className(), "QTipLabel") == 0) + if (metaObject == &QTipLabel::staticMetaObject) return nodeName == "QToolTip"_L1; #endif do { @@ -1747,7 +1747,7 @@ int QStyleSheetStyle::nativeFrameWidth(const QWidget *w) } #endif - if (qstrcmp(w->metaObject()->className(), "QTipLabel") == 0) + if (w->metaObject() == &QTipLabel::staticMetaObject) return base->pixelMetric(QStyle::PM_ToolTipLabelFrameWidth, nullptr, w); return base->pixelMetric(QStyle::PM_DefaultFrameWidth, nullptr, w); diff --git a/src/widgets/widgets/qmenu.cpp b/src/widgets/widgets/qmenu.cpp index 3575eb78ac4..9b6b96d911b 100644 --- a/src/widgets/widgets/qmenu.cpp +++ b/src/widgets/widgets/qmenu.cpp @@ -1537,8 +1537,12 @@ void QMenuPrivate::_q_actionTriggered() } activateCausedStack(list, action, QAction::Trigger, false); // if a widget action fires, we need to hide the menu explicitly - if (qobject_cast<QWidgetAction*>(action)) + if (qobject_cast<QWidgetAction*>(action)) { + // make sure QMenu::exec returns the triggered widget action + currentAction = action; + setSyncAction(); hideUpToMenuBar(); + } } } } diff --git a/src/xml/doc/src/external-resources.qdoc b/src/xml/doc/src/external-resources.qdoc index 89c30a84c47..89552477ae8 100644 --- a/src/xml/doc/src/external-resources.qdoc +++ b/src/xml/doc/src/external-resources.qdoc @@ -1,17 +1,6 @@ // Copyright (C) 2016 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only - -/*! - \externalpage http://www.w3.org/2000/xmlns/ - \title http://www.w3.org/2000/xmlns/ -*/ - -/*! - \externalpage http://www.saxproject.org/ - \title SAX2 Java interface -*/ - /*! \externalpage http://www.w3.org/TR/DOM-Level-2-Core/ \title W3C DOM Level 2 diff --git a/tests/auto/CMakeLists.txt b/tests/auto/CMakeLists.txt index ac8aece707b..7dd9340f51b 100644 --- a/tests/auto/CMakeLists.txt +++ b/tests/auto/CMakeLists.txt @@ -4,6 +4,9 @@ # Order by dependency [*], then alphabetic. [*] If bugs in part A of # our source would break tests of part B, then test A before B. + +add_subdirectory(util/testrunner) + set(run_dbus_tests OFF) if (QT_FEATURE_dbus) set(run_dbus_tests ON) diff --git a/tests/auto/cmake/mockplugins/.cmake.conf b/tests/auto/cmake/mockplugins/.cmake.conf index be788d10f8e..846c4f3b923 100644 --- a/tests/auto/cmake/mockplugins/.cmake.conf +++ b/tests/auto/cmake/mockplugins/.cmake.conf @@ -1 +1 @@ -set(QT_REPO_MODULE_VERSION "6.11.0") +set(QT_REPO_MODULE_VERSION "6.12.0") diff --git a/tests/auto/cmake/test_generating_cpp_exports/.cmake.conf b/tests/auto/cmake/test_generating_cpp_exports/.cmake.conf index be788d10f8e..846c4f3b923 100644 --- a/tests/auto/cmake/test_generating_cpp_exports/.cmake.conf +++ b/tests/auto/cmake/test_generating_cpp_exports/.cmake.conf @@ -1 +1 @@ -set(QT_REPO_MODULE_VERSION "6.11.0") +set(QT_REPO_MODULE_VERSION "6.12.0") diff --git a/tests/auto/cmake/test_qt_extract_metatypes/test_qt_extract_metatypes_project/testdata/qt6metatypetest_metatypesQ_OBJECT.json b/tests/auto/cmake/test_qt_extract_metatypes/test_qt_extract_metatypes_project/testdata/qt6metatypetest_metatypesQ_OBJECT.json index 9bd20506429..28be7330cc1 100644 --- a/tests/auto/cmake/test_qt_extract_metatypes/test_qt_extract_metatypes_project/testdata/qt6metatypetest_metatypesQ_OBJECT.json +++ b/tests/auto/cmake/test_qt_extract_metatypes/test_qt_extract_metatypes_project/testdata/qt6metatypetest_metatypesQ_OBJECT.json @@ -14,6 +14,9 @@ ] } ], + "hashes": { + "MetaType": "0$swya0mP+olQ6EImtfZ4HW3dVkKs" + }, "inputFile": "MetaType.h", "outputRevision": 69 } diff --git a/tests/auto/cmake/test_qt_extract_metatypes/test_qt_extract_metatypes_project/testdata/qt6metatypetest_metatypesQ_OBJECTandQ_PROPERTY.json b/tests/auto/cmake/test_qt_extract_metatypes/test_qt_extract_metatypes_project/testdata/qt6metatypetest_metatypesQ_OBJECTandQ_PROPERTY.json index fe80985f796..576668df12f 100644 --- a/tests/auto/cmake/test_qt_extract_metatypes/test_qt_extract_metatypes_project/testdata/qt6metatypetest_metatypesQ_OBJECTandQ_PROPERTY.json +++ b/tests/auto/cmake/test_qt_extract_metatypes/test_qt_extract_metatypes_project/testdata/qt6metatypetest_metatypesQ_OBJECTandQ_PROPERTY.json @@ -30,6 +30,9 @@ ] } ], + "hashes": { + "MetaType": "0$NMxUTKrEcV2vk8Gr4Jl/SR4Q7/c" + }, "inputFile": "MetaType.h", "outputRevision": 69 } diff --git a/tests/auto/cmake/test_static_resources/.cmake.conf b/tests/auto/cmake/test_static_resources/.cmake.conf index be788d10f8e..846c4f3b923 100644 --- a/tests/auto/cmake/test_static_resources/.cmake.conf +++ b/tests/auto/cmake/test_static_resources/.cmake.conf @@ -1 +1 @@ -set(QT_REPO_MODULE_VERSION "6.11.0") +set(QT_REPO_MODULE_VERSION "6.12.0") diff --git a/tests/auto/corelib/animation/qparallelanimationgroup/tst_qparallelanimationgroup.cpp b/tests/auto/corelib/animation/qparallelanimationgroup/tst_qparallelanimationgroup.cpp index c2c09a234c8..d134afc4323 100644 --- a/tests/auto/corelib/animation/qparallelanimationgroup/tst_qparallelanimationgroup.cpp +++ b/tests/auto/corelib/animation/qparallelanimationgroup/tst_qparallelanimationgroup.cpp @@ -463,9 +463,7 @@ void tst_QParallelAnimationGroup::deleteChildrenWithRunningGroup() QCOMPARE(group.state(), QAnimationGroup::Running); QCOMPARE(anim1->state(), QAnimationGroup::Running); - QTest::qWaitFor([&]{ - return group.currentLoopTime() > 0; - }, 200ms); + QVERIFY(QTest::qWaitFor([&] { return group.currentLoopTime() > 0; }, 200ms)); delete anim1; QCOMPARE(group.animationCount(), 0); diff --git a/tests/auto/corelib/io/CMakeLists.txt b/tests/auto/corelib/io/CMakeLists.txt index c0d5ea3136e..10327ceaefb 100644 --- a/tests/auto/corelib/io/CMakeLists.txt +++ b/tests/auto/corelib/io/CMakeLists.txt @@ -9,7 +9,7 @@ endif() if(QT_FEATURE_private_tests) add_subdirectory(qabstractfileengine) add_subdirectory(qfileinfo) - if(LINUX AND QT_FEATURE_liburing) + if((LINUX AND QT_FEATURE_liburing) OR (WIN32 AND QT_FEATURE_windows_ioring)) add_subdirectory(qioring) endif() add_subdirectory(qipaddress) diff --git a/tests/auto/corelib/io/qioring/tst_qioring.cpp b/tests/auto/corelib/io/qioring/tst_qioring.cpp index 1128bcd7979..75d4fe68c55 100644 --- a/tests/auto/corelib/io/qioring/tst_qioring.cpp +++ b/tests/auto/corelib/io/qioring/tst_qioring.cpp @@ -5,7 +5,12 @@ #include <QtCore/private/qioring_p.h> +#ifdef Q_OS_WIN +#include <QtCore/qt_windows.h> +#include <io.h> +#else #include <QtCore/private/qcore_unix_p.h> +#endif using namespace Qt::StringLiterals; using namespace std::chrono_literals; @@ -30,7 +35,13 @@ private: void tst_QIORing::closeFile(qintptr fd) { +#ifdef Q_OS_WIN + // NOLINTNEXTLINE(performance-no-int-to-ptr) + HANDLE h = HANDLE(fd); + CloseHandle(h); +#else QT_CLOSE(fd); +#endif } qintptr tst_QIORing::openHelper(QIORing *ring, const QString &path, QIODevice::OpenMode flags) @@ -109,7 +120,11 @@ void tst_QIORing::read() QFile file(QFINDTESTDATA("data/input.txt")); QVERIFY(file.open(QIODevice::ReadOnly)); int fd = file.handle(); +#ifdef Q_OS_WIN + qintptr nativeFd = _get_osfhandle(fd); +#else qintptr nativeFd = fd; +#endif QIORing ring; QVERIFY(ring.ensureInitialized()); diff --git a/tests/auto/corelib/io/qurl/tst_qurl.cpp b/tests/auto/corelib/io/qurl/tst_qurl.cpp index 71389abc976..a4ef698d380 100644 --- a/tests/auto/corelib/io/qurl/tst_qurl.cpp +++ b/tests/auto/corelib/io/qurl/tst_qurl.cpp @@ -925,7 +925,7 @@ void tst_QUrl::resolving_data() QTest::newRow("/-on-empty-no-authority") << "scheme:" << "/" << "scheme:/"; QTest::newRow(".-on-empty-no-authority") << "scheme:" << "." << "scheme:"; QTest::newRow("./-on-empty-no-authority") << "scheme:" << "./" << "scheme:"; - QTest::newRow(".//-on-empty-no-authority") << "scheme:" << "./" << "scheme:"; + QTest::newRow(".//-on-empty-no-authority") << "scheme:" << ".//" << "scheme:"; QTest::newRow("..-on-empty-no-authority") << "scheme:" << ".." << "scheme:"; QTest::newRow("../-on-empty-no-authority") << "scheme:" << "../" << "scheme:"; diff --git a/tests/auto/corelib/itemmodels/qrangemodeladapter/tst_qrangemodeladapter.cpp b/tests/auto/corelib/itemmodels/qrangemodeladapter/tst_qrangemodeladapter.cpp index eab791e57eb..29e26f99bdd 100644 --- a/tests/auto/corelib/itemmodels/qrangemodeladapter/tst_qrangemodeladapter.cpp +++ b/tests/auto/corelib/itemmodels/qrangemodeladapter/tst_qrangemodeladapter.cpp @@ -220,8 +220,8 @@ API_TEST(moveRows, moveRows(0, 0, 0)) API_TEST(moveTreeRows, moveRows(QList<int>{0, 0}, 0, QList<int>{0, 0})) API_TEST(insertColumn, insertColumn(0)) -API_TEST(insertColumnWithData, insertColumn(0, {})) -API_TEST(insertColumns, insertColumns(0, std::declval<Range&>())) +API_TEST(insertColumnWithData, insertColumn(0, QList<int>{0})) +API_TEST(insertColumns, insertColumns(0, QList<int>{0})) API_TEST(removeColumn, removeColumn(0)) API_TEST(removeColumns, removeColumns(0, 0)) API_TEST(moveColumn, moveColumn(0, 0)) @@ -849,7 +849,7 @@ void tst_QRangeModelAdapter::insertColumn_API() static_assert(has_insertColumnWithData(d.tableOfNumbers)); static_assert(!has_insertColumnWithData(d.constTableOfNumbers)); - static_assert(has_insertColumnWithData(d.tableOfPointers)); + static_assert(!has_insertColumnWithData(d.tableOfPointers)); } void tst_QRangeModelAdapter::insertColumns_API() @@ -863,7 +863,7 @@ void tst_QRangeModelAdapter::insertColumns_API() static_assert(has_insertColumns(d.tableOfNumbers)); static_assert(!has_insertColumns(d.constTableOfNumbers)); - static_assert(has_insertColumns(d.tableOfPointers)); + static_assert(!has_insertColumns(d.tableOfPointers)); static_assert(!has_insertColumns(d.tableOfRowPointers)); static_assert(!has_insertColumns(d.listOfNamedRoles)); static_assert(!has_insertColumns(d.m_tree)); @@ -1025,8 +1025,18 @@ void tst_QRangeModelAdapter::modelReset() QCOMPARE(adapter[0], 3); QCOMPARE(adapter, (std::vector<int>{3, 2, 1})); + modelAboutToBeResetSpy.clear(); + modelResetSpy.clear(); std::vector<int> modifiedData = adapter; + + adapter.assign(modifiedData.begin(), modifiedData.end()); + QCOMPARE(modelResetSpy.count(), 1); + adapter.setRange(std::vector<int>{3, 2, 1}); + QCOMPARE(modelResetSpy.count(), 2); + std::vector<short> shorts = {10, 11, 12}; + adapter.assign(shorts.begin(), shorts.end()); + QCOMPARE(modelResetSpy.count(), 3); } { @@ -1533,7 +1543,7 @@ void tst_QRangeModelAdapter::tableWriteAccess() QTest::ignoreMessage(QtCriticalMsg, QRegularExpression("Not able to assign QVariant")); QTest::ignoreMessage(QtWarningMsg, QRegularExpression("Writing value of type Object\\* to " - "role Qt::RangeModelAdapterRole at index .* of the model failed")); + "role Qt::RangeModelAdapterRole at index .* failed")); #endif adapter.at(0, 0) = new Object; QCOMPARE(dataChangedSpy.count(), 0); @@ -2620,19 +2630,44 @@ using ObjectTree = std::vector<ObjectTreeItem>; class ObjectTreeItem : public ObjectRow { public: - ObjectTreeItem(Object *item = nullptr) + ObjectTreeItem() = default; + + explicit ObjectTreeItem(Object *item) { m_objects[0] = item; } + ObjectTreeItem(const ObjectTreeItem &other) = delete; + ObjectTreeItem &operator=(const ObjectTreeItem &other) = delete; + ObjectTreeItem(ObjectTreeItem &&other) noexcept + { + m_children = std::move(other.m_children); + m_objects = std::move(other.m_objects); + other.m_objects = {}; + } + + ObjectTreeItem &operator=(ObjectTreeItem &&other) noexcept + { + m_children = std::move(other.m_children); + m_objects = std::move(other.m_objects); + other.m_objects = {}; + return *this; + } + + ~ObjectTreeItem() + { + qDeleteAll(m_objects); + } + ObjectTreeItem *parentRow() const { return m_parentRow; } void setParentRow(ObjectTreeItem *parentRow) { m_parentRow = parentRow; } const auto &childRows() const { return m_children; } auto &childRows() { return m_children; } private: - template <std::size_t I> // read-only is enough for this - friend decltype(auto) get(const ObjectTreeItem &row) { return row.m_objects[I]; } + template <std::size_t I, typename Item, + std::enable_if_t<std::is_same_v<q20::remove_cvref_t<Item>, ObjectTreeItem>, bool> = true> + friend decltype(auto) get(Item &&row) { return q23::forward_like<Item>(row.m_objects[I]); } ObjectTreeItem *m_parentRow = nullptr; std::optional<ObjectTree> m_children = std::nullopt; @@ -2645,9 +2680,7 @@ namespace std { void tst_QRangeModelAdapter::insertAutoConnectObjects() { - ObjectTree emptyTree; - - QRangeModelAdapter adapter(emptyTree); + QRangeModelAdapter adapter(ObjectTree{}); QSignalSpy dataChangedSpy(adapter.model(), &QAbstractItemModel::dataChanged); adapter.model()->setAutoConnectPolicy(QRangeModel::AutoConnectPolicy::Full); @@ -2661,11 +2694,11 @@ void tst_QRangeModelAdapter::insertAutoConnectObjects() Object *newChild = new Object; auto firstRow = adapter.begin(); - (*firstRow).children() = ObjectTree{ - ObjectTreeItem(newChild), - ObjectTreeItem(), - ObjectTreeItem() - }; + { + ObjectTree children(3); + children[0] = ObjectTreeItem(newChild); + (*firstRow).children() = std::move(children); + } QCOMPARE(dataChangedSpy.count(), 0); QVERIFY(adapter.hasChildren(0)); newChild->setString("0.0"); @@ -2683,12 +2716,14 @@ void tst_QRangeModelAdapter::insertAutoConnectObjects() newChild = new Object; Object *newGrandChild = new Object; ObjectTreeItem newBranch(newChild); - newBranch.childRows() = ObjectTree{ - ObjectTreeItem(), // skip the first column - ObjectTreeItem(newGrandChild), - ObjectTreeItem() - }; - adapter.at({0, 2}) = newBranch; + { + ObjectTree children(3); + // skip the first row to verify that we continue through nullptr + children[1] = ObjectTreeItem(newGrandChild); + newBranch.childRows() = std::move(children); + } + adapter.at({0, 2}) = std::move(newBranch); + QCOMPARE(adapter.rowCount({0, 2}), 3); QCOMPARE(dataChangedSpy.count(), 1); newChild->setNumber(1); QCOMPARE(dataChangedSpy.count(), 2); @@ -2698,9 +2733,11 @@ void tst_QRangeModelAdapter::insertAutoConnectObjects() QCOMPARE(dataChangedSpy.count(), 1); dataChangedSpy.clear(); - // newGrandChild = new Object; - // adapter.at({0, 2, 0}, 0) = newGrandChild; - // newGrandChild->setString("0.2.0"); + newGrandChild = new Object; + adapter.at({0, 2, 0}, 0) = newGrandChild; + QCOMPARE(dataChangedSpy.count(), 1); + newGrandChild->setString("0.2.0"); + QCOMPARE(dataChangedSpy.count(), 2); } QTEST_MAIN(tst_QRangeModelAdapter) diff --git a/tests/auto/corelib/kernel/qmetaobjectbuilder/tst_qmetaobjectbuilder.cpp b/tests/auto/corelib/kernel/qmetaobjectbuilder/tst_qmetaobjectbuilder.cpp index 67643606fa3..a441ed8f7ee 100644 --- a/tests/auto/corelib/kernel/qmetaobjectbuilder/tst_qmetaobjectbuilder.cpp +++ b/tests/auto/corelib/kernel/qmetaobjectbuilder/tst_qmetaobjectbuilder.cpp @@ -77,6 +77,9 @@ class SomethingOfEverything : public QObject Q_PROPERTY(SomethingEnum eprop READ eprop) Q_PROPERTY(SomethingFlagEnum fprop READ fprop) Q_PROPERTY(QLocale::Language language READ language) + Q_PROPERTY(QString virtualP READ prop VIRTUAL) + // Doesn't override anything, used only to verify MOC handling of OVERRIDE keyword + Q_PROPERTY(QString overrideP READ prop OVERRIDE) public: Q_INVOKABLE SomethingOfEverything() {} ~SomethingOfEverything() {} @@ -577,6 +580,8 @@ void tst_QMetaObjectBuilder::property() QVERIFY(!nullProp.isEnumOrFlag()); QVERIFY(!nullProp.isConstant()); QVERIFY(!nullProp.isFinal()); + QVERIFY(!nullProp.isVirtual()); + QVERIFY(!nullProp.isOverride()); QCOMPARE(nullProp.index(), 0); QCOMPARE(nullProp.revision(), 0); @@ -596,6 +601,8 @@ void tst_QMetaObjectBuilder::property() QVERIFY(!prop1.isEnumOrFlag()); QVERIFY(!prop1.isConstant()); QVERIFY(!prop1.isFinal()); + QVERIFY(!prop1.isVirtual()); + QVERIFY(!prop1.isOverride()); QCOMPARE(prop1.revision(), 0); QCOMPARE(prop1.index(), 0); QCOMPARE(builder.propertyCount(), 1); @@ -616,6 +623,8 @@ void tst_QMetaObjectBuilder::property() QVERIFY(!prop2.isEnumOrFlag()); QVERIFY(!prop2.isConstant()); QVERIFY(!prop2.isFinal()); + QVERIFY(!prop2.isVirtual()); + QVERIFY(!prop2.isOverride()); QCOMPARE(prop2.revision(), 0); QCOMPARE(prop2.index(), 1); QCOMPARE(builder.propertyCount(), 2); @@ -669,6 +678,8 @@ void tst_QMetaObjectBuilder::property() QVERIFY(!prop2.isEnumOrFlag()); QVERIFY(!prop2.isConstant()); QVERIFY(!prop2.isFinal()); + QVERIFY(!prop2.isVirtual()); + QVERIFY(!prop2.isOverride()); QCOMPARE(prop2.revision(), 0); // Remove prop1 and check that prop2 becomes index 0. @@ -686,6 +697,8 @@ void tst_QMetaObjectBuilder::property() QVERIFY(!prop2.isEnumOrFlag()); QVERIFY(!prop2.isConstant()); QVERIFY(!prop2.isFinal()); + QVERIFY(!prop2.isVirtual()); + QVERIFY(!prop2.isOverride()); QCOMPARE(prop2.revision(), 0); QCOMPARE(prop2.index(), 0); @@ -711,6 +724,8 @@ void tst_QMetaObjectBuilder::property() prop2.setEnumOrFlag(false); \ prop2.setConstant(false); \ prop2.setFinal(false); \ + prop2.setVirtual(false); \ + prop2.setOverride(false); \ prop2.setBindable(false); \ prop2.setRequired(false); \ } while (0) @@ -727,6 +742,8 @@ void tst_QMetaObjectBuilder::property() prop2.setEnumOrFlag(true); \ prop2.setConstant(true); \ prop2.setFinal(true); \ + prop2.setVirtual(true); \ + prop2.setOverride(true); \ prop2.setBindable(true); \ prop2.setRequired(true); \ } while (0) @@ -742,6 +759,8 @@ void tst_QMetaObjectBuilder::property() (prop2.isEnumOrFlag() ? 1 : 0) + \ (prop2.isConstant() ? 1 : 0) + \ (prop2.isFinal() ? 1 : 0) + \ + (prop2.isVirtual() ? 1 : 0) + \ + (prop2.isOverride() ? 1 : 0) + \ (prop2.isBindable() ? 1 : 0) + \ (prop2.isRequired() ? 1 : 0)) #define CHECK_FLAG(setFunc,isFunc) \ @@ -766,6 +785,8 @@ void tst_QMetaObjectBuilder::property() CHECK_FLAG(setConstant, isConstant); CHECK_FLAG(setBindable, isBindable); CHECK_FLAG(setFinal, isFinal); + CHECK_FLAG(setVirtual, isVirtual); + CHECK_FLAG(setOverride, isOverride); CHECK_FLAG(setRequired, isRequired); SET_ALL_FLAGS(); QCOMPARE(COUNT_FLAGS(), flagCounter); @@ -782,6 +803,22 @@ void tst_QMetaObjectBuilder::property() QCOMPARE(prototypeProp.notifySignal().signature(), QByteArray("propChanged(QString)")); QCOMPARE(builder.methodCount(), 1); QCOMPARE(builder.method(0).signature(), QByteArray("propChanged(QString)")); + + // virt specifiers + { //Q_PROPERTY(int virtualP READ prop VIRTUAL) + QMetaProperty prototype = SomethingOfEverything::staticMetaObject.property(7); + QMetaPropertyBuilder prototypeProp = builder.addProperty(prototype); + QCOMPARE(prototypeProp.isVirtual(), true); + QCOMPARE(prototypeProp.isOverride(), false); + QCOMPARE(prototypeProp.isFinal(), false); + } + { // Q_PROPERTY(int overrideP READ prop OVERRIDE) + QMetaProperty prototype = SomethingOfEverything::staticMetaObject.property(8); + QMetaPropertyBuilder prototypeProp = builder.addProperty(prototype); + QCOMPARE(prototypeProp.isVirtual(), false); + QCOMPARE(prototypeProp.isOverride(), true); + QCOMPARE(prototypeProp.isFinal(), false); + } } void tst_QMetaObjectBuilder::variantProperty() diff --git a/tests/auto/corelib/kernel/qvariant/tst_qvariant.cpp b/tests/auto/corelib/kernel/qvariant/tst_qvariant.cpp index 2fcfd056882..a30ed901fe6 100644 --- a/tests/auto/corelib/kernel/qvariant/tst_qvariant.cpp +++ b/tests/auto/corelib/kernel/qvariant/tst_qvariant.cpp @@ -6,6 +6,8 @@ #include <qvariant.h> #include <QtCore/qttypetraits.h> +#include <QtCore/qsequentialiterable.h> +#include <QtCore/qassociativeiterable.h> // don't assume <type_traits> template <typename T, typename U> @@ -5025,7 +5027,7 @@ void sortIterable(QMetaSequence::Iterable *iterable) } template<typename Container> -static void testSequentialIteration() +static void testMetaSequenceIteration() { QFETCH(bool, hasSizeAccessor); QFETCH(bool, hasIndexedAccessors); @@ -5157,7 +5159,7 @@ static void testSequentialIteration() } template<typename Container> -static void testAssociativeIteration() +static void testMetaAssociationIteration() { using Key = typename Container::key_type; using Mapped = typename Container::mapped_type; @@ -5228,12 +5230,268 @@ static void testAssociativeIteration() QCOMPARE(f, iter.constEnd()); } +#if QT_VERSION < QT_VERSION_CHECK(7, 0, 0) && QT_DEPRECATED_SINCE(6, 15) +QT_WARNING_PUSH +QT_WARNING_DISABLE_DEPRECATED + +template<typename Iterator> +void sortIterable(QSequentialIterable *iterable) +{ + std::sort(Iterator(iterable->mutableBegin()), Iterator(iterable->mutableEnd()), + [&](const QVariant &a, const QVariant &b) { + return a.toInt() < b.toInt(); + }); +} + +template<typename Container> +static void testSequentialIteration() +{ + QFETCH(bool, hasSizeAccessor); + const auto ignoreSizeWarning = [hasSizeAccessor]() { + if (hasSizeAccessor) + return; + QTest::ignoreMessage( + QtWarningMsg, + "size() called on an iterable without native size accessor. This is slow"); + }; + QTest::failOnWarning(); + + int numSeen = 0; + Container sequence; + ContainerAPI<Container>::insert(sequence, 1); + ContainerAPI<Container>::insert(sequence, 2); + ContainerAPI<Container>::insert(sequence, 3); + + QVariant listVariant = QVariant::fromValue(sequence); + QVERIFY(listVariant.canConvert<QVariantList>()); + QVariantList varList = listVariant.value<QVariantList>(); + ignoreSizeWarning(); + QCOMPARE(varList.size(), (int)std::distance(sequence.begin(), sequence.end())); + QSequentialIterable listIter = listVariant.view<QSequentialIterable>(); + ignoreSizeWarning(); + QCOMPARE(varList.size(), listIter.size()); + + typename Container::iterator containerIter = sequence.begin(); + const typename Container::iterator containerEnd = sequence.end(); + ignoreSizeWarning(); + for (int i = 0, end = listIter.size(); i < end; ++i, ++containerIter, ++numSeen) + { + QVERIFY(ContainerAPI<Container >::compare(listIter.at(i), *containerIter)); + QVERIFY(ContainerAPI<Container >::compare(listIter.at(i), varList.at(i))); + } + QCOMPARE(numSeen, (int)std::distance(sequence.begin(), sequence.end())); + QCOMPARE(containerIter, containerEnd); + + numSeen = 0; + containerIter = sequence.begin(); + for (QVariant v : listIter) { + QVERIFY(ContainerAPI<Container>::compare(v, *containerIter)); + QVERIFY(ContainerAPI<Container>::compare(v, varList.at(numSeen))); + ++containerIter; + ++numSeen; + } + QCOMPARE(numSeen, (int)std::distance(sequence.begin(), sequence.end())); + + auto compareLists = [&]() { + int numSeen = 0; + auto varList = listVariant.value<QVariantList>(); + auto varIter = varList.begin(); + for (const QVariant &v : std::as_const(listIter)) { + QVERIFY(ContainerAPI<Container>::compare(v, *varIter)); + ++varIter; + ++numSeen; + } + QCOMPARE(varIter, varList.end()); + numSeen = 0; + auto constVarIter = varList.constBegin(); + for (QVariant v : listIter) { + QVERIFY(ContainerAPI<Container>::compare(v, *constVarIter)); + ++constVarIter; + ++numSeen; + } + QCOMPARE(numSeen, (int)std::distance(varList.begin(), varList.end())); + }; + compareLists(); + + QVariant first = listIter.at(0); + QVariant second = listIter.at(1); + QVariant third = listIter.at(2); + compareLists(); + listIter.addValue(third); + compareLists(); + listIter.addValue(second); + compareLists(); + listIter.addValue(first); + compareLists(); + + QCOMPARE(listIter.size(), 6); + + if (listIter.canRandomAccessIterate()) + sortIterable<QSequentialIterable::RandomAccessIterator>(&listIter); + else if (listIter.canReverseIterate()) + sortIterable<QSequentialIterable::BidirectionalIterator>(&listIter); + else if (listIter.canForwardIterate()) + return; // std::sort cannot sort with only forward iterators. + else + QFAIL("The container has no meaningful iterators"); + + compareLists(); + ignoreSizeWarning(); + QCOMPARE(listIter.size(), 6); + QCOMPARE(listIter.at(0), first); + QCOMPARE(listIter.at(1), first); + QCOMPARE(listIter.at(2), second); + QCOMPARE(listIter.at(3), second); + QCOMPARE(listIter.at(4), third); + QCOMPARE(listIter.at(5), third); + + if (listIter.metaContainer().canRemoveValue()) { + listIter.removeValue(); + compareLists(); + ignoreSizeWarning(); + QCOMPARE(listIter.size(), 5); + QCOMPARE(listIter.at(0), first); + QCOMPARE(listIter.at(1), first); + QCOMPARE(listIter.at(2), second); + QCOMPARE(listIter.at(3), second); + QCOMPARE(listIter.at(4), third); + } else { + // QString and QByteArray have no pop_back or pop_front and it's unclear what other + // method we should use to remove an item. + QVERIFY((std::is_same_v<Container, QString> || std::is_same_v<Container, QByteArray>)); + } + + auto i = listIter.mutableBegin(); + QVERIFY(i != listIter.mutableEnd()); + + *i = QStringLiteral("17"); + if (listIter.metaContainer().valueMetaType() == QMetaType::fromType<int>()) + QCOMPARE(listIter.at(0).toInt(), 17); + else if (listIter.metaContainer().valueMetaType() == QMetaType::fromType<bool>()) + QCOMPARE(listIter.at(0).toBool(), false); + + *i = QStringLiteral("true"); + if (listIter.metaContainer().valueMetaType() == QMetaType::fromType<int>()) + QCOMPARE(listIter.at(0).toInt(), 0); + else if (listIter.metaContainer().valueMetaType() == QMetaType::fromType<bool>()) + QCOMPARE(listIter.at(0).toBool(), true); +} + +template<typename Container> +static void testAssociativeIteration() +{ + using Key = typename Container::key_type; + using Mapped = typename Container::mapped_type; + + int numSeen = 0; + Container mapping; + mapping[5] = true; + mapping[15] = false; + + QVariant mappingVariant = QVariant::fromValue(mapping); + QVariantMap varMap = mappingVariant.value<QVariantMap>(); + QVariantMap varHash = mappingVariant.value<QVariantMap>(); + QAssociativeIterable mappingIter = mappingVariant.view<QAssociativeIterable>(); + + typename Container::const_iterator containerIter = mapping.begin(); + const typename Container::const_iterator containerEnd = mapping.end(); + for ( ;containerIter != containerEnd; ++containerIter, ++numSeen) + { + Mapped expected = KeyGetter<Container>::value(containerIter); + Key key = KeyGetter<Container>::get(containerIter); + Mapped actual = qvariant_cast<Mapped>(mappingIter.value(key)); + QCOMPARE(qvariant_cast<Mapped>(varMap.value(QString::number(key))), expected); + QCOMPARE(qvariant_cast<Mapped>(varHash.value(QString::number(key))), expected); + QCOMPARE(actual, expected); + const QAssociativeIterable::const_iterator it = mappingIter.find(key); + QVERIFY(it != mappingIter.end()); + QCOMPARE(it.value().value<Mapped>(), expected); + } + QCOMPARE(numSeen, (int)std::distance(mapping.begin(), mapping.end())); + QCOMPARE(containerIter, containerEnd); + QVERIFY(mappingIter.find(10) == mappingIter.end()); + + auto i = mappingIter.mutableFind(QStringLiteral("nonono")); + QCOMPARE(i, mappingIter.mutableEnd()); + i = mappingIter.mutableFind(QStringLiteral("5")); + QVERIFY(i != mappingIter.mutableEnd()); + + *i = QStringLiteral("17"); + + if (mappingIter.metaContainer().mappedMetaType() == QMetaType::fromType<int>()) + QCOMPARE(mappingIter.value(5).toInt(), 17); + else if (mappingIter.metaContainer().mappedMetaType() == QMetaType::fromType<bool>()) + QCOMPARE(mappingIter.value(5).toBool(), true); + + *i = QStringLiteral("true"); + if (mappingIter.metaContainer().mappedMetaType() == QMetaType::fromType<int>()) + QCOMPARE(mappingIter.value(5).toInt(), 0); + else if (mappingIter.metaContainer().mappedMetaType() == QMetaType::fromType<bool>()) + QCOMPARE(mappingIter.value(5).toBool(), true); + + QVERIFY(mappingIter.containsKey("5")); + mappingIter.removeKey(QStringLiteral("5")); + QCOMPARE(mappingIter.find(5), mappingIter.end()); + + mappingIter.setValue(5, 44); + if (mappingIter.metaContainer().mappedMetaType() == QMetaType::fromType<int>()) + QCOMPARE(mappingIter.value(5).toInt(), 44); + else if (mappingIter.metaContainer().mappedMetaType() == QMetaType::fromType<bool>()) + QCOMPARE(mappingIter.value(5).toBool(), true); + + // Test that find() does not coerce + auto container = Container(); + container[0] = true; + + QVariant containerVariant = QVariant::fromValue(container); + QAssociativeIterable iter = containerVariant.value<QAssociativeIterable>(); + auto f = iter.constFind(QStringLiteral("anything")); + QCOMPARE(f, iter.constEnd()); +} + +template<typename T> +static void addRowSequential(const char *name, bool hasSizeAccessor, bool hasIndexedAccessor) +{ + QTest::newRow(name) + << &testMetaSequenceIteration<T> << hasSizeAccessor << hasIndexedAccessor; + QTest::addRow("%s_old", name) + << &testSequentialIteration<T> << hasSizeAccessor << hasIndexedAccessor; +} + +template<typename C> +static void addRowAssociative(const char *name) +{ + QTest::newRow(name) + << &testMetaAssociationIteration<C>; + QTest::addRow("%s_old", name) + << &testAssociativeIteration<C>; +} + +QT_WARNING_POP +#else + +template<typename T> +static void addRowSequential(const char *name, bool hasSizeAccessor, bool hasIndexedAccessor) +{ + QTest::newRow(name) + << &testMetaSequenceIteration<T> << hasSizeAccessor << hasIndexedAccessor; +} + +template<typename C> +static void addRowAssociative(const char *name) +{ + QTest::newRow(name) + << &testMetaAssociationIteration<C>; +} + +#endif // QT_VERSION < QT_VERSION_CHECK(7, 0, 0) && QT_DEPRECATED_SINCE(6, 15) + void tst_QVariant::iterateSequentialContainerElements_data() { QTest::addColumn<QFunctionPointer>("testFunction"); QTest::addColumn<bool>("hasSizeAccessor"); QTest::addColumn<bool>("hasIndexedAccessors"); -#define ADD(T) QTest::newRow(#T) << &testSequentialIteration<T> << true << true +#define ADD(T) addRowSequential<T>(#T, true, true) ADD(QQueue<int>); ADD(QQueue<QVariant>); ADD(QQueue<QString>); @@ -5253,13 +5511,13 @@ void tst_QVariant::iterateSequentialContainerElements_data() ADD(QByteArray); #undef ADD -#define ADD(T) QTest::newRow(#T) << &testSequentialIteration<T> << true << false +#define ADD(T) addRowSequential<T>(#T, true, false) ADD(std::list<int>); ADD(std::list<QVariant>); ADD(std::list<QString>); #undef ADD -#define ADD(T) QTest::newRow(#T) << &testSequentialIteration<T> << false << false +#define ADD(T) addRowSequential<T>(#T, false, false) #ifdef TEST_FORWARD_LIST ADD(std::forward_list<int>); ADD(std::forward_list<QVariant>); @@ -5271,7 +5529,7 @@ void tst_QVariant::iterateSequentialContainerElements_data() void tst_QVariant::iterateAssociativeContainerElements_data() { QTest::addColumn<QFunctionPointer>("testFunction"); -#define ADD(C, K, V) QTest::newRow(#C #K #V) << &testAssociativeIteration<C<K, V>>; +#define ADD(C, K, V) addRowAssociative<C<K, V>>(#C "<" #K "," #V ">"); ADD(QHash, int, bool); ADD(QHash, int, int); ADD(QMap, int, bool); @@ -6448,15 +6706,15 @@ void tst_QVariant::get_NonDefaultConstructible() struct QVariantWrapper { public: - static constexpr bool canNoexceptConvertToQVariant + static constexpr bool CanNoexceptConvertToQVariant = std::is_nothrow_copy_constructible_v<QVariant>; - static constexpr bool canNoexceptAssignQVariant + static constexpr bool CanNoexceptAssignQVariant = std::is_nothrow_copy_assignable_v<QVariant>; QVariantWrapper(QVariant *content = nullptr) noexcept : m_content(content) {} - QVariant content() const noexcept(canNoexceptConvertToQVariant) { return *m_content; } - void setContent(const QVariant &content) noexcept(canNoexceptAssignQVariant) + QVariant content() const noexcept(CanNoexceptConvertToQVariant) { return *m_content; } + void setContent(const QVariant &content) noexcept(CanNoexceptAssignQVariant) { *m_content = content; } @@ -6468,14 +6726,14 @@ private: QT_BEGIN_NAMESPACE template<> QVariant::ConstReference<QVariantWrapper>::operator QVariant() const - noexcept(QVariantWrapper::canNoexceptConvertToQVariant) + noexcept(QVariantWrapper::CanNoexceptConvertToQVariant) { return m_referred.content(); } template<> QVariant::Reference<QVariantWrapper> &QVariant::Reference<QVariantWrapper>::operator=( - const QVariant &content) noexcept(QVariantWrapper::canNoexceptAssignQVariant) + const QVariant &content) noexcept(QVariantWrapper::CanNoexceptAssignQVariant) { m_referred.setContent(content); return *this; diff --git a/tests/auto/corelib/plugin/qpluginloader/tst_qpluginloader.cpp b/tests/auto/corelib/plugin/qpluginloader/tst_qpluginloader.cpp index 2d9d351286f..f1f88891015 100644 --- a/tests/auto/corelib/plugin/qpluginloader/tst_qpluginloader.cpp +++ b/tests/auto/corelib/plugin/qpluginloader/tst_qpluginloader.cpp @@ -63,11 +63,11 @@ using namespace Qt::StringLiterals; #elif defined(Q_OS_WIN) # undef dll_VALID # define dll_VALID true -//# ifdef QT_NO_DEBUG +# if !defined(QT_NO_DEBUG) && defined(Q_CC_MSVC) +# define SUFFIX "d.dll" +# else # define SUFFIX ".dll" -//# else -//# define SUFFIX "d.dll" -//# endif +# endif # define PREFIX "" #else // all other Unix diff --git a/tests/auto/corelib/thread/qfuture/tst_qfuture.cpp b/tests/auto/corelib/thread/qfuture/tst_qfuture.cpp index 806b6b43161..f7d98eb15e2 100644 --- a/tests/auto/corelib/thread/qfuture/tst_qfuture.cpp +++ b/tests/auto/corelib/thread/qfuture/tst_qfuture.cpp @@ -5656,6 +5656,12 @@ void tst_QFuture::cancelChainWithContext() int onCancelCnt = 0; bool unexpectedThread = false; + // For in-other-thread case we need a semaphore to make sure that + // the needed continuation is executed. + // Fot the in-main-thread case it's guaranteed by the unwrap() + // implementation. + QSemaphore sem; + auto f = p1.future() .then(context, [&]() { if (QThread::currentThread() != thread) @@ -5666,6 +5672,8 @@ void tst_QFuture::cancelChainWithContext() if (QThread::currentThread() != thread) unexpectedThread = true; ++thenCnt; + if (inOtherThread) + sem.release(); return f; }).unwrap() .then(context, [&]{ @@ -5685,6 +5693,8 @@ void tst_QFuture::cancelChainWithContext() }); p1.finish(); + if (inOtherThread) + sem.acquire(); f.cancelChain(); p2.finish(); f.waitForFinished(); @@ -5737,6 +5747,75 @@ void tst_QFuture::cancelChainWithContext() QCOMPARE_EQ(thenCnt, 4); QCOMPARE_EQ(onCancelCnt, 0); } + // cancelling propagates through unwrap() + { + QPromise<void> p1, p2; + p1.start(); + p2.start(); + + int thenCnt = 0; + int onCancelCnt = 0; + bool unexpectedThread = false; + + // For in-other-thread case we need a semaphore to make sure that + // the first continuation is executed. + // Fot the in-main-thread case it's guaranteed by the unwrap() + // implementation. + QSemaphore sem; + + auto f = p1.future() + .then(context, [&, f2 = p2.future()]() { + if (QThread::currentThread() != thread) + unexpectedThread = true; + ++thenCnt; + if (inOtherThread) + sem.release(); + return f2; + }).unwrap() + .onCanceled(context, [&] { + if (QThread::currentThread() != thread) + unexpectedThread = true; + ++onCancelCnt; + }) + .then([&]() { + if (QThread::currentThread() != thread) + unexpectedThread = true; + ++thenCnt; + return QtFuture::makeReadyVoidFuture(); + }).unwrap() + .onCanceled([&] { + if (QThread::currentThread() != thread) + unexpectedThread = true; + ++onCancelCnt; + }) + .then(context, [&]{ + if (QThread::currentThread() != thread) + unexpectedThread = true; + ++thenCnt; + return QtFuture::makeReadyVoidFuture(); + }).unwrap() + .onCanceled([&] { + if (QThread::currentThread() != thread) + unexpectedThread = true; + ++onCancelCnt; + }) + .then(context, [&]{ + if (QThread::currentThread() != thread) + unexpectedThread = true; + ++thenCnt; + }); + + p1.finish(); + if (inOtherThread) + sem.acquire(); + f.cancelChain(); + p2.finish(); + f.waitForFinished(); + + QVERIFY(!unexpectedThread); + QCOMPARE_EQ(thenCnt, 1); + QCOMPARE_EQ(onCancelCnt, 3); + } } void tst_QFuture::cancelChainOnAnOverwrittenFuture() diff --git a/tests/auto/corelib/thread/qreadwritelock/tst_qreadwritelock.cpp b/tests/auto/corelib/thread/qreadwritelock/tst_qreadwritelock.cpp index 86dfa5faffc..4c089091f8d 100644 --- a/tests/auto/corelib/thread/qreadwritelock/tst_qreadwritelock.cpp +++ b/tests/auto/corelib/thread/qreadwritelock/tst_qreadwritelock.cpp @@ -57,6 +57,7 @@ private slots: void multipleReadersLoop(); void multipleWritersLoop(); void multipleReadersWritersLoop(); + void heavyLoadLocks(); void countingTest(); void limitedReaders(); void deleteOnUnlock(); @@ -603,6 +604,111 @@ public: } }; +class HeavyLoadLockThread : public QThread +{ +public: + QReadWriteLock &testRwlock; + const qsizetype iterations; + const int numThreads; + inline HeavyLoadLockThread(QReadWriteLock &l, qsizetype iters, int numThreads, QVector<QAtomicInt *> &counters): + testRwlock(l), + iterations(iters), + numThreads(numThreads), + counters(counters) + { } + +private: + QVector<QAtomicInt *> &counters; + QAtomicInt *getCounter(qsizetype index) + { + QReadLocker locker(&testRwlock); + /* + The index is increased monotonically, so the index + being requested should be always within or at the end of the + counters vector. + */ + Q_ASSERT(index <= counters.size()); + if (counters.size() <= index || counters[index] == nullptr) { + locker.unlock(); + QWriteLocker wlocker(&testRwlock); + if (counters.size() <= index) + counters.resize(index + 1, nullptr); + if (counters[index] == nullptr) + counters[index] = new QAtomicInt(0); + return counters[index]; + } + return counters[index]; + } + void releaseCounter(qsizetype index) + { + QWriteLocker locker(&testRwlock); + delete counters[index]; + counters[index] = nullptr; + } + +public: + void run() override + { + for (qsizetype i = 0; i < iterations; ++i) { + QAtomicInt *counter = getCounter(i); + /* + Here each counter is accessed by each thread + and increaed only once. As a result, when the + counter reaches numThreads, i.e. the fetched + value before the increment is numThreads-1, + we know all threads have accessed this counter + and we can delete it safely. + */ + int prev = counter->fetchAndAddRelaxed(1); + if (prev == numThreads - 1) { +#ifdef QT_BUILDING_UNDER_TSAN + /* + Under TSAN, deleting and freeing an object + will trigger a write operation on the memory + of the object. Since we used fetchAndAddRelaxed + to update the counter, TSAN will report a data + race when deleting the counter here. To avoid + the false positive, we simply reset the counter + to 0 here, with ordered semantics to establish + the sequence to ensure the the free-ing option + happens after all fetchAndAddRelaxed operations + in other threads. + + When not building under TSAN, deleting the counter + will not result in any data read or written to the + memory region of the counter, so no data race will + happen. + */ + counter->fetchAndStoreOrdered(0); +#endif + releaseCounter(i); + } + } + } +}; + +/* + Multiple threads racing acquiring and releasing + locks on the same indices. +*/ + +void tst_QReadWriteLock::heavyLoadLocks() +{ + constexpr qsizetype iterations = 65536 * 4; + constexpr int numThreads = 8; + QVector<QAtomicInt *> counters; + QReadWriteLock testLock; + std::array<std::unique_ptr<HeavyLoadLockThread>, numThreads> threads; + for (auto &thread : threads) + thread = std::make_unique<HeavyLoadLockThread>(testLock, iterations, numThreads, counters); + for (auto &thread : threads) + thread->start(); + for (auto &thread : threads) + thread->wait(); + QVERIFY(counters.size() == iterations); + for (qsizetype i = 0; i < iterations; ++i) + QVERIFY(counters[i] == nullptr); +} /* A writer acquires a read-lock, a reader locks diff --git a/tests/auto/network/access/qhttp2connection/tst_qhttp2connection.cpp b/tests/auto/network/access/qhttp2connection/tst_qhttp2connection.cpp index 417655c31d9..22aa9d44262 100644 --- a/tests/auto/network/access/qhttp2connection/tst_qhttp2connection.cpp +++ b/tests/auto/network/access/qhttp2connection/tst_qhttp2connection.cpp @@ -21,6 +21,8 @@ class tst_QHttp2Connection : public QObject private slots: void construct(); void constructStream(); + void streamConfiguration_data(); + void streamConfiguration(); void testSETTINGSFrame(); void maxHeaderTableSize(); void testPING(); @@ -204,6 +206,59 @@ void tst_QHttp2Connection::constructStream() QCOMPARE(stream->isUploadingDATA(), false); } +void tst_QHttp2Connection::streamConfiguration_data() +{ + QTest::addColumn<bool>("useDownloadBuffer"); + + QTest::addRow("useDownloadBuffer=true") << true; + QTest::addRow("useDownloadBuffer=false") << false; +} + +void tst_QHttp2Connection::streamConfiguration() +{ + QFETCH(const bool, useDownloadBuffer); + + auto [client, server] = makeFakeConnectedSockets(); + auto *clientConnection = makeHttp2Connection(client.get(), {}, Client); + auto *serverConnection = makeHttp2Connection(server.get(), {}, Server); + + QHttp2Stream::Configuration config; + config.useDownloadBuffer = useDownloadBuffer; + + QHttp2Stream *clientStream = clientConnection->createStream(config).unwrap(); + QVERIFY(clientStream); + QCOMPARE(clientStream->configuration().useDownloadBuffer, useDownloadBuffer); + QVERIFY(waitForSettingsExchange(clientConnection, serverConnection)); + + QSignalSpy newIncomingStreamSpy{ serverConnection, &QHttp2Connection::newIncomingStream }; + QSignalSpy clientDataReceivedSpy{ clientStream, &QHttp2Stream::dataReceived }; + + HPack::HttpHeader headers = getRequiredHeaders(); + clientStream->sendHEADERS(headers, false); + + QVERIFY(newIncomingStreamSpy.wait()); + auto *serverStream = newIncomingStreamSpy.front().front().value<QHttp2Stream *>(); + QVERIFY(serverStream); + + const HPack::HttpHeader responseHeaders{ { ":status", "200" } }; + serverStream->sendHEADERS(responseHeaders, false); + + const QByteArray testData = "Hello World"_ba.repeated(100); + serverStream->sendDATA(testData, true); + + QVERIFY(clientDataReceivedSpy.wait()); + QCOMPARE(clientDataReceivedSpy.count(), 1); + + const QByteArray receivedData = clientDataReceivedSpy.front().front().value<QByteArray>(); + QCOMPARE(receivedData, testData); + + if (useDownloadBuffer) { + QCOMPARE(clientStream->downloadBuffer().byteAmount(), testData.size()); + } else { + QVERIFY(clientStream->downloadBuffer().isEmpty()); + } +} + void tst_QHttp2Connection::testSETTINGSFrame() { constexpr qint32 PrefaceLength = 24; diff --git a/tests/auto/other/qaccessibility/tst_qaccessibility.cpp b/tests/auto/other/qaccessibility/tst_qaccessibility.cpp index c65f6645d01..4fbaed0d7b3 100644 --- a/tests/auto/other/qaccessibility/tst_qaccessibility.cpp +++ b/tests/auto/other/qaccessibility/tst_qaccessibility.cpp @@ -2988,6 +2988,10 @@ void tst_QAccessibility::listTest() model->appendRow({new QStandardItem("Norway"), new QStandardItem("Oslo"), new QStandardItem("NOK")}); model->appendRow({new QStandardItem("Germany"), new QStandardItem("Berlin"), new QStandardItem("EUR")}); model->appendRow({new QStandardItem("Australia"), new QStandardItem("Brisbane"), new QStandardItem("AUD")}); + model->item(0, 1)->setCheckable(true); + model->item(1, 1)->setCheckable(true); + model->item(2, 1)->setCheckable(true); + model->item(2, 1)->setCheckState(Qt::Checked); auto lvHolder = std::make_unique<QListView>(); auto listView = lvHolder.get(); listView->setModel(model); @@ -3016,16 +3020,19 @@ void tst_QAccessibility::listTest() QCOMPARE(iface->indexOfChild(child1), 0); QCOMPARE(child1->text(QAccessible::Name), QString("Oslo")); QCOMPARE(child1->role(), QAccessible::ListItem); + QVERIFY(child1->state().checkable); QAccessibleInterface *child2 = iface->child(1); QVERIFY(child2); QCOMPARE(iface->indexOfChild(child2), 1); QCOMPARE(child2->text(QAccessible::Name), QString("Berlin")); + QVERIFY(child2->state().checkable); QAccessibleInterface *child3 = iface->child(2); QVERIFY(child3); QCOMPARE(iface->indexOfChild(child3), 2); QCOMPARE(child3->text(QAccessible::Name), QString("Brisbane")); + QVERIFY(child3->state().checkable); } // Check that application is accessible parent, since it's a top-level widget @@ -3044,9 +3051,21 @@ void tst_QAccessibility::listTest() // skip focus event tests on platforms where window focus cannot be ensured const bool checkFocus = QGuiApplicationPrivate::platformIntegration()->hasCapability(QPlatformIntegration::WindowActivation); if (checkFocus) { + QVERIFY(QTest::qWaitForWindowActive(listView)); + QAccessibleEvent focusEvent(listView, QAccessible::Focus); focusEvent.setChild(1); QVERIFY(QTestAccessibility::containsEvent(&focusEvent)); + + // check the item + QVERIFY(!iface->child(1)->state().checked); + QTest::keyClick(listView, Qt::Key_Space); + QVERIFY(iface->child(1)->state().checked); + QAccessible::State s; + s.checked = true; + QAccessibleStateChangeEvent checkedStateChangedEvent(listView, s); + checkedStateChangedEvent.setChild(1); + QVERIFY(QTestAccessibility::containsEvent(&checkedStateChangedEvent)); } QTest::mouseClick(listView->viewport(), Qt::LeftButton, { }, listView->visualRect(model->index(2, listView->modelColumn())).center()); @@ -3058,6 +3077,16 @@ void tst_QAccessibility::listTest() QAccessibleEvent focusEvent2(listView, QAccessible::Focus); focusEvent2.setChild(2); QVERIFY(QTestAccessibility::containsEvent(&focusEvent2)); + + // uncheck the item + QVERIFY(iface->child(2)->state().checked); + QTest::keyClick(listView, Qt::Key_Space); + QVERIFY(!iface->child(2)->state().checked); + QAccessible::State s; + s.checked = true; + QAccessibleStateChangeEvent checkedStateChangedEvent(listView, s); + checkedStateChangedEvent.setChild(2); + QVERIFY(QTestAccessibility::containsEvent(&checkedStateChangedEvent)); } QAccessibleTableInterface *table = iface->tableInterface(); @@ -3174,6 +3203,7 @@ void tst_QAccessibility::treeTest() QTreeWidgetItem *item1 = new QTreeWidgetItem; item1->setText(0, "Picasso"); item1->setText(1, "Guernica"); + item1->setCheckState(0, Qt::Unchecked); root1->addChild(item1); QTreeWidgetItem *item2 = new QTreeWidgetItem; @@ -3188,6 +3218,7 @@ void tst_QAccessibility::treeTest() QTreeWidgetItem *item3 = new QTreeWidgetItem; item3->setText(0, "Klimt"); item3->setText(1, "The Kiss"); + item3->setCheckState(0, Qt::Checked); root2->addChild(item3); treeView->resize(400,400); @@ -3291,6 +3322,40 @@ void tst_QAccessibility::treeTest() QCOMPARE(table2->columnDescription(0), QString("Artist")); QCOMPARE(table2->columnDescription(1), QString("Work")); + // skip accessible state change event tests on platforms where window focus cannot be ensured + if (QGuiApplicationPrivate::platformIntegration()->hasCapability( + QPlatformIntegration::WindowActivation)) { + QVERIFY(QTest::qWaitForWindowActive(treeView.get())); + + // check item1 (Picasso) + QVERIFY(cell1->state().checkable); + QVERIFY(!cell1->state().checked); + treeView->setCurrentItem(item1); + QTest::keyClick(treeView.get(), Qt::Key_Space); + QVERIFY(cell1->state().checked); + { + QAccessible::State s; + s.checked = true; + QAccessibleStateChangeEvent checkedStateChangedEvent(treeView.get(), s); + checkedStateChangedEvent.setChild(iface->indexOfChild(cell1)); + QVERIFY(QTestAccessibility::containsEvent(&checkedStateChangedEvent)); + } + + // uncheck item3 (Klimt) + QVERIFY(cell2->state().checkable); + QVERIFY(cell2->state().checked); + treeView->setCurrentItem(item3); + QTest::keyClick(treeView.get(), Qt::Key_Space); + QVERIFY(!cell2->state().checked); + { + QAccessible::State s; + s.checked = true; + QAccessibleStateChangeEvent checkedStateChangedEvent(treeView.get(), s); + checkedStateChangedEvent.setChild(iface->indexOfChild(cell2)); + QVERIFY(QTestAccessibility::containsEvent(&checkedStateChangedEvent)); + } + } + QTestAccessibility::clearEvents(); } @@ -3315,6 +3380,7 @@ void tst_QAccessibility::tableTest() for (int i = 0; i<9; ++i) { QTableWidgetItem *item = new QTableWidgetItem; item->setText(QString::number(i/3) + QString(".") + QString::number(i%3)); + item->setCheckState(Qt::Unchecked); tableView->setItem(i/3, i%3, item); } @@ -3582,6 +3648,40 @@ void tst_QAccessibility::tableTest() tableView->horizontalHeader()->setVisible(false); } + + // skip accessible state change event tests on platforms where window focus cannot be ensured + if (QGuiApplicationPrivate::platformIntegration()->hasCapability( + QPlatformIntegration::WindowActivation)) { + QVERIFY(QTest::qWaitForWindowActive(tableView)); + + // check cell (0, 0) + tableView->setCurrentItem(tableView->item(0, 0)); + QVERIFY(cell00->state().checkable); + QVERIFY(!cell00->state().checked); + QTest::keyClick(tableView, Qt::Key_Space); + QVERIFY(cell00->state().checked); + { + QAccessible::State s; + s.checked = true; + QAccessibleStateChangeEvent checkedStateChangedEvent(tableView, s); + checkedStateChangedEvent.setChild(iface->indexOfChild(cell00)); + QVERIFY(QTestAccessibility::containsEvent(&checkedStateChangedEvent)); + } + + QTestAccessibility::clearEvents(); + + // uncheck cell (0, 0) again + QTest::keyClick(tableView, Qt::Key_Space); + QVERIFY(!cell00->state().checked); + { + QAccessible::State s; + s.checked = true; + QAccessibleStateChangeEvent checkedStateChangedEvent(tableView, s); + checkedStateChangedEvent.setChild(iface->indexOfChild(cell00)); + QVERIFY(QTestAccessibility::containsEvent(&checkedStateChangedEvent)); + } + } + { QTestAccessibility::clearEvents(); auto cell0 = table2->cellAt(0, 2); diff --git a/tests/auto/tools/moc/allmocs_baseline_in.json b/tests/auto/tools/moc/allmocs_baseline_in.json index d8e6c4df538..d36bdb907ae 100644 --- a/tests/auto/tools/moc/allmocs_baseline_in.json +++ b/tests/auto/tools/moc/allmocs_baseline_in.json @@ -23,6 +23,9 @@ ] } ], + "hashes": { + "BackslashNewlines": "0$VAkStqKr6pw5W81yZ3rCFn98h9U" + }, "inputFile": "backslash-newlines.h", "outputRevision": 69 }, @@ -41,6 +44,9 @@ ] } ], + "hashes": { + "IfdefedClass": "0$OochknExuVUAqq2zc5gt2Ldj4+4" + }, "inputFile": "c-comments.h", "outputRevision": 69 }, @@ -75,6 +81,9 @@ "qualifiedClassName": "CStyleEnums" } ], + "hashes": { + "CStyleEnums": "0$fqbHabuJUT79FJr/S/6WsAiFcJI" + }, "inputFile": "cstyle-enums.h", "outputRevision": 69 }, @@ -325,6 +334,11 @@ ] } ], + "hashes": { + "CXX11Enums": "0$SPzFG1raX8GCqiq6g21PoU/7ixs", + "CXX11Enums2": "0$uYFCI2+nT8TI8jLcKAvO8vp9OYA", + "CXX11Enums3": "0$bRezVo4UbDNuGatmLEd0/n8K8FA" + }, "inputFile": "cxx11-enums.h", "outputRevision": 69 }, @@ -727,6 +741,17 @@ ] } ], + "hashes": { + "ExplicitOverrideControlBase": "0$PFsRqLGh0wP5gfqwF9CVeUi9EVU", + "ExplicitOverrideControlFinalCxx11": "0$RQTN9wEJCVZ1N4IcieHs2LuRKY8", + "ExplicitOverrideControlFinalCxx11OverrideCxx11": "0$ZEBCUhi5oEGiDVdmoqD+x4n1KJk", + "ExplicitOverrideControlFinalQt": "0$GuS6YD8Kcf/9KyehmFsrmDWW42M", + "ExplicitOverrideControlFinalQtOverrideQt": "0$q48mSBftA2LJFVSB4uqTmKs2gOc", + "ExplicitOverrideControlOverrideCxx11": "0$he1BJyFAtIcrEf74bZY1d54aCbc", + "ExplicitOverrideControlOverrideQt": "0$dWQ+iDw9oKEYXiSgy2izIISyRMg", + "ExplicitOverrideControlSealed": "0$3nq5psg7nFNDlPnBpaYraCr3CHQ", + "ExplicitOverrideControlSealedOverride": "0$I7ySe3SgUSARroswRh74wJoZkkA" + }, "inputFile": "cxx11-explicit-override-control.h", "outputRevision": 69 }, @@ -850,6 +875,17 @@ ] } ], + "hashes": { + "ExportedFinalTestClassCpp11": "0$eUEIDQi+/lB9KOjB+oul4rZe9xE", + "ExportedFinalTestClassCpp11X": "0$g03sLKAEeX8BLjKjFsG3MMfunaY", + "ExportedFinalTestClassQt": "0$fYFeeRSmEnokVTW4eIiCU6PR5HM", + "ExportedFinalTestClassQtX": "0$7+xgvb70xVV3Xot9q7FhSm8mUew", + "ExportedSealedTestClass": "0$jzcARHlbTm1whxXOtg9edtN1ctg", + "ExportedSealedTestClassX": "0$tEmYiad6hqJMlGaCnNc2GhZtDgM", + "FinalTestClassCpp11": "0$Opb/8DnbfVjpH2CZvHo9Rv93iV8", + "FinalTestClassQt": "0$DtnHOKRNbQ5Y3QbO5AeLM4qYDW0", + "SealedTestClass": "0$VUU19XJgT+KhqRBlwQmXkd0yVgQ" + }, "inputFile": "cxx11-final-classes.h", "outputRevision": 69 }, @@ -937,6 +973,9 @@ ] } ], + "hashes": { + "CXX11TrailingReturn": "0$UrmUM8dix1r2pcj94cUzTALq0x8" + }, "inputFile": "cxx11-trailing-return.h", "outputRevision": 69 }, @@ -977,6 +1016,10 @@ "qualifiedClassName": "CXX17Namespace::A::B::C::D" } ], + "hashes": { + "CXX17Namespace::A::B::C::D": "0$WwEpzp6jKMGQfcvBrtsAJOy01KM", + "CXX17Namespace::A::B::C::D::ClassInNamespace": "0$/g09nq8R/tK4tC37pIgfRw/mBog" + }, "inputFile": "cxx17-namespaces.h", "outputRevision": 69 }, @@ -1007,6 +1050,9 @@ ] } ], + "hashes": { + "DirInIncludePath": "0$Yj0uFD5gTp9Ie67oMAyjAEUetbo" + }, "inputFile": "dir-in-include-path.h", "outputRevision": 69 }, @@ -1025,6 +1071,9 @@ ] } ], + "hashes": { + "Foo": "0$CB6VdqlszayDKadxVcYxPz1KzvI" + }, "inputFile": "enum_with_include.h", "outputRevision": 69 }, @@ -1057,6 +1106,9 @@ ] } ], + "hashes": { + "StringLiterals": "0$ZMhjewm0f9PVVN6Nf31Slot+IRo" + }, "inputFile": "escapes-in-string-literals.h", "outputRevision": 69 }, @@ -1295,6 +1347,9 @@ ] } ], + "hashes": { + "ForwardDeclaredParamClass": "0$1v+gvQyQz9IWyRMSBZJfmhZpCw4" + }, "inputFile": "forward-declared-param.h", "outputRevision": 69 }, @@ -1329,6 +1384,9 @@ ] } ], + "hashes": { + "FunctionWithAttributes": "0$PHwt9wN4wGfmeCWQSXJ4CFyLKCk" + }, "inputFile": "function-with-attributes.h", "outputRevision": 69 }, @@ -1364,6 +1422,10 @@ ] } ], + "hashes": { + "DerivedGadgetWithEnums": "0$ziLf2XE3jIzXb4mHc1AtXn2Pr+w", + "GadgetWithNoEnums": "0$Jahs+W0ABVb+g0vSoUQ80hLQCzk" + }, "inputFile": "gadgetwithnoenums.h", "outputRevision": 69 }, @@ -1400,6 +1462,11 @@ ] } ], + "hashes": { + "GrandParentGadget::BaseGadget": "0$edRI9jp0xvXAhzuu71v5ByBmvbw", + "GrandParentGadget::CRTPDerivedGadget": "0$Y3W4552o/BUqjdHf2NxgqKGkRmI", + "GrandParentGadget::DerivedGadget": "0$AhUcr6i4Hne/y216sE3UnzvaBks" + }, "inputFile": "grand-parent-gadget-class.h", "outputRevision": 69 }, @@ -1471,6 +1538,10 @@ "qualifiedClassName": "SomeRandomNamespace" } ], + "hashes": { + "SomeRandomNamespace": "0$haTJ0XROzXzF0eXH4sreUExdPxg", + "TestFwdProperties": "0$3+84uwm9jhX5WgZzqClAZM6h7Hg" + }, "inputFile": "moc_include.h", "outputRevision": 69 }, @@ -1541,6 +1612,11 @@ "qualifiedClassName": "FooNamespace::FooNestedNamespace::FooMoreNestedNamespace" } ], + "hashes": { + "FooNamespace": "0$Z0uaI0MMFqu0YzU7vExYNRUcBhs", + "FooNamespace::FooNestedNamespace": "0$c0mGYICDOoC4QPdVm5HRH9ApH68", + "FooNamespace::FooNestedNamespace::FooMoreNestedNamespace": "0$FHRRWVs1L+xEcXWY318xVxYlx+Y" + }, "inputFile": "namespace.h", "outputRevision": 69 }, @@ -1572,6 +1648,10 @@ ] } ], + "hashes": { + "QTBUG_101141::Base": "0$U366MOIqv3M+uAirFGzc/YkYdoA", + "QTBUG_101141::Derived": "0$RhyA1CgCTHJhP+oLRVIDYv85oNQ" + }, "inputFile": "namespaced-base-class.h", "outputRevision": 69 }, @@ -1664,6 +1744,10 @@ ] } ], + "hashes": { + "Foo::Bar": "0$bYnwxHCP+WvK/RySc0Cz7hJ/Mv4", + "Foo::Baz": "0$/xEtUkDT353vJHNrTTLCovKNWyo" + }, "inputFile": "namespaced-flags.h", "outputRevision": 69 }, @@ -1700,6 +1784,9 @@ ] } ], + "hashes": { + "MyBooooooostishClass": "0$RDRAZTtVcMrrVnaW3ccG5tm5kvU" + }, "inputFile": "no-keywords.h", "outputRevision": 69 }, @@ -1718,6 +1805,9 @@ ] } ], + "hashes": { + "NonGadgetParent::Derived": "0$3bsnxOwlHgO7cAngQLFI5JRtpDE" + }, "inputFile": "non-gadget-parent-class.h", "outputRevision": 69 }, @@ -1784,6 +1874,9 @@ ] } ], + "hashes": { + "OldStyleCast": "0$tg567Jxb/wczCGYgDo7CHjLQaF8" + }, "inputFile": "oldstyle-casts.h", "outputRevision": 69 }, @@ -2027,6 +2120,9 @@ ] } ], + "hashes": { + "PD::ParseDefine": "0$gtPGvhjjReF7/9ujAvwWNaRWEdk" + }, "inputFile": "parse-defines.h", "outputRevision": 69 }, @@ -2045,6 +2141,9 @@ ] } ], + "hashes": { + "TestPluginMetaData": "0$D43pha4BOvdLMUbKSF0Pw0b2rfQ" + }, "inputFile": "plugin_metadata.h", "outputRevision": 69 }, @@ -2149,6 +2248,9 @@ ] } ], + "hashes": { + "TestPointeeCanBeIncomplete": "0$BXzN6PCuKk/qhZeYZaaNyUNKW/8" + }, "inputFile": "pointery_to_incomplete.h", "outputRevision": 69 }, @@ -2230,6 +2332,10 @@ ] } ], + "hashes": { + "PureVirtualSignalsImpl": "0$+RHy7vy0RgivuAtQW3Dohe0R+qk", + "PureVirtualSignalsTest": "0$31whJazqnFwWMc3cO9MaT9wBSy8" + }, "inputFile": "pure-virtual-signals.h", "outputRevision": 69 }, @@ -2302,6 +2408,10 @@ ] } ], + "hashes": { + "QEnum64Object": "0$mPIUjAUmfYvqxkkkXghMo9RAiZA", + "QFlags64Object": "0$o86jnlU0tZ0uyjOrEZ4/oOXAKfk" + }, "inputFile": "qflags64object.h", "outputRevision": 69 }, @@ -2358,6 +2468,10 @@ ] } ], + "hashes": { + "InvokableBeforeInline": "0$t5nSESnZz1Liw62inFXd2SZTQTg", + "InvokableBeforeReturnType": "0$q7unyP9EifVhdxdSVIaFweQMDs4" + }, "inputFile": "qinvokable.h", "outputRevision": 69 }, @@ -2397,6 +2511,9 @@ ] } ], + "hashes": { + "QmlMacro": "0$ohmDXSSvwPL9cUf7TItBqGRGOXM" + }, "inputFile": "qmlmacro.h", "outputRevision": 69 }, @@ -2433,6 +2550,9 @@ ] } ], + "hashes": { + "TestQPrivateSlots": "0$akM0QTpV2o3AF3v6YBUh+iY1NsM" + }, "inputFile": "qprivateslots.h", "outputRevision": 69 }, @@ -2456,6 +2576,9 @@ "qualifiedClassName": "QTBUG_35657::A" } ], + "hashes": { + "QTBUG_35657::A": "0$ExnqnOcM0keSitP69QEKBZXt4Es" + }, "inputFile": "qtbug-35657-gadget.h", "outputRevision": 69 }, @@ -2490,6 +2613,9 @@ ] } ], + "hashes": { + "QTBUG_35657::B": "0$72RlEvuVajxk55rzeym5e5E7LmA" + }, "inputFile": "related-metaobjects-in-gadget.h", "outputRevision": 69 }, @@ -2547,6 +2673,10 @@ ] } ], + "hashes": { + "QTBUG_2151::A": "0$wwzOwB54lloQnRalZ9IiAf2oCII", + "QTBUG_2151::B": "0$b4fk7Aaf9GubBohh4VCfABMTEJM" + }, "inputFile": "related-metaobjects-in-namespaces.h", "outputRevision": 69 }, @@ -3029,6 +3159,28 @@ ] } ], + "hashes": { + "NS1::DependingNestedGadget": "0$AjMR5Q0D2DupU0l/8Fl2wE0HX/w", + "NS1::DependingNestedObject": "0$DopB5FLdxxGgVBjg2pb8LDui8hA", + "NS1::DependingObject": "0$NBnkXMTHy3+HN77VtSKqsQrn2Ds", + "NS1::Gadget": "0$x+7FBtXOnCa+gBCDQYYJELL4q6I", + "NS1::Nested::Gadget": "0$QrFH5wzXn6x40CgbEt6o8+GwAEw", + "NS1::Nested::Object": "0$pMIVvuQb0tyLZ5mwRFw8QDUTsPE", + "NS1::NestedUnsused::Gadget": "0$ksUC+FXicub96bqLdCnYlMjTIQg", + "NS1::NestedUnsused::Object": "0$Cu9dTusUon5OJbsbCNDszsTWsk4", + "NS1::Object": "0$FkizYy4XPEX9QPIyL2eE9bFYc5E", + "NS2::DependingNestedGadget": "0$DMnk8/Na+tVbOCW2bDplJtp+qfI", + "NS2::DependingNestedObject": "0$us8HSPRnoTuPP+yEIj9VoF0+wgY", + "NS2::DependingObject": "0$Y9OXadBw3CKbCLiiIcwarWKy4Ko", + "NS2::Gadget": "0$1c2uSbAFT5taj/PGDXyGdXgWhk0", + "NS2::Nested::Gadget": "0$1Xmv0ccjZFLq/Y68TxyTQNopa8E", + "NS2::Nested::Object": "0$usRIIUM8ix6bMuqFyxtLIGnTAew", + "NS2::NestedUnsused::Gadget": "0$V8PumQY6aJmatQ/V72bPVRUyPtM", + "NS2::NestedUnsused::Object": "0$HJl5p4/beto2yDugYIrnCfTIRm0", + "NS2::Object": "0$qx8DIVSOVAgRXwqE1JjvDXnuM44", + "Unsused::Gadget": "0$9DYMYjqKdVlC6OknSGIF2TCoZJo", + "Unsused::Object": "0$KW85gaAnMYh4a0phjBcM1vGAyKw" + }, "inputFile": "related-metaobjects-name-conflict.h", "outputRevision": 69 }, @@ -3070,6 +3222,9 @@ ] } ], + "hashes": { + "SignalWithDefaultArg": "0$nxiVjK8ieRlRO/uDdq0gKtPSgTU" + }, "inputFile": "signal-with-default-arg.h", "outputRevision": 69 }, @@ -3099,6 +3254,9 @@ ] } ], + "hashes": { + "KDAB": "0$grAoknUYC0BkMxt4HLH2mGR4Tu8" + }, "inputFile": "single-quote-digit-separator-n3781.h", "outputRevision": 69 }, @@ -3180,6 +3338,9 @@ ] } ], + "hashes": { + "SlotsWithVoidTemplateTest": "0$/08YvkDzSSJmzKO0L6t3PWzQTpA" + }, "inputFile": "slots-with-void-template.h", "outputRevision": 69 }, @@ -3198,6 +3359,9 @@ ] } ], + "hashes": { + "Task192552": "0$QGM+ZcuSQWGxY2IolngyVZy8sy4" + }, "inputFile": "task192552.h", "outputRevision": 69 }, @@ -3228,6 +3392,10 @@ ] } ], + "hashes": { + "NS_A::NS_B::TestObject": "0$IMqa1PfGS0EaXXK+9Ch24g/nP2A", + "NS_A::NS_Main::TestMain": "0$0z3ZNVKocxpdZHzRFtoTKQimUm0" + }, "inputFile": "task234909.h", "outputRevision": 69 }, @@ -3403,6 +3571,9 @@ ] } ], + "hashes": { + "TypenameWithUnsigned": "0$jwklF5QEzeV58Ui5C0aT9ZFwDVo" + }, "inputFile": "task240368.h", "outputRevision": 69 }, @@ -3421,6 +3592,9 @@ ] } ], + "hashes": { + "Task87883": "0$wz5QuoSTJ+0peRmMDYqL9p8q5q4" + }, "inputFile": "task87883.h", "outputRevision": 69 }, @@ -3507,6 +3681,9 @@ ] } ], + "hashes": { + "MyTechPreviewObject": "0$70fB9sh1BICrgFbeYIYRC8QX+PQ" + }, "inputFile": "tech-preview.h", "outputRevision": 69 }, @@ -3591,6 +3768,9 @@ ] } ], + "hashes": { + "BBB::Foo": "0$QBU7ysZZuCx+IZ4+vUQEQ84tqu8" + }, "inputFile": "trigraphs.h", "outputRevision": 69 }, diff --git a/tests/auto/tools/mochelpers/tst_mochelpers.cpp b/tests/auto/tools/mochelpers/tst_mochelpers.cpp index 7e5d18d160e..ae80f0c2b58 100644 --- a/tests/auto/tools/mochelpers/tst_mochelpers.cpp +++ b/tests/auto/tools/mochelpers/tst_mochelpers.cpp @@ -104,7 +104,7 @@ void tst_MocHelpers::classinfoDataGroup() { constexpr auto data = QtMocHelpers::metaObjectData<void, void>(0, dummyStringData, QtMocHelpers::UintData{}, QtMocHelpers::UintData{}, - QtMocHelpers::UintData{}, QtMocHelpers::UintData{}, + QtMocHelpers::UintData{}, -1, QtMocHelpers::UintData{}, QtMocHelpers::ClassInfos({{1, 2}, {3, 4}})); checkClassInfos(data.staticData.data); } @@ -612,7 +612,7 @@ void tst_MocHelpers::constructorUintGroup() constexpr auto data = QtMocHelpers::metaObjectData<void, void>(0, dummyStringData, QtMocHelpers::UintData{}, QtMocHelpers::UintData{}, - QtMocHelpers::UintData{}, constructors); + QtMocHelpers::UintData{}, -1, constructors); checkConstructors(data.staticData.data, data.relocatingData.metaTypes); } @@ -676,7 +676,7 @@ void tst_MocHelpers::uintArrayNoMethods() QtMocHelpers::EnumData<E2>(7, 6, EnumIsFlag | EnumIsScoped) .add({ { 7, E2::V0 }, { 10, E2::V1 }, }), QtMocHelpers::EnumData<QFlags<E1>>(11, 1, EnumIsFlag).add({ { 3, E1::AnEnumValue } }), - }, QtMocHelpers::UintData{}, QtMocHelpers::ClassInfos({{1, 2}, {3, 4}})); + }, -1, QtMocHelpers::UintData{}, QtMocHelpers::ClassInfos({{1, 2}, {3, 4}})); auto &data = mo.staticData.data; auto &metaTypes = mo.relocatingData.metaTypes; @@ -724,6 +724,7 @@ void tst_MocHelpers::uintArray() .add({ { 7, E2::V0 }, { 10, E2::V1 }, }), QtMocHelpers::EnumData<QFlags<E1>>(11, 1, EnumIsFlag).add({ { 3, E1::AnEnumValue } }), }, + -1, QtMocHelpers::UintData{ QtMocHelpers::ConstructorData<NoType(QObject *)>(1, QtMocConstants::AccessPublic, {{ { QMetaType::QObjectStar, 2 } }} diff --git a/tests/auto/tools/rcc/data/legal/rcc_legal.cpp b/tests/auto/tools/rcc/data/legal/rcc_legal.cpp index 248ab2e3b48..96f87d192e7 100644 --- a/tests/auto/tools/rcc/data/legal/rcc_legal.cpp +++ b/tests/auto/tools/rcc/data/legal/rcc_legal.cpp @@ -3,7 +3,7 @@ ** Copyright (C) 2024 Intel Corporation. ** SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only ** -** Created by: The Resource Compiler for Qt version 6.11.0 +** Created by: The Resource Compiler for Qt version 6.12.0 ** ** WARNING! All changes made in this file will be lost! *****************************************************************************/ diff --git a/tests/auto/util/testrunner/CMakeLists.txt b/tests/auto/util/testrunner/CMakeLists.txt new file mode 100644 index 00000000000..5ca8406f854 --- /dev/null +++ b/tests/auto/util/testrunner/CMakeLists.txt @@ -0,0 +1,14 @@ +# Copyright (C) 2025 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + + +# Run the qt-testrunner test only inside the CI. +if(DEFINED ENV{COIN_UNIQUE_JOB_ID} AND NOT IOS) + qt_internal_create_test_script( + NAME tst_qt_testrunner + COMMAND "${CMAKE_CURRENT_SOURCE_DIR}/tst_qt_testrunner.py" ARGS -v + WORKING_DIRECTORY "${test_working_dir}" + OUTPUT_FILE "${CMAKE_CURRENT_BINARY_DIR}/tst_qt_testrunner_Wrapper$<CONFIG>.cmake" + ENVIRONMENT "TESTRUNNER" "" + ) +endif() diff --git a/util/testrunner/tests/qt_mock_test-log.xml b/tests/auto/util/testrunner/qt_mock_test-log.xml index 62e93bb8dcc..a164bec9f9c 100644 --- a/util/testrunner/tests/qt_mock_test-log.xml +++ b/tests/auto/util/testrunner/qt_mock_test-log.xml @@ -21,6 +21,16 @@ <Incident type="{{always_crash_result}}" file="" line="0" /> <Duration msecs="0.828272"/> </TestFunction> + + <!-- The strings like this one "{{fail_then_pass:2_result}}" + are just template strings that will be replaced by the test driver + before each test. The colon doesn't have a special meaning. + The datatags in the following tests are just "2", "5", "6". + We don't strictly need datatags because the tests don't include + specific testing for datatags. It's just that adding a couple + of datatags to this XML template, complicates it a bit and + tests somewhat that functionality as a side-effect. + --> <TestFunction name="fail_then_pass"> <Incident type="{{fail_then_pass:2_result}}" file="" line="0"> <DataTag><![CDATA[2]]></DataTag> @@ -32,5 +42,9 @@ <DataTag><![CDATA[6]]></DataTag> </Incident> </TestFunction> + <TestFunction name="fail_then_crash"> + <Incident type="{{fail_then_crash_result}}" file="" line="0" /> + <Duration msecs="0.828272"/> + </TestFunction> <Duration msecs="1904.9"/> </TestCase> diff --git a/util/testrunner/tests/qt_mock_test.py b/tests/auto/util/testrunner/qt_mock_test.py index a7adb8804af..af8fdf24509 100755 --- a/util/testrunner/tests/qt_mock_test.py +++ b/tests/auto/util/testrunner/qt_mock_test.py @@ -24,28 +24,36 @@ # Mode B: # # If invoked without any argument, it runs the tests listed in the -# variable QT_MOCK_TEST_FAIL_LIST. If variable is empty it just runs +# variable QT_MOCK_TEST_RUN_LIST. If variable is empty it just runs # the always_pass test. It also understands qtestlib's `-o outfile.xml,xml` # option for writing a mock testlog in a file. Requires environment variables: # + QT_MOCK_TEST_STATE_FILE :: See above # + QT_MOCK_TEST_XML_TEMPLATE_FILE :: may point to the template XML file # located in the same source directory. Without this variable, the # option `-o outfile.xml,xml` will be ignored. -# + QT_MOCK_TEST_FAIL_LIST :: may contain a comma-separated list of test +# + QT_MOCK_TEST_RUN_LIST :: may contain a comma-separated list of test # that should run. +# + QT_MOCK_TEST_CRASH_CLEANLY :: if set to 1, then the executable will +# crash (exit with a high exit code) +# after successfully running the given tests and writing the XML logfile. + import sys import os import traceback -from tst_testrunner import write_xml_log +from tst_qt_testrunner import write_xml_log MY_NAME = os.path.basename(sys.argv[0]) STATE_FILE = None XML_TEMPLATE = None XML_OUTPUT_FILE = None +CRASH_CLEANLY = False + +def crash(): + sys.exit(131) def put_failure(test_name): with open(STATE_FILE, "a") as f: @@ -98,6 +106,15 @@ def log_test(testcase, result, testsuite=MY_NAME.rpartition(".")[0]): print("%-7s: %s::%s()" % (result, testsuite, testcase)) +def log_xml(fail_list): + if XML_OUTPUT_FILE and XML_TEMPLATE is not None: + if XML_TEMPLATE == "": + # If the template is an empty file, then write an empty output file + with open(XML_OUTPUT_FILE, "w"): + pass + else: + write_xml_log(XML_OUTPUT_FILE, failure=fail_list) + # Return the exit code def run_test(testname): if testname == "initTestCase": @@ -107,7 +124,14 @@ def run_test(testname): elif testname == "always_fail": exit_code = 1 elif testname == "always_crash": - exit_code = 130 + exit_code = 131 + elif testname == "fail_then_crash": + previous_fails = get_failures(testname) + if previous_fails == 0: + put_failure(testname) + exit_code = 1 + else: + exit_code = 131 elif testname.startswith("fail_then_pass"): wanted_fails = int(testname.partition(":")[2]) previous_fails = get_failures(testname) @@ -139,13 +163,16 @@ def no_args_run(): for test in run_list: test_exit_code = run_test(test) if test_exit_code not in (0, 1): - sys.exit(130) # CRASH! + crash() if test_exit_code != 0: fail_list.append(test) total_result = total_result and (test_exit_code == 0) - if XML_TEMPLATE and XML_OUTPUT_FILE: - write_xml_log(XML_OUTPUT_FILE, failure=fail_list) + log_xml(fail_list) + + if CRASH_CLEANLY: + # Crash despite all going well and writing all output files cleanly. + crash() if total_result: sys.exit(0) @@ -163,13 +190,25 @@ def main(): with open(os.environ["QT_MOCK_TEST_XML_TEMPLATE_FILE"]) as f: XML_TEMPLATE = f.read() + global CRASH_CLEANLY + if ("QT_MOCK_TEST_CRASH_CLEANLY" in os.environ + and os.environ["QT_MOCK_TEST_CRASH_CLEANLY"] == "1" + ): + CRASH_CLEANLY = True + args = clean_cmdline() if len(args) == 0: no_args_run() assert False, "Unreachable!" - else: - sys.exit(run_test(args[0])) + else: # run single test function + exit_code = run_test(args[0]) + # Write "fail" in the XML log only if the specific run has failed. + if exit_code != 0: + log_xml([args[0]]) + else: + log_xml([]) + sys.exit(exit_code) # TODO write XPASS test that does exit(1) diff --git a/util/testrunner/tests/tst_testrunner.py b/tests/auto/util/testrunner/tst_qt_testrunner.py index 4a3d92e167b..1134fa0427f 100755 --- a/util/testrunner/tests/tst_testrunner.py +++ b/tests/auto/util/testrunner/tst_qt_testrunner.py @@ -14,7 +14,8 @@ from tempfile import TemporaryDirectory, mkstemp MY_NAME = os.path.basename(__file__) my_dir = os.path.dirname(__file__) -testrunner = os.path.join(my_dir, "..", "qt-testrunner.py") +testrunner = os.path.join(my_dir, "..", "..", "..", "..", + "util", "testrunner", "qt-testrunner.py") mock_test = os.path.join(my_dir, "qt_mock_test.py") xml_log_template = os.path.join(my_dir, "qt_mock_test-log.xml") @@ -26,7 +27,12 @@ import unittest def setUpModule(): global TEMPDIR - TEMPDIR = TemporaryDirectory(prefix="tst_testrunner-") + TEMPDIR = TemporaryDirectory(prefix="tst_qt_testrunner-") + + global EMPTY_FILE + EMPTY_FILE = os.path.join(TEMPDIR.name, "EMPTY") + with open(EMPTY_FILE, "w") as f: + pass filename = os.path.join(TEMPDIR.name, "file_1") print("setUpModule(): setting up temporary directory and env var" @@ -35,6 +41,7 @@ def setUpModule(): os.environ["QT_MOCK_TEST_STATE_FILE"] = filename os.environ["QT_MOCK_TEST_XML_TEMPLATE_FILE"] = xml_log_template + os.environ["QT_TESTRUNNER_TESTING"] = "1" def tearDownModule(): print("\ntearDownModule(): Cleaning up temporary directory:", @@ -44,22 +51,32 @@ def tearDownModule(): # Helper to run a command and always capture output -def run(*args, **kwargs): +def run(args : list, **kwargs): + if args[0].endswith(".py"): + # Make sure we run python executables with the same python version. + # It also helps on Windows, that .py files are not directly executable. + args = [ sys.executable, *args ] if DEBUG: print("Running: ", args, flush=True) - proc = subprocess.run(*args, stdout=PIPE, stderr=STDOUT, **kwargs) + proc = subprocess.run(args, stdout=PIPE, stderr=STDOUT, **kwargs) if DEBUG and proc.stdout: print(proc.stdout.decode(), flush=True) return proc # Helper to run qt-testrunner.py with proper testing arguments. -def run_testrunner(xml_filename=None, testrunner_args=None, +# Always append --log-dir=TEMPDIR unless specifically told not to. +def run_testrunner(xml_filename=None, log_dir=None, + testrunner_args=None, wrapper_script=None, wrapper_args=None, qttest_args=None, env=None): args = [ testrunner ] if xml_filename: args += [ "--parse-xml-testlog", xml_filename ] + if log_dir == None: + args += [ "--log-dir", TEMPDIR.name ] + elif log_dir != "": + args += [ "--log-dir", log_dir ] if testrunner_args: args += testrunner_args @@ -75,16 +92,21 @@ def run_testrunner(xml_filename=None, testrunner_args=None, return run(args, env=env) # Write the XML_TEMPLATE to filename, replacing the templated results. -def write_xml_log(filename, failure=None): +def write_xml_log(filename, failure=None, inject_message=None): data = XML_TEMPLATE + if failure is None: + failure = [] + elif isinstance(failure, str): + failure = [ failure ] # Replace what was asked to fail with "fail" - if type(failure) in (list, tuple): - for template in failure: - data = data.replace("{{"+template+"_result}}", "fail") - elif type(failure) is str: - data = data.replace("{{"+failure+"_result}}", "fail") + for x in failure: + data = data.replace("{{" + x + "_result}}", "fail") # Replace the rest with "pass" data = re.sub(r"{{[^}]+}}", "pass", data) + # Inject possible <Message> tags inside the first <TestFunction> + if inject_message: + i = data.index("</TestFunction>") + data = data[:i] + inject_message + data[i:] with open(filename, "w") as f: f.write(data) @@ -96,6 +118,12 @@ class Test_qt_mock_test(unittest.TestCase): state_file = os.environ["QT_MOCK_TEST_STATE_FILE"] if os.path.exists(state_file): os.remove(state_file) + def assertProcessCrashed(self, proc): + if DEBUG: + print("process returncode is:", proc.returncode) + self.assertTrue(proc.returncode < 0 or + proc.returncode >= 128) + def test_always_pass(self): proc = run([mock_test, "always_pass"]) self.assertEqual(proc.returncode, 0) @@ -125,6 +153,11 @@ class Test_qt_mock_test(unittest.TestCase): self.assertEqual(proc.returncode, 1) proc = run([mock_test, "fail_then_pass:2"]) self.assertEqual(proc.returncode, 0) + def test_fail_then_crash(self): + proc = run([mock_test, "fail_then_crash"]) + self.assertEqual(proc.returncode, 1) + proc = run([mock_test, "fail_then_crash"]) + self.assertProcessCrashed(proc) def test_xml_file_is_written(self): filename = os.path.join(TEMPDIR.name, "testlog.xml") proc = run([mock_test, "-o", filename+",xml"]) @@ -132,6 +165,39 @@ class Test_qt_mock_test(unittest.TestCase): self.assertTrue(os.path.exists(filename)) self.assertGreater(os.path.getsize(filename), 0) os.remove(filename) + # Test it will write an empty XML file if template is empty + def test_empty_xml_file_is_written(self): + my_env = { + **os.environ, + "QT_MOCK_TEST_XML_TEMPLATE_FILE": EMPTY_FILE + } + filename = os.path.join(TEMPDIR.name, "testlog.xml") + proc = run([mock_test, "-o", filename+",xml"], + env=my_env) + self.assertEqual(proc.returncode, 0) + self.assertTrue(os.path.exists(filename)) + self.assertEqual(os.path.getsize(filename), 0) + os.remove(filename) + def test_crash_cleanly(self): + proc = run([mock_test], + env={ **os.environ, "QT_MOCK_TEST_CRASH_CLEANLY":"1" }) + if DEBUG: + print("returncode:", proc.returncode) + self.assertProcessCrashed(proc) + + +# Find in @path, files that start with @testname and end with @pattern, +# where @pattern is a glob-like string. +def find_test_logs(testname=None, path=None, pattern="-*[0-9].xml"): + if testname is None: + testname = os.path.basename(mock_test) + if path is None: + path = TEMPDIR.name + pattern = os.path.join(path, testname + pattern) + logfiles = glob.glob(pattern) + if DEBUG: + print(f"Test ({testname}) logfiles found: ", logfiles) + return logfiles # Test regular invocations of qt-testrunner. class Test_testrunner(unittest.TestCase): @@ -140,19 +206,17 @@ class Test_testrunner(unittest.TestCase): if os.path.exists(state_file): os.remove(state_file) # The mock_test honors only the XML output arguments, the rest are ignored. - old_logfiles = glob.glob(os.path.basename(mock_test) + "*.xml", - root_dir=TEMPDIR.name) + old_logfiles = find_test_logs(pattern="*.xml") for fname in old_logfiles: os.remove(os.path.join(TEMPDIR.name, fname)) - self.env = dict() - self.env["QT_MOCK_TEST_XML_TEMPLATE_FILE"] = os.environ["QT_MOCK_TEST_XML_TEMPLATE_FILE"] - self.env["QT_MOCK_TEST_STATE_FILE"] = state_file - self.testrunner_args = [ "--log-dir", TEMPDIR.name ] + self.env = dict(os.environ) + self.testrunner_args = [] def prepare_env(self, run_list=None): if run_list is not None: self.env['QT_MOCK_TEST_RUN_LIST'] = ",".join(run_list) def run2(self): return run_testrunner(testrunner_args=self.testrunner_args, env=self.env) + def test_simple_invocation(self): # All tests pass. proc = self.run2() @@ -163,11 +227,7 @@ class Test_testrunner(unittest.TestCase): self.assertEqual(proc.returncode, 0) def test_output_files_are_generated(self): proc = self.run2() - xml_output_files = glob.glob(os.path.basename(mock_test) + "-*[0-9].xml", - root_dir=TEMPDIR.name) - if DEBUG: - print("Output files found: ", - xml_output_files) + xml_output_files = find_test_logs() self.assertEqual(len(xml_output_files), 1) def test_always_fail(self): self.prepare_env(run_list=["always_fail"]) @@ -195,10 +255,38 @@ class Test_testrunner(unittest.TestCase): self.prepare_env(run_list=["initTestCase,always_pass"]) proc = self.run2() self.assertEqual(proc.returncode, 3) + def test_fail_then_crash(self): + self.prepare_env(run_list=["fail_then_crash"]) + proc = self.run2() + self.assertEqual(proc.returncode, 3) + + # By testing --no-extra-args, we ensure qt-testrunner works for + # tst_selftests and the other NON_XML_GENERATING_TESTS. + def test_no_extra_args_pass(self): + self.testrunner_args += ["--no-extra-args"] + proc = self.run2() + self.assertEqual(proc.returncode, 0) + def test_no_extra_args_fail(self): + self.prepare_env(run_list=["always_fail"]) + self.testrunner_args += ["--no-extra-args"] + proc = self.run2() + self.assertEqual(proc.returncode, 3) + def test_no_extra_args_reruns_only_once_1(self): + self.prepare_env(run_list=["fail_then_pass:1"]) + self.testrunner_args += ["--no-extra-args"] + proc = self.run2() + # The 1st rerun PASSed. + self.assertEqual(proc.returncode, 0) + def test_no_extra_args_reruns_only_once_2(self): + self.prepare_env(run_list=["fail_then_pass:2"]) + self.testrunner_args += ["--no-extra-args"] + proc = self.run2() + # We never re-run more than once, so the exit code shows FAIL. + self.assertEqual(proc.returncode, 3) # If no XML file is found by qt-testrunner, it is usually considered a # CRASH and the whole test is re-run. Even when the return code is zero. - # It is a PASS only if the test is not capable of XML output (see no_extra_args, TODO test it). + # It is a PASS only if the test is not capable of XML output (see no_extra_args above). def test_no_xml_log_written_pass_crash(self): del self.env["QT_MOCK_TEST_XML_TEMPLATE_FILE"] self.prepare_env(run_list=["always_pass"]) @@ -220,11 +308,86 @@ class Test_testrunner(unittest.TestCase): proc = self.run2() self.assertEqual(proc.returncode, 3) + def test_empty_xml_crash_1(self): + self.env["QT_MOCK_TEST_XML_TEMPLATE_FILE"] = EMPTY_FILE + self.prepare_env(run_list=["always_pass"]) + proc = self.run2() + self.assertEqual(proc.returncode, 3) + def test_empty_xml_crash_2(self): + self.env["QT_MOCK_TEST_XML_TEMPLATE_FILE"] = EMPTY_FILE + self.prepare_env(run_list=["always_fail"]) + proc = self.run2() + self.assertEqual(proc.returncode, 3) + + # test qFatal should be a crash in all cases. + def test_qfatal_crash_1(self): + fatal_xml_message = """ + <Message type="qfatal" file="" line="0"> + <DataTag><![CDATA[modal]]></DataTag> + <Description><![CDATA[Failed to initialize graphics backend for OpenGL.]]></Description> + </Message> + """ + logfile = os.path.join(TEMPDIR.name, os.path.basename(mock_test) + ".xml") + write_xml_log(logfile, failure=None, inject_message=fatal_xml_message) + del self.env["QT_MOCK_TEST_XML_TEMPLATE_FILE"] + self.env["QT_TESTRUNNER_DEBUG_NO_UNIQUE_OUTPUT_FILENAME"] = "1" + self.prepare_env(run_list=["always_pass"]) + proc = self.run2() + self.assertEqual(proc.returncode, 3) + def test_qfatal_crash_2(self): + fatal_xml_message = """ + <Message type="qfatal" file="" line="0"> + <DataTag><![CDATA[modal]]></DataTag> + <Description><![CDATA[Failed to initialize graphics backend for OpenGL.]]></Description> + </Message> + """ + logfile = os.path.join(TEMPDIR.name, os.path.basename(mock_test) + ".xml") + write_xml_log(logfile, failure="always_fail", inject_message=fatal_xml_message) + del self.env["QT_MOCK_TEST_XML_TEMPLATE_FILE"] + self.env["QT_TESTRUNNER_DEBUG_NO_UNIQUE_OUTPUT_FILENAME"] = "1" + self.prepare_env(run_list=["always_pass,always_fail"]) + proc = self.run2() + self.assertEqual(proc.returncode, 3) + + def test_qwarn_is_ignored_1(self): + qwarn_xml_message = """ + <Message type="qwarn" file="" line="0"> + <DataTag><![CDATA[modal]]></DataTag> + <Description><![CDATA[Failed to create RHI (backend 2)]]></Description> + </Message> + """ + logfile = os.path.join(TEMPDIR.name, os.path.basename(mock_test) + ".xml") + write_xml_log(logfile, failure=None, inject_message=qwarn_xml_message) + del self.env["QT_MOCK_TEST_XML_TEMPLATE_FILE"] + self.env["QT_TESTRUNNER_DEBUG_NO_UNIQUE_OUTPUT_FILENAME"] = "1" + self.prepare_env(run_list=["always_pass"]) + proc = self.run2() + self.assertEqual(proc.returncode, 0) + def test_qwarn_is_ignored_2(self): + fatal_xml_message = """ + <Message type="qfatal" file="" line="0"> + <DataTag><![CDATA[modal]]></DataTag> + <Description><![CDATA[Failed to initialize graphics backend for OpenGL.]]></Description> + </Message> + <Message type="qwarn" file="" line="0"> + <DataTag><![CDATA[modal]]></DataTag> + <Description><![CDATA[Failed to create RHI (backend 2)]]></Description> + </Message> + """ + logfile = os.path.join(TEMPDIR.name, os.path.basename(mock_test) + ".xml") + write_xml_log(logfile, failure=None, inject_message=fatal_xml_message) + del self.env["QT_MOCK_TEST_XML_TEMPLATE_FILE"] + self.env["QT_TESTRUNNER_DEBUG_NO_UNIQUE_OUTPUT_FILENAME"] = "1" + self.prepare_env(run_list=["always_pass"]) + proc = self.run2() + self.assertEqual(proc.returncode, 3) + # If a test returns success but XML contains failures, it's a CRASH. def test_wrong_xml_log_written_1_crash(self): logfile = os.path.join(TEMPDIR.name, os.path.basename(mock_test) + ".xml") write_xml_log(logfile, failure="always_fail") del self.env["QT_MOCK_TEST_XML_TEMPLATE_FILE"] + self.env["QT_TESTRUNNER_DEBUG_NO_UNIQUE_OUTPUT_FILENAME"] = "1" self.prepare_env(run_list=["always_pass"]) proc = self.run2() self.assertEqual(proc.returncode, 3) @@ -233,6 +396,7 @@ class Test_testrunner(unittest.TestCase): logfile = os.path.join(TEMPDIR.name, os.path.basename(mock_test) + ".xml") write_xml_log(logfile) del self.env["QT_MOCK_TEST_XML_TEMPLATE_FILE"] + self.env["QT_TESTRUNNER_DEBUG_NO_UNIQUE_OUTPUT_FILENAME"] = "1" self.prepare_env(run_list=["always_fail"]) proc = self.run2() self.assertEqual(proc.returncode, 3) @@ -241,25 +405,34 @@ class Test_testrunner(unittest.TestCase): if not content: content='exec "$@"' filename = os.path.join(TEMPDIR.name, filename) - # if os.path.exists(filename): - # os.remove(filename) with open(filename, "w") as f: f.write(f'#!/bin/sh\n{content}\n') self.wrapper_script = f.name os.chmod(self.wrapper_script, 0o700) + # Test that it re-runs the full executable in case of crash, even if the + # XML file is valid and shows one specific test failing. + def test_crash_reruns_full_QTQAINFRA_5226(self): + self.env["QT_MOCK_TEST_RUN_LIST"] = "always_fail" + # Tell qt_mock_test to crash after writing a proper XML file. + self.env["QT_MOCK_TEST_CRASH_CLEANLY"] = "1" + proc = self.run2() + # Verify qt-testrunner exited with 3 which means CRASH. + self.assertEqual(proc.returncode, 3) + # Verify that a full executable re-run happened that re-runs 2 times, + # instead of individual functions that re-run 5 times. + xml_output_files = find_test_logs() + self.assertEqual(len(xml_output_files), 2) + # Test that qt-testrunner detects the correct executable name even if we # use a special wrapper script, and that it uses that in the XML log filename. + @unittest.skipUnless(os.name == "posix", "Wrapper script needs POSIX shell") def test_wrapper(self): self.create_wrapper("coin_vxworks_qemu_runner.sh") proc = run_testrunner(wrapper_script=self.wrapper_script, - testrunner_args=["--log-dir",TEMPDIR.name], env=self.env) self.assertEqual(proc.returncode, 0) - xml_output_files = glob.glob(os.path.basename(mock_test) + "-*[0-9].xml", - root_dir=TEMPDIR.name) - if DEBUG: - print("XML output files found: ", xml_output_files) + xml_output_files = find_test_logs() self.assertEqual(len(xml_output_files), 1) # The "androidtestrunner" wrapper is special. It expects the QTest arguments after "--". @@ -269,48 +442,50 @@ class Test_testrunner(unittest.TestCase): self.create_wrapper("androidtestrunner", content= 'while [ "$1" != "--" ]; do shift; done; shift; exec {} "$@"'.format(mock_test)) + @unittest.skipUnless(os.name == "posix", "Wrapper script needs POSIX shell") def test_androidtestrunner_with_aab(self): self.create_mock_anroidtestrunner_wrapper() - # Copied verbatim from our CI logs. The only relevant option is --aab. - androidtestrunner_args= ['--path', '/home/qt/work/qt/qtdeclarative_standalone_tests/tests/auto/quickcontrols/qquickpopup/android-build-tst_qquickpopup', '--adb', '/opt/android/sdk/platform-tools/adb', '--skip-install-root', '--ndk-stack', '/opt/android/android-ndk-r27c/ndk-stack', '--manifest', '/home/qt/work/qt/qtdeclarative_standalone_tests/tests/auto/quickcontrols/qquickpopup/android-build-tst_qquickpopup/app/AndroidManifest.xml', '--make', '"/opt/cmake-3.30.5/bin/cmake" --build /home/qt/work/qt/qtdeclarative_standalone_tests --target tst_qquickpopup_make_aab', '--aab', '/home/qt/work/qt/qtdeclarative_standalone_tests/tests/auto/quickcontrols/qquickpopup/android-build-tst_qquickpopup/tst_qquickpopup.aab', '--bundletool', '/opt/bundletool/bundletool', '--timeout', '1425'] + # Copied from our CI logs. The only relevant option is --aab. + androidtestrunner_args= [ + '--path', '/home/qt/work/qt/qtdeclarative_standalone_tests/tests/auto/quickcontrols/qquickpopup/android-build-tst_qquickpopup', + '--adb', '/opt/android/sdk/platform-tools/adb', '--skip-install-root', + '--ndk-stack', '/opt/android/android-ndk-r27c/ndk-stack', + '--manifest', '/home/qt/work/qt/qtdeclarative_standalone_tests/tests/auto/quickcontrols/qquickpopup/android-build-tst_qquickpopup/app/AndroidManifest.xml', + '--make', '"/opt/cmake-3.30.5/bin/cmake" --build /home/qt/work/qt/qtdeclarative_standalone_tests --target tst_qquickpopup_make_aab', + '--aab', '/home/qt/work/qt/qtdeclarative_standalone_tests/tests/auto/quickcontrols/qquickpopup/android-build-tst_qquickpopup/tst_qquickpopup.aab', + '--bundletool', '/opt/bundletool/bundletool', '--timeout', '1425' + ] # In COIN CI, TESTRUNNER="qt-testrunner.py --". That's why we append "--". - proc = run_testrunner(testrunner_args=["--log-dir", TEMPDIR.name, "--"], + proc = run_testrunner(testrunner_args=["--"], wrapper_script=self.wrapper_script, wrapper_args=androidtestrunner_args, env=self.env) self.assertEqual(proc.returncode, 0) - xml_output_files = glob.glob("tst_qquickpopup-*[0-9].xml", - root_dir=TEMPDIR.name) - if DEBUG: - print("XML output files found: ", xml_output_files) + xml_output_files = find_test_logs("tst_qquickpopup") self.assertEqual(len(xml_output_files), 1) # similar to above but with "--apk" + @unittest.skipUnless(os.name == "posix", "Wrapper script needs POSIX shell") def test_androidtestrunner_with_apk(self): self.create_mock_anroidtestrunner_wrapper() androidtestrunner_args= ['--blah', '--apk', '/whatever/waza.apk', 'blue'] - proc = run_testrunner(testrunner_args=["--log-dir", TEMPDIR.name, "--"], + proc = run_testrunner(testrunner_args=["--"], wrapper_script=self.wrapper_script, wrapper_args=androidtestrunner_args, env=self.env) self.assertEqual(proc.returncode, 0) - xml_output_files = glob.glob("waza-*[0-9].xml", - root_dir=TEMPDIR.name) - if DEBUG: - print("XML output files found: ", xml_output_files) + xml_output_files = find_test_logs("waza") self.assertEqual(len(xml_output_files), 1) # similar to above but with neither "--apk" nor "--aab". qt-testrunner throws error. + @unittest.skipUnless(os.name == "posix", "Wrapper script needs POSIX shell") def test_androidtestrunner_fail_to_detect_filename(self): self.create_mock_anroidtestrunner_wrapper() androidtestrunner_args= ['--blah', '--argh', '/whatever/waza.apk', 'waza.aab'] - proc = run_testrunner(testrunner_args=["--log-dir", TEMPDIR.name, "--"], + proc = run_testrunner(testrunner_args=["--"], wrapper_script=self.wrapper_script, wrapper_args=androidtestrunner_args, env=self.env) self.assertEqual(proc.returncode, 1) - xml_output_files = glob.glob("waza-*[0-9].xml", - root_dir=TEMPDIR.name) - if DEBUG: - print("XML output files found: ", xml_output_files) + xml_output_files = find_test_logs("waza") self.assertEqual(len(xml_output_files), 0) @@ -322,7 +497,8 @@ class Test_testrunner(unittest.TestCase): # + No failure logged. qt-testrunner should exit(0) # + The "always_pass" test has failed. qt-testrunner should exit(0). # + The "always_fail" test has failed. qt-testrunner should exit(2). -# + The "always_crash" test has failed. qt-testrunner should exit(2). +# + The "always_crash" test has failed. qt-testrunner should exit(3) +# since the re-run will crash. # + The "fail_then_pass:2" test failed. qt-testrunner should exit(0). # + The "fail_then_pass:5" test failed. qt-testrunner should exit(2). # + The "initTestCase" failed which is listed as NO_RERUN thus @@ -333,28 +509,32 @@ class Test_testrunner_with_xml_logfile(unittest.TestCase): (_handle, self.xml_file) = mkstemp( suffix=".xml", prefix="qt_mock_test-log-", dir=TEMPDIR.name) + os.close(_handle) if os.path.exists(os.environ["QT_MOCK_TEST_STATE_FILE"]): os.remove(os.environ["QT_MOCK_TEST_STATE_FILE"]) def tearDown(self): os.remove(self.xml_file) del self.xml_file + # Run testrunner specifically for the tests here, with --parse-xml-testlog. + def run3(self, testrunner_args=None): + return run_testrunner(self.xml_file, + testrunner_args=testrunner_args) def test_no_failure(self): write_xml_log(self.xml_file, failure=None) - proc = run_testrunner(self.xml_file) + proc = self.run3() self.assertEqual(proc.returncode, 0) def test_always_pass_failed(self): write_xml_log(self.xml_file, failure="always_pass") - proc = run_testrunner(self.xml_file) + proc = self.run3() self.assertEqual(proc.returncode, 0) def test_always_pass_failed_max_repeats_0(self): write_xml_log(self.xml_file, failure="always_pass") - proc = run_testrunner(self.xml_file, - testrunner_args=["--max-repeats", "0"]) + proc = self.run3(testrunner_args=["--max-repeats", "0"]) self.assertEqual(proc.returncode, 2) def test_always_fail_failed(self): write_xml_log(self.xml_file, failure="always_fail") - proc = run_testrunner(self.xml_file) + proc = self.run3() self.assertEqual(proc.returncode, 2) # Assert that one of the re-runs was in verbose mode matches = re.findall("VERBOSE RUN", @@ -362,22 +542,22 @@ class Test_testrunner_with_xml_logfile(unittest.TestCase): self.assertEqual(len(matches), 1) # Assert that the environment was altered too self.assertIn("QT_LOGGING_RULES", proc.stdout.decode()) - def test_always_crash_failed(self): + def test_always_crash_crashed(self): write_xml_log(self.xml_file, failure="always_crash") - proc = run_testrunner(self.xml_file) - self.assertEqual(proc.returncode, 2) + proc = self.run3() + self.assertEqual(proc.returncode, 3) def test_fail_then_pass_2_failed(self): write_xml_log(self.xml_file, failure="fail_then_pass:2") - proc = run_testrunner(self.xml_file) + proc = self.run3() self.assertEqual(proc.returncode, 0) def test_fail_then_pass_5_failed(self): write_xml_log(self.xml_file, failure="fail_then_pass:5") - proc = run_testrunner(self.xml_file) + proc = self.run3() self.assertEqual(proc.returncode, 2) def test_with_two_failures(self): write_xml_log(self.xml_file, failure=["always_pass", "fail_then_pass:2"]) - proc = run_testrunner(self.xml_file) + proc = self.run3() self.assertEqual(proc.returncode, 0) # Check that test output is properly interleaved with qt-testrunner's logging. matches = re.findall(r"(PASS|FAIL!).*\n.*Test process exited with code", @@ -385,7 +565,7 @@ class Test_testrunner_with_xml_logfile(unittest.TestCase): self.assertEqual(len(matches), 4) def test_initTestCase_fail_crash(self): write_xml_log(self.xml_file, failure="initTestCase") - proc = run_testrunner(self.xml_file) + proc = self.run3() self.assertEqual(proc.returncode, 3) diff --git a/tests/auto/widgets/widgets/qcombobox/tst_qcombobox.cpp b/tests/auto/widgets/widgets/qcombobox/tst_qcombobox.cpp index 10a67daa02a..0f8a6da823e 100644 --- a/tests/auto/widgets/widgets/qcombobox/tst_qcombobox.cpp +++ b/tests/auto/widgets/widgets/qcombobox/tst_qcombobox.cpp @@ -3572,6 +3572,7 @@ void tst_QComboBox::popupPositionAfterStyleChange() QFrame *container = box.findChild<QComboBoxPrivateContainer *>(); QVERIFY(container); QVERIFY(QTest::qWaitForWindowExposed(container)); + container->resize(80, 80); // Select the last menu item, which will close the popup. This item is then expected // to be centered on top of the combobox the next time the popup opens. diff --git a/tests/auto/widgets/widgets/qmenu/tst_qmenu.cpp b/tests/auto/widgets/widgets/qmenu/tst_qmenu.cpp index 1f57ae43fae..50e8646499d 100644 --- a/tests/auto/widgets/widgets/qmenu/tst_qmenu.cpp +++ b/tests/auto/widgets/widgets/qmenu/tst_qmenu.cpp @@ -127,6 +127,7 @@ private slots: #endif void invisibleActions(); + void execReturnsWidgetAction(); protected slots: void onActivated(QAction*); @@ -2335,5 +2336,26 @@ void tst_QMenu::dontSelectDisabledActionByShortcut() } #endif +void tst_QMenu::execReturnsWidgetAction() +{ + QWidget window; + + QMenu menu(&window); + QWidgetAction *widgetAction = new QWidgetAction(&menu); + QPushButton *menuButton = new QPushButton("Button", &menu); + widgetAction->setDefaultWidget(menuButton); + QObject::connect(menuButton, &QPushButton::clicked, widgetAction, &QAction::trigger); + + menu.addAction("First"); + menu.addAction(widgetAction); + menu.addAction("Last"); + + window.show(); + QVERIFY(QTest::qWaitForWindowActive(&window)); + + QTimer::singleShot(0, menuButton, &QPushButton::click); + QCOMPARE(menu.exec(window.geometry().center()), widgetAction); +} + QTEST_MAIN(tst_QMenu) #include "tst_qmenu.moc" diff --git a/tests/benchmarks/gui/painting/qcolor/tst_qcolor.cpp b/tests/benchmarks/gui/painting/qcolor/tst_qcolor.cpp index 02fe3f986e9..54ba45c1256 100644 --- a/tests/benchmarks/gui/painting/qcolor/tst_qcolor.cpp +++ b/tests/benchmarks/gui/painting/qcolor/tst_qcolor.cpp @@ -2,8 +2,24 @@ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <qtest.h> + #include <QColor> +#include <vector> + +static std::vector<QColor> all_rgb_colors() +{ + std::vector<QColor> colors; + colors.reserve(256 * 256 * 256); + for (int r = 0; r < 256; ++r) { + for (int g = 0; g < 256; ++g) { + for (int b = 0; b < 256; ++b) + colors.emplace_back(r, g, b); + } + } + return colors; +} + class tst_QColor : public QObject { @@ -12,6 +28,11 @@ class tst_QColor : public QObject private slots: void nameRgb(); void nameArgb(); + void toHsl(); + void toHsv(); + +private: + const std::vector<QColor> m_all_rgb = all_rgb_colors(); }; void tst_QColor::nameRgb() @@ -32,6 +53,22 @@ void tst_QColor::nameArgb() } } +void tst_QColor::toHsl() +{ + QBENCHMARK { + for (const QColor &c : m_all_rgb) + [[maybe_unused]] const auto r = c.toHsl(); + } +} + +void tst_QColor::toHsv() +{ + QBENCHMARK { + for (const QColor &c : m_all_rgb) + [[maybe_unused]] const auto r = c.toHsv(); + } +} + QTEST_MAIN(tst_QColor) #include "tst_qcolor.moc" diff --git a/util/testrunner/README b/util/testrunner/README index 5758e325140..cb6722ac807 100644 --- a/util/testrunner/README +++ b/util/testrunner/README @@ -15,10 +15,4 @@ It offers the following functionality The script itself has a testsuite that is simply run by invoking -qtbase/util/testrunner/tests/tst_testrunner.py - -Please *run this manually* before submitting a change to qt-testrunner and -make sure it's passing. The reason it does not run automatically during the -usual qtbase test run, is because -+ the test run should not depend on Python -+ we don't want to wrap the testrunner tests with testrunner. +qtbase/tests/auto/util/testrunner/tst_qt_testrunner.py diff --git a/util/testrunner/qt-testrunner.py b/util/testrunner/qt-testrunner.py index 41e81e83122..1573534cee9 100755 --- a/util/testrunner/qt-testrunner.py +++ b/util/testrunner/qt-testrunner.py @@ -4,10 +4,9 @@ # !!!IMPORTANT!!! If you change anything to this script, run the testsuite -# manually and make sure it still passes, as it doesn't run automatically. -# Just execute the command line as such: +# and make sure it still passes: # -# ./util/testrunner/tests/tst_testrunner.py -v [--debug] +# qtbase/tests/auto/util/testrunner/tst_qt_testrunner.py -v [--debug] # # ======== qt-testrunner ======== # @@ -15,24 +14,44 @@ # tst_whatever, and tries to iron out unpredictable test failures. # In particular: # -# + Appends output argument to it: "-o tst_whatever.xml,xml" -# + Checks the exit code. If it is zero, the script exits with zero, -# otherwise proceeds. -# + Reads the XML test log and Understands exactly which function -# of the test failed. -# + If no XML file is found or was invalid, the test executable -# probably CRASHed, so we *re-run the full test once again*. -# + If some testcases failed it executes only those individually -# until they pass, or until max-repeats times is reached. +# + Append output argument to it: "-o tst_whatever.xml,xml" and +# execute it. +# + Save the exit code. +# - If it is <0 or >=128 (see NOTE_2), mark the test run as CRASH. +# + Read the XML test log and find exactly which functions +# of the test FAILed. +# + Mark the test run as CRASH, if: +# - no XML file is found, +# - or an invalid XML file is found, +# - or the XML contains a QFatal message: <Message type="qfatal"> +# - or no test FAILures are listed in the XML but the saved +# exit code is not 0. +# + If, based on the rules above, the test run is marked as CRASH, +# then *re-run the full test once again* and start this logic over. +# If we are on the 2nd run and CRASH happens again, then exit(3). +# + Examine the saved exit code: +# if it is 0, then exit(0) (success, all tests have PASSed). +# + Otherwise, some testcases failed, so execute only those individually +# until they pass, or until max-repeats (default: 5) times is reached. # # The regular way to use is to set the environment variable TESTRUNNER to -# point to this script before invoking ctest. +# point to this script before invoking ctest. In COIN CI it is set as +# TESTRUNNER="qt-testrunner.py --" to stop it from parsing further args. # # NOTE: this script is crafted specifically for use with Qt tests and for # using it in Qt's CI. For example it detects and acts specially if test # executable is "tst_selftests" or "androidtestrunner". It also detects # env var "COIN_CTEST_RESULTSDIR" and uses it as log-dir. # +# NOTE_2: Why is qt-testrunner considering exit code outside [0,127] as CRASH? +# On Linux, Python subprocess module returns positive `returncode` +# (255 for example), even if the child does exit(-1 for example). It +# returns negative `returncode` only if the child is killed by a signal. +# Qt-testrunner wants to catch both of these cases as CRASH. +# On Windows, a crash is usually accompanied by exitcode >= 0xC0000000. +# Finally, QTest is limiting itself to exit codes in [0,127] +# so anything outside that range is abnormal, thus treated as CRASH. +# # TODO implement --dry-run. # Exit codes of this script: @@ -63,9 +82,17 @@ from pprint import pprint from typing import NamedTuple, Tuple, List, Optional # Define a custom type for returning a fail incident -class WhatFailed(NamedTuple): +class TestResult(NamedTuple): func: str tag: Optional[str] = None +class WhatFailed(NamedTuple): + qfatal_message: Optional[str] = None + failed_tests: List[TestResult] = [] + +class ReRunCrash(Exception): + pass +class BadXMLCrash(Exception): + pass # In the last test re-run, we add special verbosity arguments, in an attempt @@ -83,9 +110,11 @@ NO_RERUN_FUNCTIONS = { # not try to append "-o" to their command-line or re-run failed testcases. # Only add tests here if absolutely necessary! NON_XML_GENERATING_TESTS = { - "tst_selftests", # qtestlib's selftests are using an external test framework (Catch) that does not support -o argument - "tst_QDoc", # Some of QDoc's tests are using an external test framework (Catch) that does not support -o argument - "tst_QDoc_Catch_Generators", # Some of QDoc's tests are using an external test framework (Catch) that does not support -o argument + # These tests use an external test framework (Catch) that doesn't support + # QtTest's -o argument. + "tst_selftests", + "tst_QDoc", + "tst_QDoc_Catch_Generators", } # These are scripts that are used to wrap test execution for special platforms. # They need special handling (most times just skipping the wrapper name in argv[]). @@ -131,6 +160,9 @@ Default flags: --max-repeats 5 --passes-needed 1 " -o log_file.xml -v2 -vs. This will disable some functionality like the" " failed test repetition and the verbose output on failure. This is" " activated by default when TESTARGS is tst_selftests.") + # TODO parser.parse_args(args=sys.argv[0:cmd_index]). + # Where cmd_index is either the first positional argument, or the argument right after "--". + # This way it won't interpet arguments after the first positional arg. args = parser.parse_args() args.self_name = os.path.basename(sys.argv[0]) args.specific_extra_args = [] @@ -198,11 +230,13 @@ Default flags: --max-repeats 5 --passes-needed 1 return args -def parse_log(results_file) -> List[WhatFailed]: - """Parse the XML test log file. Return the failed testcases, if any. +def parse_log(results_file) -> WhatFailed: + """ + Parse the XML test log file. Return the failed testcases, if any, + and the first qfatal message possibly printed. Failures are considered the "fail" and "xpass" incidents. - A testcase is a function with an optional data tag.""" + """ start_timer = timeit.default_timer() try: @@ -222,10 +256,12 @@ def parse_log(results_file) -> List[WhatFailed]: root = tree.getroot() if root.tag != "TestCase": - raise AssertionError( + raise BadXMLCrash( f"The XML test log must have <TestCase> as root tag, but has: <{root.tag}>") failures = [] + qfatal_message = None + n_passes = 0 for e1 in root: if e1.tag == "TestFunction": @@ -233,23 +269,43 @@ def parse_log(results_file) -> List[WhatFailed]: if e2.tag == "Incident": if e2.attrib["type"] in ("fail", "xpass"): func = e1.attrib["name"] + datatag = None e3 = e2.find("DataTag") # every <Incident> might have a <DataTag> if e3 is not None: - failures.append(WhatFailed(func, tag=e3.text)) - else: - failures.append(WhatFailed(func)) + datatag = e3.text + failures.append(TestResult(func, datatag)) else: n_passes += 1 + # Use iter() here to _recursively_ search root for <Message>, + # as we don't trust that messages are always at the same depth. + for message_tag in root.iter(tag="Message"): + messagetype = message_tag.get("type") + if messagetype == "qfatal": + message_desc = message_tag.find("Description") + if message_desc is not None: + qfatal_message = message_desc.text + else: + qfatal_message = "--EMPTY QFATAL--" + L.warning("qFatal message ('%s') found in the XML, treating this run as a CRASH!", + qfatal_message) + break + end_timer = timeit.default_timer() t = end_timer - start_timer L.info(f"Parsed XML file {results_file} in {t:.3f} seconds") L.info(f"Found {n_passes} passes and {len(failures)} failures") - return failures + return WhatFailed(qfatal_message, failures) def run_test(arg_list: List[str], **kwargs): + if (os.environ.get("QT_TESTRUNNER_TESTING", "0") == "1" + and os.name == "nt" + and arg_list[0].endswith(".py") + ): + # For executing qt_mock_test.py under the same Python interpreter when testing. + arg_list = [ sys.executable ] + arg_list L.debug("Running test command line: %s", arg_list) proc = subprocess.run(arg_list, **kwargs) L.info("Test process exited with code: %d", proc.returncode) @@ -257,6 +313,11 @@ def run_test(arg_list: List[str], **kwargs): return proc def unique_filename(test_basename: str) -> str: + + # Hidden env var for testing, enforcing a predictable, non-unique filename. + if os.environ.get("QT_TESTRUNNER_DEBUG_NO_UNIQUE_OUTPUT_FILENAME"): + return f"{test_basename}" + timestamp = round(time.time() * 1000) return f"{test_basename}-{timestamp}" @@ -291,18 +352,19 @@ def run_full_test(test_basename, testargs: List[str], output_dir: str, def rerun_failed_testcase(test_basename, testargs: List[str], output_dir: str, - what_failed: WhatFailed, + testcase: TestResult, max_repeats, passes_needed, dryrun=False, timeout=None) -> bool: """Run a specific function:tag of a test, until it passes enough times, or until max_repeats is reached. Return True if it passes eventually, False if it fails. + Raise ReRunCrash Exception if it crashes. """ assert passes_needed <= max_repeats - failed_arg = what_failed.func - if what_failed.tag: - failed_arg += ":" + what_failed.tag + failed_arg = testcase.func + if testcase.tag: + failed_arg += ":" + testcase.tag n_passes = 0 @@ -325,6 +387,19 @@ def rerun_failed_testcase(test_basename, testargs: List[str], output_dir: str, proc = run_test(testargs + output_args + VERBOSE_ARGS + [failed_arg], timeout=timeout, env={**os.environ, **VERBOSE_ENV}) + # There are platforms that run tests wrapped with some test-runner + # script, that can possibly fail to extract a process exit code. + # Because of these cases, we *also* parse the XML file and signify + # CRASH in case of QFATAL/empty/corrupt result. + what_failed = parse_log(f"{pathname_stem}.xml") + if what_failed.qfatal_message: + raise ReRunCrash(f"CRASH! returncode:{proc.returncode} " + f"QFATAL:'{what_failed.qfatal_message}'") + if proc.returncode < 0 or proc.returncode >= 128: + raise ReRunCrash(f"CRASH! returncode:{proc.returncode}") + if proc.returncode == 0 and len(what_failed.failed_tests) > 0: + raise ReRunCrash("CRASH! returncode:0 but failures were found: " + + what_failed.failed_tests) if proc.returncode == 0: n_passes += 1 if n_passes == passes_needed: @@ -354,20 +429,22 @@ def main(): try: results_file = None - failed_functions = [] + what_failed = WhatFailed() if args.parse_xml_testlog: # do not run test, just parse file - failed_functions = parse_log(args.parse_xml_testlog) + what_failed = parse_log(args.parse_xml_testlog) # Pretend the test returned correct exit code - retcode = len(failed_functions) + retcode = len(what_failed.failed_tests) else: # normal invocation, run test (retcode, results_file) = \ run_full_test(args.test_basename, args.testargs, args.log_dir, args.no_extra_args, args.dry_run, args.timeout, args.specific_extra_args) if results_file: - failed_functions = parse_log(results_file) + what_failed = parse_log(results_file) + + failed_functions = what_failed.failed_tests - if retcode < 0: + if retcode < 0 or retcode >= 128 or what_failed.qfatal_message: L.warning("CRASH detected, re-running the whole executable") continue if retcode == 0: @@ -392,6 +469,8 @@ def main(): assert len(failed_functions) > 0 and retcode != 0 break # all is fine, goto re-running individual failed testcases + except AssertionError: + raise except Exception as e: L.error("exception:%s %s", type(e).__name__, e) L.error("The test executable probably crashed, see above for details") @@ -402,13 +481,15 @@ def main(): L.info("Some tests failed, will re-run at most %d times.\n", args.max_repeats) - for what_failed in failed_functions: + for test_result in failed_functions: try: ret = rerun_failed_testcase(args.test_basename, args.testargs, args.log_dir, - what_failed, args.max_repeats, args.passes_needed, + test_result, args.max_repeats, args.passes_needed, dryrun=args.dry_run, timeout=args.timeout) + except AssertionError: + raise except Exception as e: - L.error("exception:%s %s", type(e).__name__, e) + L.error("exception:%s", e) L.error("The testcase re-run probably crashed, giving up") sys.exit(3) # Test re-run CRASH |
