summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/3rdparty/libjpeg/LICENSE31
-rwxr-xr-xsrc/3rdparty/libjpeg/import_from_libjpeg_tarball.sh2
-rw-r--r--src/3rdparty/libjpeg/qt_attribution.json8
-rw-r--r--src/3rdparty/libjpeg/src/ChangeLog.md24
-rw-r--r--src/3rdparty/libjpeg/src/jcmaster.c122
-rw-r--r--src/3rdparty/libjpeg/src/jconfig.h4
-rw-r--r--src/3rdparty/libjpeg/src/jconfigint.h2
-rw-r--r--src/3rdparty/libjpeg/src/jerror.c14
-rw-r--r--src/3rdparty/libjpeg/src/jversion.h7
-rw-r--r--src/3rdparty/libjpeg/zlib-license.txt15
-rw-r--r--src/3rdparty/sqlite/qt_attribution.json4
-rw-r--r--src/android/CMakeLists.txt1
-rw-r--r--src/android/jar/CMakeLists.txt2
-rw-r--r--src/android/jar/src/org/qtproject/qt/android/QtEmbeddedDelegate.java48
-rw-r--r--src/android/jar/src/org/qtproject/qt/android/QtEmbeddedLoader.java1
-rw-r--r--src/android/jar/src/org/qtproject/qt/android/QtEmbeddedViewInterface.java15
-rw-r--r--src/android/jar/src/org/qtproject/qt/android/QtNative.java13
-rw-r--r--src/android/jar/src/org/qtproject/qt/android/QtServiceEmbeddedDelegate.java115
-rw-r--r--src/android/jar/src/org/qtproject/qt/android/QtView.java25
-rw-r--r--src/android/templates/build.gradle8
-rw-r--r--src/android/templates/doc/src/android-manifest-file-configuration.qdoc2
-rw-r--r--src/android/templates_aar/AndroidManifest.xml9
-rw-r--r--src/android/templates_aar/CMakeLists.txt24
-rw-r--r--src/corelib/CMakeLists.txt4
-rw-r--r--src/corelib/Qt6AndroidMacros.cmake50
-rw-r--r--src/corelib/Qt6CoreMacros.cmake8
-rw-r--r--src/corelib/compat/removed_api.cpp92
-rw-r--r--src/corelib/configure.cmake33
-rw-r--r--src/corelib/doc/src/cmake/cmake-properties.qdoc49
-rw-r--r--src/corelib/doc/src/cmake/qt_android_generate_deployment_settings.qdoc1
-rw-r--r--src/corelib/doc/src/foreach-keyword.qdoc3
-rw-r--r--src/corelib/global/qcomparehelpers.h28
-rw-r--r--src/corelib/global/qcompilerdetection.h4
-rw-r--r--src/corelib/global/qglobal.cpp13
-rw-r--r--src/corelib/global/qglobal.h3
-rw-r--r--src/corelib/global/qlibraryinfo.cpp4
-rw-r--r--src/corelib/global/qnamespace.h6
-rw-r--r--src/corelib/global/qnamespace.qdoc12
-rw-r--r--src/corelib/global/qtconfigmacros.h1
-rw-r--r--src/corelib/global/qversiontagging.h6
-rw-r--r--src/corelib/io/qbuffer.cpp3
-rw-r--r--src/corelib/io/qdir.cpp5
-rw-r--r--src/corelib/io/qfilesystemengine_unix.cpp2
-rw-r--r--src/corelib/io/qresource.cpp289
-rw-r--r--src/corelib/io/qsavefile.cpp8
-rw-r--r--src/corelib/io/qurl.cpp21
-rw-r--r--src/corelib/itemmodels/qsortfilterproxymodel.cpp11
-rw-r--r--src/corelib/kernel/qcore_mac.mm2
-rw-r--r--src/corelib/kernel/qcore_mac_p.h5
-rw-r--r--src/corelib/kernel/qcoreapplication.cpp18
-rw-r--r--src/corelib/kernel/qelapsedtimer.cpp5
-rw-r--r--src/corelib/kernel/qelapsedtimer.h39
-rw-r--r--src/corelib/kernel/qeventloop.cpp6
-rw-r--r--src/corelib/kernel/qjniarray.h6
-rw-r--r--src/corelib/kernel/qjnienvironment.h1
-rw-r--r--src/corelib/kernel/qjniobject.cpp4
-rw-r--r--src/corelib/kernel/qjniobject.h4
-rw-r--r--src/corelib/kernel/qjnitypes.h9
-rw-r--r--src/corelib/kernel/qmetacontainer.cpp16
-rw-r--r--src/corelib/kernel/qmetacontainer.h31
-rw-r--r--src/corelib/kernel/qmetaobject.cpp13
-rw-r--r--src/corelib/kernel/qmetaobject.h10
-rw-r--r--src/corelib/kernel/qmetaobject_p.h20
-rw-r--r--src/corelib/kernel/qmetatype.cpp63
-rw-r--r--src/corelib/kernel/qmetatype.h40
-rw-r--r--src/corelib/kernel/qobject.cpp2
-rw-r--r--src/corelib/kernel/qproperty_p.h5
-rw-r--r--src/corelib/kernel/qpropertyprivate.h34
-rw-r--r--src/corelib/kernel/qsingleshottimer_p.h15
-rw-r--r--src/corelib/kernel/qsocketnotifier.h16
-rw-r--r--src/corelib/kernel/qtimer.cpp8
-rw-r--r--src/corelib/mimetypes/mime/packages/freedesktop.org.xml62
-rw-r--r--src/corelib/mimetypes/qmimetype.cpp15
-rw-r--r--src/corelib/mimetypes/qmimetype.h8
-rw-r--r--src/corelib/serialization/qdatastream.cpp22
-rw-r--r--src/corelib/text/qbytearray.cpp40
-rw-r--r--src/corelib/text/qbytearraymatcher.cpp61
-rw-r--r--src/corelib/text/qlatin1stringmatcher.cpp42
-rw-r--r--src/corelib/text/qlatin1stringmatcher.h9
-rw-r--r--src/corelib/text/qlocale.cpp5
-rw-r--r--src/corelib/text/qlocale.h7
-rw-r--r--src/corelib/text/qlocale.qdoc1
-rw-r--r--src/corelib/text/qregularexpression.cpp96
-rw-r--r--src/corelib/text/qregularexpression.h57
-rw-r--r--src/corelib/text/qstaticlatin1stringmatcher.h23
-rw-r--r--src/corelib/text/qstaticlatin1stringmatcher.qdoc1
-rw-r--r--src/corelib/text/qstring.cpp12
-rw-r--r--src/corelib/text/qstringconverter.cpp20
-rw-r--r--src/corelib/text/qstringconverter.h6
-rw-r--r--src/corelib/thread/qfuture.h8
-rw-r--r--src/corelib/thread/qfuture.qdoc8
-rw-r--r--src/corelib/thread/qfuture_impl.h4
-rw-r--r--src/corelib/thread/qresultstore.cpp10
-rw-r--r--src/corelib/thread/qresultstore.h9
-rw-r--r--src/corelib/thread/qthread.cpp24
-rw-r--r--src/corelib/thread/qthread_unix.cpp24
-rw-r--r--src/corelib/thread/qthread_win.cpp8
-rw-r--r--src/corelib/thread/qthreadpool.cpp39
-rw-r--r--src/corelib/thread/qthreadpool.h13
-rw-r--r--src/corelib/thread/qthreadpool_p.h3
-rw-r--r--src/corelib/time/qdatetime.h2
-rw-r--r--src/corelib/time/qdatetimeparser.cpp6
-rw-r--r--src/corelib/time/qdatetimeparser_p.h4
-rw-r--r--src/corelib/time/qtimezonelocale.cpp99
-rw-r--r--src/corelib/time/qtimezonelocale_p.h13
-rw-r--r--src/corelib/time/qtimezoneprivate.cpp110
-rw-r--r--src/corelib/time/qtimezoneprivate_data_p.h21
-rw-r--r--src/corelib/time/qtimezoneprivate_icu.cpp73
-rw-r--r--src/corelib/time/qtimezoneprivate_p.h35
-rw-r--r--src/corelib/time/qtimezoneprivate_tz.cpp150
-rw-r--r--src/corelib/tools/qbitarray.cpp10
-rw-r--r--src/corelib/tools/qbitarray.h13
-rw-r--r--src/corelib/tools/qeasingcurve.cpp33
-rw-r--r--src/corelib/tools/qeasingcurve.h8
-rw-r--r--src/corelib/tools/qline.cpp41
-rw-r--r--src/corelib/tools/qline.h42
-rw-r--r--src/corelib/tools/qmargins.cpp32
-rw-r--r--src/corelib/tools/qmargins.h49
-rw-r--r--src/corelib/tools/qpoint.cpp47
-rw-r--r--src/corelib/tools/qpoint.h26
-rw-r--r--src/corelib/tools/qrect.cpp44
-rw-r--r--src/corelib/tools/qrect.h28
-rw-r--r--src/corelib/tools/qsize.cpp33
-rw-r--r--src/corelib/tools/qsize.h23
-rw-r--r--src/corelib/tools/qvarlengtharray.h7
-rw-r--r--src/corelib/tools/qversionnumber.cpp1
-rw-r--r--src/corelib/tools/qversionnumber.h32
-rw-r--r--src/dbus/qdbusextratypes.cpp2
-rw-r--r--src/dbus/qdbusextratypes.h5
-rw-r--r--src/dbus/qdbusintegrator.cpp3
-rw-r--r--src/dbus/qdbusmarshaller.cpp2
-rw-r--r--src/dbus/qdbusutil.cpp8
-rw-r--r--src/gui/CMakeLists.txt20
-rw-r--r--src/gui/accessible/linux/atspiadaptor.cpp38
-rw-r--r--src/gui/accessible/linux/atspiadaptor_p.h5
-rw-r--r--src/gui/accessible/qaccessible.cpp74
-rw-r--r--src/gui/accessible/qaccessible.h32
-rw-r--r--src/gui/accessible/qaccessible_base.h6
-rw-r--r--src/gui/compat/removed_api.cpp11
-rw-r--r--src/gui/configure.cmake2
-rw-r--r--src/gui/image/qicon.cpp24
-rw-r--r--src/gui/image/qicon_p.h20
-rw-r--r--src/gui/image/qiconengine.h1
-rw-r--r--src/gui/image/qimage.cpp9
-rw-r--r--src/gui/image/qpixmap.cpp9
-rw-r--r--src/gui/kernel/qguiapplication.cpp48
-rw-r--r--src/gui/kernel/qguiapplication.h2
-rw-r--r--src/gui/kernel/qguiapplication_p.h2
-rw-r--r--src/gui/kernel/qguiapplication_platform.h30
-rw-r--r--src/gui/kernel/qguivariant.cpp2
-rw-r--r--src/gui/kernel/qhighdpiscaling_p.h4
-rw-r--r--src/gui/kernel/qplatformtheme.cpp5
-rw-r--r--src/gui/kernel/qplatformtheme.h1
-rw-r--r--src/gui/kernel/qstylehints.cpp83
-rw-r--r--src/gui/kernel/qstylehints.h10
-rw-r--r--src/gui/kernel/qstylehints_p.h1
-rw-r--r--src/gui/kernel/qwindow.cpp8
-rw-r--r--src/gui/kernel/qwindow_p.h10
-rw-r--r--src/gui/painting/qbackingstore.cpp29
-rw-r--r--src/gui/painting/qcolorspace.cpp8
-rw-r--r--src/gui/painting/qcolortransform.cpp116
-rw-r--r--src/gui/painting/qcolortransform_p.h6
-rw-r--r--src/gui/painting/qicc.cpp6
-rw-r--r--src/gui/painting/qpagesize.h2
-rw-r--r--src/gui/platform/ios/qiosnativeinterface.cpp26
-rw-r--r--src/gui/qt_cmdline.cmake3
-rw-r--r--src/gui/rhi/qrhigles2.cpp16
-rw-r--r--src/gui/rhi/qrhigles2_p.h6
-rw-r--r--src/gui/rhi/qrhimetal.mm35
-rw-r--r--src/gui/rhi/qrhivulkan.cpp24
-rw-r--r--src/gui/rhi/qrhivulkan_p.h4
-rw-r--r--src/gui/text/qcssparser.cpp62
-rw-r--r--src/gui/text/qcssparser_p.h17
-rw-r--r--src/gui/text/qfont.cpp4
-rw-r--r--src/gui/text/qtextdocument.cpp62
-rw-r--r--src/gui/text/qtextengine.cpp6
-rw-r--r--src/gui/text/qtextformat.cpp24
-rw-r--r--src/gui/text/qtextformat.h8
-rw-r--r--src/gui/text/qtexthtmlparser.cpp62
-rw-r--r--src/gui/text/qtextimagehandler.cpp26
-rw-r--r--src/gui/util/qdesktopservices.cpp42
-rw-r--r--src/network/CMakeLists.txt1
-rw-r--r--src/network/access/http2/http2protocol_p.h2
-rw-r--r--src/network/access/qabstractnetworkcache.cpp43
-rw-r--r--src/network/access/qabstractnetworkcache.h3
-rw-r--r--src/network/access/qabstractprotocolhandler_p.h4
-rw-r--r--src/network/access/qhttpheaders.h3
-rw-r--r--src/network/access/qhttpheadershelper.cpp25
-rw-r--r--src/network/access/qhttpheadershelper_p.h30
-rw-r--r--src/network/access/qhttpmultipart.cpp51
-rw-r--r--src/network/access/qhttpmultipart.h4
-rw-r--r--src/network/access/qhttpmultipart_p.h7
-rw-r--r--src/network/access/qhttpnetworkconnection.cpp82
-rw-r--r--src/network/access/qhttpnetworkconnection_p.h17
-rw-r--r--src/network/access/qhttpnetworkconnectionchannel.cpp175
-rw-r--r--src/network/access/qhttpnetworkconnectionchannel_p.h5
-rw-r--r--src/network/access/qhttpprotocolhandler.cpp12
-rw-r--r--src/network/access/qhttpthreaddelegate.cpp22
-rw-r--r--src/network/access/qnetworkaccessbackend.cpp37
-rw-r--r--src/network/access/qnetworkaccessbackend_p.h3
-rw-r--r--src/network/access/qnetworkaccesscachebackend.cpp28
-rw-r--r--src/network/access/qnetworkaccessdebugpipebackend.cpp4
-rw-r--r--src/network/access/qnetworkaccessmanager.cpp37
-rw-r--r--src/network/access/qnetworkdiskcache.cpp58
-rw-r--r--src/network/access/qnetworkfile.cpp7
-rw-r--r--src/network/access/qnetworkfile_p.h2
-rw-r--r--src/network/access/qnetworkreply.cpp60
-rw-r--r--src/network/access/qnetworkreply.h4
-rw-r--r--src/network/access/qnetworkreplydataimpl.cpp7
-rw-r--r--src/network/access/qnetworkreplyfileimpl.cpp16
-rw-r--r--src/network/access/qnetworkreplyhttpimpl.cpp252
-rw-r--r--src/network/access/qnetworkreplyimpl.cpp43
-rw-r--r--src/network/access/qnetworkreplywasmimpl.cpp177
-rw-r--r--src/network/access/qnetworkreplywasmimpl_p.h26
-rw-r--r--src/network/access/qnetworkrequest.cpp532
-rw-r--r--src/network/access/qnetworkrequest.h8
-rw-r--r--src/network/access/qnetworkrequest_p.h42
-rw-r--r--src/network/access/qnetworkrequestfactory.cpp12
-rw-r--r--src/network/access/qrestaccessmanager_p.h8
-rw-r--r--src/network/access/qrestreply.cpp68
-rw-r--r--src/network/compat/removed_api.cpp3
-rw-r--r--src/network/kernel/qdnslookup.cpp554
-rw-r--r--src/network/kernel/qdnslookup.h110
-rw-r--r--src/network/kernel/qdnslookup_p.h44
-rw-r--r--src/network/kernel/qdnslookup_unix.cpp142
-rw-r--r--src/network/kernel/qdnslookup_win.cpp90
-rw-r--r--src/network/kernel/qnetworkinformation.h1
-rw-r--r--src/network/kernel/qnetworkproxy.cpp55
-rw-r--r--src/network/kernel/qnetworkproxy.h4
-rw-r--r--src/network/kernel/qnetworkproxy_libproxy.cpp6
-rw-r--r--src/network/socket/qhttpsocketengine.cpp11
-rw-r--r--src/network/socket/qsocks5socketengine.cpp2
-rw-r--r--src/plugins/platforms/android/CMakeLists.txt2
-rw-r--r--src/plugins/platforms/android/androidjnimain.cpp2
-rw-r--r--src/plugins/platforms/android/androidwindowembedding.cpp17
-rw-r--r--src/plugins/platforms/android/qandroidplatformintegration.cpp19
-rw-r--r--src/plugins/platforms/android/qandroidplatformservices.cpp21
-rw-r--r--src/plugins/platforms/android/qandroidplatformtheme.cpp15
-rw-r--r--src/plugins/platforms/android/qandroidplatformtheme.h3
-rw-r--r--src/plugins/platforms/cocoa/CMakeLists.txt2
-rw-r--r--src/plugins/platforms/cocoa/qcocoaaccessibility.mm17
-rw-r--r--src/plugins/platforms/cocoa/qcocoaapplicationdelegate.mm36
-rw-r--r--src/plugins/platforms/cocoa/qcocoaservices.h6
-rw-r--r--src/plugins/platforms/cocoa/qcocoaservices.mm17
-rw-r--r--src/plugins/platforms/cocoa/qcocoatheme.h1
-rw-r--r--src/plugins/platforms/cocoa/qcocoatheme.mm17
-rw-r--r--src/plugins/platforms/cocoa/qcocoawindow.mm2
-rw-r--r--src/plugins/platforms/eglfs/CMakeLists.txt2
-rw-r--r--src/plugins/platforms/ios/CMakeLists.txt19
-rw-r--r--src/plugins/platforms/ios/SwiftIntegration.cmake78
-rw-r--r--src/plugins/platforms/ios/module.modulemap4
-rw-r--r--src/plugins/platforms/ios/qiosapplication.swift82
-rw-r--r--src/plugins/platforms/ios/qiosapplicationdelegate.h5
-rw-r--r--src/plugins/platforms/ios/qiosdocumentpickercontroller.mm8
-rw-r--r--src/plugins/platforms/ios/qioseventdispatcher.h2
-rw-r--r--src/plugins/platforms/ios/qioseventdispatcher.mm21
-rw-r--r--src/plugins/platforms/ios/qiosglobal.mm20
-rw-r--r--src/plugins/platforms/ios/qiosintegration.h30
-rw-r--r--src/plugins/platforms/ios/qiosintegration.mm37
-rw-r--r--src/plugins/platforms/ios/qiostextinputoverlay.mm2
-rw-r--r--src/plugins/platforms/ios/qiostheme.h5
-rw-r--r--src/plugins/platforms/ios/qiostheme.mm35
-rw-r--r--src/plugins/platforms/ios/qioswindow.mm6
-rw-r--r--src/plugins/platforms/ios/quiwindow.mm9
-rw-r--r--src/plugins/platforms/linuxfb/CMakeLists.txt2
-rw-r--r--src/plugins/platforms/minimal/CMakeLists.txt2
-rw-r--r--src/plugins/platforms/minimalegl/CMakeLists.txt2
-rw-r--r--src/plugins/platforms/offscreen/CMakeLists.txt2
-rw-r--r--src/plugins/platforms/qnx/CMakeLists.txt2
-rw-r--r--src/plugins/platforms/qnx/qqnxabstractnavigator.cpp10
-rw-r--r--src/plugins/platforms/qnx/qqnxabstractnavigator.h3
-rw-r--r--src/plugins/platforms/qnx/qqnxbuffer.cpp24
-rw-r--r--src/plugins/platforms/qnx/qqnxbuffer.h3
-rw-r--r--src/plugins/platforms/qnx/qqnxbuttoneventnotifier.cpp24
-rw-r--r--src/plugins/platforms/qnx/qqnxbuttoneventnotifier.h3
-rw-r--r--src/plugins/platforms/qnx/qqnxclipboard.cpp22
-rw-r--r--src/plugins/platforms/qnx/qqnxclipboard.h2
-rw-r--r--src/plugins/platforms/qnx/qqnxcursor.cpp12
-rw-r--r--src/plugins/platforms/qnx/qqnxcursor.h3
-rw-r--r--src/plugins/platforms/qnx/qqnxeglwindow.cpp12
-rw-r--r--src/plugins/platforms/qnx/qqnxeglwindow.h3
-rw-r--r--src/plugins/platforms/qnx/qqnxglcontext.cpp12
-rw-r--r--src/plugins/platforms/qnx/qqnxglobal.cpp2
-rw-r--r--src/plugins/platforms/qnx/qqnxinputcontext_imf.cpp116
-rw-r--r--src/plugins/platforms/qnx/qqnxinputcontext_noimf.cpp22
-rw-r--r--src/plugins/platforms/qnx/qqnxintegration.cpp66
-rw-r--r--src/plugins/platforms/qnx/qqnxintegration.h4
-rw-r--r--src/plugins/platforms/qnx/qqnxnavigatoreventhandler.cpp20
-rw-r--r--src/plugins/platforms/qnx/qqnxnavigatoreventhandler.h3
-rw-r--r--src/plugins/platforms/qnx/qqnxnavigatoreventnotifier.cpp27
-rw-r--r--src/plugins/platforms/qnx/qqnxnavigatorpps.cpp19
-rw-r--r--src/plugins/platforms/qnx/qqnxnavigatorpps.h3
-rw-r--r--src/plugins/platforms/qnx/qqnxrasterbackingstore.cpp20
-rw-r--r--src/plugins/platforms/qnx/qqnxrasterwindow.cpp14
-rw-r--r--src/plugins/platforms/qnx/qqnxscreen.cpp56
-rw-r--r--src/plugins/platforms/qnx/qqnxscreen.h4
-rw-r--r--src/plugins/platforms/qnx/qqnxscreeneventhandler.cpp49
-rw-r--r--src/plugins/platforms/qnx/qqnxscreeneventhandler.h3
-rw-r--r--src/plugins/platforms/qnx/qqnxscreeneventthread.cpp14
-rw-r--r--src/plugins/platforms/qnx/qqnxvirtualkeyboardpps.cpp24
-rw-r--r--src/plugins/platforms/qnx/qqnxvirtualkeyboardpps.h2
-rw-r--r--src/plugins/platforms/qnx/qqnxwindow.cpp56
-rw-r--r--src/plugins/platforms/qnx/qqnxwindow.h3
-rw-r--r--src/plugins/platforms/vkkhrdisplay/CMakeLists.txt2
-rw-r--r--src/plugins/platforms/vnc/CMakeLists.txt2
-rw-r--r--src/plugins/platforms/wasm/CMakeLists.txt3
-rw-r--r--src/plugins/platforms/wasm/qwasmscreen.cpp12
-rw-r--r--src/plugins/platforms/windows/CMakeLists.txt2
-rw-r--r--src/plugins/platforms/windows/qwindowscontext.cpp2
-rw-r--r--src/plugins/platforms/windows/qwindowsscreen.cpp6
-rw-r--r--src/plugins/platforms/windows/qwindowsscreen.h1
-rw-r--r--src/plugins/platforms/windows/qwindowstheme.cpp119
-rw-r--r--src/plugins/platforms/windows/qwindowstheme.h15
-rw-r--r--src/plugins/platforms/windows/qwindowswindow.cpp22
-rw-r--r--src/plugins/platforms/windows/uiautomation/qwindowsuiaaccessibility.cpp3
-rw-r--r--src/plugins/platforms/windows/uiautomation/qwindowsuiamainprovider.cpp18
-rw-r--r--src/plugins/platforms/windows/uiautomation/qwindowsuiamainprovider.h1
-rw-r--r--src/plugins/platforms/windows/uiautomation/qwindowsuiautomation.cpp8
-rw-r--r--src/plugins/platforms/xcb/CMakeLists.txt2
-rw-r--r--src/plugins/sqldrivers/ibase/qsql_ibase.cpp89
-rw-r--r--src/plugins/styles/modernwindows/qwindows11style.cpp266
-rw-r--r--src/plugins/styles/modernwindows/qwindowsvistastyle.cpp2
-rw-r--r--src/testlib/doc/src/qttestlib-manual.qdoc2
-rw-r--r--src/testlib/qcomparisontesthelper_p.h14
-rw-r--r--src/testlib/qsignalspy.cpp50
-rw-r--r--src/testlib/qsignalspy.h38
-rw-r--r--src/testlib/qtestcase.cpp25
-rw-r--r--src/testlib/qtestcase.h1
-rw-r--r--src/testlib/qtestlog.cpp5
-rw-r--r--src/testlib/qtestlog_p.h1
-rw-r--r--src/tools/androiddeployqt/main.cpp326
-rw-r--r--src/tools/moc/generator.cpp2
-rw-r--r--src/tools/moc/main.cpp69
-rw-r--r--src/tools/qdbusxml2cpp/qdbusxml2cpp.cpp2
-rw-r--r--src/tools/qlalr/cppgenerator.cpp5
-rw-r--r--src/tools/rcc/rcc.cpp4
-rw-r--r--src/tools/rcc/rcc.h2
-rw-r--r--src/tools/uic/cpp/cppwriteinitialization.cpp2
-rw-r--r--src/tools/uic/uic.cpp4
-rw-r--r--src/tools/windeployqt/main.cpp22
-rw-r--r--src/widgets/Qt6WidgetsMacros.cmake290
-rw-r--r--src/widgets/doc/snippets/cmake-macros/examples.cmake27
-rw-r--r--src/widgets/doc/snippets/cmake-macros/examples.cpp26
-rw-r--r--src/widgets/doc/src/cmake-macros.qdoc133
-rw-r--r--src/widgets/kernel/qapplication.cpp6
-rw-r--r--src/widgets/kernel/qwidget.cpp18
-rw-r--r--src/widgets/kernel/qwidgetwindow.cpp42
-rw-r--r--src/widgets/kernel/qwindowcontainer.cpp12
-rw-r--r--src/widgets/styles/qcommonstyle.cpp59
-rw-r--r--src/widgets/styles/qfusionstyle.cpp2
-rw-r--r--src/widgets/styles/qstylehelper.cpp2
-rw-r--r--src/widgets/styles/qstylesheetstyle_default.cpp3
-rw-r--r--src/widgets/util/qcompleter.cpp19
-rw-r--r--src/widgets/widgets/qcalendarwidget.cpp17
-rw-r--r--src/widgets/widgets/qcheckbox.cpp5
-rw-r--r--src/widgets/widgets/qdatetimeedit.cpp18
-rw-r--r--src/widgets/widgets/qdatetimeedit_p.h2
-rw-r--r--src/widgets/widgets/qdialogbuttonbox.cpp58
-rw-r--r--src/widgets/widgets/qdialogbuttonbox_p.h5
-rw-r--r--src/widgets/widgets/qlineedit.cpp7
-rw-r--r--src/widgets/widgets/qmainwindowlayout.cpp16
361 files changed, 7517 insertions, 2764 deletions
diff --git a/src/3rdparty/libjpeg/LICENSE b/src/3rdparty/libjpeg/LICENSE
index bf8a7fda7f..2204864fa1 100644
--- a/src/3rdparty/libjpeg/LICENSE
+++ b/src/3rdparty/libjpeg/LICENSE
@@ -1,30 +1,33 @@
libjpeg-turbo Licenses
======================
-libjpeg-turbo is covered by three compatible BSD-style open source licenses:
+libjpeg-turbo is covered by two compatible BSD-style open source licenses:
- The IJG (Independent JPEG Group) License, which is listed in
[README.ijg](README.ijg)
- This license applies to the libjpeg API library and associated programs
- (any code inherited from libjpeg, and any modifications to that code.)
+ This license applies to the libjpeg API library and associated programs,
+ including any code inherited from libjpeg and any modifications to that
+ code. Note that the libjpeg-turbo SIMD source code bears the
+ [zlib License](https://opensource.org/licenses/Zlib), but in the context of
+ the overall libjpeg API library, the terms of the zlib License are subsumed
+ by the terms of the IJG License.
- The Modified (3-clause) BSD License, which is listed below
- This license covers the TurboJPEG API library and associated programs, as
- well as the build system.
-
-- The [zlib License](https://opensource.org/licenses/Zlib)
-
- This license is a subset of the other two, and it covers the libjpeg-turbo
- SIMD extensions.
+ This license applies to the TurboJPEG API library and associated programs, as
+ well as the build system. Note that the TurboJPEG API library wraps the
+ libjpeg API library, so in the context of the overall TurboJPEG API library,
+ both the terms of the IJG License and the terms of the Modified (3-clause)
+ BSD License apply.
Complying with the libjpeg-turbo Licenses
=========================================
This section provides a roll-up of the libjpeg-turbo licensing terms, to the
-best of our understanding.
+best of our understanding. This is not a license in and of itself. It is
+intended solely for clarification.
1. If you are distributing a modified version of the libjpeg-turbo source,
then:
@@ -38,7 +41,7 @@ best of our understanding.
- Clauses 1 and 3 of the zlib License
2. You must add your own copyright notice to the header of each source
- file you modified, so others can tell that you modified that file (if
+ file you modified, so others can tell that you modified that file. (If
there is not an existing copyright header in that file, then you can
simply add a notice stating that you modified the file.)
@@ -119,8 +122,8 @@ ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
-Why Three Licenses?
-===================
+Why Two Licenses?
+=================
The zlib License could have been used instead of the Modified (3-clause) BSD
License, and since the IJG License effectively subsumes the distribution
diff --git a/src/3rdparty/libjpeg/import_from_libjpeg_tarball.sh b/src/3rdparty/libjpeg/import_from_libjpeg_tarball.sh
index fea6c5c9b4..0c984a8eed 100755
--- a/src/3rdparty/libjpeg/import_from_libjpeg_tarball.sh
+++ b/src/3rdparty/libjpeg/import_from_libjpeg_tarball.sh
@@ -145,7 +145,7 @@ sed -i -e "s/@COPYRIGHT_YEAR@/$cyear/" $TARGET_DIR/src/jversion.h
sed -n -e 's/^[ ]*"//
s/\(\\n\)*"[ ]*\\*$//
- /JCOPYRIGHT\ /,/^[ ]*$/ {
+ /JCOPYRIGHT.\ /,/^[ ]*$/ {
/Copyright/p
}
' $TARGET_DIR/src/jversion.h > $TARGET_DIR/COPYRIGHT.txt
diff --git a/src/3rdparty/libjpeg/qt_attribution.json b/src/3rdparty/libjpeg/qt_attribution.json
index 0b06d5c01e..a73ef1572d 100644
--- a/src/3rdparty/libjpeg/qt_attribution.json
+++ b/src/3rdparty/libjpeg/qt_attribution.json
@@ -7,11 +7,11 @@
"Description": "The Independent JPEG Group's JPEG software",
"Homepage": "http://libjpeg-turbo.virtualgl.org/",
- "Version": "3.0.2",
- "DownloadLocation": "https://github.com/libjpeg-turbo/libjpeg-turbo/releases/download/3.0.2/libjpeg-turbo-3.0.2.tar.gz",
+ "Version": "3.0.3",
+ "DownloadLocation": "https://github.com/libjpeg-turbo/libjpeg-turbo/releases/download/3.0.3/libjpeg-turbo-3.0.3.tar.gz",
"License": "Independent JPEG Group License and BSD 3-Clause \"New\" or \"Revised\" License and zlib License",
- "LicenseId": "IJG AND BSD-3-Clause AND Zlib",
- "LicenseFiles": [ "LICENSE", "ijg-license.txt", "zlib-license.txt"],
+ "LicenseId": "IJG AND BSD-3-Clause",
+ "LicenseFiles": [ "LICENSE", "ijg-license.txt" ],
"CopyrightFile": "COPYRIGHT.txt"
}
diff --git a/src/3rdparty/libjpeg/src/ChangeLog.md b/src/3rdparty/libjpeg/src/ChangeLog.md
index a929b62a8c..8039c5536d 100644
--- a/src/3rdparty/libjpeg/src/ChangeLog.md
+++ b/src/3rdparty/libjpeg/src/ChangeLog.md
@@ -1,3 +1,27 @@
+3.0.3
+=====
+
+### Significant changes relative to 3.0.2:
+
+1. Fixed an issue in the build system, introduced in 3.0.2, that caused all
+libjpeg-turbo components to depend on the Visual C++ run-time DLL when built
+with Visual C++ and CMake 3.15 or later, regardless of value of the
+`WITH_CRT_DLL` CMake variable.
+
+2. The x86-64 SIMD extensions now include support for Intel Control-flow
+Enforcement Technology (CET), which is enabled automatically if CET is enabled
+in the C compiler.
+
+3. Fixed a regression introduced by 3.0 beta2[6] that made it impossible for
+calling applications to supply custom Huffman tables when generating
+12-bit-per-component lossy JPEG images using the libjpeg API.
+
+4. Fixed a segfault that occurred when attempting to use the jpegtran `-drop`
+option with a specially-crafted malformed input image or drop image
+(specifically an image in which all of the scans contain fewer components than
+the number of components specified in the Start Of Frame segment.)
+
+
3.0.2
=====
diff --git a/src/3rdparty/libjpeg/src/jcmaster.c b/src/3rdparty/libjpeg/src/jcmaster.c
index 7e1408fcc9..161019763d 100644
--- a/src/3rdparty/libjpeg/src/jcmaster.c
+++ b/src/3rdparty/libjpeg/src/jcmaster.c
@@ -7,7 +7,7 @@
* Lossless JPEG Modifications:
* Copyright (C) 1999, Ken Murchison.
* libjpeg-turbo Modifications:
- * Copyright (C) 2010, 2016, 2018, 2022-2023, D. R. Commander.
+ * Copyright (C) 2010, 2016, 2018, 2022-2024, D. R. Commander.
* For conditions of distribution and use, see the accompanying README.ijg
* file.
*
@@ -50,6 +50,113 @@ jpeg_calc_jpeg_dimensions(j_compress_ptr cinfo)
#endif
+LOCAL(boolean)
+using_std_huff_tables(j_compress_ptr cinfo)
+{
+ int i;
+
+ static const UINT8 bits_dc_luminance[17] = {
+ /* 0-base */ 0, 0, 1, 5, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0
+ };
+ static const UINT8 val_dc_luminance[] = {
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11
+ };
+
+ static const UINT8 bits_dc_chrominance[17] = {
+ /* 0-base */ 0, 0, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0
+ };
+ static const UINT8 val_dc_chrominance[] = {
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11
+ };
+
+ static const UINT8 bits_ac_luminance[17] = {
+ /* 0-base */ 0, 0, 2, 1, 3, 3, 2, 4, 3, 5, 5, 4, 4, 0, 0, 1, 0x7d
+ };
+ static const UINT8 val_ac_luminance[] = {
+ 0x01, 0x02, 0x03, 0x00, 0x04, 0x11, 0x05, 0x12,
+ 0x21, 0x31, 0x41, 0x06, 0x13, 0x51, 0x61, 0x07,
+ 0x22, 0x71, 0x14, 0x32, 0x81, 0x91, 0xa1, 0x08,
+ 0x23, 0x42, 0xb1, 0xc1, 0x15, 0x52, 0xd1, 0xf0,
+ 0x24, 0x33, 0x62, 0x72, 0x82, 0x09, 0x0a, 0x16,
+ 0x17, 0x18, 0x19, 0x1a, 0x25, 0x26, 0x27, 0x28,
+ 0x29, 0x2a, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39,
+ 0x3a, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49,
+ 0x4a, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59,
+ 0x5a, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69,
+ 0x6a, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79,
+ 0x7a, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89,
+ 0x8a, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98,
+ 0x99, 0x9a, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7,
+ 0xa8, 0xa9, 0xaa, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6,
+ 0xb7, 0xb8, 0xb9, 0xba, 0xc2, 0xc3, 0xc4, 0xc5,
+ 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xd2, 0xd3, 0xd4,
+ 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xe1, 0xe2,
+ 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea,
+ 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8,
+ 0xf9, 0xfa
+ };
+
+ static const UINT8 bits_ac_chrominance[17] = {
+ /* 0-base */ 0, 0, 2, 1, 2, 4, 4, 3, 4, 7, 5, 4, 4, 0, 1, 2, 0x77
+ };
+ static const UINT8 val_ac_chrominance[] = {
+ 0x00, 0x01, 0x02, 0x03, 0x11, 0x04, 0x05, 0x21,
+ 0x31, 0x06, 0x12, 0x41, 0x51, 0x07, 0x61, 0x71,
+ 0x13, 0x22, 0x32, 0x81, 0x08, 0x14, 0x42, 0x91,
+ 0xa1, 0xb1, 0xc1, 0x09, 0x23, 0x33, 0x52, 0xf0,
+ 0x15, 0x62, 0x72, 0xd1, 0x0a, 0x16, 0x24, 0x34,
+ 0xe1, 0x25, 0xf1, 0x17, 0x18, 0x19, 0x1a, 0x26,
+ 0x27, 0x28, 0x29, 0x2a, 0x35, 0x36, 0x37, 0x38,
+ 0x39, 0x3a, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48,
+ 0x49, 0x4a, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58,
+ 0x59, 0x5a, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68,
+ 0x69, 0x6a, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78,
+ 0x79, 0x7a, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87,
+ 0x88, 0x89, 0x8a, 0x92, 0x93, 0x94, 0x95, 0x96,
+ 0x97, 0x98, 0x99, 0x9a, 0xa2, 0xa3, 0xa4, 0xa5,
+ 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xb2, 0xb3, 0xb4,
+ 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xc2, 0xc3,
+ 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xd2,
+ 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda,
+ 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9,
+ 0xea, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8,
+ 0xf9, 0xfa
+ };
+
+ if (cinfo->dc_huff_tbl_ptrs[0] == NULL ||
+ cinfo->ac_huff_tbl_ptrs[0] == NULL ||
+ cinfo->dc_huff_tbl_ptrs[1] == NULL ||
+ cinfo->ac_huff_tbl_ptrs[1] == NULL)
+ return FALSE;
+
+ for (i = 2; i < NUM_HUFF_TBLS; i++) {
+ if (cinfo->dc_huff_tbl_ptrs[i] != NULL ||
+ cinfo->ac_huff_tbl_ptrs[i] != NULL)
+ return FALSE;
+ }
+
+ if (memcmp(cinfo->dc_huff_tbl_ptrs[0]->bits, bits_dc_luminance,
+ sizeof(bits_dc_luminance)) ||
+ memcmp(cinfo->dc_huff_tbl_ptrs[0]->huffval, val_dc_luminance,
+ sizeof(val_dc_luminance)) ||
+ memcmp(cinfo->ac_huff_tbl_ptrs[0]->bits, bits_ac_luminance,
+ sizeof(bits_ac_luminance)) ||
+ memcmp(cinfo->ac_huff_tbl_ptrs[0]->huffval, val_ac_luminance,
+ sizeof(val_ac_luminance)) ||
+ memcmp(cinfo->dc_huff_tbl_ptrs[1]->bits, bits_dc_chrominance,
+ sizeof(bits_dc_chrominance)) ||
+ memcmp(cinfo->dc_huff_tbl_ptrs[1]->huffval, val_dc_chrominance,
+ sizeof(val_dc_chrominance)) ||
+ memcmp(cinfo->ac_huff_tbl_ptrs[1]->bits, bits_ac_chrominance,
+ sizeof(bits_ac_chrominance)) ||
+ memcmp(cinfo->ac_huff_tbl_ptrs[1]->huffval, val_ac_chrominance,
+ sizeof(val_ac_chrominance)))
+ return FALSE;
+
+ return TRUE;
+}
+
+
LOCAL(void)
initial_setup(j_compress_ptr cinfo, boolean transcode_only)
/* Do computations that are needed before master selection phase */
@@ -605,6 +712,8 @@ GLOBAL(void)
jinit_c_master_control(j_compress_ptr cinfo, boolean transcode_only)
{
my_master_ptr master = (my_master_ptr)cinfo->master;
+ boolean empty_huff_tables = TRUE;
+ int i;
master->pub.prepare_for_pass = prepare_for_pass;
master->pub.pass_startup = pass_startup;
@@ -646,7 +755,16 @@ jinit_c_master_control(j_compress_ptr cinfo, boolean transcode_only)
(cinfo->progressive_mode && !cinfo->arith_code))
cinfo->optimize_coding = TRUE; /* assume default tables no good for
progressive mode or lossless mode */
- if (cinfo->data_precision == 12 && !cinfo->arith_code)
+ for (i = 0; i < NUM_HUFF_TBLS; i++) {
+ if (cinfo->dc_huff_tbl_ptrs[i] != NULL ||
+ cinfo->ac_huff_tbl_ptrs[i] != NULL) {
+ empty_huff_tables = FALSE;
+ break;
+ }
+ }
+ if (cinfo->data_precision == 12 && !cinfo->arith_code &&
+ !cinfo->optimize_coding &&
+ (empty_huff_tables || using_std_huff_tables(cinfo)))
cinfo->optimize_coding = TRUE; /* assume default tables no good for 12-bit
data precision */
diff --git a/src/3rdparty/libjpeg/src/jconfig.h b/src/3rdparty/libjpeg/src/jconfig.h
index 1e03e166e7..e81574b9a4 100644
--- a/src/3rdparty/libjpeg/src/jconfig.h
+++ b/src/3rdparty/libjpeg/src/jconfig.h
@@ -2,9 +2,9 @@
#define JPEG_LIB_VERSION 80
-#define LIBJPEG_TURBO_VERSION 3.0.2
+#define LIBJPEG_TURBO_VERSION 3.0.3
-#define LIBJPEG_TURBO_VERSION_NUMBER 3000002
+#define LIBJPEG_TURBO_VERSION_NUMBER 3000003
#define C_ARITH_CODING_SUPPORTED 1
diff --git a/src/3rdparty/libjpeg/src/jconfigint.h b/src/3rdparty/libjpeg/src/jconfigint.h
index afcb25d247..6e7dbd75a1 100644
--- a/src/3rdparty/libjpeg/src/jconfigint.h
+++ b/src/3rdparty/libjpeg/src/jconfigint.h
@@ -10,7 +10,7 @@
#define PACKAGE_NAME "libjpeg-turbo"
-#define VERSION "3.0.2"
+#define VERSION "3.0.3"
#if SIZE_MAX == 0xffffffff
#define SIZEOF_SIZE_T 4
diff --git a/src/3rdparty/libjpeg/src/jerror.c b/src/3rdparty/libjpeg/src/jerror.c
index d0ab5b88b0..3a75fec02c 100644
--- a/src/3rdparty/libjpeg/src/jerror.c
+++ b/src/3rdparty/libjpeg/src/jerror.c
@@ -4,7 +4,7 @@
* This file was part of the Independent JPEG Group's software:
* Copyright (C) 1991-1998, Thomas G. Lane.
* libjpeg-turbo Modifications:
- * Copyright (C) 2022, D. R. Commander.
+ * Copyright (C) 2022, 2024, D. R. Commander.
* For conditions of distribution and use, see the accompanying README.ijg
* file.
*
@@ -46,7 +46,7 @@
#define JMESSAGE(code, string) string,
-const char * const jpeg_std_message_table[] = {
+static const char * const jpeg_std_message_table[] = {
#include "jerror.h"
NULL
};
@@ -229,23 +229,17 @@ reset_error_mgr(j_common_ptr cinfo)
GLOBAL(struct jpeg_error_mgr *)
jpeg_std_error(struct jpeg_error_mgr *err)
{
+ memset(err, 0, sizeof(struct jpeg_error_mgr));
+
err->error_exit = error_exit;
err->emit_message = emit_message;
err->output_message = output_message;
err->format_message = format_message;
err->reset_error_mgr = reset_error_mgr;
- err->trace_level = 0; /* default = no tracing */
- err->num_warnings = 0; /* no warnings emitted yet */
- err->msg_code = 0; /* may be useful as a flag for "no error" */
-
/* Initialize message table pointers */
err->jpeg_message_table = jpeg_std_message_table;
err->last_jpeg_message = (int)JMSG_LASTMSGCODE - 1;
- err->addon_message_table = NULL;
- err->first_addon_message = 0; /* for safety */
- err->last_addon_message = 0;
-
return err;
}
diff --git a/src/3rdparty/libjpeg/src/jversion.h b/src/3rdparty/libjpeg/src/jversion.h
index 5c127dc635..3b21737362 100644
--- a/src/3rdparty/libjpeg/src/jversion.h
+++ b/src/3rdparty/libjpeg/src/jversion.h
@@ -36,20 +36,21 @@
* their code
*/
-#define JCOPYRIGHT \
+#define JCOPYRIGHT1 \
"Copyright (C) 2009-2024 D. R. Commander\n" \
"Copyright (C) 2015, 2020 Google, Inc.\n" \
"Copyright (C) 2019-2020 Arm Limited\n" \
"Copyright (C) 2015-2016, 2018 Matthieu Darbois\n" \
"Copyright (C) 2011-2016 Siarhei Siamashka\n" \
- "Copyright (C) 2015 Intel Corporation\n" \
+ "Copyright (C) 2015 Intel Corporation\n"
+#define JCOPYRIGHT2 \
"Copyright (C) 2013-2014 Linaro Limited\n" \
"Copyright (C) 2013-2014 MIPS Technologies, Inc.\n" \
"Copyright (C) 2009, 2012 Pierre Ossman for Cendio AB\n" \
"Copyright (C) 2009-2011 Nokia Corporation and/or its subsidiary(-ies)\n" \
"Copyright (C) 1999-2006 MIYASAKA Masaru\n" \
"Copyright (C) 1999 Ken Murchison\n" \
- "Copyright (C) 1991-2020 Thomas G. Lane, Guido Vollbeding"
+ "Copyright (C) 1991-2020 Thomas G. Lane, Guido Vollbeding\n"
#define JCOPYRIGHT_SHORT \
"Copyright (C) 1991-2024 The libjpeg-turbo Project and many others"
diff --git a/src/3rdparty/libjpeg/zlib-license.txt b/src/3rdparty/libjpeg/zlib-license.txt
deleted file mode 100644
index 480c61edca..0000000000
--- a/src/3rdparty/libjpeg/zlib-license.txt
+++ /dev/null
@@ -1,15 +0,0 @@
-This software is provided 'as-is', without any express or implied
-warranty. In no event will the authors be held liable for any damages
-arising from the use of this software.
-
-Permission is granted to anyone to use this software for any purpose,
-including commercial applications, and to alter it and redistribute it
-freely, subject to the following restrictions:
-
-1. The origin of this software must not be misrepresented; you must not
- claim that you wrote the original software. If you use this software
- in a product, an acknowledgment in the product documentation would be
- appreciated but is not required.
-2. Altered source versions must be plainly marked as such, and must not be
- misrepresented as being the original software.
-3. This notice may not be removed or altered from any source distribution.
diff --git a/src/3rdparty/sqlite/qt_attribution.json b/src/3rdparty/sqlite/qt_attribution.json
index f878c0cb48..c5a5b12062 100644
--- a/src/3rdparty/sqlite/qt_attribution.json
+++ b/src/3rdparty/sqlite/qt_attribution.json
@@ -9,7 +9,7 @@
"Homepage": "https://www.sqlite.org/",
"Version": "3.45.3",
"DownloadLocation": "https://www.sqlite.org/2024/sqlite-amalgamation-3450300.zip",
- "License": "Public Domain",
- "LicenseId": "CC0-1.0",
+ "License": "SQLite Blessing",
+ "LicenseId": "blessing",
"Copyright": "The authors disclaim copyright to the source code. However, a license can be obtained if needed."
}
diff --git a/src/android/CMakeLists.txt b/src/android/CMakeLists.txt
index 045076c8d3..007430598e 100644
--- a/src/android/CMakeLists.txt
+++ b/src/android/CMakeLists.txt
@@ -8,5 +8,6 @@ if (ANDROID)
add_subdirectory(jar)
add_subdirectory(java)
add_subdirectory(templates)
+ add_subdirectory(templates_aar)
endif()
diff --git a/src/android/jar/CMakeLists.txt b/src/android/jar/CMakeLists.txt
index 698853588c..c36bbdf75b 100644
--- a/src/android/jar/CMakeLists.txt
+++ b/src/android/jar/CMakeLists.txt
@@ -38,6 +38,8 @@ set(java_sources
src/org/qtproject/qt/android/QtEmbeddedDelegateFactory.java
src/org/qtproject/qt/android/QtEmbeddedLoader.java
src/org/qtproject/qt/android/QtView.java
+ src/org/qtproject/qt/android/QtEmbeddedViewInterface.java
+ src/org/qtproject/qt/android/QtServiceEmbeddedDelegate.java
)
qt_internal_add_jar(Qt${QtBase_VERSION_MAJOR}Android
diff --git a/src/android/jar/src/org/qtproject/qt/android/QtEmbeddedDelegate.java b/src/android/jar/src/org/qtproject/qt/android/QtEmbeddedDelegate.java
index 1c0fd0f7d8..ff694777d5 100644
--- a/src/android/jar/src/org/qtproject/qt/android/QtEmbeddedDelegate.java
+++ b/src/android/jar/src/org/qtproject/qt/android/QtEmbeddedDelegate.java
@@ -21,16 +21,14 @@ import android.view.ViewGroup;
import java.util.ArrayList;
import java.util.HashMap;
-class QtEmbeddedDelegate extends QtActivityDelegateBase implements QtNative.AppStateDetailsListener {
+class QtEmbeddedDelegate extends QtActivityDelegateBase
+ implements QtNative.AppStateDetailsListener, QtEmbeddedViewInterface
+{
// TODO simplistic implementation with one QtView, expand to support multiple views QTBUG-117649
private QtView m_view;
- private long m_rootWindowRef = 0L;
private QtNative.ApplicationStateDetails m_stateDetails;
private boolean m_windowLoaded = false;
- private static native void createRootWindow(View rootView, int x, int y, int width, int height);
- static native void deleteWindow(long windowReference);
-
public QtEmbeddedDelegate(Activity context) {
super(context);
@@ -82,7 +80,6 @@ class QtEmbeddedDelegate extends QtActivityDelegateBase implements QtNative.AppS
QtNative.terminateQt();
QtNative.setActivity(null);
QtNative.getQtThread().exit();
- onDestroy();
}
}
});
@@ -92,11 +89,17 @@ class QtEmbeddedDelegate extends QtActivityDelegateBase implements QtNative.AppS
public void onAppStateDetailsChanged(QtNative.ApplicationStateDetails details) {
synchronized (this) {
m_stateDetails = details;
- if (m_stateDetails.nativePluginIntegrationReady) {
+ }
+ }
+
+ @Override
+ public void onNativePluginIntegrationReadyChanged(boolean ready)
+ {
+ synchronized (this) {
+ if (ready) {
QtNative.runAction(() -> {
DisplayMetrics metrics = Resources.getSystem().getDisplayMetrics();
- QtDisplayManager.setApplicationDisplayMetrics(m_activity,
- metrics.widthPixels,
+ QtDisplayManager.setApplicationDisplayMetrics(m_activity, metrics.widthPixels,
metrics.heightPixels);
});
@@ -131,6 +134,14 @@ class QtEmbeddedDelegate extends QtActivityDelegateBase implements QtNative.AppS
return m_view.getQtWindow();
}
+ // QtEmbeddedViewInterface implementation begin
+ @Override
+ public void startQtApplication(String appParams, String mainLib)
+ {
+ super.startNativeApplication(appParams, mainLib);
+ }
+
+ @Override
public void queueLoadWindow()
{
synchronized (this) {
@@ -139,12 +150,15 @@ class QtEmbeddedDelegate extends QtActivityDelegateBase implements QtNative.AppS
}
}
- void setView(QtView view) {
+ @Override
+ public void setView(QtView view)
+ {
m_view = view;
updateInputDelegate();
if (m_view != null)
registerGlobalFocusChangeListener(m_view);
}
+ // QtEmbeddedViewInterface implementation end
private void updateInputDelegate() {
if (m_view == null) {
@@ -154,20 +168,10 @@ class QtEmbeddedDelegate extends QtActivityDelegateBase implements QtNative.AppS
m_inputDelegate.setEditPopupMenu(new EditPopupMenu(m_activity, m_view));
}
-
- public void setRootWindowRef(long ref) {
- m_rootWindowRef = ref;
- }
-
- public void onDestroy() {
- if (m_rootWindowRef != 0L)
- deleteWindow(m_rootWindowRef);
- m_rootWindowRef = 0L;
- }
-
private void createRootWindow() {
if (m_view != null && !m_windowLoaded) {
- createRootWindow(m_view, m_view.getLeft(), m_view.getTop(), m_view.getWidth(), m_view.getHeight());
+ QtView.createRootWindow(m_view, m_view.getLeft(), m_view.getTop(), m_view.getWidth(),
+ m_view.getHeight());
m_windowLoaded = true;
}
}
diff --git a/src/android/jar/src/org/qtproject/qt/android/QtEmbeddedLoader.java b/src/android/jar/src/org/qtproject/qt/android/QtEmbeddedLoader.java
index 65cfcbeef1..0c6c4b49f0 100644
--- a/src/android/jar/src/org/qtproject/qt/android/QtEmbeddedLoader.java
+++ b/src/android/jar/src/org/qtproject/qt/android/QtEmbeddedLoader.java
@@ -41,6 +41,7 @@ class QtEmbeddedLoader extends QtLoader {
setEnvironmentVariable("QT_ANDROID_THEME_DISPLAY_DPI", String.valueOf(displayDensity));
String stylePath = ExtractStyle.setup(m_context, "minimal", displayDensity);
setEnvironmentVariable("ANDROID_STYLE_PATH", stylePath);
+ setEnvironmentVariable("QT_ANDROID_NO_EXIT_CALL", String.valueOf(true));
}
@Override
diff --git a/src/android/jar/src/org/qtproject/qt/android/QtEmbeddedViewInterface.java b/src/android/jar/src/org/qtproject/qt/android/QtEmbeddedViewInterface.java
new file mode 100644
index 0000000000..a83a65e32c
--- /dev/null
+++ b/src/android/jar/src/org/qtproject/qt/android/QtEmbeddedViewInterface.java
@@ -0,0 +1,15 @@
+// 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
+
+package org.qtproject.qt.android;
+
+/**
+ * QtEmbeddedViewInterface is intended to encapsulate the needs of QtView, so that the Activity and
+ * Service implementations of these functions may be split clearly, and the interface can be stored
+ * and used conveniently in QtView.
+**/
+interface QtEmbeddedViewInterface {
+ void startQtApplication(String appParams, String mainLib);
+ void setView(QtView view);
+ void queueLoadWindow();
+};
diff --git a/src/android/jar/src/org/qtproject/qt/android/QtNative.java b/src/android/jar/src/org/qtproject/qt/android/QtNative.java
index 97a45ef8fa..b2a2887ad5 100644
--- a/src/android/jar/src/org/qtproject/qt/android/QtNative.java
+++ b/src/android/jar/src/org/qtproject/qt/android/QtNative.java
@@ -196,7 +196,8 @@ class QtNative
}
interface AppStateDetailsListener {
- void onAppStateDetailsChanged(ApplicationStateDetails details);
+ default void onAppStateDetailsChanged(ApplicationStateDetails details) {}
+ default void onNativePluginIntegrationReadyChanged(boolean ready) {}
}
// Keep in sync with src/corelib/global/qnamespace.h
@@ -228,6 +229,7 @@ class QtNative
public static void notifyNativePluginIntegrationReady(boolean ready)
{
m_stateDetails.nativePluginIntegrationReady = ready;
+ notifyNativePluginIntegrationReadyChanged(ready);
notifyAppStateDetailsChanged(m_stateDetails);
}
@@ -258,6 +260,13 @@ class QtNative
}
}
+ static void notifyNativePluginIntegrationReadyChanged(boolean ready) {
+ synchronized (m_appStateListenersLock) {
+ for (final AppStateDetailsListener listener : m_appStateListeners)
+ listener.onNativePluginIntegrationReadyChanged(ready);
+ }
+ }
+
static void notifyAppStateDetailsChanged(ApplicationStateDetails details) {
synchronized (m_appStateListenersLock) {
for (AppStateDetailsListener listener : m_appStateListeners)
@@ -343,7 +352,7 @@ class QtNative
if (isServiceValid())
m_service.get().stopSelf();
m_stateDetails.isStarted = false;
- // Likely no use to call notifyAppStateDetailsChanged at this point since we are exiting
+ notifyAppStateDetailsChanged(m_stateDetails);
});
}
diff --git a/src/android/jar/src/org/qtproject/qt/android/QtServiceEmbeddedDelegate.java b/src/android/jar/src/org/qtproject/qt/android/QtServiceEmbeddedDelegate.java
new file mode 100644
index 0000000000..29f1d1790f
--- /dev/null
+++ b/src/android/jar/src/org/qtproject/qt/android/QtServiceEmbeddedDelegate.java
@@ -0,0 +1,115 @@
+// 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
+
+package org.qtproject.qt.android;
+
+import static org.qtproject.qt.android.QtNative.ApplicationState.ApplicationSuspended;
+
+import android.app.Service;
+import android.content.Context;
+import android.content.res.Resources;
+import android.hardware.display.DisplayManager;
+import android.view.Display;
+import android.view.View;
+import android.util.DisplayMetrics;
+
+/**
+ * QtServiceEmbeddedDelegate is used for embedding QML into Android Service contexts. Implements
+ * {@link QtEmbeddedViewInterface} so it can be used by QtView to communicate with the Qt layer.
+ */
+class QtServiceEmbeddedDelegate implements QtEmbeddedViewInterface, QtNative.AppStateDetailsListener
+{
+ private final Service m_service;
+ private QtView m_view;
+ private boolean m_windowLoaded = false;
+
+ QtServiceEmbeddedDelegate(Service service)
+ {
+ m_service = service;
+ QtNative.registerAppStateListener(this);
+ QtNative.setService(service);
+ }
+
+ @UsedFromNativeCode
+ QtInputDelegate getInputDelegate()
+ {
+ // TODO Implement text input (QTBUG-122552)
+ return null;
+ }
+
+ @Override
+ public void onNativePluginIntegrationReadyChanged(boolean ready)
+ {
+ synchronized (this) {
+ if (ready) {
+ QtNative.runAction(() -> {
+ if (m_view == null)
+ return;
+
+ final DisplayMetrics metrics = Resources.getSystem().getDisplayMetrics();
+
+ final int maxWidth = m_view.getWidth();
+ final int maxHeight = m_view.getHeight();
+ final int width = maxWidth;
+ final int height = maxHeight;
+ final int insetLeft = m_view.getLeft();
+ final int insetTop = m_view.getTop();
+
+ final DisplayManager dm = m_service.getSystemService(DisplayManager.class);
+ QtDisplayManager.setDisplayMetrics(
+ maxWidth, maxHeight, insetLeft, insetTop, width, height,
+ QtDisplayManager.getXDpi(metrics), QtDisplayManager.getYDpi(metrics),
+ metrics.scaledDensity, metrics.density,
+ QtDisplayManager.getRefreshRate(
+ dm.getDisplay(Display.DEFAULT_DISPLAY)));
+ });
+ createRootWindow();
+ }
+ }
+ }
+
+ // QtEmbeddedViewInterface implementation begin
+ @Override
+ public void startQtApplication(String appParams, String mainLib)
+ {
+ QtNative.startApplication(appParams, mainLib);
+ }
+
+ @Override
+ public void setView(QtView view)
+ {
+ m_view = view;
+ // If the embedded view is destroyed, do cleanup:
+ if (view == null)
+ cleanup();
+ }
+
+ @Override
+ public void queueLoadWindow()
+ {
+ synchronized (this) {
+ if (QtNative.getStateDetails().nativePluginIntegrationReady)
+ createRootWindow();
+ }
+ }
+ // QtEmbeddedViewInterface implementation end
+
+ private void createRootWindow()
+ {
+ if (m_view != null && !m_windowLoaded) {
+ QtView.createRootWindow(m_view, m_view.getLeft(), m_view.getTop(), m_view.getWidth(),
+ m_view.getHeight());
+ m_windowLoaded = true;
+ }
+ }
+
+ private void cleanup()
+ {
+ QtNative.setApplicationState(ApplicationSuspended);
+ QtNative.unregisterAppStateListener(QtServiceEmbeddedDelegate.this);
+
+ QtNative.terminateQt();
+ QtNative.setService(null);
+ QtNative.getQtThread().exit();
+ }
+}
diff --git a/src/android/jar/src/org/qtproject/qt/android/QtView.java b/src/android/jar/src/org/qtproject/qt/android/QtView.java
index 6836171187..ddf70b3b5b 100644
--- a/src/android/jar/src/org/qtproject/qt/android/QtView.java
+++ b/src/android/jar/src/org/qtproject/qt/android/QtView.java
@@ -29,14 +29,17 @@ abstract class QtView extends ViewGroup {
protected QtWindow m_window;
protected long m_windowReference;
+ protected long m_parentWindowReference;
protected QtWindowListener m_windowListener;
- protected QtEmbeddedDelegate m_delegate;
+ protected QtEmbeddedViewInterface m_viewInterface;
// Implement in subclass to handle the creation of the QWindow and its parent container.
// TODO could we take care of the parent window creation and parenting outside of the
// window creation method to simplify things if user would extend this? Preferably without
// too much JNI back and forth. Related to parent window creation, so handle with QTBUG-121511.
abstract protected void createWindow(long parentWindowRef);
+ static native void createRootWindow(View rootView, int x, int y, int width, int height);
+ static native void deleteWindow(long windowReference);
private static native void setWindowVisible(long windowReference, boolean visible);
private static native void resizeWindow(long windowReference,
int x, int y, int width, int height);
@@ -57,7 +60,7 @@ abstract class QtView extends ViewGroup {
}
QtEmbeddedLoader loader = new QtEmbeddedLoader(context);
- m_delegate = QtEmbeddedDelegateFactory.create((Activity)context);
+ m_viewInterface = QtEmbeddedDelegateFactory.create((Activity)context);
loader.setMainLibraryName(appLibName);
addOnLayoutChangeListener(new View.OnLayoutChangeListener() {
@Override
@@ -77,22 +80,22 @@ abstract class QtView extends ViewGroup {
});
loader.loadQtLibraries();
// Start Native Qt application
- m_delegate.startNativeApplication(loader.getApplicationParameters(),
- loader.getMainLibraryPath());
+ m_viewInterface.startQtApplication(loader.getApplicationParameters(),
+ loader.getMainLibraryPath());
}
@Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
- m_delegate.setView(this);
- m_delegate.queueLoadWindow();
+ m_viewInterface.setView(this);
+ m_viewInterface.queueLoadWindow();
}
@Override
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
destroyWindow();
- m_delegate.setView(null);
+ m_viewInterface.setView(null);
}
@Override
@@ -156,7 +159,7 @@ abstract class QtView extends ViewGroup {
// viewReference - the reference to the created QQuickView
void addQtWindow(QtWindow window, long viewReference, long parentWindowRef) {
setWindowReference(viewReference);
- m_delegate.setRootWindowRef(parentWindowRef);
+ m_parentWindowReference = parentWindowRef;
final Handler handler = new Handler(Looper.getMainLooper());
handler.post(new Runnable() {
@Override
@@ -176,9 +179,9 @@ abstract class QtView extends ViewGroup {
// Destroy the underlying QWindow
void destroyWindow() {
- if (m_windowReference != 0L)
- QtEmbeddedDelegate.deleteWindow(m_windowReference);
- m_windowReference = 0L;
+ if (m_parentWindowReference != 0L)
+ deleteWindow(m_parentWindowReference);
+ m_parentWindowReference = 0L;
}
QtWindow getQtWindow() {
diff --git a/src/android/templates/build.gradle b/src/android/templates/build.gradle
index f94ffbde54..c5d92698a3 100644
--- a/src/android/templates/build.gradle
+++ b/src/android/templates/build.gradle
@@ -5,7 +5,7 @@ buildscript {
}
dependencies {
- classpath 'com.android.tools.build:gradle:7.4.1'
+ classpath 'com.android.tools.build:gradle:8.2.2'
}
}
@@ -14,11 +14,11 @@ repositories {
mavenCentral()
}
-apply plugin: 'com.android.application'
+apply plugin: qtGradlePluginType
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar', '*.aar'])
- implementation 'androidx.core:core:1.10.1'
+ implementation 'androidx.core:core:1.13.1'
}
android {
@@ -29,12 +29,14 @@ android {
* - qtAndroidDir - holds the path to qt android files
* needed to build any Qt application
* on Android.
+ * - qtGradlePluginType - whether to build an app or a library
*
* are defined in gradle.properties file. This file is
* updated by QtCreator and androiddeployqt tools.
* Changing them manually might break the compilation!
*******************************************************/
+ namespace androidPackageName
compileSdkVersion androidCompileSdkVersion
buildToolsVersion androidBuildToolsVersion
ndkVersion androidNdkVersion
diff --git a/src/android/templates/doc/src/android-manifest-file-configuration.qdoc b/src/android/templates/doc/src/android-manifest-file-configuration.qdoc
index db0d3c7277..79268f7576 100644
--- a/src/android/templates/doc/src/android-manifest-file-configuration.qdoc
+++ b/src/android/templates/doc/src/android-manifest-file-configuration.qdoc
@@ -50,6 +50,8 @@ Qt sets the following manifest configuration by default:
\li {1, 5} \l {Android: App Manifest <manifest>}{<manifest>}
\li package
\li Sets the package name. The default value is \c {org.qtproject.example.app_name}.
+ \warning This field is deprecated and moved to \c build.gradle. It will be removed
+ in an upcoming release.
\row
\li \c {android:installLocation}
\li Sets the app's installation location, whether internal or external storage.
diff --git a/src/android/templates_aar/AndroidManifest.xml b/src/android/templates_aar/AndroidManifest.xml
new file mode 100644
index 0000000000..9c9e91e650
--- /dev/null
+++ b/src/android/templates_aar/AndroidManifest.xml
@@ -0,0 +1,9 @@
+<manifest
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ package="org.qtproject.example"
+ android:versionCode="-- %%INSERT_VERSION_CODE%% --"
+ android:versionName="-- %%INSERT_VERSION_NAME%% --">
+ <!-- %%INSERT_PERMISSIONS -->
+ <!-- %%INSERT_FEATURES -->
+</manifest>
diff --git a/src/android/templates_aar/CMakeLists.txt b/src/android/templates_aar/CMakeLists.txt
new file mode 100644
index 0000000000..07c421fbab
--- /dev/null
+++ b/src/android/templates_aar/CMakeLists.txt
@@ -0,0 +1,24 @@
+# Copyright (C) 2024 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
+# Android aar specific template files
+
+set(templates_aar_files
+ "${CMAKE_CURRENT_SOURCE_DIR}/AndroidManifest.xml")
+
+add_custom_target(Qt${QtBase_VERSION_MAJOR}AndroidAarTemplates
+ SOURCES
+ ${templates_aar_files}
+)
+
+qt_path_join(destination ${QT_INSTALL_DIR} ${INSTALL_DATADIR} "src/android/templates_aar")
+
+qt_copy_or_install(FILES ${templates_aar_files}
+ DESTINATION "${destination}")
+
+if(NOT QT_WILL_INSTALL)
+ qt_internal_copy_at_build_time(TARGET Qt${QtBase_VERSION_MAJOR}AndroidAarTemplates
+ FILES ${templates_aar_files}
+ DESTINATION ${destination}
+ )
+endif()
diff --git a/src/corelib/CMakeLists.txt b/src/corelib/CMakeLists.txt
index e1fb0d7e39..3707d5f886 100644
--- a/src/corelib/CMakeLists.txt
+++ b/src/corelib/CMakeLists.txt
@@ -456,7 +456,7 @@ endif()
set(core_version_tagging_files
global/qversiontagging.cpp)
qt_internal_extend_target(Core
- CONDITION TEST_ld_version_script OR APPLE OR WIN32
+ CONDITION QT_FEATURE_version_tagging
SOURCES ${core_version_tagging_files}
)
@@ -897,7 +897,7 @@ qt_internal_extend_target(Core CONDITION QT_FEATURE_timezone AND UNIX AND NOT AN
qt_internal_extend_target(Core
CONDITION
- QT_FEATURE_icu AND QT_FEATURE_timezone AND NOT ANDROID AND NOT APPLE
+ QT_FEATURE_icu AND QT_FEATURE_timezone AND NOT UNIX
SOURCES
time/qtimezoneprivate_icu.cpp
)
diff --git a/src/corelib/Qt6AndroidMacros.cmake b/src/corelib/Qt6AndroidMacros.cmake
index 6218df1947..3e96130b85 100644
--- a/src/corelib/Qt6AndroidMacros.cmake
+++ b/src/corelib/Qt6AndroidMacros.cmake
@@ -217,6 +217,10 @@ function(qt6_android_generate_deployment_settings target)
${target} "_qt_android_native_package_source_dir")
# version code
+ _qt_internal_add_android_deployment_property(file_contents "android-package-name"
+ ${target} "QT_ANDROID_PACKAGE_NAME")
+
+ # version code
_qt_internal_add_android_deployment_property(file_contents "android-version-code"
${target} "QT_ANDROID_VERSION_CODE")
@@ -381,6 +385,9 @@ function(qt6_android_add_apk_target target)
if(TARGET aab)
add_dependencies(aab ${target}_make_aab)
endif()
+ if(TARGET aar)
+ add_dependencies(aar ${target}_make_aar)
+ endif()
if(TARGET apk)
add_dependencies(apk ${target}_make_apk)
_qt_internal_create_global_apk_all_target_if_needed()
@@ -418,8 +425,10 @@ function(qt6_android_add_apk_target target)
endif()
set(apk_file_name "${target}.apk")
+ set(aar_file_name "${target}.aar")
set(dep_file_name "${target}.d")
set(apk_final_file_path "${apk_final_dir}/${apk_file_name}")
+ set(aar_final_file_path "${apk_final_dir}/${aar_file_name}")
set(dep_file_path "${apk_final_dir}/${dep_file_name}")
set(target_file_copy_relative_path
"libs/${CMAKE_ANDROID_ARCH_ABI}/$<TARGET_FILE_NAME:${target}>")
@@ -523,10 +532,33 @@ function(qt6_android_add_apk_target target)
VERBATIM
${uses_terminal}
)
+
+ # Add custom command that creates the aar and triggers rebuild if files listed in
+ # ${dep_file_path} are changed.
+ add_custom_command(OUTPUT "${aar_final_file_path}"
+ COMMAND ${CMAKE_COMMAND}
+ -E copy "$<TARGET_FILE:${target}>"
+ "${apk_final_dir}/${target_file_copy_relative_path}"
+ COMMAND "${deployment_tool}"
+ --input "${deployment_file}"
+ --output "${apk_final_dir}"
+ --apk "${aar_final_file_path}"
+ --depfile "${dep_file_path}"
+ --builddir "${relative_to_dir}"
+ --build-aar
+ ${extra_args}
+ COMMENT "Creating AAR for ${target}"
+ DEPENDS "${target}" "${deployment_file}" ${extra_deps}
+ DEPFILE "${dep_file_path}"
+ VERBATIM
+ ${uses_terminal}
+ )
cmake_policy(POP)
# Create a ${target}_make_apk target to trigger the apk build.
add_custom_target(${target}_make_apk DEPENDS "${apk_final_file_path}")
+ # Create a ${target}_make_aar target to trigger the aar build.
+ add_custom_target(${target}_make_aar DEPENDS "${aar_final_file_path}")
else()
add_custom_target(${target}_make_apk
DEPENDS ${target}_prepare_apk_dir
@@ -540,6 +572,19 @@ function(qt6_android_add_apk_target target)
VERBATIM
${uses_terminal}
)
+
+ add_custom_target(${target}_make_aar
+ DEPENDS ${target}_prepare_apk_dir
+ COMMAND ${deployment_tool}
+ --input ${deployment_file}
+ --output ${apk_final_dir}
+ --apk ${aar_final_file_path}
+ --build-aar
+ ${extra_args}
+ COMMENT "Creating AAR for ${target}"
+ VERBATIM
+ ${uses_terminal}
+ )
endif()
# Add target triggering AAB creation. Since the _make_aab target is not added to the ALL
@@ -638,6 +683,11 @@ function(_qt_internal_create_global_android_targets)
# It will trigger building all the apk build targets that are added as part of the project.
# Allow opting out.
_qt_internal_create_global_android_targets_impl(aab)
+
+ # Create a top-level "aar" target for convenience, so that users can call 'ninja aar'.
+ # It will trigger building all the aar build targets that are added as part of the project.
+ # Allow opting out.
+ _qt_internal_create_global_android_targets_impl(aar)
endfunction()
# The function collects all known non-imported shared libraries that are created in the build tree.
diff --git a/src/corelib/Qt6CoreMacros.cmake b/src/corelib/Qt6CoreMacros.cmake
index 9e71b4265a..1a449b4aa3 100644
--- a/src/corelib/Qt6CoreMacros.cmake
+++ b/src/corelib/Qt6CoreMacros.cmake
@@ -3155,13 +3155,17 @@ endfunction()
# Write deployment information for the targets of the project.
function(_qt_internal_write_target_deploy_info out_file)
set(targets "")
+ set(dynamic_target_types EXECUTABLE SHARED_LIBRARY MODULE_LIBRARY)
_qt_internal_collect_buildsystem_targets(targets
- "${CMAKE_SOURCE_DIR}" INCLUDE EXECUTABLE SHARED_LIBRARY MODULE_LIBRARY)
+ "${CMAKE_SOURCE_DIR}" INCLUDE ${dynamic_target_types} STATIC_LIBRARY)
set(content "")
foreach(target IN LISTS targets)
set(var_prefix "__QT_DEPLOY_TARGET_${target}")
string(APPEND content "set(${var_prefix}_FILE $<TARGET_FILE:${target}>)\n")
- if(WIN32 AND CMAKE_VERSION GREATER_EQUAL "3.21")
+ get_target_property(target_type ${target} TYPE)
+ string(APPEND content "set(${var_prefix}_TYPE ${target_type})\n")
+ if(WIN32 AND CMAKE_VERSION GREATER_EQUAL "3.21"
+ AND target_type IN_LIST dynamic_target_types)
string(APPEND content
"set(${var_prefix}_RUNTIME_DLLS $<TARGET_RUNTIME_DLLS:${target}>)\n")
endif()
diff --git a/src/corelib/compat/removed_api.cpp b/src/corelib/compat/removed_api.cpp
index 9b7ad48384..8bb7dad008 100644
--- a/src/corelib/compat/removed_api.cpp
+++ b/src/corelib/compat/removed_api.cpp
@@ -4,7 +4,6 @@
#define QT_CORE_BUILD_REMOVED_API
#include "qglobal.h"
-#include "qnumeric.h"
QT_USE_NAMESPACE
@@ -211,7 +210,7 @@ void QObject::setObjectName(const QString &name)
void QSettings::beginGroup(const QString &prefix)
{
- return beginGroup(qToAnyStringViewIgnoringNull(prefix));
+ beginGroup(qToAnyStringViewIgnoringNull(prefix));
}
int QSettings::beginReadArray(const QString &prefix)
@@ -929,6 +928,7 @@ QUrl QUrl::fromEncoded(const QByteArray &input, ParsingMode mode)
#endif // QT_CORE_REMOVED_SINCE(6, 7)
#if QT_CORE_REMOVED_SINCE(6, 8)
+#include "qbitarray.h" // inlined API
#include "qbytearray.h" // inlined API
@@ -952,6 +952,13 @@ bool QDir::operator==(const QDir &dir) const
return comparesEqual(*this, dir);
}
+#include "qeasingcurve.h"
+
+bool QEasingCurve::operator==(const QEasingCurve &other) const
+{
+ return comparesEqual(*this, other);
+}
+
#include "qfileinfo.h" // inlined API
bool QFileInfo::operator==(const QFileInfo &fileinfo) const
@@ -1005,17 +1012,29 @@ bool QJsonValue::operator!=(const QJsonValue &other) const
return !comparesEqual(*this, other);
}
+#include "qline.h" // inlined API
+
+#include "qmimetype.h"
+
+bool QMimeType::operator==(const QMimeType &other) const
+{
+ return comparesEqual(*this, other);
+}
+
#include "qobject.h"
+#include "qnumeric.h"
int QObject::startTimer(std::chrono::milliseconds time, Qt::TimerType timerType)
{
using namespace std::chrono;
using ratio = std::ratio_divide<std::milli, std::nano>;
- if (nanoseconds::rep r; qMulOverflow<ratio::num>(time.count(), &r)) {
- qWarning("QObject::startTimer(std::chrono::milliseconds time ...): "
- "'time' arg will overflow when converted to nanoseconds.");
+ nanoseconds::rep r;
+ if (qMulOverflow<ratio::num>(time.count(), &r)) {
+ qWarning("QObject::startTimer(std::chrono::milliseconds): "
+ "'time' arg overflowed when converted to nanoseconds.");
+ r = nanoseconds::max().count();
}
- return startTimer(nanoseconds{time}, timerType);
+ return startTimer(nanoseconds{r}, timerType);
}
#if QT_CONFIG(processenvironment)
@@ -1027,6 +1046,65 @@ bool QProcessEnvironment::operator==(const QProcessEnvironment &other) const
}
#endif // QT_CONFIG(processenvironment)
+#if QT_CONFIG(regularexpression)
+#include "qregularexpression.h"
+
+bool QRegularExpressionMatch::hasCaptured(QStringView name) const
+{
+ return hasCaptured(QAnyStringView(name));
+}
+
+QString QRegularExpressionMatch::captured(QStringView name) const
+{
+ return captured(QAnyStringView(name));
+}
+
+QStringView QRegularExpressionMatch::capturedView(QStringView name) const
+{
+ return capturedView(QAnyStringView(name));
+}
+
+qsizetype QRegularExpressionMatch::capturedStart(QStringView name) const
+{
+ return capturedStart(QAnyStringView(name));
+}
+
+qsizetype QRegularExpressionMatch::capturedLength(QStringView name) const
+{
+ return capturedLength(QAnyStringView(name));
+}
+
+qsizetype QRegularExpressionMatch::capturedEnd(QStringView name) const
+{
+ return capturedEnd(QAnyStringView(name));
+}
+
+bool QRegularExpression::operator==(const QRegularExpression &other) const
+{
+ return comparesEqual(*this, other);
+}
+#endif // QT_CONFIG(regularexpression)
+
+#if QT_CONFIG(future)
+#include "qresultstore.h"
+
+bool QtPrivate::ResultIteratorBase::operator==(const QtPrivate::ResultIteratorBase &other) const
+{
+ return comparesEqual(*this, other);
+}
+
+bool QtPrivate::ResultIteratorBase::operator!=(const QtPrivate::ResultIteratorBase &other) const
+{
+ return !comparesEqual(*this, other);
+}
+#endif // QT_CONFIG(future)
+
+#include "qstring.h" // inlined API
+
+#if QT_CONFIG(thread)
+# include "qthreadpool.h" // inlined API
+#endif
+
#include "qurl.h"
bool QUrl::operator<(const QUrl &url) const
@@ -1051,8 +1129,6 @@ bool QUrlQuery::operator==(const QUrlQuery &other) const
return comparesEqual(*this, other);
}
-#include "qstring.h" // inlined API
-
#include "qxmlstream.h" // inlined API
// #include "qotherheader.h"
diff --git a/src/corelib/configure.cmake b/src/corelib/configure.cmake
index 80e6d93193..83983dfa2e 100644
--- a/src/corelib/configure.cmake
+++ b/src/corelib/configure.cmake
@@ -430,6 +430,34 @@ const auto backtrace = std::stacktrace::current();
CXX_STANDARD 23
)
+# <future>
+qt_config_compile_test(cxx_std_async_noncopyable
+ LABEL "std::async() NonCopyable"
+ CODE
+"// Calling std::async with lambda which takes non-copyable argument causes compilation error on
+// some platforms (VxWorks 24.03 and older with C++17-compatibility for example)
+#include <future>
+
+class NonCopyable {
+public:
+ NonCopyable(const NonCopyable&) = delete;
+ NonCopyable(NonCopyable&&) = default;
+
+ NonCopyable(int value)
+ :value (value)
+ {}
+
+ int value;
+};
+
+int main(int argc, char** argv) {
+ return std::async(
+ std::launch::deferred,
+ [](NonCopyable value) { return value.value; },
+ NonCopyable(argc - 1)).get();
+}
+")
+
#### Features
qt_feature("clock-gettime" PRIVATE
@@ -458,7 +486,7 @@ qt_feature("system-doubleconversion" PRIVATE
)
qt_feature("cxx11_future" PUBLIC
LABEL "C++11 <future>"
- CONDITION NOT VXWORKS
+ CONDITION TEST_cxx_std_async_noncopyable
)
qt_feature("cxx17_filesystem" PUBLIC
LABEL "C++17 <filesystem>"
@@ -832,7 +860,8 @@ qt_feature("timezone_locale" PRIVATE
SECTION "Utilities"
LABEL "QTimeZone"
PURPOSE "Provides support for localized time-zone display names."
- DISABLE ON # Implementation is currently incomplete, so leave turned off
+ CONDITION
+ QT_FEATURE_timezone AND ( ( UNIX AND NOT APPLE AND NOT ANDROID ) OR QT_FEATURE_icu )
)
qt_feature("datetimeparser" PRIVATE
SECTION "Utilities"
diff --git a/src/corelib/doc/src/cmake/cmake-properties.qdoc b/src/corelib/doc/src/cmake/cmake-properties.qdoc
index 8fe2b0e88f..3740b29612 100644
--- a/src/corelib/doc/src/cmake/cmake-properties.qdoc
+++ b/src/corelib/doc/src/cmake/cmake-properties.qdoc
@@ -245,6 +245,55 @@ CMake will attempt to use the latest installed version.
*/
/*!
+\page cmake-target-property-qt-android-package-name.html
+\ingroup cmake-properties-qtcore
+\ingroup cmake-target-properties-qtcore
+
+\title QT_ANDROID_PACKAGE_NAME
+\target cmake-target-property-QT_ANDROID_PACKAGE_NAME
+
+\summary {The app's package name.}
+
+\cmakepropertysince 6.8
+\preliminarycmakeproperty
+\cmakepropertyandroidonly
+
+Specifies the app's package name. This is usually a unique dot separated
+name for the app, that will be used to identify the app on devices or in
+the Play Store. For example, "org.qtproject.example.gallery".
+
+The package name set by this property is passed to the \c build.gradle file
+as a \c namespace property, instead of \c AndroidManifest.xml, since the
+latter is deprecated since Android Gradle Plugin 7.4.
+
+The package name considers some words or characters as illegal and the build
+will clean such names if any is encountered. An underscore (\c _) either replaces
+illegal characters or is appended to illegal words.
+
+\list
+ \li Allowed characters: alphanumeric, an underscore or a dot [a-zA-Z0-9_.].
+ \li Illegal words: abstract, continue, for, new, switch, assert, default,
+ if, package, synchronized, boolean, do, goto, private, this, break,
+ double, implements, protected, throw, byte, else, import, public,
+ throws, case, enum, instanceof, return, transient, catch, extends,
+ int, short, try, char, final, interface, static, void, class, finally,
+ long, strictfp, volatile, const, float, native, super, while.
+\endlist
+
+The default package name for Qt for Android apps is \c org.qtproject.example.<target_name>.
+
+\note Setting the package name manually in \c build.gradle (via
+\c namespace property) takes precedence over \c AndroidManifest.xml
+(via \c package attribute), and the latter also takes precedence over
+this property.
+
+For more information, see Android's
+\l{Android: Configure the app module}{configure the app module}.
+
+\sa{qt6_android_generate_deployment_settings}{qt_android_generate_deployment_settings()}
+*/
+
+/*!
\page cmake-target-property-qt-android-version-code.html
\ingroup cmake-properties-qtcore
\ingroup cmake-target-properties-qtcore
diff --git a/src/corelib/doc/src/cmake/qt_android_generate_deployment_settings.qdoc b/src/corelib/doc/src/cmake/qt_android_generate_deployment_settings.qdoc
index 5a7bbdc33f..daa3680070 100644
--- a/src/corelib/doc/src/cmake/qt_android_generate_deployment_settings.qdoc
+++ b/src/corelib/doc/src/cmake/qt_android_generate_deployment_settings.qdoc
@@ -63,6 +63,7 @@ how to accomplish this.
\li \l{cmake-target-property-QT_ANDROID_MIN_SDK_VERSION}{QT_ANDROID_MIN_SDK_VERSION}
\li \l{cmake-target-property-QT_ANDROID_PACKAGE_SOURCE_DIR}{QT_ANDROID_PACKAGE_SOURCE_DIR}
\li \l{cmake-target-property-QT_ANDROID_TARGET_SDK_VERSION}{QT_ANDROID_TARGET_SDK_VERSION}
+\li \l{cmake-target-property-QT_ANDROID_PACKAGE_NAME}{QT_ANDROID_PACKAGE_NAME}
\li \l{cmake-target-property-QT_ANDROID_VERSION_NAME}{QT_ANDROID_VERSION_NAME}
\li \l{cmake-target-property-QT_ANDROID_VERSION_CODE}{QT_ANDROID_VERSION_CODE}
\li \l{cmake-target-property-QT_QML_IMPORT_PATH}{QT_QML_IMPORT_PATH}
diff --git a/src/corelib/doc/src/foreach-keyword.qdoc b/src/corelib/doc/src/foreach-keyword.qdoc
index b3a4482528..6aa21d5880 100644
--- a/src/corelib/doc/src/foreach-keyword.qdoc
+++ b/src/corelib/doc/src/foreach-keyword.qdoc
@@ -81,7 +81,10 @@
/*!
\macro QT_NO_FOREACH
\since 6.0
+ \relates <QtGlobal>
Defining this macro removes the availability of Qt's \c foreach
loop.
+
+ \sa QT_NO_KEYWORDS
*/
diff --git a/src/corelib/global/qcomparehelpers.h b/src/corelib/global/qcomparehelpers.h
index 0e43ac296b..97c972bfa7 100644
--- a/src/corelib/global/qcomparehelpers.h
+++ b/src/corelib/global/qcomparehelpers.h
@@ -145,8 +145,8 @@ template <typename In> constexpr auto to_Qt(In in) noexcept
noexcept(noexcept(compareThreeWay(rhs, lhs))) \
{ \
const auto r = compareThreeWay(rhs, lhs); \
- if (r > 0) return std::strong_ordering::less; \
- if (r < 0) return std::strong_ordering::greater; \
+ if (is_gt(r)) return std::strong_ordering::less; \
+ if (is_lt(r)) return std::strong_ordering::greater; \
return r; \
}
@@ -157,8 +157,8 @@ template <typename In> constexpr auto to_Qt(In in) noexcept
noexcept(noexcept(compareThreeWay(rhs, lhs))) \
{ \
const auto r = compareThreeWay(rhs, lhs); \
- if (r > 0) return std::weak_ordering::less; \
- if (r < 0) return std::weak_ordering::greater; \
+ if (is_gt(r)) return std::weak_ordering::less; \
+ if (is_lt(r)) return std::weak_ordering::greater; \
return r; \
}
@@ -169,8 +169,8 @@ template <typename In> constexpr auto to_Qt(In in) noexcept
noexcept(noexcept(compareThreeWay(rhs, lhs))) \
{ \
const auto r = compareThreeWay(rhs, lhs); \
- if (r > 0) return std::partial_ordering::less; \
- if (r < 0) return std::partial_ordering::greater; \
+ if (is_gt(r)) return std::partial_ordering::less; \
+ if (is_lt(r)) return std::partial_ordering::greater; \
return r; \
}
@@ -218,19 +218,19 @@ template <typename In> constexpr auto to_Qt(In in) noexcept
Attributes \
friend Constexpr bool operator<(LeftType const &lhs, RightType const &rhs) \
noexcept(noexcept(compareThreeWay(lhs, rhs))) \
- { return compareThreeWay(lhs, rhs) < 0; } \
+ { return is_lt(compareThreeWay(lhs, rhs)); } \
Attributes \
friend Constexpr bool operator>(LeftType const &lhs, RightType const &rhs) \
noexcept(noexcept(compareThreeWay(lhs, rhs))) \
- { return compareThreeWay(lhs, rhs) > 0; } \
+ { return is_gt(compareThreeWay(lhs, rhs)); } \
Attributes \
friend Constexpr bool operator<=(LeftType const &lhs, RightType const &rhs) \
noexcept(noexcept(compareThreeWay(lhs, rhs))) \
- { return compareThreeWay(lhs, rhs) <= 0; } \
+ { return is_lteq(compareThreeWay(lhs, rhs)); } \
Attributes \
friend Constexpr bool operator>=(LeftType const &lhs, RightType const &rhs) \
noexcept(noexcept(compareThreeWay(lhs, rhs))) \
- { return compareThreeWay(lhs, rhs) >= 0; }
+ { return is_gteq(compareThreeWay(lhs, rhs)); }
#define QT_DECLARE_ORDERING_HELPER_PARTIAL(LeftType, RightType, Constexpr, Attributes) \
QT_DECLARE_ORDERING_HELPER_TEMPLATE(Qt::partial_ordering, LeftType, RightType, Constexpr, \
@@ -255,19 +255,19 @@ template <typename In> constexpr auto to_Qt(In in) noexcept
Attributes \
friend Constexpr bool operator<(RightType const &lhs, LeftType const &rhs) \
noexcept(noexcept(compareThreeWay(rhs, lhs))) \
- { return compareThreeWay(rhs, lhs) > 0; } \
+ { return is_gt(compareThreeWay(rhs, lhs)); } \
Attributes \
friend Constexpr bool operator>(RightType const &lhs, LeftType const &rhs) \
noexcept(noexcept(compareThreeWay(rhs, lhs))) \
- { return compareThreeWay(rhs, lhs) < 0; } \
+ { return is_lt(compareThreeWay(rhs, lhs)); } \
Attributes \
friend Constexpr bool operator<=(RightType const &lhs, LeftType const &rhs) \
noexcept(noexcept(compareThreeWay(rhs, lhs))) \
- { return compareThreeWay(rhs, lhs) >= 0; } \
+ { return is_gteq(compareThreeWay(rhs, lhs)); } \
Attributes \
friend Constexpr bool operator>=(RightType const &lhs, LeftType const &rhs) \
noexcept(noexcept(compareThreeWay(rhs, lhs))) \
- { return compareThreeWay(rhs, lhs) <= 0; }
+ { return is_lteq(compareThreeWay(rhs, lhs)); }
#define QT_DECLARE_REVERSED_ORDERING_HELPER_PARTIAL(LeftType, RightType, Constexpr, Attributes) \
QT_DECLARE_REVERSED_ORDERING_HELPER_TEMPLATE(Qt::partial_ordering, LeftType, RightType, \
diff --git a/src/corelib/global/qcompilerdetection.h b/src/corelib/global/qcompilerdetection.h
index 0230b5a784..b2340bff8e 100644
--- a/src/corelib/global/qcompilerdetection.h
+++ b/src/corelib/global/qcompilerdetection.h
@@ -1406,7 +1406,11 @@ QT_WARNING_DISABLE_MSVC(4530) /* C++ exception handler used, but unwind semantic
#endif
#if defined(__cplusplus) && __cplusplus >= 202002L // P0846 doesn't have a feature macro :/
+# if !defined(Q_CC_MSVC_ONLY) || Q_CC_MSVC < 1939 // claims C++20 support but lacks P0846
+ // 1939 is known to work
+ // 1936 is known to fail
# define QT_COMPILER_HAS_P0846
+# endif
#endif
#ifdef QT_COMPILER_HAS_P0846
diff --git a/src/corelib/global/qglobal.cpp b/src/corelib/global/qglobal.cpp
index 222c008f8a..99e4e49c9c 100644
--- a/src/corelib/global/qglobal.cpp
+++ b/src/corelib/global/qglobal.cpp
@@ -170,6 +170,19 @@ bool QInternal::activateCallbacks(Callback cb, void **parameters)
}
/*!
+ \macro QT_NO_KEYWORDS
+ \relates <QtGlobal>
+
+ Define this macro to disable the Qt-specific keywords that are usually enabled,
+ such as \c signals and \c slots. Use \c Q_SIGNALS and \c Q_SLOTS instead.
+
+ Libraries should define this macro to make sure that they don't use the generic
+ keywords without the \c Q_ prefix in their public headers.
+
+ \sa QT_NO_FOREACH
+*/
+
+/*!
\macro QT_NAMESPACE
\internal
diff --git a/src/corelib/global/qglobal.h b/src/corelib/global/qglobal.h
index 1009057bad..2d70e82370 100644
--- a/src/corelib/global/qglobal.h
+++ b/src/corelib/global/qglobal.h
@@ -67,8 +67,9 @@
#include <QtCore/qtresource.h>
#include <QtCore/qttranslation.h>
#include <QtCore/qttypetraits.h>
+#if QT_CONFIG(version_tagging)
#include <QtCore/qversiontagging.h>
-
+#endif
#endif /* __cplusplus */
#endif /* QGLOBAL_H */
diff --git a/src/corelib/global/qlibraryinfo.cpp b/src/corelib/global/qlibraryinfo.cpp
index 92729b06f1..1b03733d2a 100644
--- a/src/corelib/global/qlibraryinfo.cpp
+++ b/src/corelib/global/qlibraryinfo.cpp
@@ -554,6 +554,10 @@ QString QLibraryInfoPrivate::path(QLibraryInfo::LibraryPath p, UsageMode usageMo
}
qsizetype startIndex = 0;
+ /* We support placeholders of the form $(<ENV_VAR>) in qt.conf.
+ The loop below tries to find all such placeholders, and replaces
+ them with the actual value of the ENV_VAR environment variable
+ */
while (true) {
startIndex = ret.indexOf(u'$', startIndex);
if (startIndex < 0)
diff --git a/src/corelib/global/qnamespace.h b/src/corelib/global/qnamespace.h
index 2398c0a1a4..1569577b12 100644
--- a/src/corelib/global/qnamespace.h
+++ b/src/corelib/global/qnamespace.h
@@ -1356,6 +1356,11 @@ namespace Qt {
PreventContextMenu
};
+ enum class ContextMenuTrigger {
+ Press,
+ Release,
+ };
+
enum InputMethodQuery {
ImEnabled = 0x1,
ImCursorRectangle = 0x2,
@@ -1731,6 +1736,7 @@ namespace Qt {
Q_ENUM_NS(ScrollBarPolicy)
Q_ENUM_NS(FocusPolicy)
Q_ENUM_NS(ContextMenuPolicy)
+ Q_ENUM_NS(ContextMenuTrigger)
Q_ENUM_NS(ArrowType)
Q_ENUM_NS(ToolButtonStyle)
Q_ENUM_NS(PenStyle)
diff --git a/src/corelib/global/qnamespace.qdoc b/src/corelib/global/qnamespace.qdoc
index ddfade675a..b2ec64f435 100644
--- a/src/corelib/global/qnamespace.qdoc
+++ b/src/corelib/global/qnamespace.qdoc
@@ -2087,6 +2087,18 @@
*/
/*!
+ \enum Qt::ContextMenuTrigger
+ \since 6.8
+
+ This enum type defines the mouse event used to trigger a context menu event.
+
+ \value Press context menu on mouse press event, default on UNIX systems.
+ \value Release context menu on mouse release event, default on Windows.
+
+ \sa QStyleHints::contextMenuTrigger
+*/
+
+/*!
\enum Qt::FocusPolicy
This enum type defines the various policies a widget can have with
diff --git a/src/corelib/global/qtconfigmacros.h b/src/corelib/global/qtconfigmacros.h
index 018161eac4..03d52d885a 100644
--- a/src/corelib/global/qtconfigmacros.h
+++ b/src/corelib/global/qtconfigmacros.h
@@ -9,6 +9,7 @@
#endif
#include <QtCore/qtconfiginclude.h>
+#include <QtCore/qtversionchecks.h>
#include <assert.h>
diff --git a/src/corelib/global/qversiontagging.h b/src/corelib/global/qversiontagging.h
index 73faf5b6eb..fa2dd23728 100644
--- a/src/corelib/global/qversiontagging.h
+++ b/src/corelib/global/qversiontagging.h
@@ -4,11 +4,17 @@
#if !defined(QVERSIONTAGGING_H)
#define QVERSIONTAGGING_H
+#if 0
+#pragma qt_no_master_include
+#endif
+
#include <QtCore/qcompilerdetection.h>
#include <QtCore/qtconfigmacros.h>
#include <QtCore/qtversionchecks.h>
#include <QtCore/qtypes.h>
+QT_REQUIRE_CONFIG(version_tagging);
+
QT_BEGIN_NAMESPACE
/*
diff --git a/src/corelib/io/qbuffer.cpp b/src/corelib/io/qbuffer.cpp
index 763620692c..4e513bc7cf 100644
--- a/src/corelib/io/qbuffer.cpp
+++ b/src/corelib/io/qbuffer.cpp
@@ -282,8 +282,7 @@ void QBuffer::setData(const char *data, qsizetype size)
qWarning("QBuffer::setData: Buffer is open");
return;
}
- d->buf->replace(qsizetype(0), d->buf->size(), // ### QByteArray lacks assign(ptr, n)
- data, size);
+ d->buf->assign(data, data + size);
}
/*!
diff --git a/src/corelib/io/qdir.cpp b/src/corelib/io/qdir.cpp
index 9291201d88..05947f3380 100644
--- a/src/corelib/io/qdir.cpp
+++ b/src/corelib/io/qdir.cpp
@@ -313,9 +313,10 @@ inline void QDirPrivate::sortFileList(QDir::SortFlags sort, const QFileInfoList
names->append(fi.fileName());
}
} else {
- QScopedArrayPointer<QDirSortItem> si(new QDirSortItem[n]);
+ QVarLengthArray<QDirSortItem, 64> si;
+ si.reserve(n);
for (qsizetype i = 0; i < n; ++i)
- si[i] = QDirSortItem{l.at(i), sort};
+ si.emplace_back(l.at(i), sort);
#ifndef QT_BOOTSTRAPPED
if (sort.testAnyFlag(QDir::LocaleAware)) {
diff --git a/src/corelib/io/qfilesystemengine_unix.cpp b/src/corelib/io/qfilesystemengine_unix.cpp
index bda2962f8d..2f6c0ae184 100644
--- a/src/corelib/io/qfilesystemengine_unix.cpp
+++ b/src/corelib/io/qfilesystemengine_unix.cpp
@@ -642,7 +642,7 @@ QFileSystemEntry QFileSystemEngine::canonicalName(const QFileSystemEntry &entry,
{
Q_CHECK_FILE_NAME(entry, entry);
-#if !defined(Q_OS_DARWIN) && !defined(Q_OS_QNX) && !defined(Q_OS_ANDROID) && !defined(Q_OS_HAIKU) && _POSIX_VERSION < 200809L
+#if !defined(Q_OS_DARWIN) && !defined(Q_OS_QNX) && !defined(Q_OS_ANDROID) && !defined(Q_OS_HAIKU) && _POSIX_VERSION < 200809L && !defined(Q_OS_VXWORKS)
// realpath(X,0) is not supported
Q_UNUSED(data);
return QFileSystemEntry(slowCanonicalized(absoluteName(entry).filePath()));
diff --git a/src/corelib/io/qresource.cpp b/src/corelib/io/qresource.cpp
index b76d2b6478..581e1e75ef 100644
--- a/src/corelib/io/qresource.cpp
+++ b/src/corelib/io/qresource.cpp
@@ -36,6 +36,16 @@
#if defined(Q_OS_UNIX) && !defined(Q_OS_INTEGRITY)
# define QT_USE_MMAP
# include <sys/mman.h>
+# ifdef Q_OS_LINUX
+// since 5.7, so define in case we're being compiled with older kernel headers
+# define MREMAP_DONTUNMAP 4
+# elif defined(Q_OS_DARWIN)
+# include <mach/mach.h>
+# include <mach/vm_map.h>
+# endif
+#endif
+#ifdef Q_OS_WIN
+# include <qt_windows.h>
#endif
//#define DEBUG_RESOURCE_MATCH
@@ -65,6 +75,7 @@ RCC_FEATURE_SYMBOL(Zstd)
#undef RCC_FEATURE_SYMBOL
+namespace {
class QStringSplitter
{
public:
@@ -130,7 +141,7 @@ public:
return QResource::NoCompression;
}
const uchar *data(int node, qint64 *size) const;
- quint64 lastModified(int node) const;
+ qint64 lastModified(int node) const;
QStringList children(int node) const;
virtual QString mappingRoot() const { return QString(); }
bool mappingRootSubdir(const QString &path, QString *match = nullptr) const;
@@ -159,15 +170,18 @@ static QString cleanPath(const QString &_path)
path.remove(0, 1);
return path;
}
+} // unnamed namespace
Q_DECLARE_TYPEINFO(QResourceRoot, Q_RELOCATABLE_TYPE);
typedef QList<QResourceRoot*> ResourceList;
+namespace {
struct QResourceGlobalData
{
QRecursiveMutex resourceMutex;
ResourceList resourceList;
};
+}
Q_GLOBAL_STATIC(QResourceGlobalData, resourceGlobalData)
static inline QRecursiveMutex &resourceMutex()
@@ -279,14 +293,16 @@ public:
bool load(const QString &file);
void clear();
+ static bool mayRemapData(const QResource &resource);
+
QLocale locale;
QString fileName, absoluteFilePath;
QList<QResourceRoot *> related;
- mutable qint64 size;
- mutable quint64 lastModified;
- mutable const uchar *data;
+ qint64 size;
+ qint64 lastModified;
+ const uchar *data;
mutable QStringList children;
- mutable quint8 compressionAlgo;
+ quint8 compressionAlgo;
bool container;
/* 2 or 6 padding bytes */
@@ -928,14 +944,14 @@ const uchar *QResourceRoot::data(int node, qint64 *size) const
return nullptr;
}
-quint64 QResourceRoot::lastModified(int node) const
+qint64 QResourceRoot::lastModified(int node) const
{
if (node == -1 || version < 0x02)
return 0;
const int offset = findOffset(node) + 14;
- return qFromBigEndian<quint64>(tree + offset);
+ return qFromBigEndian<qint64>(tree + offset);
}
QStringList QResourceRoot::children(int node) const
@@ -1031,8 +1047,8 @@ Q_CORE_EXPORT bool qUnregisterResourceData(int version, const unsigned char *tre
return false;
}
+namespace {
// run time resource creation
-
class QDynamicBufferResourceRoot : public QResourceRoot
{
QString root;
@@ -1105,6 +1121,11 @@ public:
class QDynamicFileResourceRoot : public QDynamicBufferResourceRoot
{
+public:
+ static uchar *map_sys(QFile &file, qint64 base, qsizetype size);
+ static void unmap_sys(void *base, qsizetype size);
+
+private:
QString fileName;
// for mmap'ed files, this is what needs to be unmapped.
uchar *unmapPointer;
@@ -1115,22 +1136,18 @@ public:
: QDynamicBufferResourceRoot(_root), unmapPointer(nullptr), unmapLength(0)
{ }
~QDynamicFileResourceRoot() {
-#if defined(QT_USE_MMAP)
- if (unmapPointer) {
- munmap(reinterpret_cast<char *>(unmapPointer), unmapLength);
- unmapPointer = nullptr;
- unmapLength = 0;
- } else
-#endif
- {
+ if (wasMemoryMapped())
+ unmap_sys(unmapPointer, unmapLength);
+ else
delete[] mappingBuffer();
- }
}
QString mappingFile() const { return fileName; }
ResourceRootType type() const override { return Resource_File; }
+ bool wasMemoryMapped() const { return unmapPointer; }
bool registerSelf(const QString &f);
};
+} // unnamed namespace
#ifndef MAP_FILE
# define MAP_FILE 0
@@ -1139,49 +1156,69 @@ public:
# define MAP_FAILED reinterpret_cast<void *>(-1)
#endif
-bool QDynamicFileResourceRoot::registerSelf(const QString &f)
+void QDynamicFileResourceRoot::unmap_sys(void *base, qsizetype size)
+{
+#if defined(QT_USE_MMAP)
+ munmap(base, size);
+#elif defined(Q_OS_WIN)
+ Q_UNUSED(size)
+ UnmapViewOfFile(reinterpret_cast<void *>(base));
+#endif
+}
+
+// Note: caller must ensure \a offset and \a size are acceptable to the OS.
+uchar *QDynamicFileResourceRoot::map_sys(QFile &file, qint64 offset, qsizetype size)
{
- bool fromMM = false;
- uchar *data = nullptr;
- qsizetype data_len = 0;
+ Q_ASSERT(file.isOpen());
+ void *ptr = nullptr;
+ if (size < 0)
+ size = qMin(file.size() - offset, (std::numeric_limits<qsizetype>::max)());
+ // We don't use QFile::map() here because we want to dispose of the QFile object
#if defined(QT_USE_MMAP)
- int fd = QT_OPEN(QFile::encodeName(f), O_RDONLY);
- if (fd >= 0) {
- QT_STATBUF st;
- if (!QT_FSTAT(fd, &st) && st.st_size <= std::numeric_limits<qsizetype>::max()) {
- int protection = PROT_READ; // read-only memory
- int flags = MAP_FILE | MAP_PRIVATE; // swap-backed map from file
- void *ptr = QT_MMAP(nullptr, st.st_size, // any address, whole file
- protection, flags,
- fd, 0); // from offset 0 of fd
- if (ptr != MAP_FAILED) {
- data = static_cast<uchar *>(ptr);
- data_len = st.st_size;
- fromMM = true;
- }
+ int fd = file.handle();
+ int protection = PROT_READ; // read-only memory
+ int flags = MAP_FILE | MAP_PRIVATE; // swap-backed map from file
+ ptr = QT_MMAP(nullptr, size, protection, flags, fd, offset);
+ if (ptr == MAP_FAILED)
+ ptr = nullptr;
+#elif defined(Q_OS_WIN)
+ int fd = file.handle();
+ HANDLE fileHandle = reinterpret_cast<HANDLE>(_get_osfhandle(fd));
+ if (fileHandle != INVALID_HANDLE_VALUE) {
+ HANDLE mapHandle = CreateFileMapping(fileHandle, 0, PAGE_WRITECOPY, 0, 0, 0);
+ if (mapHandle) {
+ ptr = MapViewOfFile(mapHandle, FILE_MAP_COPY, DWORD(offset >> 32), DWORD(offset), size);
+ CloseHandle(mapHandle);
}
- QT_CLOSE(fd);
}
#endif // QT_USE_MMAP
- if (!data) {
- QFile file(f);
+ return static_cast<uchar *>(ptr);
+}
+
+bool QDynamicFileResourceRoot::registerSelf(const QString &f)
+{
+ QFile file(f);
+ if (!file.open(QIODevice::ReadOnly))
+ return false;
+
+ qint64 data_len = file.size();
+ if (data_len > std::numeric_limits<qsizetype>::max())
+ return false;
+
+ uchar *data = map_sys(file, 0, data_len);
+ bool fromMM = !!data;
+
+ if (!fromMM) {
bool ok = false;
- if (file.open(QIODevice::ReadOnly)) {
- qint64 fsize = file.size();
- if (fsize <= std::numeric_limits<qsizetype>::max()) {
- data_len = file.size();
- data = new uchar[data_len];
- ok = (data_len == file.read(reinterpret_cast<char *>(data), data_len));
- }
- }
+ data = new uchar[data_len];
+ ok = (data_len == file.read(reinterpret_cast<char *>(data), data_len));
if (!ok) {
delete[] data;
data = nullptr;
data_len = 0;
return false;
}
- fromMM = false;
}
if (data && QDynamicBufferResourceRoot::registerSelf(data, data_len)) {
if (fromMM) {
@@ -1349,11 +1386,22 @@ private:
uchar *map(qint64 offset, qint64 size, QFile::MemoryMapFlags flags);
bool unmap(uchar *ptr);
void uncompress() const;
- qint64 offset;
+ void mapUncompressed();
+ bool mapUncompressed_sys();
+ void unmapUncompressed_sys();
+ qint64 offset = 0;
QResource resource;
mutable QByteArray uncompressed;
+ bool mustUnmap = false;
+
+ // minimum size for which we'll try to re-open ourselves in mapUncompressed()
+ static constexpr qsizetype RemapCompressedThreshold = 16384;
protected:
- QResourceFileEnginePrivate() : offset(0) { }
+ ~QResourceFileEnginePrivate()
+ {
+ if (mustUnmap)
+ unmapUncompressed_sys();
+ }
};
bool QResourceFileEngine::caseSensitive() const
@@ -1565,7 +1613,9 @@ bool QResourceFileEngine::supportsExtension(Extension extension) const
uchar *QResourceFileEnginePrivate::map(qint64 offset, qint64 size, QFile::MemoryMapFlags flags)
{
Q_Q(QResourceFileEngine);
- Q_UNUSED(flags);
+ Q_ASSERT_X(resource.compressionAlgorithm() == QResource::NoCompression
+ || !uncompressed.isNull(), "QFile::map()",
+ "open() should have uncompressed compressed resources");
qint64 max = resource.uncompressedSize();
qint64 end;
@@ -1575,11 +1625,15 @@ uchar *QResourceFileEnginePrivate::map(qint64 offset, qint64 size, QFile::Memory
return nullptr;
}
- const uchar *address = resource.data();
- if (resource.compressionAlgorithm() != QResource::NoCompression) {
- uncompress();
- if (uncompressed.isNull())
- return nullptr;
+ const uchar *address = reinterpret_cast<const uchar *>(uncompressed.constBegin());
+ if (!uncompressed.isNull())
+ return const_cast<uchar *>(address) + offset;
+
+ // resource was not compressed
+ address = resource.data();
+ if (flags & QFile::MapPrivateOption) {
+ // We need to provide read-write memory
+ mapUncompressed();
address = reinterpret_cast<const uchar *>(uncompressed.constData());
}
@@ -1600,6 +1654,131 @@ void QResourceFileEnginePrivate::uncompress() const
uncompressed = resource.uncompressedData();
}
+void QResourceFileEnginePrivate::mapUncompressed()
+{
+ Q_ASSERT(resource.compressionAlgorithm() == QResource::NoCompression);
+ if (!uncompressed.isNull())
+ return; // nothing to do
+
+ if (resource.uncompressedSize() >= RemapCompressedThreshold) {
+ if (mapUncompressed_sys())
+ return;
+ }
+
+ uncompressed = resource.uncompressedData();
+ uncompressed.detach();
+}
+
+#if defined(MREMAP_MAYMOVE) && defined(MREMAP_DONTUNMAP)
+inline bool QResourcePrivate::mayRemapData(const QResource &resource)
+{
+ auto d = resource.d_func();
+
+ // assumptions from load():
+ // - d->related is not empty
+ // - the first item in d->related is the one with our data
+ // by current construction, it's also the only item
+ const QResourceRoot *root = d->related.at(0);
+
+ switch (root->type()) {
+ case QResourceRoot::Resource_Builtin:
+ return true; // always acceptable, memory is read-only
+ case QResourceRoot::Resource_Buffer:
+ return false; // never acceptable, memory is heap
+ case QResourceRoot::Resource_File:
+ break;
+ }
+
+ auto df = static_cast<const QDynamicFileResourceRoot *>(root);
+ return df->wasMemoryMapped();
+}
+#endif
+
+// Returns the page boundaries of where \a location is located in memory.
+static auto mappingBoundaries(const void *location, qsizetype size)
+{
+#ifdef Q_OS_WIN
+ auto getpagesize = [] {
+ SYSTEM_INFO sysinfo;
+ ::GetSystemInfo(&sysinfo);
+ return sysinfo.dwAllocationGranularity;
+ };
+#endif
+ struct R {
+ void *begin;
+ qsizetype size;
+ qptrdiff offset;
+ } r;
+
+ const quintptr pageMask = getpagesize() - 1;
+ quintptr data = quintptr(location);
+ quintptr begin = data & ~pageMask;
+ quintptr end = (data + size + pageMask) & ~pageMask;
+ r.begin = reinterpret_cast<void *>(begin);
+ r.size = end - begin;
+ r.offset = data & pageMask;
+ return r;
+}
+
+bool QResourceFileEnginePrivate::mapUncompressed_sys()
+{
+ auto r = mappingBoundaries(resource.data(), resource.uncompressedSize());
+ void *ptr = nullptr;
+
+#if defined(MREMAP_MAYMOVE) && defined(MREMAP_DONTUNMAP)
+ // Use MREMAP_MAYMOVE to tell the kernel to give us a new address and use
+ // MREMAP_DONTUNMAP (supported since kernel 5.7) to request that it create
+ // a new mapping of the same pages, instead of moving. We can only do that
+ // for pages that are read-only, otherwise the kernel replaces the source
+ // with pages full of nulls.
+ if (!QResourcePrivate::mayRemapData(resource))
+ return false;
+
+ ptr = mremap(r.begin, r.size, r.size, MREMAP_MAYMOVE | MREMAP_DONTUNMAP);
+ if (ptr == MAP_FAILED)
+ return false;
+
+ // Allow writing, which the documentation says we allow. This is safe
+ // because MREMAP_DONTUNMAP only works for private mappings.
+ if (mprotect(ptr, r.size, PROT_READ | PROT_WRITE) != 0) {
+ munmap(ptr, r.size);
+ return false;
+ }
+#elif defined(Q_OS_DARWIN)
+ mach_port_t self = mach_task_self();
+ vm_address_t addr = 0;
+ vm_address_t mask = 0;
+ bool anywhere = true;
+ bool copy = true;
+ vm_prot_t cur_prot = VM_PROT_READ | VM_PROT_WRITE;
+ vm_prot_t max_prot = VM_PROT_ALL;
+ kern_return_t res = vm_remap(self, &addr, r.size, mask, anywhere,
+ self, vm_address_t(r.begin), copy, &cur_prot,
+ &max_prot, VM_INHERIT_DEFAULT);
+ if (res != KERN_SUCCESS)
+ return false;
+
+ ptr = reinterpret_cast<void *>(addr);
+ if ((max_prot & VM_PROT_WRITE) == 0 || mprotect(ptr, r.size, PROT_READ | PROT_WRITE) != 0) {
+ munmap(ptr, r.size);
+ return false;
+ }
+#endif
+
+ if (!ptr)
+ return false;
+ const char *newdata = static_cast<char *>(ptr) + r.offset;
+ uncompressed = QByteArray::fromRawData(newdata, resource.uncompressedSize());
+ mustUnmap = true;
+ return true;
+}
+
+void QResourceFileEnginePrivate::unmapUncompressed_sys()
+{
+ auto r = mappingBoundaries(uncompressed.constBegin(), uncompressed.size());
+ QDynamicFileResourceRoot::unmap_sys(r.begin, r.size);
+}
+
#endif // !defined(QT_BOOTSTRAPPED)
QT_END_NAMESPACE
diff --git a/src/corelib/io/qsavefile.cpp b/src/corelib/io/qsavefile.cpp
index 6d8918c29c..cc59bb3725 100644
--- a/src/corelib/io/qsavefile.cpp
+++ b/src/corelib/io/qsavefile.cpp
@@ -113,10 +113,10 @@ QSaveFile::QSaveFile(const QString &name, QObject *parent)
QSaveFile::~QSaveFile()
{
Q_D(QSaveFile);
- QFileDevice::close();
- if (d->fileEngine) {
+ if (isOpen()) {
+ QFileDevice::close();
+ Q_ASSERT(d->fileEngine);
d->fileEngine->remove();
- d->fileEngine.reset();
}
}
@@ -298,7 +298,7 @@ bool QSaveFile::commit()
}
QFileDevice::close(); // calls flush()
- const auto fe = std::move(d->fileEngine);
+ const auto &fe = d->fileEngine;
// Sync to disk if possible. Ignore errors (e.g. not supported).
fe->syncToDisk();
diff --git a/src/corelib/io/qurl.cpp b/src/corelib/io/qurl.cpp
index 4360b5b076..1e285bb36b 100644
--- a/src/corelib/io/qurl.cpp
+++ b/src/corelib/io/qurl.cpp
@@ -1014,8 +1014,9 @@ inline bool QUrlPrivate::setScheme(const QString &value, qsizetype len, bool doS
inline void QUrlPrivate::setAuthority(const QString &auth, qsizetype from, qsizetype end, QUrl::ParsingMode mode)
{
sectionIsPresent &= ~Authority;
- sectionIsPresent |= Host;
port = -1;
+ if (from == end && !auth.isNull())
+ sectionIsPresent |= Host; // empty but not null authority implies host
// we never actually _loop_
while (from != end) {
@@ -1155,8 +1156,11 @@ inline void QUrlPrivate::setQuery(const QString &value, qsizetype from, qsizetyp
inline void QUrlPrivate::appendHost(QString &appendTo, QUrl::FormattingOptions options) const
{
- if (host.isEmpty())
+ if (host.isEmpty()) {
+ if ((sectionIsPresent & Host) && appendTo.isNull())
+ appendTo.detach();
return;
+ }
if (host.at(0).unicode() == '[') {
// IPv6 addresses might contain a zone-id which needs to be recoded
if (options != 0)
@@ -1274,7 +1278,9 @@ QUrlPrivate::setHost(const QString &value, qsizetype from, qsizetype iend, QUrl:
const qsizetype len = end - begin;
host.clear();
- sectionIsPresent |= Host;
+ sectionIsPresent &= ~Host;
+ if (!value.isNull() || (sectionIsPresent & Authority))
+ sectionIsPresent |= Host;
if (len == 0)
return true;
@@ -2029,11 +2035,6 @@ void QUrl::setAuthority(const QString &authority, ParsingMode mode)
}
d->setAuthority(authority, 0, authority.size(), mode);
- if (authority.isNull()) {
- // QUrlPrivate::setAuthority cleared almost everything
- // but it leaves the Host bit set
- d->sectionIsPresent &= ~QUrlPrivate::Authority;
- }
}
/*!
@@ -2297,8 +2298,7 @@ void QUrl::setHost(const QString &host, ParsingMode mode)
}
if (d->setHost(data, 0, data.size(), mode)) {
- if (host.isNull())
- d->sectionIsPresent &= ~QUrlPrivate::Host;
+ return;
} else if (!data.startsWith(u'[')) {
// setHost failed, it might be IPv6 or IPvFuture in need of bracketing
Q_ASSERT(d->error);
@@ -2311,6 +2311,7 @@ void QUrl::setHost(const QString &host, ParsingMode mode)
// source data contains ':', so it's an IPv6 error
d->error->code = QUrlPrivate::InvalidIPv6AddressError;
}
+ d->sectionIsPresent &= ~QUrlPrivate::Host;
} else {
// succeeded
d->clearError();
diff --git a/src/corelib/itemmodels/qsortfilterproxymodel.cpp b/src/corelib/itemmodels/qsortfilterproxymodel.cpp
index a9ead2e1eb..a5284dbad4 100644
--- a/src/corelib/itemmodels/qsortfilterproxymodel.cpp
+++ b/src/corelib/itemmodels/qsortfilterproxymodel.cpp
@@ -688,8 +688,10 @@ void QSortFilterProxyModelPrivate::sort_source_rows(
QSortFilterProxyModelGreaterThan gt(source_sort_column, source_parent, model, q);
std::stable_sort(source_rows.begin(), source_rows.end(), gt);
}
- } else { // restore the source model order
- std::stable_sort(source_rows.begin(), source_rows.end());
+ } else if (sort_order == Qt::AscendingOrder) {
+ std::stable_sort(source_rows.begin(), source_rows.end(), std::less{});
+ } else {
+ std::stable_sort(source_rows.begin(), source_rows.end(), std::greater{});
}
}
@@ -2490,7 +2492,10 @@ QSize QSortFilterProxyModel::span(const QModelIndex &index) const
}
/*!
- \reimp
+ \reimp
+ Sorts the model by \a column in the given \a order.
+ If the sort \a column is less than zero, the model will be sorted by source model row
+ in the given \a order.
*/
void QSortFilterProxyModel::sort(int column, Qt::SortOrder order)
{
diff --git a/src/corelib/kernel/qcore_mac.mm b/src/corelib/kernel/qcore_mac.mm
index 54c4373aed..00b0d078d7 100644
--- a/src/corelib/kernel/qcore_mac.mm
+++ b/src/corelib/kernel/qcore_mac.mm
@@ -541,7 +541,7 @@ QMacRootLevelAutoReleasePool::QMacRootLevelAutoReleasePool()
if (qEnvironmentVariableIsSet(ROOT_LEVEL_POOL_DISABLE_SWITCH))
return;
- pool.reset(new QMacAutoReleasePool);
+ pool.emplace();
[[[ROOT_LEVEL_POOL_MARKER alloc] init] autorelease];
diff --git a/src/corelib/kernel/qcore_mac_p.h b/src/corelib/kernel/qcore_mac_p.h
index e63c320805..5ecf8072f4 100644
--- a/src/corelib/kernel/qcore_mac_p.h
+++ b/src/corelib/kernel/qcore_mac_p.h
@@ -19,6 +19,8 @@
#include <QtCore/qoperatingsystemversion.h>
+#include <optional>
+
#ifdef Q_OS_MACOS
#include <mach/port.h>
struct mach_header;
@@ -48,7 +50,6 @@ kern_return_t IOObjectRelease(io_object_t object);
#endif
#include "qstring.h"
-#include "qscopedpointer.h"
#include "qpair.h"
#if defined( __OBJC__) && defined(QT_NAMESPACE)
@@ -129,7 +130,7 @@ public:
Q_NODISCARD_CTOR QMacRootLevelAutoReleasePool();
~QMacRootLevelAutoReleasePool();
private:
- QScopedPointer<QMacAutoReleasePool> pool;
+ std::optional<QMacAutoReleasePool> pool = std::nullopt;
};
#endif
diff --git a/src/corelib/kernel/qcoreapplication.cpp b/src/corelib/kernel/qcoreapplication.cpp
index 13108cecea..a494369c5d 100644
--- a/src/corelib/kernel/qcoreapplication.cpp
+++ b/src/corelib/kernel/qcoreapplication.cpp
@@ -1091,6 +1091,14 @@ bool QCoreApplication::testAttribute(Qt::ApplicationAttribute attribute)
\brief Whether the use of the QEventLoopLocker feature can cause the
application to quit.
+ When this property is \c true the release of the last remaining
+ QEventLoopLocker operating on the application will attempt to
+ quit the application.
+
+ Note that attempting a quit may not necessarily result in the
+ application quitting, for example if there still are open windows,
+ or the QEvent::Quit event is ignored.
+
The default is \c true.
\sa QEventLoopLocker
@@ -2094,7 +2102,13 @@ bool QCoreApplicationPrivate::canQuitAutomatically()
if (!in_exec)
return false;
- if (quitLockEnabled && quitLockRef.loadRelaxed())
+ // The automatic quit functionality is triggered by
+ // both QEventLoopLocker and maybeLastWindowClosed.
+ // In either case, we don't want to quit if there
+ // are active QEventLoopLockers, even if quitLockEnabled
+ // is not enabled, as the property signals whether to
+ // trigger the automatic quit, not whether to block it.
+ if (quitLockRef.loadRelaxed())
return false;
return true;
@@ -3237,7 +3251,7 @@ void QCoreApplication::installNativeEventFilter(QAbstractNativeEventFilter *filt
*/
void QCoreApplication::removeNativeEventFilter(QAbstractNativeEventFilter *filterObject)
{
- QAbstractEventDispatcher *eventDispatcher = QAbstractEventDispatcher::instance();
+ QAbstractEventDispatcher *eventDispatcher = QAbstractEventDispatcher::instance(QCoreApplicationPrivate::theMainThread.loadAcquire());
if (!filterObject || !eventDispatcher)
return;
eventDispatcher->removeNativeEventFilter(filterObject);
diff --git a/src/corelib/kernel/qelapsedtimer.cpp b/src/corelib/kernel/qelapsedtimer.cpp
index 511b81a04e..c4308a0b8f 100644
--- a/src/corelib/kernel/qelapsedtimer.cpp
+++ b/src/corelib/kernel/qelapsedtimer.cpp
@@ -14,6 +14,8 @@ QT_BEGIN_NAMESPACE
\reentrant
\ingroup tools
+ \compares strong
+
The QElapsedTimer class is usually used to quickly calculate how much
time has elapsed between two events. Its API is similar to that of QTime,
so code that was using that can be ported quickly to the new class.
@@ -155,8 +157,7 @@ QT_BEGIN_NAMESPACE
Returns \c true if \a lhs and \a rhs contain different times, false otherwise.
*/
/*!
- \fn bool operator<(const QElapsedTimer &lhs, const QElapsedTimer &rhs) noexcept
- \relates QElapsedTimer
+ \fn bool QElapsedTimer::operator<(const QElapsedTimer &lhs, const QElapsedTimer &rhs) noexcept
Returns \c true if \a lhs was started before \a rhs, false otherwise.
diff --git a/src/corelib/kernel/qelapsedtimer.h b/src/corelib/kernel/qelapsedtimer.h
index 7d8b889f61..e71573456d 100644
--- a/src/corelib/kernel/qelapsedtimer.h
+++ b/src/corelib/kernel/qelapsedtimer.h
@@ -4,6 +4,7 @@
#ifndef QELAPSEDTIMER_H
#define QELAPSEDTIMER_H
+#include <QtCore/qcompare.h>
#include <QtCore/qglobal.h>
#include <chrono>
@@ -45,15 +46,41 @@ public:
Duration durationTo(const QElapsedTimer &other) const noexcept;
qint64 msecsTo(const QElapsedTimer &other) const noexcept;
qint64 secsTo(const QElapsedTimer &other) const noexcept;
-
- friend bool operator==(const QElapsedTimer &lhs, const QElapsedTimer &rhs) noexcept
- { return lhs.t1 == rhs.t1 && lhs.t2 == rhs.t2; }
- friend bool operator!=(const QElapsedTimer &lhs, const QElapsedTimer &rhs) noexcept
- { return !(lhs == rhs); }
-
friend bool Q_CORE_EXPORT operator<(const QElapsedTimer &lhs, const QElapsedTimer &rhs) noexcept;
private:
+ friend bool comparesEqual(const QElapsedTimer &lhs, const QElapsedTimer &rhs) noexcept
+ {
+ return lhs.t1 == rhs.t1 && lhs.t2 == rhs.t2;
+ }
+ Q_DECLARE_EQUALITY_COMPARABLE(QElapsedTimer)
+
+ friend Qt::strong_ordering compareThreeWay(const QElapsedTimer &lhs,
+ const QElapsedTimer &rhs) noexcept
+ {
+ return Qt::compareThreeWay(lhs.t1, rhs.t1);
+ }
+
+#if defined(__cpp_lib_three_way_comparison)
+ friend std::strong_ordering
+ operator<=>(const QElapsedTimer &lhs, const QElapsedTimer &rhs) noexcept
+ {
+ return compareThreeWay(lhs, rhs);
+ }
+#else
+ friend bool operator>(const QElapsedTimer &lhs, const QElapsedTimer &rhs) noexcept
+ {
+ return is_gt(compareThreeWay(lhs, rhs));
+ }
+ friend bool operator<=(const QElapsedTimer &lhs, const QElapsedTimer &rhs) noexcept
+ {
+ return is_lteq(compareThreeWay(lhs, rhs));
+ }
+ friend bool operator>=(const QElapsedTimer &lhs, const QElapsedTimer &rhs) noexcept
+ {
+ return is_gteq(compareThreeWay(lhs, rhs));
+ }
+#endif // defined(__cpp_lib_three_way_comparison)
qint64 t1 = Q_INT64_C(0x8000000000000000);
qint64 t2 = Q_INT64_C(0x8000000000000000);
};
diff --git a/src/corelib/kernel/qeventloop.cpp b/src/corelib/kernel/qeventloop.cpp
index d318069ca0..e314a17ff8 100644
--- a/src/corelib/kernel/qeventloop.cpp
+++ b/src/corelib/kernel/qeventloop.cpp
@@ -346,7 +346,11 @@ static_assert(alignof(QCoreApplication) >= 4);
/*!
Creates an event locker operating on the QCoreApplication.
- The application will quit when there are no more QEventLoopLockers operating on it.
+ The application will attempt to quit when there are no more QEventLoopLockers
+ operating on it, as long as QCoreApplication::isQuitLockEnabled() is \c true.
+
+ Note that attempting a quit may not necessarily result in the application quitting,
+ if there for example are open windows, or the QEvent::Quit event is ignored.
\sa QCoreApplication::quit(), QCoreApplication::isQuitLockEnabled()
*/
diff --git a/src/corelib/kernel/qjniarray.h b/src/corelib/kernel/qjniarray.h
index 976b4e92e3..2ea82e39db 100644
--- a/src/corelib/kernel/qjniarray.h
+++ b/src/corelib/kernel/qjniarray.h
@@ -18,7 +18,7 @@ QT_BEGIN_NAMESPACE
template <typename T> class QJniArray;
template <typename T>
-struct QJniArrayIterator
+struct QT_TECH_PREVIEW_API QJniArrayIterator
{
QJniArrayIterator() = default;
@@ -86,7 +86,7 @@ private:
{}
};
-class QJniArrayBase
+class QT_TECH_PREVIEW_API QJniArrayBase
{
// for SFINAE'ing out the fromContainer named constructor
template <typename Container, typename = void> struct CanConvertHelper : std::false_type {};
@@ -193,7 +193,7 @@ private:
};
template <typename T>
-class QJniArray : public QJniArrayBase
+class QT_TECH_PREVIEW_API QJniArray : public QJniArrayBase
{
friend struct QJniArrayIterator<T>;
public:
diff --git a/src/corelib/kernel/qjnienvironment.h b/src/corelib/kernel/qjnienvironment.h
index dda8dc0950..09f7ec7948 100644
--- a/src/corelib/kernel/qjnienvironment.h
+++ b/src/corelib/kernel/qjnienvironment.h
@@ -69,6 +69,7 @@ public:
, std::enable_if_t<QtJniTypes::isObjectType<Class>(), bool> = true
#endif
>
+ QT_TECH_PREVIEW_API
bool registerNativeMethods(std::initializer_list<JNINativeMethod> methods)
{
return registerNativeMethods(QtJniTypes::Traits<Class>::className().data(), methods);
diff --git a/src/corelib/kernel/qjniobject.cpp b/src/corelib/kernel/qjniobject.cpp
index 8244a4390f..892f02e7a4 100644
--- a/src/corelib/kernel/qjniobject.cpp
+++ b/src/corelib/kernel/qjniobject.cpp
@@ -105,9 +105,9 @@ using namespace Qt::StringLiterals;
// C++ code
QJniObject string1 = QJniObject::fromString("String1");
QJniObject string2 = QJniObject::fromString("String2");
- QJniObject stringArray = QJniObject::callStaticObjectMethod<jstringArray>(
+ QJniObject stringArray = QJniObject::callStaticObjectMethod<jobjectArray>(
"org/qtproject/qt/TestClass",
- "stringArray"
+ "stringArray",
string1.object<jstring>(),
string2.object<jstring>());
\endcode
diff --git a/src/corelib/kernel/qjniobject.h b/src/corelib/kernel/qjniobject.h
index 589f6489f7..707d1ae28a 100644
--- a/src/corelib/kernel/qjniobject.h
+++ b/src/corelib/kernel/qjniobject.h
@@ -670,7 +670,7 @@ inline bool operator!=(const QJniObject &obj1, const QJniObject &obj2)
}
namespace QtJniTypes {
-struct JObjectBase
+struct QT_TECH_PREVIEW_API JObjectBase
{
operator QJniObject() const { return m_object; }
@@ -695,7 +695,7 @@ protected:
};
template<typename Type>
-class JObject : public JObjectBase
+class QT_TECH_PREVIEW_API JObject : public JObjectBase
{
public:
using Class = Type;
diff --git a/src/corelib/kernel/qjnitypes.h b/src/corelib/kernel/qjnitypes.h
index 1eaae6312b..e071a3f784 100644
--- a/src/corelib/kernel/qjnitypes.h
+++ b/src/corelib/kernel/qjnitypes.h
@@ -11,6 +11,7 @@
QT_BEGIN_NAMESPACE
+// QT_TECH_PREVIEW_API
#define Q_DECLARE_JNI_TYPE_HELPER(Type) \
namespace QtJniTypes { \
struct Type : JObject<Type> \
@@ -19,7 +20,7 @@ struct Type : JObject<Type> \
}; \
} \
-
+// QT_TECH_PREVIEW_API
#define Q_DECLARE_JNI_TYPE(Type, Signature) \
Q_DECLARE_JNI_TYPE_HELPER(Type) \
template<> \
@@ -35,6 +36,7 @@ struct QtJniTypes::Traits<QtJniTypes::Type> { \
} \
}; \
+// QT_TECH_PREVIEW_API
#define Q_DECLARE_JNI_CLASS(Type, Signature) \
Q_DECLARE_JNI_TYPE_HELPER(Type) \
template<> \
@@ -176,7 +178,7 @@ va_##Method(JNIEnv *env, jclass thiz, ...)
}, argTuple); \
} \
-
+// QT_TECH_PREVIEW_API
#define Q_DECLARE_JNI_NATIVE_METHOD(...) \
QT_OVERLOADED_MACRO(QT_DECLARE_JNI_NATIVE_METHOD, __VA_ARGS__) \
@@ -194,8 +196,10 @@ static const JNINativeMethod Method##_method = { \
#define QT_DECLARE_JNI_NATIVE_METHOD_1(Method) \
QT_DECLARE_JNI_NATIVE_METHOD_2(Method, Method) \
+// QT_TECH_PREVIEW_API
#define Q_JNI_NATIVE_METHOD(Method) QtJniMethods::Method##_method
+// QT_TECH_PREVIEW_API
#define Q_DECLARE_JNI_NATIVE_METHOD_IN_CURRENT_SCOPE(...) \
QT_OVERLOADED_MACRO(QT_DECLARE_JNI_NATIVE_METHOD_IN_CURRENT_SCOPE, __VA_ARGS__) \
@@ -209,6 +213,7 @@ static const JNINativeMethod Method##_method = { \
#define QT_DECLARE_JNI_NATIVE_METHOD_IN_CURRENT_SCOPE_1(Method) \
QT_DECLARE_JNI_NATIVE_METHOD_IN_CURRENT_SCOPE_2(Method, Method) \
+// QT_TECH_PREVIEW_API
#define Q_JNI_NATIVE_SCOPED_METHOD(Method, Scope) Scope::Method##_method
QT_END_NAMESPACE
diff --git a/src/corelib/kernel/qmetacontainer.cpp b/src/corelib/kernel/qmetacontainer.cpp
index 200724c9f4..5f68f8fe74 100644
--- a/src/corelib/kernel/qmetacontainer.cpp
+++ b/src/corelib/kernel/qmetacontainer.cpp
@@ -14,6 +14,8 @@ QT_BEGIN_NAMESPACE
\ingroup objectmodel
+ \compares equality
+
The class provides a number of primitive container operations, using void*
as operands. This way, you can manipulate a generic container retrieved from
a Variant without knowing its type.
@@ -790,21 +792,19 @@ void QMetaSequence::valueAtConstIterator(const void *iterator, void *result) con
}
/*!
- \fn bool operator==(QMetaSequence a, QMetaSequence b)
+ \fn bool QMetaSequence::operator==(const QMetaSequence &lhs, const QMetaSequence &rhs)
\since 6.0
- \relates QMetaSequence
- Returns \c true if the QMetaSequence \a a represents the same container type
- as the QMetaSequence \a b, otherwise returns \c false.
+ Returns \c true if the QMetaSequence \a lhs represents the same container type
+ as the QMetaSequence \a rhs, otherwise returns \c false.
*/
/*!
- \fn bool operator!=(QMetaSequence a, QMetaSequence b)
+ \fn bool QMetaSequence::operator!=(const QMetaSequence &lhs, const QMetaSequence &rhs)
\since 6.0
- \relates QMetaSequence
- Returns \c true if the QMetaSequence \a a represents a different container
- type than the QMetaSequence \a b, otherwise returns \c false.
+ Returns \c true if the QMetaSequence \a lhs represents a different container
+ type than the QMetaSequence \a rhs, otherwise returns \c false.
*/
diff --git a/src/corelib/kernel/qmetacontainer.h b/src/corelib/kernel/qmetacontainer.h
index 67c0ddcf36..1bed7f9f7b 100644
--- a/src/corelib/kernel/qmetacontainer.h
+++ b/src/corelib/kernel/qmetacontainer.h
@@ -5,6 +5,7 @@
#define QMETACONTAINER_H
#include <QtCore/qcontainerinfo.h>
+#include <QtCore/qcompare.h>
#include <QtCore/qflags.h>
#include <QtCore/qglobal.h>
@@ -975,18 +976,15 @@ public:
bool canGetValueAtConstIterator() const;
void valueAtConstIterator(const void *iterator, void *result) const;
- friend bool operator==(const QMetaSequence &a, const QMetaSequence &b)
- {
- return a.d() == b.d();
- }
- friend bool operator!=(const QMetaSequence &a, const QMetaSequence &b)
- {
- return a.d() != b.d();
- }
-
const QtMetaContainerPrivate::QMetaSequenceInterface *iface() const { return d(); }
private:
+ friend bool comparesEqual(const QMetaSequence &lhs, const QMetaSequence &rhs) noexcept
+ {
+ return lhs.d() == rhs.d();
+ }
+ Q_DECLARE_EQUALITY_COMPARABLE(QMetaSequence)
+
template<typename T>
struct MetaSequence
{
@@ -1171,18 +1169,15 @@ public:
return nullptr;
}
- friend bool operator==(const QMetaAssociation &a, const QMetaAssociation &b)
- {
- return a.d() == b.d();
- }
- friend bool operator!=(const QMetaAssociation &a, const QMetaAssociation &b)
- {
- return a.d() != b.d();
- }
-
const QtMetaContainerPrivate::QMetaAssociationInterface *iface() const { return d(); }
private:
+ friend bool comparesEqual(const QMetaAssociation &lhs, const QMetaAssociation &rhs) noexcept
+ {
+ return lhs.d() == rhs.d();
+ }
+ Q_DECLARE_EQUALITY_COMPARABLE(QMetaAssociation)
+
template<typename T>
struct MetaAssociation
{
diff --git a/src/corelib/kernel/qmetaobject.cpp b/src/corelib/kernel/qmetaobject.cpp
index 8d304bd890..05662b385a 100644
--- a/src/corelib/kernel/qmetaobject.cpp
+++ b/src/corelib/kernel/qmetaobject.cpp
@@ -1798,6 +1798,7 @@ bool QMetaObject::invokeMethodImpl(QObject *object, QtPrivate::QSlotObjectBase *
function.
\ingroup objectmodel
+ \compares equality
A QMetaMethod has a methodType(), a methodSignature(), a list of
parameterTypes() and parameterNames(), a return typeName(), a
@@ -1825,19 +1826,19 @@ bool QMetaObject::invokeMethodImpl(QObject *object, QtPrivate::QSlotObjectBase *
invoked), otherwise returns \c false.
*/
-/*! \fn bool QMetaMethod::operator==(const QMetaMethod &m1, const QMetaMethod &m2)
+/*! \fn bool QMetaMethod::operator==(const QMetaMethod &lhs, const QMetaMethod &rhs)
\since 5.0
\overload
- Returns \c true if method \a m1 is equal to method \a m2,
+ Returns \c true if method \a lhs is equal to method \a rhs,
otherwise returns \c false.
*/
-/*! \fn bool QMetaMethod::operator!=(const QMetaMethod &m1, const QMetaMethod &m2)
+/*! \fn bool QMetaMethod::operator!=(const QMetaMethod &lhs, const QMetaMethod &rhs)
\since 5.0
\overload
- Returns \c true if method \a m1 is not equal to method \a m2,
+ Returns \c true if method \a lhs is not equal to method \a rhs,
otherwise returns \c false.
*/
@@ -3655,8 +3656,8 @@ QMetaProperty::QMetaProperty(const QMetaObject *mobj, int index)
data(getMetaPropertyData(mobj, index))
{
Q_ASSERT(index >= 0 && index < priv(mobj->d.data)->propertyCount);
-
- if (!(data.flags() & EnumOrFlag))
+ // The code below here just resolves menum if the property is an enum type:
+ if (!(data.flags() & EnumOrFlag) || !metaType().flags().testFlag(QMetaType::IsEnumeration))
return;
QByteArrayView enum_name = typeNameFromTypeInfo(mobj, data.type());
menum = mobj->enumerator(QMetaObjectPrivate::indexOfEnumerator(mobj, enum_name));
diff --git a/src/corelib/kernel/qmetaobject.h b/src/corelib/kernel/qmetaobject.h
index 4e52e854d9..91f287a8d3 100644
--- a/src/corelib/kernel/qmetaobject.h
+++ b/src/corelib/kernel/qmetaobject.h
@@ -6,6 +6,7 @@
#define QMETAOBJECT_H
#include <QtCore/qobjectdefs.h>
+#include <QtCore/qcompare.h>
#include <QtCore/qvariant.h>
QT_BEGIN_NAMESPACE
@@ -251,10 +252,11 @@ protected:
friend struct QMetaObject;
friend struct QMetaObjectPrivate;
friend class QObject;
- friend bool operator==(const QMetaMethod &m1, const QMetaMethod &m2) noexcept
- { return m1.data == m2.data; }
- friend bool operator!=(const QMetaMethod &m1, const QMetaMethod &m2) noexcept
- { return !(m1 == m2); }
+
+private:
+ friend bool comparesEqual(const QMetaMethod &lhs, const QMetaMethod &rhs) noexcept
+ { return lhs.data == rhs.data; }
+ Q_DECLARE_EQUALITY_COMPARABLE(QMetaMethod)
};
Q_DECLARE_TYPEINFO(QMetaMethod, Q_RELOCATABLE_TYPE);
diff --git a/src/corelib/kernel/qmetaobject_p.h b/src/corelib/kernel/qmetaobject_p.h
index d2c36fceb4..d5dc9a356a 100644
--- a/src/corelib/kernel/qmetaobject_p.h
+++ b/src/corelib/kernel/qmetaobject_p.h
@@ -111,22 +111,18 @@ public:
const_cast<QArgumentType *>(this)->_name = QMetaType(_type).name();
return _name;
}
- bool operator==(const QArgumentType &other) const
- {
- if (_type && other._type)
- return _type == other._type;
- else
- return name() == other.name();
- }
- bool operator!=(const QArgumentType &other) const
+
+private:
+ friend bool comparesEqual(const QArgumentType &lhs,
+ const QArgumentType &rhs) noexcept
{
- if (_type && other._type)
- return _type != other._type;
+ if (lhs._type && rhs._type)
+ return lhs._type == rhs._type;
else
- return name() != other.name();
+ return lhs.name() == rhs.name();
}
+ Q_DECLARE_EQUALITY_COMPARABLE(QArgumentType)
-private:
int _type;
QByteArray _name;
};
diff --git a/src/corelib/kernel/qmetatype.cpp b/src/corelib/kernel/qmetatype.cpp
index 387c0f49ab..1c2665e53c 100644
--- a/src/corelib/kernel/qmetatype.cpp
+++ b/src/corelib/kernel/qmetatype.cpp
@@ -474,6 +474,7 @@ const char *QtMetaTypePrivate::typedefNameForType(const QtPrivate::QMetaTypeInte
\ingroup objectmodel
\threadsafe
+ \compares equality
The class is used as a helper to marshall types in QVariant and
in queued signals and slots connections. It associates a type
@@ -925,20 +926,20 @@ void QMetaType::unregisterMetaType(QMetaType type)
Returns the QMetaType corresponding to the type in the template parameter.
*/
-/*! \fn bool QMetaType::operator==(QMetaType a, QMetaType b)
+/*! \fn bool QMetaType::operator==(const QMetaType &lhs, const QMetaType &rhs)
\since 5.15
\overload
- Returns \c true if the QMetaType \a a represents the same type
- as the QMetaType \a b, otherwise returns \c false.
+ Returns \c true if the QMetaType \a lhs represents the same type
+ as the QMetaType \a rhs, otherwise returns \c false.
*/
-/*! \fn bool QMetaType::operator!=(QMetaType a, QMetaType b)
+/*! \fn bool QMetaType::operator!=(const QMetaType &lhs, const QMetaType &rhs)
\since 5.15
\overload
- Returns \c true if the QMetaType \a a represents a different type
- than the QMetaType \a b, otherwise returns \c false.
+ Returns \c true if the QMetaType \a lhs represents a different type
+ than the QMetaType \a rhs, otherwise returns \c false.
*/
/*! \internal */
@@ -2659,6 +2660,36 @@ bool QMetaType::hasRegisteredConverterFunction(QMetaType fromType, QMetaType toT
}
/*!
+ \internal
+ Non-template helper ("SCARY") for IsMetaTypePair::registerConverter().
+*/
+bool QtPrivate::hasRegisteredConverterFunctionToPairVariantInterface(QMetaType m)
+{
+ const QMetaType to = QMetaType::fromType<QtMetaTypePrivate::QPairVariantInterfaceImpl>();
+ return QMetaType::hasRegisteredConverterFunction(m, to);
+}
+
+/*!
+ \internal
+ Non-template helper ("SCARY") for SequentialValueTypeIsMetaType::registerConverter().
+*/
+bool QtPrivate::hasRegisteredConverterFunctionToIterableMetaSequence(QMetaType m)
+{
+ const QMetaType to = QMetaType::fromType<QIterable<QMetaSequence>>();
+ return QMetaType::hasRegisteredConverterFunction(m, to);
+}
+
+/*!
+ \internal
+ Non-template helper ("SCARY") for AssociativeKeyTypeIsMetaType::registerConverter().
+*/
+bool QtPrivate::hasRegisteredConverterFunctionToIterableMetaAssociation(QMetaType m)
+{
+ const QMetaType to = QMetaType::fromType<QIterable<QMetaAssociation>>();
+ return QMetaType::hasRegisteredConverterFunction(m, to);
+}
+
+/*!
\fn template<typename From, typename To> bool QMetaType::hasRegisteredMutableViewFunction()
Returns \c true, if the meta type system has a registered mutable view on type From of type To.
\since 6.0
@@ -2676,6 +2707,26 @@ bool QMetaType::hasRegisteredMutableViewFunction(QMetaType fromType, QMetaType t
}
/*!
+ \internal
+ Non-template helper ("SCARY") for SequentialValueTypeIsMetaType::registerMutableView().
+*/
+bool QtPrivate::hasRegisteredMutableViewFunctionToIterableMetaSequence(QMetaType m)
+{
+ const QMetaType to = QMetaType::fromType<QIterable<QMetaSequence>>();
+ return QMetaType::hasRegisteredMutableViewFunction(m, to);
+}
+
+/*!
+ \internal
+ Non-template helper ("SCARY") for AssociativeKeyTypeIsMetaType::registerMutableView().
+*/
+bool QtPrivate::hasRegisteredMutableViewFunctionToIterableMetaAssociation(QMetaType m)
+{
+ const QMetaType to = QMetaType::fromType<QIterable<QMetaAssociation>>();
+ return QMetaType::hasRegisteredMutableViewFunction(m, to);
+}
+
+/*!
\fn const char *QMetaType::typeName(int typeId)
\deprecated
diff --git a/src/corelib/kernel/qmetatype.h b/src/corelib/kernel/qmetatype.h
index e3ef1474da..12a67aef58 100644
--- a/src/corelib/kernel/qmetatype.h
+++ b/src/corelib/kernel/qmetatype.h
@@ -518,20 +518,20 @@ public:
template<typename T>
constexpr static QMetaType fromType();
static QMetaType fromName(QByteArrayView name);
-
- friend bool operator==(QMetaType a, QMetaType b)
+private:
+ friend bool comparesEqual(const QMetaType &lhs,
+ const QMetaType &rhs) noexcept
{
- if (a.d_ptr == b.d_ptr)
+ if (lhs.d_ptr == rhs.d_ptr)
return true;
- if (!a.d_ptr || !b.d_ptr)
+ if (!lhs.d_ptr || !rhs.d_ptr)
return false; // one type is undefined, the other is defined
// avoid id call if we already have the id
- const int aId = a.id();
- const int bId = b.id();
+ const int aId = lhs.id();
+ const int bId = rhs.id();
return aId == bId;
}
- friend bool operator!=(QMetaType a, QMetaType b) { return !(a == b); }
-
+ Q_DECLARE_EQUALITY_COMPARABLE(QMetaType)
#ifndef QT_NO_DEBUG_STREAM
private:
friend Q_CORE_EXPORT QDebug operator<<(QDebug d, QMetaType m);
@@ -1750,11 +1750,19 @@ QT_FOR_EACH_STATIC_TYPE(Q_DECLARE_BUILTIN_METATYPE)
QT_BEGIN_NAMESPACE
+namespace QtPrivate {
+// out-of-line helpers to reduce template code bloat ("SCARY") and improve compile times:
+Q_CORE_EXPORT bool hasRegisteredConverterFunctionToPairVariantInterface(QMetaType m);
+Q_CORE_EXPORT bool hasRegisteredConverterFunctionToIterableMetaSequence(QMetaType m);
+Q_CORE_EXPORT bool hasRegisteredMutableViewFunctionToIterableMetaSequence(QMetaType m);
+Q_CORE_EXPORT bool hasRegisteredConverterFunctionToIterableMetaAssociation(QMetaType m);
+Q_CORE_EXPORT bool hasRegisteredMutableViewFunctionToIterableMetaAssociation(QMetaType m);
+}
+
template <typename T>
inline bool QtPrivate::IsMetaTypePair<T, true>::registerConverter()
{
- const QMetaType to = QMetaType::fromType<QtMetaTypePrivate::QPairVariantInterfaceImpl>();
- if (!QMetaType::hasRegisteredConverterFunction(QMetaType::fromType<T>(), to)) {
+ if (!QtPrivate::hasRegisteredConverterFunctionToPairVariantInterface(QMetaType::fromType<T>())) {
QtMetaTypePrivate::QPairVariantInterfaceConvertFunctor<T> o;
return QMetaType::registerConverter<T, QtMetaTypePrivate::QPairVariantInterfaceImpl>(o);
}
@@ -1786,8 +1794,7 @@ struct SequentialValueTypeIsMetaType<T, true>
{
static bool registerConverter()
{
- const QMetaType to = QMetaType::fromType<QIterable<QMetaSequence>>();
- if (!QMetaType::hasRegisteredConverterFunction(QMetaType::fromType<T>(), to)) {
+ if (!QtPrivate::hasRegisteredConverterFunctionToIterableMetaSequence(QMetaType::fromType<T>())) {
QSequentialIterableConvertFunctor<T> o;
return QMetaType::registerConverter<T, QIterable<QMetaSequence>>(o);
}
@@ -1796,8 +1803,7 @@ struct SequentialValueTypeIsMetaType<T, true>
static bool registerMutableView()
{
- const QMetaType to = QMetaType::fromType<QIterable<QMetaSequence>>();
- if (!QMetaType::hasRegisteredMutableViewFunction(QMetaType::fromType<T>(), to)) {
+ if (!QtPrivate::hasRegisteredMutableViewFunctionToIterableMetaSequence(QMetaType::fromType<T>())) {
QSequentialIterableMutableViewFunctor<T> o;
return QMetaType::registerMutableView<T, QIterable<QMetaSequence>>(o);
}
@@ -1830,8 +1836,7 @@ struct AssociativeKeyTypeIsMetaType<T, true> : AssociativeMappedTypeIsMetaType<T
{
static bool registerConverter()
{
- const QMetaType to = QMetaType::fromType<QIterable<QMetaAssociation>>();
- if (!QMetaType::hasRegisteredConverterFunction(QMetaType::fromType<T>(), to)) {
+ if (!QtPrivate::hasRegisteredConverterFunctionToIterableMetaAssociation(QMetaType::fromType<T>())) {
QAssociativeIterableConvertFunctor<T> o;
return QMetaType::registerConverter<T, QIterable<QMetaAssociation>>(o);
}
@@ -1840,8 +1845,7 @@ struct AssociativeKeyTypeIsMetaType<T, true> : AssociativeMappedTypeIsMetaType<T
static bool registerMutableView()
{
- const QMetaType to = QMetaType::fromType<QIterable<QMetaAssociation>>();
- if (!QMetaType::hasRegisteredMutableViewFunction(QMetaType::fromType<T>(), to)) {
+ if (!QtPrivate::hasRegisteredMutableViewFunctionToIterableMetaAssociation(QMetaType::fromType<T>())) {
QAssociativeIterableMutableViewFunctor<T> o;
return QMetaType::registerMutableView<T, QIterable<QMetaAssociation>>(o);
}
diff --git a/src/corelib/kernel/qobject.cpp b/src/corelib/kernel/qobject.cpp
index 708b10a75e..e1129c5d25 100644
--- a/src/corelib/kernel/qobject.cpp
+++ b/src/corelib/kernel/qobject.cpp
@@ -1816,6 +1816,8 @@ void QObjectPrivate::setThreadData_helper(QThreadData *currentData, QThreadData
int QObject::startTimer(int interval, Qt::TimerType timerType)
{
+ // no overflow can happen here:
+ // 2^31 ms * 1,000,000 always fits a 64-bit signed integer type
return startTimer(std::chrono::milliseconds{interval}, timerType);
}
diff --git a/src/corelib/kernel/qproperty_p.h b/src/corelib/kernel/qproperty_p.h
index 8ae6664a2b..376482a6af 100644
--- a/src/corelib/kernel/qproperty_p.h
+++ b/src/corelib/kernel/qproperty_p.h
@@ -19,12 +19,13 @@
#include <qproperty.h>
#include <qmetaobject.h>
-#include <qscopedpointer.h>
#include <qscopedvaluerollback.h>
#include <qvariant.h>
#include <vector>
#include <QtCore/QVarLengthArray>
+#include <memory>
+
QT_BEGIN_NAMESPACE
namespace QtPrivate {
@@ -292,7 +293,7 @@ private:
ObserverArray inlineDependencyObservers; // for things we are observing
QPropertyObserverPointer firstObserver; // list of observers observing us
- QScopedPointer<std::vector<QPropertyObserver>> heapObservers; // for things we are observing
+ std::unique_ptr<std::vector<QPropertyObserver>> heapObservers; // for things we are observing
protected:
QUntypedPropertyData *propertyDataPtr = nullptr;
diff --git a/src/corelib/kernel/qpropertyprivate.h b/src/corelib/kernel/qpropertyprivate.h
index 86dc08a6bc..a8456721d0 100644
--- a/src/corelib/kernel/qpropertyprivate.h
+++ b/src/corelib/kernel/qpropertyprivate.h
@@ -95,28 +95,20 @@ public:
void swap(QPropertyBindingPrivatePtr &other) noexcept
{ qt_ptr_swap(d, other.d); }
- friend bool operator==(const QPropertyBindingPrivatePtr &p1, const QPropertyBindingPrivatePtr &p2) noexcept
- { return p1.d == p2.d; }
- friend bool operator!=(const QPropertyBindingPrivatePtr &p1, const QPropertyBindingPrivatePtr &p2) noexcept
- { return p1.d != p2.d; }
- friend bool operator==(const QPropertyBindingPrivatePtr &p1, const T *ptr) noexcept
- { return p1.d == ptr; }
- friend bool operator!=(const QPropertyBindingPrivatePtr &p1, const T *ptr) noexcept
- { return p1.d != ptr; }
- friend bool operator==(const T *ptr, const QPropertyBindingPrivatePtr &p2) noexcept
- { return ptr == p2.d; }
- friend bool operator!=(const T *ptr, const QPropertyBindingPrivatePtr &p2) noexcept
- { return ptr != p2.d; }
- friend bool operator==(const QPropertyBindingPrivatePtr &p1, std::nullptr_t) noexcept
- { return !p1; }
- friend bool operator!=(const QPropertyBindingPrivatePtr &p1, std::nullptr_t) noexcept
- { return p1; }
- friend bool operator==(std::nullptr_t, const QPropertyBindingPrivatePtr &p2) noexcept
- { return !p2; }
- friend bool operator!=(std::nullptr_t, const QPropertyBindingPrivatePtr &p2) noexcept
- { return p2; }
-
private:
+ friend bool comparesEqual(const QPropertyBindingPrivatePtr &lhs,
+ const QPropertyBindingPrivatePtr &rhs) noexcept
+ { return lhs.d == rhs.d; }
+ Q_DECLARE_EQUALITY_COMPARABLE(QPropertyBindingPrivatePtr)
+ friend bool comparesEqual(const QPropertyBindingPrivatePtr &lhs,
+ const T *rhs) noexcept
+ { return lhs.d == rhs; }
+ Q_DECLARE_EQUALITY_COMPARABLE(QPropertyBindingPrivatePtr, T*)
+ friend bool comparesEqual(const QPropertyBindingPrivatePtr &lhs,
+ std::nullptr_t) noexcept
+ { return !lhs; }
+ Q_DECLARE_EQUALITY_COMPARABLE(QPropertyBindingPrivatePtr, std::nullptr_t)
+
QtPrivate::RefCounted *d;
};
diff --git a/src/corelib/kernel/qsingleshottimer_p.h b/src/corelib/kernel/qsingleshottimer_p.h
index d7e33c5221..dd1402f63a 100644
--- a/src/corelib/kernel/qsingleshottimer_p.h
+++ b/src/corelib/kernel/qsingleshottimer_p.h
@@ -19,6 +19,7 @@
#include "qabstracteventdispatcher.h"
#include "qcoreapplication.h"
#include "qmetaobject_p.h"
+#include "private/qnumeric_p.h"
#include <chrono>
@@ -43,6 +44,20 @@ public:
inline void startTimerForReceiver(Duration interval, Qt::TimerType timerType,
const QObject *receiver);
+ static Duration fromMsecs(std::chrono::milliseconds ms)
+ {
+ using namespace std::chrono;
+ using ratio = std::ratio_divide<std::milli, Duration::period>;
+ static_assert(ratio::den == 1);
+
+ Duration::rep r;
+ if (qMulOverflow<ratio::num>(ms.count(), &r)) {
+ qWarning("QTimer::singleShot(std::chrono::milliseconds, ...): "
+ "interval argument overflowed when converted to nanoseconds.");
+ return Duration::max();
+ }
+ return Duration{r};
+ }
Q_SIGNALS:
void timeout();
diff --git a/src/corelib/kernel/qsocketnotifier.h b/src/corelib/kernel/qsocketnotifier.h
index 8288a6b2b5..ac9e577ebc 100644
--- a/src/corelib/kernel/qsocketnotifier.h
+++ b/src/corelib/kernel/qsocketnotifier.h
@@ -83,20 +83,20 @@ public:
Q_DECL_CONSTEXPR_NOT_WIN bool isValid() const noexcept { return *this != QSocketDescriptor(); }
- friend Q_DECL_CONSTEXPR_NOT_WIN bool operator==(QSocketDescriptor lhs,
- QSocketDescriptor rhs) noexcept
+private:
+ friend Q_DECL_CONSTEXPR_NOT_WIN bool comparesEqual(const QSocketDescriptor &lhs,
+ const QSocketDescriptor &rhs) noexcept
{
return lhs.sockfd == rhs.sockfd;
}
- friend Q_DECL_CONSTEXPR_NOT_WIN bool operator!=(QSocketDescriptor lhs,
- QSocketDescriptor rhs) noexcept
- {
- return lhs.sockfd != rhs.sockfd;
- }
+#if defined(Q_OS_WIN) || defined(Q_QDOC)
+ Q_DECLARE_EQUALITY_COMPARABLE(QSocketDescriptor)
+#else
+ Q_DECLARE_EQUALITY_COMPARABLE_LITERAL_TYPE(QSocketDescriptor)
+#endif
#undef Q_DECL_CONSTEXPR_NOT_WIN
-private:
DescriptorType sockfd;
};
diff --git a/src/corelib/kernel/qtimer.cpp b/src/corelib/kernel/qtimer.cpp
index cc46c1433b..294369c1b3 100644
--- a/src/corelib/kernel/qtimer.cpp
+++ b/src/corelib/kernel/qtimer.cpp
@@ -213,7 +213,7 @@ void QTimer::start()
if (d->isActive()) // stop running timer
stop();
- const auto newId = Qt::TimerId{QObject::startTimer(d->inter * 1ms, d->type)};
+ Qt::TimerId newId{ QObject::startTimer(d->inter * 1ms, d->type) }; // overflow impossible
if (newId > Qt::TimerId::Invalid) {
d->id = newId;
d->isActiveData.notify();
@@ -332,7 +332,7 @@ void QTimer::singleShotImpl(std::chrono::milliseconds msec, Qt::TimerType timerT
return;
}
- new QSingleShotTimer(msec, timerType, receiver, slotObj);
+ new QSingleShotTimer(QSingleShotTimer::fromMsecs(msec), timerType, receiver, slotObj);
}
/*!
@@ -396,7 +396,7 @@ void QTimer::singleShot(std::chrono::milliseconds msec, Qt::TimerType timerType,
Qt::QueuedConnection);
return;
}
- (void) new QSingleShotTimer(msec, timerType, receiver, member);
+ (void) new QSingleShotTimer(QSingleShotTimer::fromMsecs(msec), timerType, receiver, member);
}
}
@@ -592,7 +592,7 @@ void QTimer::setInterval(std::chrono::milliseconds interval)
d->inter.setValueBypassingBindings(msec);
if (d->isActive()) { // create new timer
QObject::killTimer(d->id); // restart timer
- const auto newId = Qt::TimerId{QObject::startTimer(msec * 1ms, d->type)};
+ Qt::TimerId newId{ QObject::startTimer(msec * 1ms, d->type) }; // overflow impossible
if (newId > Qt::TimerId::Invalid) {
// Restarted successfully. No need to update the active state.
d->id = newId;
diff --git a/src/corelib/mimetypes/mime/packages/freedesktop.org.xml b/src/corelib/mimetypes/mime/packages/freedesktop.org.xml
index b7aa6a1995..e77349a12e 100644
--- a/src/corelib/mimetypes/mime/packages/freedesktop.org.xml
+++ b/src/corelib/mimetypes/mime/packages/freedesktop.org.xml
@@ -10699,39 +10699,39 @@ command to generate the output files.
<glob pattern="*.udeb"/>
</mime-type>
<mime-type type="application/x-designer">
- <comment>Qt Designer interface document</comment>
- <comment xml:lang="zh_TW">Qt Designer 介面文件</comment>
- <comment xml:lang="zh_CN">Qt Designer 界面文档</comment>
- <comment xml:lang="uk">документ інтерфейсу Qt Designer</comment>
- <comment xml:lang="tr">Qt Designer arayüz belgesi</comment>
- <comment xml:lang="sv">Qt Designer-gränssnittsdokument</comment>
- <comment xml:lang="sq">dokument ndërfaqesh Qt Designer</comment>
- <comment xml:lang="sl">Dokument vmesnika Qt Designer</comment>
- <comment xml:lang="si">Qt Designer අතුරුමුහුණත් ලේඛනය</comment>
- <comment xml:lang="ru">Документ интерфейса Qt Designer</comment>
- <comment xml:lang="pt_BR">Documento de interface do Qt Designer</comment>
- <comment xml:lang="pt">documento de interface Qt Designer</comment>
- <comment xml:lang="pl">Dokument interfejsu Qt Designer</comment>
- <comment xml:lang="nl">Qt Designer-interfacedocument</comment>
+ <comment>Qt Widgets Designer interface document</comment>
+ <comment xml:lang="zh_TW">Qt Widgets Designer 介面文件</comment>
+ <comment xml:lang="zh_CN">Qt Widgets Designer 界面文档</comment>
+ <comment xml:lang="uk">документ інтерфейсу Qt Widgets Designer</comment>
+ <comment xml:lang="tr">Qt Widgets Designer arayüz belgesi</comment>
+ <comment xml:lang="sv">Qt Widgets Designer-gränssnittsdokument</comment>
+ <comment xml:lang="sq">dokument ndërfaqesh Qt Widgets Designer</comment>
+ <comment xml:lang="sl">Dokument vmesnika Qt Widgets Designer</comment>
+ <comment xml:lang="si">Qt Widgets Designer අතුරුමුහුණත් ලේඛනය</comment>
+ <comment xml:lang="ru">Документ интерфейса Qt Widgets Designer</comment>
+ <comment xml:lang="pt_BR">Documento de interface do Qt Widgets Designer</comment>
+ <comment xml:lang="pt">documento de interface Qt Widgets Designer</comment>
+ <comment xml:lang="pl">Dokument interfejsu Qt Widgets Designer</comment>
+ <comment xml:lang="nl">Qt Widgets Designer-interfacedocument</comment>
<comment xml:lang="ko">Qt 디자이너 인터페이스 문서</comment>
- <comment xml:lang="kk">Qt Designer интерфейс құжаты</comment>
- <comment xml:lang="ja">Qt Designer インターフェイスドキュメント</comment>
- <comment xml:lang="it">Documento interfaccia Qt Designer</comment>
- <comment xml:lang="is">Qt Designer viðmótsskjal</comment>
- <comment xml:lang="id">Dokumen antarmuka Qt Designer</comment>
- <comment xml:lang="hu">Qt Designer felületleíró dokumentum</comment>
- <comment xml:lang="hr">Qt Designer dokument sučelja</comment>
- <comment xml:lang="he">מסמך מנשק של Qt Designer</comment>
- <comment xml:lang="fr">document d'interface Qt Designer</comment>
- <comment xml:lang="fi">Qt Designer -käyttöliittymän asiakirja</comment>
- <comment xml:lang="eu">Qt Designer interfaze dokumentua</comment>
- <comment xml:lang="es">documento de interfaz de Qt Designer</comment>
- <comment xml:lang="en_GB">Qt Designer interface document</comment>
+ <comment xml:lang="kk">Qt Widgets Designer интерфейс құжаты</comment>
+ <comment xml:lang="ja">Qt Widgets Designer インターフェイスドキュメント</comment>
+ <comment xml:lang="it">Documento interfaccia Qt Widgets Designer</comment>
+ <comment xml:lang="is">Qt Widgets Designer viðmótsskjal</comment>
+ <comment xml:lang="id">Dokumen antarmuka Qt Widgets Designer</comment>
+ <comment xml:lang="hu">Qt Widgets Designer felületleíró dokumentum</comment>
+ <comment xml:lang="hr">Qt Widgets Designer dokument sučelja</comment>
+ <comment xml:lang="he">מסמך מנשק של Qt Widgets Designer</comment>
+ <comment xml:lang="fr">document d'interface Qt Widgets Designer</comment>
+ <comment xml:lang="fi">Qt Widgets Designer -käyttöliittymän asiakirja</comment>
+ <comment xml:lang="eu">Qt Widgets Designer interfaze dokumentua</comment>
+ <comment xml:lang="es">documento de interfaz de Qt Widgets Designer</comment>
+ <comment xml:lang="en_GB">Qt Widgets Designer interface document</comment>
<comment xml:lang="de">Qt-Designer-Oberflächendokument</comment>
- <comment xml:lang="da">Qt Designer-brugerflade-dokument</comment>
- <comment xml:lang="ca">document d'interfície Qt Designer</comment>
- <comment xml:lang="bg">Документ — интерфейс, Qt Designer</comment>
- <comment xml:lang="be">дакумент інтэрфейсу Qt Designer</comment>
+ <comment xml:lang="da">Qt Widgets Designer-brugerflade-dokument</comment>
+ <comment xml:lang="ca">document d'interfície Qt Widgets Designer</comment>
+ <comment xml:lang="bg">Документ — интерфейс, Qt Widgets Designer</comment>
+ <comment xml:lang="be">дакумент інтэрфейсу Qt Widgets Designer</comment>
<comment xml:lang="ar">مستند واجهة مصمم كيوت</comment>
<generic-icon name="x-office-document"/>
<sub-class-of type="application/xml"/>
diff --git a/src/corelib/mimetypes/qmimetype.cpp b/src/corelib/mimetypes/qmimetype.cpp
index ad3c484f30..e26c8b898d 100644
--- a/src/corelib/mimetypes/qmimetype.cpp
+++ b/src/corelib/mimetypes/qmimetype.cpp
@@ -24,6 +24,7 @@ using namespace Qt::StringLiterals;
\brief The QMimeType class describes types of file or data, represented by a MIME type string.
\since 5.0
+ \compares equality
For instance a file named "readme.txt" has the MIME type "text/plain".
The MIME type can be determined from the file name, or from the file
@@ -111,14 +112,15 @@ QMimeType::~QMimeType()
}
/*!
- \fn bool QMimeType::operator==(const QMimeType &other) const;
- Returns \c true if \a other equals this QMimeType object, otherwise returns \c false.
+ \fn bool QMimeType::operator==(const QMimeType &lhs, const QMimeType &rhs);
+ Returns \c true if \a lhs equals to the \a rhs QMimeType object, otherwise
+ returns \c false.
The name is the unique identifier for a mimetype, so two mimetypes with
the same name, are equal.
*/
-bool QMimeType::operator==(const QMimeType &other) const
+bool comparesEqual(const QMimeType &lhs, const QMimeType &rhs) noexcept
{
- return d == other.d || d->name == other.d->name;
+ return lhs.d == rhs.d || lhs.d->name == rhs.d->name;
}
/*!
@@ -134,8 +136,9 @@ size_t qHash(const QMimeType &key, size_t seed) noexcept
}
/*!
- \fn bool QMimeType::operator!=(const QMimeType &other) const;
- Returns \c true if \a other does not equal this QMimeType object, otherwise returns \c false.
+ \fn bool QMimeType::operator!=(const QMimeType &lhs, const QMimeType &rhs);
+ Returns \c true if QMimeType \a lhs is not equal to QMimeType \a rhs,
+ otherwise returns \c false.
*/
/*!
diff --git a/src/corelib/mimetypes/qmimetype.h b/src/corelib/mimetypes/qmimetype.h
index 3421638f5b..508a5cfb53 100644
--- a/src/corelib/mimetypes/qmimetype.h
+++ b/src/corelib/mimetypes/qmimetype.h
@@ -49,14 +49,14 @@ public:
}
explicit QMimeType(const QMimeTypePrivate &dd);
~QMimeType();
-
+#if QT_CORE_REMOVED_SINCE(6, 8)
bool operator==(const QMimeType &other) const;
inline bool operator!=(const QMimeType &other) const
{
return !operator==(other);
}
-
+#endif
bool isValid() const;
bool isDefault() const;
@@ -86,6 +86,10 @@ protected:
friend Q_CORE_EXPORT size_t qHash(const QMimeType &key, size_t seed) noexcept;
QExplicitlySharedDataPointer<QMimeTypePrivate> d;
+
+private:
+ friend Q_CORE_EXPORT bool comparesEqual(const QMimeType &lhs, const QMimeType &rhs) noexcept;
+ Q_DECLARE_EQUALITY_COMPARABLE(QMimeType)
};
Q_DECLARE_SHARED(QMimeType)
diff --git a/src/corelib/serialization/qdatastream.cpp b/src/corelib/serialization/qdatastream.cpp
index d35e871de0..329be4a294 100644
--- a/src/corelib/serialization/qdatastream.cpp
+++ b/src/corelib/serialization/qdatastream.cpp
@@ -1088,26 +1088,22 @@ QDataStream &QDataStream::readBytes(char *&s, qint64 &l)
qsizetype step = (dev->bytesAvailable() >= len) ? len : 1024 * 1024;
qsizetype allocated = 0;
- char *prevBuf = nullptr;
- char *curBuf = nullptr;
+ std::unique_ptr<char[]> curBuf = nullptr;
+ constexpr qsizetype StepIncreaseThreshold = std::numeric_limits<qsizetype>::max() / 2;
do {
qsizetype blockSize = qMin(step, len - allocated);
- prevBuf = curBuf;
- curBuf = new char[allocated + blockSize + 1];
- if (prevBuf) {
- memcpy(curBuf, prevBuf, allocated);
- delete [] prevBuf;
- }
- if (readBlock(curBuf + allocated, blockSize) != blockSize) {
- delete [] curBuf;
+ const qsizetype n = allocated + blockSize + 1;
+ if (const auto prevBuf = std::exchange(curBuf, std::make_unique<char[]>(n)))
+ memcpy(curBuf.get(), prevBuf.get(), allocated);
+ if (readBlock(curBuf.get() + allocated, blockSize) != blockSize)
return *this;
- }
allocated += blockSize;
- step *= 2;
+ if (step <= StepIncreaseThreshold)
+ step *= 2;
} while (allocated < len);
- s = curBuf;
+ s = curBuf.release();
s[len] = '\0';
l = len;
return *this;
diff --git a/src/corelib/text/qbytearray.cpp b/src/corelib/text/qbytearray.cpp
index e6387e4bed..4c9282df26 100644
--- a/src/corelib/text/qbytearray.cpp
+++ b/src/corelib/text/qbytearray.cpp
@@ -56,10 +56,6 @@ static constexpr inline uchar asciiLower(uchar c)
return c >= 'A' && c <= 'Z' ? c | 0x20 : c;
}
-qsizetype qFindByteArray(
- const char *haystack0, qsizetype haystackLen, qsizetype from,
- const char *needle0, qsizetype needleLen);
-
/*****************************************************************************
Safe and portable C string functions; extensions to standard string.h
*****************************************************************************/
@@ -2680,31 +2676,6 @@ QByteArray QByteArray::repeated(qsizetype times) const
return result;
}
-#define REHASH(a) \
- if (ol_minus_1 < sizeof(std::size_t) * CHAR_BIT) \
- hashHaystack -= std::size_t(a) << ol_minus_1; \
- hashHaystack <<= 1
-
-qsizetype QtPrivate::findByteArray(QByteArrayView haystack, qsizetype from, QByteArrayView needle) noexcept
-{
- const auto ol = needle.size();
- const auto l = haystack.size();
- if (ol == 0) {
- if (from < 0)
- return qMax(from + l, 0);
- else
- return from > l ? -1 : from;
- }
-
- if (ol == 1)
- return findByteArray(haystack, from, needle.front());
-
- if (from > l || ol + from > l)
- return -1;
-
- return qFindByteArray(haystack.data(), haystack.size(), from, needle.data(), ol);
-}
-
/*! \fn qsizetype QByteArray::indexOf(QByteArrayView bv, qsizetype from) const
\since 6.0
@@ -2745,10 +2716,10 @@ static qsizetype lastIndexOfHelper(const char *haystack, qsizetype l, const char
const char *end = haystack;
haystack += from;
- const auto ol_minus_1 = std::size_t(ol - 1);
+ const qregisteruint ol_minus_1 = ol - 1;
const char *n = needle + ol_minus_1;
const char *h = haystack + ol_minus_1;
- std::size_t hashNeedle = 0, hashHaystack = 0;
+ qregisteruint hashNeedle = 0, hashHaystack = 0;
qsizetype idx;
for (idx = 0; idx < ol; ++idx) {
hashNeedle = ((hashNeedle<<1) + *(n-idx));
@@ -2760,10 +2731,11 @@ static qsizetype lastIndexOfHelper(const char *haystack, qsizetype l, const char
if (hashHaystack == hashNeedle && memcmp(needle, haystack, ol) == 0)
return haystack - end;
--haystack;
- REHASH(*(haystack + ol));
+ if (ol_minus_1 < sizeof(ol_minus_1) * CHAR_BIT)
+ hashHaystack -= qregisteruint(*(haystack + ol)) << ol_minus_1;
+ hashHaystack <<= 1;
}
return -1;
-
}
static inline qsizetype lastIndexOfCharHelper(QByteArrayView haystack, qsizetype from, char needle) noexcept
@@ -5206,5 +5178,3 @@ size_t qHash(const QByteArray::FromBase64Result &key, size_t seed) noexcept
*/
QT_END_NAMESPACE
-
-#undef REHASH
diff --git a/src/corelib/text/qbytearraymatcher.cpp b/src/corelib/text/qbytearraymatcher.cpp
index ae38fb584b..a332f035ef 100644
--- a/src/corelib/text/qbytearraymatcher.cpp
+++ b/src/corelib/text/qbytearraymatcher.cpp
@@ -212,26 +212,10 @@ qsizetype QByteArrayMatcher::indexIn(QByteArrayView data, qsizetype from) const
\sa setPattern()
*/
-
-static qsizetype findChar(const char *str, qsizetype len, char ch, qsizetype from)
-{
- const uchar *s = (const uchar *)str;
- uchar c = (uchar)ch;
- if (from < 0)
- from = qMax(from + len, qsizetype(0));
- if (from < len) {
- const uchar *n = s + from - 1;
- const uchar *e = s + len;
- while (++n != e)
- if (*n == c)
- return n - s;
- }
- return -1;
-}
-
/*!
\internal
*/
+Q_NEVER_INLINE
static qsizetype qFindByteArrayBoyerMoore(
const char *haystack, qsizetype haystackLen, qsizetype haystackOffset,
const char *needle, qsizetype needleLen)
@@ -244,20 +228,19 @@ static qsizetype qFindByteArrayBoyerMoore(
(const uchar *)needle, needleLen, skiptable);
}
-#define REHASH(a) \
- if (sl_minus_1 < sizeof(std::size_t) * CHAR_BIT) \
- hashHaystack -= std::size_t(a) << sl_minus_1; \
- hashHaystack <<= 1
-
/*!
\internal
*/
-qsizetype qFindByteArray(
- const char *haystack0, qsizetype haystackLen, qsizetype from,
- const char *needle, qsizetype needleLen)
+static qsizetype qFindByteArray(const char *haystack0, qsizetype l, qsizetype from,
+ const char *needle, qsizetype sl);
+qsizetype QtPrivate::findByteArray(QByteArrayView haystack, qsizetype from, QByteArrayView needle) noexcept
{
- const auto l = haystackLen;
- const auto sl = needleLen;
+ const auto haystack0 = haystack.data();
+ const auto l = haystack.size();
+ const auto sl = needle.size();
+ if (sl == 1)
+ return findByteArray(haystack, from, needle.front());
+
if (from < 0)
from += l;
if (std::size_t(sl + from) > std::size_t(l))
@@ -267,27 +250,28 @@ qsizetype qFindByteArray(
if (!l)
return -1;
- if (sl == 1)
- return findChar(haystack0, haystackLen, needle[0], from);
-
/*
We use the Boyer-Moore algorithm in cases where the overhead
for the skip table should pay off, otherwise we use a simple
hash function.
*/
if (l > 500 && sl > 5)
- return qFindByteArrayBoyerMoore(haystack0, haystackLen, from,
- needle, needleLen);
+ return qFindByteArrayBoyerMoore(haystack0, l, from, needle.data(), sl);
+ return qFindByteArray(haystack0, l, from, needle.data(), sl);
+}
+qsizetype qFindByteArray(const char *haystack0, qsizetype l, qsizetype from,
+ const char *needle, qsizetype sl)
+{
/*
We use some hashing for efficiency's sake. Instead of
comparing strings, we compare the hash value of str with that
- of a part of this QString. Only if that matches, we call memcmp().
+ of a part of this QByteArray. Only if that matches, we call memcmp().
*/
const char *haystack = haystack0 + from;
const char *end = haystack0 + (l - sl);
- const auto sl_minus_1 = std::size_t(sl - 1);
- std::size_t hashNeedle = 0, hashHaystack = 0;
+ const qregisteruint sl_minus_1 = sl - 1;
+ qregisteruint hashNeedle = 0, hashHaystack = 0;
qsizetype idx;
for (idx = 0; idx < sl; ++idx) {
hashNeedle = ((hashNeedle<<1) + needle[idx]);
@@ -301,7 +285,9 @@ qsizetype qFindByteArray(
&& memcmp(needle, haystack, sl) == 0)
return haystack - haystack0;
- REHASH(*haystack);
+ if (sl_minus_1 < sizeof(sl_minus_1) * CHAR_BIT)
+ hashHaystack -= qregisteruint(*haystack) << sl_minus_1;
+ hashHaystack <<= 1;
++haystack;
}
return -1;
@@ -409,7 +395,4 @@ qsizetype QStaticByteArrayMatcherBase::indexOfIn(const char *needle, size_t nlen
\snippet code/src_corelib_text_qbytearraymatcher.cpp 1
*/
-
QT_END_NAMESPACE
-
-#undef REHASH
diff --git a/src/corelib/text/qlatin1stringmatcher.cpp b/src/corelib/text/qlatin1stringmatcher.cpp
index 68bf97db5c..9036048fff 100644
--- a/src/corelib/text/qlatin1stringmatcher.cpp
+++ b/src/corelib/text/qlatin1stringmatcher.cpp
@@ -160,6 +160,31 @@ Qt::CaseSensitivity QLatin1StringMatcher::caseSensitivity() const noexcept
*/
qsizetype QLatin1StringMatcher::indexIn(QLatin1StringView haystack, qsizetype from) const noexcept
{
+ return indexIn_helper(haystack, from);
+}
+
+/*!
+ \since 6.8
+ \overload
+
+ Searches for the pattern in the given \a haystack starting from index
+ position \a from.
+
+ \sa caseSensitivity(), pattern()
+*/
+qsizetype QLatin1StringMatcher::indexIn(QStringView haystack, qsizetype from) const noexcept
+{
+ return indexIn_helper(haystack, from);
+}
+
+/*!
+ \internal
+*/
+template <typename String>
+qsizetype QLatin1StringMatcher::indexIn_helper(String haystack, qsizetype from) const noexcept
+{
+ static_assert(QtPrivate::isLatin1OrUtf16View<String>);
+
if (m_pattern.isEmpty() && from == haystack.size())
return from;
if (from < 0) // Historical behavior (see QString::indexOf and co.)
@@ -167,8 +192,15 @@ qsizetype QLatin1StringMatcher::indexIn(QLatin1StringView haystack, qsizetype fr
if (from >= haystack.size())
return -1;
- auto begin = haystack.begin() + from;
- auto end = haystack.end();
+ const auto start = [haystack] {
+ if constexpr (std::is_same_v<String, QStringView>)
+ return haystack.utf16();
+ else
+ return haystack.begin();
+ }();
+
+ auto begin = start + from;
+ auto end = start + haystack.size();
auto found = begin;
if (m_cs == Qt::CaseSensitive) {
found = m_caseSensitiveSearcher(begin, end, m_pattern.begin(), m_pattern.end()).begin;
@@ -178,7 +210,7 @@ qsizetype QLatin1StringMatcher::indexIn(QLatin1StringView haystack, qsizetype fr
const qsizetype bufferSize = std::min(m_pattern.size(), qsizetype(sizeof m_foldBuffer));
const QLatin1StringView restNeedle = m_pattern.sliced(bufferSize);
const bool needleLongerThanBuffer = restNeedle.size() > 0;
- QLatin1StringView restHaystack = haystack;
+ String restHaystack = haystack;
do {
found = m_caseInsensitiveSearcher(found, end, m_foldBuffer, &m_foldBuffer[bufferSize])
.begin;
@@ -189,13 +221,13 @@ qsizetype QLatin1StringMatcher::indexIn(QLatin1StringView haystack, qsizetype fr
}
restHaystack = haystack.sliced(
qMin(haystack.size(),
- bufferSize + qsizetype(std::distance(haystack.begin(), found))));
+ bufferSize + qsizetype(std::distance(start, found))));
if (restHaystack.startsWith(restNeedle, Qt::CaseInsensitive))
break;
++found;
} while (true);
}
- return std::distance(haystack.begin(), found);
+ return std::distance(start, found);
}
QT_END_NAMESPACE
diff --git a/src/corelib/text/qlatin1stringmatcher.h b/src/corelib/text/qlatin1stringmatcher.h
index 3b8c24fc92..dd3414fc6d 100644
--- a/src/corelib/text/qlatin1stringmatcher.h
+++ b/src/corelib/text/qlatin1stringmatcher.h
@@ -14,6 +14,10 @@
QT_BEGIN_NAMESPACE
namespace QtPrivate {
+template <typename T>
+constexpr inline bool isLatin1OrUtf16View =
+ std::disjunction_v<std::is_same<T, QLatin1StringView>, std::is_same<T, QStringView>>;
+
template<class RandomIt1,
class Hash = std::hash<typename std::iterator_traits<RandomIt1>::value_type>,
class BinaryPredicate = std::equal_to<>>
@@ -147,6 +151,7 @@ public:
Q_CORE_EXPORT Qt::CaseSensitivity caseSensitivity() const noexcept;
Q_CORE_EXPORT qsizetype indexIn(QLatin1StringView haystack, qsizetype from = 0) const noexcept;
+ Q_CORE_EXPORT qsizetype indexIn(QStringView haystack, qsizetype from = 0) const noexcept;
private:
void setSearcher() noexcept;
@@ -164,6 +169,10 @@ private:
CaseSensitiveSearcher m_caseSensitiveSearcher;
CaseInsensitiveSearcher m_caseInsensitiveSearcher;
};
+
+ template <typename String>
+ qsizetype indexIn_helper(String haystack, qsizetype from) const noexcept;
+
char m_foldBuffer[256];
};
diff --git a/src/corelib/text/qlocale.cpp b/src/corelib/text/qlocale.cpp
index ab95b300eb..86ab072b73 100644
--- a/src/corelib/text/qlocale.cpp
+++ b/src/corelib/text/qlocale.cpp
@@ -4760,6 +4760,11 @@ QStringList QLocale::uiLanguages(TagSeparator separator) const
const bool isSystem = d->m_data == &systemLocaleData;
if (isSystem) {
uiLanguages = systemLocale()->query(QSystemLocale::UILanguages).toStringList();
+ if (separator != TagSeparator::Dash) {
+ // Map from default separator, Dash, used by backends:
+ const QChar join = QLatin1Char(sep);
+ uiLanguages = uiLanguages.replaceInStrings(u"-", QStringView(&join, 1));
+ }
// ... but we need to include likely-adjusted forms of each of those, too.
// For now, collect up locale Ids representing the entries, for later processing:
for (const auto &entry : std::as_const(uiLanguages))
diff --git a/src/corelib/text/qlocale.h b/src/corelib/text/qlocale.h
index cb3eb64193..abef24ea0e 100644
--- a/src/corelib/text/qlocale.h
+++ b/src/corelib/text/qlocale.h
@@ -1173,8 +1173,11 @@ private:
friend class QRomanCalendar;
friend Q_CORE_EXPORT size_t qHash(const QLocale &key, size_t seed) noexcept;
- friend bool operator==(const QLocale &lhs, const QLocale &rhs) { return lhs.equals(rhs); }
- friend bool operator!=(const QLocale &lhs, const QLocale &rhs) { return !lhs.equals(rhs); }
+ friend bool comparesEqual(const QLocale &lhs, const QLocale &rhs) noexcept
+ {
+ return lhs.equals(rhs);
+ }
+ Q_DECLARE_EQUALITY_COMPARABLE(QLocale)
QSharedDataPointer<QLocalePrivate> d;
};
diff --git a/src/corelib/text/qlocale.qdoc b/src/corelib/text/qlocale.qdoc
index f3f0a5cc2d..0cdacfd8e5 100644
--- a/src/corelib/text/qlocale.qdoc
+++ b/src/corelib/text/qlocale.qdoc
@@ -7,6 +7,7 @@
\brief The QLocale class converts between numbers and their
string representations in various languages.
+ \compares equality
\reentrant
\ingroup i18n
\ingroup string-processing
diff --git a/src/corelib/text/qregularexpression.cpp b/src/corelib/text/qregularexpression.cpp
index 95fd0e3d9a..78261e14cb 100644
--- a/src/corelib/text/qregularexpression.cpp
+++ b/src/corelib/text/qregularexpression.cpp
@@ -42,6 +42,7 @@ using namespace Qt::StringLiterals;
\keyword regular expression
+ \compares equality
Regular expressions, or \e{regexps}, are a very powerful tool to handle
strings and texts. This is useful in many contexts, e.g.,
@@ -721,7 +722,7 @@ struct QRegularExpressionPrivate : QSharedData
CheckSubjectStringOption checkSubjectStringOption = CheckSubjectString,
const QRegularExpressionMatchPrivate *previous = nullptr) const;
- int captureIndexForName(QStringView name) const;
+ int captureIndexForName(QAnyStringView name) const;
// sizeof(QSharedData) == 4, so start our members with an enum
QRegularExpression::PatternOptions patternOptions;
@@ -1013,7 +1014,7 @@ void QRegularExpressionPrivate::optimizePattern()
Returns the capturing group number for the given name. Duplicated names for
capturing groups are not supported.
*/
-int QRegularExpressionPrivate::captureIndexForName(QStringView name) const
+int QRegularExpressionPrivate::captureIndexForName(QAnyStringView name) const
{
Q_ASSERT(!name.isEmpty());
@@ -1734,18 +1735,20 @@ void QRegularExpression::optimize() const
}
/*!
- Returns \c true if the regular expression is equal to \a re, or false
+ \fn bool QRegularExpression::operator==(const QRegularExpression &lhs, const QRegularExpression &rhs) noexcept
+
+ Returns \c true if the \a lhs regular expression is equal to the \a rhs, or false
otherwise. Two QRegularExpression objects are equal if they have
the same pattern string and the same pattern options.
\sa operator!=()
*/
-bool QRegularExpression::operator==(const QRegularExpression &re) const
+bool comparesEqual(const QRegularExpression &lhs,
+ const QRegularExpression &rhs) noexcept
{
- return (d == re.d) ||
- (d->pattern == re.d->pattern && d->patternOptions == re.d->patternOptions);
+ return (lhs.d == rhs.d) ||
+ (lhs.d->pattern == rhs.d->pattern && lhs.d->patternOptions == rhs.d->patternOptions);
}
-
/*!
\fn QRegularExpression & QRegularExpression::operator=(QRegularExpression && re)
@@ -1758,9 +1761,9 @@ bool QRegularExpression::operator==(const QRegularExpression &re) const
*/
/*!
- \fn bool QRegularExpression::operator!=(const QRegularExpression &re) const
+ \fn bool QRegularExpression::operator!=(const QRegularExpression &lhs, const QRegularExpression &rhs) noexcept
- Returns \c true if the regular expression is different from \a re, or
+ Returns \c true if the \a lhs regular expression is different from the \a rhs, or
false otherwise.
\sa operator==()
@@ -2217,8 +2220,7 @@ int QRegularExpressionMatch::lastCapturedIndex() const
}
/*!
- \fn bool QRegularExpressionMatch::hasCaptured(const QString &name) const
- \fn bool QRegularExpressionMatch::hasCaptured(QStringView name) const
+ \fn bool QRegularExpressionMatch::hasCaptured(QAnyStringView name) const
\since 6.3
Returns true if the capturing group named \a name captured something
@@ -2235,9 +2237,12 @@ int QRegularExpressionMatch::lastCapturedIndex() const
Similarly, a capturing group may capture a substring of length 0;
this function will return \c{true} for such a capturing group.
+ \note In Qt versions prior to 6.8, this function took QString or
+ QStringView, not QAnyStringView.
+
\sa captured(), hasMatch()
*/
-bool QRegularExpressionMatch::hasCaptured(QStringView name) const
+bool QRegularExpressionMatch::hasCaptured(QAnyStringView name) const
{
const int nth = d->regularExpression.d->captureIndexForName(name);
return hasCaptured(nth);
@@ -2317,17 +2322,6 @@ QStringView QRegularExpressionMatch::capturedView(int nth) const
return d->subject.mid(start, capturedLength(nth));
}
-/*! \fn QString QRegularExpressionMatch::captured(const QString &name) const
-
- Returns the substring captured by the capturing group named \a name.
-
- If the named capturing group \a name did not capture a string, or if
- there is no capturing group named \a name, returns a null QString.
-
- \sa capturedView(), capturedStart(), capturedEnd(), capturedLength(),
- QString::isNull()
-*/
-
/*!
\since 5.10
@@ -2336,10 +2330,13 @@ QStringView QRegularExpressionMatch::capturedView(int nth) const
If the named capturing group \a name did not capture a string, or if
there is no capturing group named \a name, returns a null QString.
+ \note In Qt versions prior to 6.8, this function took QString or
+ QStringView, not QAnyStringView.
+
\sa capturedView(), capturedStart(), capturedEnd(), capturedLength(),
QString::isNull()
*/
-QString QRegularExpressionMatch::captured(QStringView name) const
+QString QRegularExpressionMatch::captured(QAnyStringView name) const
{
if (name.isEmpty()) {
qWarning("QRegularExpressionMatch::captured: empty capturing group name passed");
@@ -2358,10 +2355,13 @@ QString QRegularExpressionMatch::captured(QStringView name) const
If the named capturing group \a name did not capture a string, or if
there is no capturing group named \a name, returns a null QStringView.
+ \note In Qt versions prior to 6.8, this function took QString or
+ QStringView, not QAnyStringView.
+
\sa captured(), capturedStart(), capturedEnd(), capturedLength(),
QStringView::isNull()
*/
-QStringView QRegularExpressionMatch::capturedView(QStringView name) const
+QStringView QRegularExpressionMatch::capturedView(QAnyStringView name) const
{
if (name.isEmpty()) {
qWarning("QRegularExpressionMatch::capturedView: empty capturing group name passed");
@@ -2433,37 +2433,6 @@ qsizetype QRegularExpressionMatch::capturedEnd(int nth) const
return d->capturedOffsets.at(nth * 2 + 1);
}
-/*! \fn qsizetype QRegularExpressionMatch::capturedStart(const QString &name) const
-
- Returns the offset inside the subject string corresponding to the starting
- position of the substring captured by the capturing group named \a name.
- If the capturing group named \a name did not capture a string or doesn't
- exist, returns -1.
-
- \sa capturedEnd(), capturedLength(), captured()
-*/
-
-/*! \fn qsizetype QRegularExpressionMatch::capturedLength(const QString &name) const
-
- Returns the length of the substring captured by the capturing group named
- \a name.
-
- \note This function returns 0 if the capturing group named \a name did not
- capture a string or doesn't exist.
-
- \sa capturedStart(), capturedEnd(), captured()
-*/
-
-/*! \fn qsizetype QRegularExpressionMatch::capturedEnd(const QString &name) const
-
- Returns the offset inside the subject string immediately after the ending
- position of the substring captured by the capturing group named \a name. If
- the capturing group named \a name did not capture a string or doesn't
- exist, returns -1.
-
- \sa capturedStart(), capturedLength(), captured()
-*/
-
/*!
\since 5.10
@@ -2472,9 +2441,12 @@ qsizetype QRegularExpressionMatch::capturedEnd(int nth) const
If the capturing group named \a name did not capture a string or doesn't
exist, returns -1.
+ \note In Qt versions prior to 6.8, this function took QString or
+ QStringView, not QAnyStringView.
+
\sa capturedEnd(), capturedLength(), captured()
*/
-qsizetype QRegularExpressionMatch::capturedStart(QStringView name) const
+qsizetype QRegularExpressionMatch::capturedStart(QAnyStringView name) const
{
if (name.isEmpty()) {
qWarning("QRegularExpressionMatch::capturedStart: empty capturing group name passed");
@@ -2495,9 +2467,12 @@ qsizetype QRegularExpressionMatch::capturedStart(QStringView name) const
\note This function returns 0 if the capturing group named \a name did not
capture a string or doesn't exist.
+ \note In Qt versions prior to 6.8, this function took QString or
+ QStringView, not QAnyStringView.
+
\sa capturedStart(), capturedEnd(), captured()
*/
-qsizetype QRegularExpressionMatch::capturedLength(QStringView name) const
+qsizetype QRegularExpressionMatch::capturedLength(QAnyStringView name) const
{
if (name.isEmpty()) {
qWarning("QRegularExpressionMatch::capturedLength: empty capturing group name passed");
@@ -2517,9 +2492,12 @@ qsizetype QRegularExpressionMatch::capturedLength(QStringView name) const
the capturing group named \a name did not capture a string or doesn't
exist, returns -1.
+ \note In Qt versions prior to 6.8, this function took QString or
+ QStringView, not QAnyStringView.
+
\sa capturedStart(), capturedLength(), captured()
*/
-qsizetype QRegularExpressionMatch::capturedEnd(QStringView name) const
+qsizetype QRegularExpressionMatch::capturedEnd(QAnyStringView name) const
{
if (name.isEmpty()) {
qWarning("QRegularExpressionMatch::capturedEnd: empty capturing group name passed");
diff --git a/src/corelib/text/qregularexpression.h b/src/corelib/text/qregularexpression.h
index ed838327ef..ab147b87d4 100644
--- a/src/corelib/text/qregularexpression.h
+++ b/src/corelib/text/qregularexpression.h
@@ -157,11 +157,15 @@ public:
static QRegularExpression fromWildcard(QStringView pattern, Qt::CaseSensitivity cs = Qt::CaseInsensitive,
WildcardConversionOptions options = DefaultWildcardConversion);
-
+#if QT_CORE_REMOVED_SINCE(6, 8)
bool operator==(const QRegularExpression &re) const;
inline bool operator!=(const QRegularExpression &re) const { return !operator==(re); }
-
+#endif
private:
+ friend Q_CORE_EXPORT bool comparesEqual(const QRegularExpression &lhs,
+ const QRegularExpression &rhs) noexcept;
+ Q_DECLARE_EQUALITY_COMPARABLE(QRegularExpression)
+
friend struct QRegularExpressionPrivate;
friend class QRegularExpressionMatch;
friend struct QRegularExpressionMatchPrivate;
@@ -213,18 +217,26 @@ public:
int lastCapturedIndex() const;
+#if QT_CORE_REMOVED_SINCE(6, 8)
bool hasCaptured(const QString &name) const
- { return hasCaptured(QStringView(name)); }
+ { return hasCaptured(qToAnyStringViewIgnoringNull(name)); }
bool hasCaptured(QStringView name) const;
+#endif
+ bool hasCaptured(QAnyStringView name) const;
bool hasCaptured(int nth) const;
QString captured(int nth = 0) const;
QStringView capturedView(int nth = 0) const;
+#if QT_CORE_REMOVED_SINCE(6, 8)
QString captured(const QString &name) const
- { return captured(QStringView(name)); }
+ { return captured(qToAnyStringViewIgnoringNull(name)); }
+
QString captured(QStringView name) const;
QStringView capturedView(QStringView name) const;
+#endif
+ QString captured(QAnyStringView name) const;
+ QStringView capturedView(QAnyStringView name) const;
QStringList capturedTexts() const;
@@ -232,16 +244,21 @@ public:
qsizetype capturedLength(int nth = 0) const;
qsizetype capturedEnd(int nth = 0) const;
+#if QT_CORE_REMOVED_SINCE(6, 8)
qsizetype capturedStart(const QString &name) const
- { return capturedStart(QStringView(name)); }
+ { return capturedStart(qToAnyStringViewIgnoringNull(name)); }
qsizetype capturedLength(const QString &name) const
- { return capturedLength(QStringView(name)); }
+ { return capturedLength(qToAnyStringViewIgnoringNull(name)); }
qsizetype capturedEnd(const QString &name) const
- { return capturedEnd(QStringView(name)); }
+ { return capturedEnd(qToAnyStringViewIgnoringNull(name)); }
qsizetype capturedStart(QStringView name) const;
qsizetype capturedLength(QStringView name) const;
qsizetype capturedEnd(QStringView name) const;
+#endif
+ qsizetype capturedStart(QAnyStringView name) const;
+ qsizetype capturedLength(QAnyStringView name) const;
+ qsizetype capturedEnd(QAnyStringView name) const;
private:
friend class QRegularExpression;
@@ -352,30 +369,24 @@ private:
// [input.iterators] imposes operator== on us. Unfortunately, it's not
// trivial to implement, so just do the bare minimum to satifisfy
// Cpp17EqualityComparable.
- friend bool operator==(const QRegularExpressionMatchIteratorRangeBasedForIterator &lhs,
- const QRegularExpressionMatchIteratorRangeBasedForIterator &rhs) noexcept
+ friend bool comparesEqual(const QRegularExpressionMatchIteratorRangeBasedForIterator &lhs,
+ const QRegularExpressionMatchIteratorRangeBasedForIterator &rhs)
+ noexcept
{
return (&lhs == &rhs);
}
-
- friend bool operator!=(const QRegularExpressionMatchIteratorRangeBasedForIterator &lhs,
- const QRegularExpressionMatchIteratorRangeBasedForIterator &rhs) noexcept
- {
- return !(lhs == rhs);
- }
+ Q_DECLARE_EQUALITY_COMPARABLE(QRegularExpressionMatchIteratorRangeBasedForIterator)
// This is what we really use in a range-based for.
- friend bool operator==(const QRegularExpressionMatchIteratorRangeBasedForIterator &lhs,
- QRegularExpressionMatchIteratorRangeBasedForIteratorSentinel) noexcept
+ friend bool comparesEqual(const QRegularExpressionMatchIteratorRangeBasedForIterator &lhs,
+ const QRegularExpressionMatchIteratorRangeBasedForIteratorSentinel &rhs)
+ noexcept
{
+ Q_UNUSED(rhs);
return lhs.m_atEnd;
}
-
- friend bool operator!=(const QRegularExpressionMatchIteratorRangeBasedForIterator &lhs,
- QRegularExpressionMatchIteratorRangeBasedForIteratorSentinel) noexcept
- {
- return !lhs.m_atEnd;
- }
+ Q_DECLARE_EQUALITY_COMPARABLE(QRegularExpressionMatchIteratorRangeBasedForIterator,
+ QRegularExpressionMatchIteratorRangeBasedForIteratorSentinel)
QRegularExpressionMatchIterator m_matchIterator;
QRegularExpressionMatch m_currentMatch;
diff --git a/src/corelib/text/qstaticlatin1stringmatcher.h b/src/corelib/text/qstaticlatin1stringmatcher.h
index d80ebd8547..bd6d9db08b 100644
--- a/src/corelib/text/qstaticlatin1stringmatcher.h
+++ b/src/corelib/text/qstaticlatin1stringmatcher.h
@@ -109,13 +109,30 @@ public:
}
constexpr qsizetype indexIn(QLatin1StringView haystack, qsizetype from = 0) const noexcept
+ { return indexIn_helper(haystack, from); }
+
+ constexpr qsizetype indexIn(QStringView haystack, qsizetype from = 0) const noexcept
+ { return indexIn_helper(haystack, from); }
+
+private:
+ template <typename String>
+ constexpr qsizetype indexIn_helper(String haystack, qsizetype from = 0) const noexcept
{
+ static_assert(QtPrivate::isLatin1OrUtf16View<String>);
+
if (from >= haystack.size())
return -1;
- const char *begin = haystack.begin() + from;
- const char *end = haystack.end();
+
+ const auto start = [haystack]() constexpr {
+ if constexpr (std::is_same_v<String, QStringView>)
+ return haystack.utf16();
+ else
+ return haystack.begin();
+ }();
+ const auto begin = start + from;
+ const auto end = start + haystack.size();
const auto r = m_searcher(begin, end, m_pattern.begin(), m_pattern.end());
- return r.begin == end ? -1 : std::distance(haystack.begin(), r.begin);
+ return r.begin == end ? -1 : std::distance(start, r.begin);
}
};
diff --git a/src/corelib/text/qstaticlatin1stringmatcher.qdoc b/src/corelib/text/qstaticlatin1stringmatcher.qdoc
index 6577f985b2..86edf69bc2 100644
--- a/src/corelib/text/qstaticlatin1stringmatcher.qdoc
+++ b/src/corelib/text/qstaticlatin1stringmatcher.qdoc
@@ -44,6 +44,7 @@
/*!
\fn template<Qt::CaseSensitivity CS, size_t N> constexpr qsizetype QStaticLatin1StringMatcher<CS, N>::indexIn(QLatin1StringView haystack, qsizetype from) const
+ \fn template<Qt::CaseSensitivity CS, size_t N> constexpr qsizetype QStaticLatin1StringMatcher<CS, N>::indexIn(QStringView haystack, qsizetype from) const
Searches the QLatin1StringView \a haystack, from byte position \a from
(default 0, i.e. from the first byte), for QLatin1StringView pattern()
diff --git a/src/corelib/text/qstring.cpp b/src/corelib/text/qstring.cpp
index f0bf0c50a3..d0c39e0037 100644
--- a/src/corelib/text/qstring.cpp
+++ b/src/corelib/text/qstring.cpp
@@ -63,8 +63,8 @@
#endif
#define REHASH(a) \
- if (sl_minus_1 < sizeof(std::size_t) * CHAR_BIT) \
- hashHaystack -= std::size_t(a) << sl_minus_1; \
+ if (sl_minus_1 < sizeof(sl_minus_1) * CHAR_BIT) \
+ hashHaystack -= decltype(hashHaystack)(a) << sl_minus_1; \
hashHaystack <<= 1
QT_BEGIN_NAMESPACE
@@ -183,10 +183,10 @@ static qsizetype qLastIndexOf(Haystack haystack0, qsizetype from,
const auto needle = needle0.data();
const auto *end = haystack;
haystack += from;
- const std::size_t sl_minus_1 = sl ? sl - 1 : 0;
+ const qregisteruint sl_minus_1 = sl ? sl - 1 : 0;
const auto *n = needle + sl_minus_1;
const auto *h = haystack + sl_minus_1;
- std::size_t hashNeedle = 0, hashHaystack = 0;
+ qregisteruint hashNeedle = 0, hashHaystack = 0;
if (cs == Qt::CaseSensitive) {
for (qsizetype idx = 0; idx < sl; ++idx) {
@@ -9905,8 +9905,8 @@ qsizetype QtPrivate::findString(QStringView haystack0, qsizetype from, QStringVi
const char16_t *needle = needle0.utf16();
const char16_t *haystack = haystack0.utf16() + from;
const char16_t *end = haystack0.utf16() + (l - sl);
- const std::size_t sl_minus_1 = sl - 1;
- std::size_t hashNeedle = 0, hashHaystack = 0;
+ const qregisteruint sl_minus_1 = sl - 1;
+ qregisteruint hashNeedle = 0, hashHaystack = 0;
qsizetype idx;
if (cs == Qt::CaseSensitive) {
diff --git a/src/corelib/text/qstringconverter.cpp b/src/corelib/text/qstringconverter.cpp
index 565e3e598b..67c75d708e 100644
--- a/src/corelib/text/qstringconverter.cpp
+++ b/src/corelib/text/qstringconverter.cpp
@@ -2510,6 +2510,16 @@ const char *QStringConverter::nameForEncoding(QStringConverter::Encoding e)
*/
/*!
+ \fn constexpr QStringEncoder::QStringEncoder(const QString &name, Flags flags = Flag::Default)
+ \since 6.8
+
+ Creates an encoder object using \a name and \a flags.
+ If \a name is not the name of a known encoding an invalid converter will get created.
+
+ \sa isValid()
+*/
+
+/*!
\fn QStringEncoder::DecodedData<const QString &> QStringEncoder::encode(const QString &in)
\fn QStringEncoder::DecodedData<QStringView> QStringEncoder::encode(QStringView in)
\fn QStringEncoder::DecodedData<const QString &> QStringEncoder::operator()(const QString &in)
@@ -2601,6 +2611,16 @@ const char *QStringConverter::nameForEncoding(QStringConverter::Encoding e)
*/
/*!
+ \fn constexpr QStringDecoder::QStringDecoder(const QString &name, Flags flags = Flag::Default)
+ \since 6.8
+
+ Creates an decoder object using \a name and \a flags.
+ If \a name is not the name of a known encoding an invalid converter will get created.
+
+ \sa isValid()
+*/
+
+/*!
\fn QStringDecoder::EncodedData<const QByteArray &> QStringDecoder::operator()(const QByteArray &ba)
\fn QStringDecoder::EncodedData<const QByteArray &> QStringDecoder::decode(const QByteArray &ba)
\fn QStringDecoder::EncodedData<QByteArrayView> QStringDecoder::operator()(QByteArrayView ba)
diff --git a/src/corelib/text/qstringconverter.h b/src/corelib/text/qstringconverter.h
index 055019836a..40791f8e26 100644
--- a/src/corelib/text/qstringconverter.h
+++ b/src/corelib/text/qstringconverter.h
@@ -33,6 +33,9 @@ public:
explicit QStringEncoder(const char *name, Flags flags = Flag::Default)
: QStringConverter(name, flags)
{}
+ Q_WEAK_OVERLOAD explicit QStringEncoder(const QString &name, Flags flags = Flag::Default)
+ : QStringEncoder(name.toLatin1().constData(), flags)
+ {}
template<typename T>
struct DecodedData
@@ -95,6 +98,9 @@ public:
explicit QStringDecoder(const char *name, Flags f = Flag::Default)
: QStringConverter(name, f)
{}
+ Q_WEAK_OVERLOAD explicit QStringDecoder(const QString &name, Flags f = Flag::Default)
+ : QStringDecoder(name.toLatin1().constData(), f)
+ {}
template<typename T>
struct EncodedData
diff --git a/src/corelib/thread/qfuture.h b/src/corelib/thread/qfuture.h
index 5939a93780..3945df066a 100644
--- a/src/corelib/thread/qfuture.h
+++ b/src/corelib/thread/qfuture.h
@@ -183,8 +183,6 @@ QT_WARNING_POP
{ future = o.future; index = o.index; return *this; }
inline const T &operator*() const { return future->d.resultReference(index); }
inline const T *operator->() const { return future->d.resultPointer(index); }
- inline bool operator!=(const const_iterator &other) const { return index != other.index; }
- inline bool operator==(const const_iterator &o) const { return !operator!=(o); }
inline const_iterator &operator++()
{ index = advanceIndex(index, 1); return *this; }
inline const_iterator &operator--()
@@ -213,6 +211,12 @@ QT_WARNING_POP
{ return const_iterator(k.future, k.advanceIndex(k.index, j)); }
private:
+ friend bool comparesEqual(const const_iterator &lhs, const const_iterator &rhs) noexcept
+ {
+ return lhs.index == rhs.index;
+ }
+ Q_DECLARE_EQUALITY_COMPARABLE(const_iterator)
+
/*! \internal
Advances the iterator index \a idx \a n steps, waits for the
diff --git a/src/corelib/thread/qfuture.qdoc b/src/corelib/thread/qfuture.qdoc
index 9eda766968..b278d39882 100644
--- a/src/corelib/thread/qfuture.qdoc
+++ b/src/corelib/thread/qfuture.qdoc
@@ -613,17 +613,17 @@
Returns a pointer to the current result.
*/
-/*! \fn template <typename T> bool QFuture<T>::const_iterator::operator!=(const const_iterator &other) const
+/*! \fn template <typename T> bool QFuture<T>::const_iterator::operator!=(const const_iterator &lhs, const const_iterator &rhs)
- Returns \c true if \a other points to a different result than this iterator;
+ Returns \c true if \a lhs points to a different result than \a rhs iterator;
otherwise returns \c false.
\sa operator==()
*/
-/*! \fn template <typename T> bool QFuture<T>::const_iterator::operator==(const const_iterator &other) const
+/*! \fn template <typename T> bool QFuture<T>::const_iterator::operator==(const const_iterator &lhs, const const_iterator &rhs)
- Returns \c true if \a other points to the same result as this iterator;
+ Returns \c true if \a lhs points to the same result as \a rhs iterator;
otherwise returns \c false.
\sa operator!=()
diff --git a/src/corelib/thread/qfuture_impl.h b/src/corelib/thread/qfuture_impl.h
index 79fc6d9a01..351093adc7 100644
--- a/src/corelib/thread/qfuture_impl.h
+++ b/src/corelib/thread/qfuture_impl.h
@@ -287,6 +287,7 @@ using IsForwardIterable =
template<typename Function, typename ResultType, typename ParentResultType>
class Continuation
{
+ Q_DISABLE_COPY_MOVE(Continuation)
public:
template<typename F = Function>
Continuation(F &&func, const QFuture<ParentResultType> &f, QPromise<ResultType> &&p)
@@ -588,9 +589,6 @@ void Continuation<Function, ResultType, ParentResultType>::create(F &&func,
f->d.setContinuation(ContinuationWrapper(std::move(continuation)), fi.d);
}
-// defined in qfutureinterface.cpp:
-Q_CORE_EXPORT void watchContinuationImpl(const QObject *context, QSlotObjectBase *slotObj,
- QFutureInterfaceBase &fi);
template <typename Continuation>
void watchContinuation(const QObject *context, Continuation &&c, QFutureInterfaceBase &fi)
{
diff --git a/src/corelib/thread/qresultstore.cpp b/src/corelib/thread/qresultstore.cpp
index 14ed7c6b87..8b7601f5b0 100644
--- a/src/corelib/thread/qresultstore.cpp
+++ b/src/corelib/thread/qresultstore.cpp
@@ -88,16 +88,6 @@ void ResultIteratorBase::batchedAdvance()
m_vectorIndex = 0;
}
-bool ResultIteratorBase::operator==(const ResultIteratorBase &other) const
-{
- return (mapIterator == other.mapIterator && m_vectorIndex == other.m_vectorIndex);
-}
-
-bool ResultIteratorBase::operator!=(const ResultIteratorBase &other) const
-{
- return !operator==(other);
-}
-
bool ResultIteratorBase::isVector() const
{
return mapIterator.value().isVector();
diff --git a/src/corelib/thread/qresultstore.h b/src/corelib/thread/qresultstore.h
index 30ce1fe904..f21068206f 100644
--- a/src/corelib/thread/qresultstore.h
+++ b/src/corelib/thread/qresultstore.h
@@ -46,12 +46,21 @@ public:
ResultIteratorBase operator++();
int batchSize() const;
void batchedAdvance();
+#if QT_CORE_REMOVED_SINCE(6, 8)
bool operator==(const ResultIteratorBase &other) const;
bool operator!=(const ResultIteratorBase &other) const;
+#endif
bool isVector() const;
bool canIncrementVectorIndex() const;
bool isValid() const;
+private:
+ friend bool comparesEqual(const ResultIteratorBase &lhs,
+ const ResultIteratorBase &rhs) noexcept
+ {
+ return (lhs.mapIterator == rhs.mapIterator && lhs.m_vectorIndex == rhs.m_vectorIndex);
+ }
+ Q_DECLARE_EQUALITY_COMPARABLE(ResultIteratorBase)
protected:
QMap<int, ResultItem>::const_iterator mapIterator;
int m_vectorIndex;
diff --git a/src/corelib/thread/qthread.cpp b/src/corelib/thread/qthread.cpp
index 0ad402b77c..ea76a2ccad 100644
--- a/src/corelib/thread/qthread.cpp
+++ b/src/corelib/thread/qthread.cpp
@@ -65,7 +65,7 @@ QThreadData::~QThreadData()
// crashing during QCoreApplicationData's global static cleanup we need to
// safeguard the main thread here.. This fix is a bit crude, but it solves
// the problem...
- if (this->thread.loadAcquire() == QCoreApplicationPrivate::theMainThread.loadAcquire()) {
+ if (threadId.loadAcquire() == QCoreApplicationPrivate::theMainThreadId.loadAcquire()) {
QCoreApplicationPrivate::theMainThread.storeRelease(nullptr);
QCoreApplicationPrivate::theMainThreadId.storeRelaxed(nullptr);
QThreadData::clearCurrentThreadData();
@@ -531,10 +531,18 @@ bool QThread::isRunning() const
}
/*!
- Sets the maximum stack size for the thread to \a stackSize. If \a
- stackSize is greater than zero, the maximum stack size is set to
- \a stackSize bytes, otherwise the maximum stack size is
- automatically determined by the operating system.
+ Sets the stack size for the thread to \a stackSize. If \a stackSize is
+ zero, the operating system or runtime will choose a default value.
+ Otherwise, the thread's stack size will be the value provided (which may be
+ rounded up or down).
+
+ On most operating systems, the amount of memory allocated to serve the
+ stack will initially be smaller than \a stackSize and will grow as the
+ thread uses the stack. This parameter sets the maximum size it will be
+ allowed to grow to (that is, it sets the size of the virtual memory space
+ the stack is allowed to occupy).
+
+ This function can only be called before the thread is started.
\warning Most operating systems place minimum and maximum limits
on thread stack sizes. The thread will fail to start if the stack
@@ -1084,7 +1092,7 @@ QThreadData *QThreadData::current(bool createIfNecessary)
data->threadId.storeRelaxed(Qt::HANDLE(data->thread.loadAcquire()));
data->deref();
data->isAdopted = true;
- if (!QCoreApplicationPrivate::theMainThread.loadAcquire()) {
+ if (!QCoreApplicationPrivate::theMainThreadId.loadAcquire()) {
QCoreApplicationPrivate::theMainThread.storeRelease(data->thread.loadRelaxed());
QCoreApplicationPrivate::theMainThreadId.storeRelaxed(data->threadId.loadRelaxed());
}
@@ -1207,11 +1215,11 @@ bool QThread::event(QEvent *event)
void QThread::requestInterruption()
{
- if (this == QCoreApplicationPrivate::theMainThread.loadAcquire()) {
+ Q_D(QThread);
+ if (d->threadId() == QCoreApplicationPrivate::theMainThreadId.loadAcquire()) {
qWarning("QThread::requestInterruption has no effect on the main thread");
return;
}
- Q_D(QThread);
QMutexLocker locker(&d->mutex);
if (!d->running || d->finished || d->isInFinish)
return;
diff --git a/src/corelib/thread/qthread_unix.cpp b/src/corelib/thread/qthread_unix.cpp
index 8916da09b2..44487a2cc2 100644
--- a/src/corelib/thread/qthread_unix.cpp
+++ b/src/corelib/thread/qthread_unix.cpp
@@ -191,7 +191,7 @@ QThreadData *QThreadData::current(bool createIfNecessary)
data->deref();
data->isAdopted = true;
data->threadId.storeRelaxed(to_HANDLE(pthread_self()));
- if (!QCoreApplicationPrivate::theMainThread.loadAcquire()) {
+ if (!QCoreApplicationPrivate::theMainThreadId.loadAcquire()) {
QCoreApplicationPrivate::theMainThread.storeRelease(data->thread.loadRelaxed());
QCoreApplicationPrivate::theMainThreadId.storeRelaxed(data->threadId.loadRelaxed());
}
@@ -282,8 +282,16 @@ void *QThreadPrivate::start(void *arg)
#ifdef PTHREAD_CANCEL_DISABLE
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, nullptr);
#endif
- pthread_cleanup_push(QThreadPrivate::finish, arg);
-
+#if !defined(Q_OS_QNX) && !defined(Q_OS_VXWORKS)
+ // On QNX, calling finish() from a thread_local destructor causes the C
+ // library to hang.
+ // On VxWorks, its pthread implementation fails on call to `pthead_setspecific` which is made
+ // by first QObject constructor during `finish()`. This causes call to QThread::current, since
+ // QObject doesn't have parent, and since the pthread is already removed, it tries to set
+ // QThreadData for current pthread key, which crashes.
+ static thread_local
+#endif
+ auto cleanup = qScopeGuard([=] { finish(arg); });
terminate_on_exception([&] {
QThread *thr = reinterpret_cast<QThread *>(arg);
QThreadData *data = QThreadData::get2(thr);
@@ -328,11 +336,7 @@ void *QThreadPrivate::start(void *arg)
thr->run();
});
- // This pop runs finish() below. It's outside the try/catch (and has its
- // own try/catch) to prevent finish() to be run in case an exception is
- // thrown.
- pthread_cleanup_pop(1);
-
+ // The qScopeGuard above call runs finish() below.
return nullptr;
}
@@ -365,7 +369,7 @@ void QThreadPrivate::finish(void *arg)
d->running = false;
d->finished = true;
- d->interruptionRequested = false;
+ d->interruptionRequested.store(false, std::memory_order_relaxed);
d->isInFinish = false;
d->data->threadId.storeRelaxed(nullptr);
@@ -642,7 +646,7 @@ void QThread::start(Priority priority)
d->finished = false;
d->returnCode = 0;
d->exited = false;
- d->interruptionRequested = false;
+ d->interruptionRequested.store(false, std::memory_order_relaxed);
pthread_attr_t attr;
pthread_attr_init(&attr);
diff --git a/src/corelib/thread/qthread_win.cpp b/src/corelib/thread/qthread_win.cpp
index 475a61bf65..74bc1d2650 100644
--- a/src/corelib/thread/qthread_win.cpp
+++ b/src/corelib/thread/qthread_win.cpp
@@ -87,8 +87,8 @@ QThreadData *QThreadData::current(bool createIfNecessary)
threadData->isAdopted = true;
threadData->threadId.storeRelaxed(reinterpret_cast<Qt::HANDLE>(quintptr(GetCurrentThreadId())));
- if (!QCoreApplicationPrivate::theMainThread) {
- QCoreApplicationPrivate::theMainThread = threadData->thread.loadRelaxed();
+ if (!QCoreApplicationPrivate::theMainThreadId) {
+ QCoreApplicationPrivate::theMainThread.storeRelease(threadData->thread.loadRelaxed());
QCoreApplicationPrivate::theMainThreadId.storeRelaxed(threadData->threadId.loadRelaxed());
} else {
HANDLE realHandle = INVALID_HANDLE_VALUE;
@@ -314,7 +314,7 @@ void QThreadPrivate::finish(void *arg, bool lockAnyway) noexcept
d->running = false;
d->finished = true;
d->isInFinish = false;
- d->interruptionRequested = false;
+ d->interruptionRequested.store(false, std::memory_order_relaxed);
if (!d->waiters) {
CloseHandle(d->handle);
@@ -391,7 +391,7 @@ void QThread::start(Priority priority)
d->finished = false;
d->exited = false;
d->returnCode = 0;
- d->interruptionRequested = false;
+ d->interruptionRequested.store(false, std::memory_order_relaxed);
/*
NOTE: we create the thread in the suspended state, set the
diff --git a/src/corelib/thread/qthreadpool.cpp b/src/corelib/thread/qthreadpool.cpp
index ae584656fe..c7531111da 100644
--- a/src/corelib/thread/qthreadpool.cpp
+++ b/src/corelib/thread/qthreadpool.cpp
@@ -258,7 +258,7 @@ void QThreadPoolPrivate::startThread(QRunnable *runnable)
/*!
\internal
- Helper function only to be called from waitForDone(int)
+ Helper function only to be called from waitForDone()
Deletes all current threads.
*/
@@ -285,22 +285,17 @@ void QThreadPoolPrivate::reset()
/*!
\internal
- Helper function only to be called from waitForDone(int)
+ Helper function only to be called from the public waitForDone()
*/
bool QThreadPoolPrivate::waitForDone(const QDeadlineTimer &timer)
{
+ QMutexLocker locker(&mutex);
while (!(queue.isEmpty() && activeThreads == 0) && !timer.hasExpired())
noActiveThreads.wait(&mutex, timer);
- return queue.isEmpty() && activeThreads == 0;
-}
-
-bool QThreadPoolPrivate::waitForDone(int msecs)
-{
- QMutexLocker locker(&mutex);
- QDeadlineTimer timer(msecs);
- if (!waitForDone(timer))
+ if (!queue.isEmpty() || activeThreads)
return false;
+
reset();
// New jobs might have started during reset, but return anyway
// as the active thread and task count did reach 0 once, and
@@ -598,18 +593,17 @@ bool QThreadPool::tryStart(QRunnable *runnable)
int QThreadPool::expiryTimeout() const
{
+ using namespace std::chrono;
Q_D(const QThreadPool);
QMutexLocker locker(&d->mutex);
- return d->expiryTimeout;
+ return duration_cast<milliseconds>(d->expiryTimeout).count();
}
void QThreadPool::setExpiryTimeout(int expiryTimeout)
{
Q_D(QThreadPool);
QMutexLocker locker(&d->mutex);
- if (d->expiryTimeout == expiryTimeout)
- return;
- d->expiryTimeout = expiryTimeout;
+ d->expiryTimeout = std::chrono::milliseconds(expiryTimeout);
}
/*! \property QThreadPool::maxThreadCount
@@ -808,15 +802,24 @@ void QThreadPool::startOnReservedThread(QRunnable *runnable)
*/
/*!
+ \fn bool QThreadPool::waitForDone(int msecs)
Waits up to \a msecs milliseconds for all threads to exit and removes all
threads from the thread pool. Returns \c true if all threads were removed;
- otherwise it returns \c false. If \a msecs is -1 (the default), the timeout
- is ignored (waits for the last thread to exit).
+ otherwise it returns \c false. If \a msecs is -1, this function waits for
+ the last thread to exit.
+*/
+
+/*!
+ \since 6.8
+
+ Waits until \a deadline expires for all threads to exit and removes all
+ threads from the thread pool. Returns \c true if all threads were removed;
+ otherwise it returns \c false.
*/
-bool QThreadPool::waitForDone(int msecs)
+bool QThreadPool::waitForDone(QDeadlineTimer deadline)
{
Q_D(QThreadPool);
- return d->waitForDone(msecs);
+ return d->waitForDone(deadline);
}
/*!
diff --git a/src/corelib/thread/qthreadpool.h b/src/corelib/thread/qthreadpool.h
index a097ace14b..0640f41587 100644
--- a/src/corelib/thread/qthreadpool.h
+++ b/src/corelib/thread/qthreadpool.h
@@ -9,7 +9,9 @@
#include <QtCore/qthread.h>
#include <QtCore/qrunnable.h>
+#if QT_CORE_REMOVED_SINCE(6, 6)
#include <functional>
+#endif
QT_REQUIRE_CONFIG(thread);
@@ -70,7 +72,9 @@ public:
void reserveThread();
void releaseThread();
- bool waitForDone(int msecs = -1);
+ QT_CORE_INLINE_SINCE(6, 8)
+ bool waitForDone(int msecs);
+ bool waitForDone(QDeadlineTimer deadline = QDeadlineTimer::Forever);
void clear();
@@ -101,6 +105,13 @@ void QThreadPool::startOnReservedThread(Callable &&functionToRun)
startOnReservedThread(QRunnable::create(std::forward<Callable>(functionToRun)));
}
+#if QT_CORE_INLINE_IMPL_SINCE(6, 8)
+bool QThreadPool::waitForDone(int msecs)
+{
+ return waitForDone(QDeadlineTimer(msecs));
+}
+#endif
+
QT_END_NAMESPACE
#endif
diff --git a/src/corelib/thread/qthreadpool_p.h b/src/corelib/thread/qthreadpool_p.h
index 67c703fabd..7910592f70 100644
--- a/src/corelib/thread/qthreadpool_p.h
+++ b/src/corelib/thread/qthreadpool_p.h
@@ -128,7 +128,6 @@ public:
{ return qMax(requestedMaxThreadCount, 1); } // documentation says we start at least one
void startThread(QRunnable *runnable = nullptr);
void reset();
- bool waitForDone(int msecs);
bool waitForDone(const QDeadlineTimer &timer);
void clear();
void stealAndRunRunnable(QRunnable *runnable);
@@ -144,7 +143,7 @@ public:
QWaitCondition noActiveThreads;
QString objectName;
- int expiryTimeout = 30000;
+ std::chrono::duration<int, std::milli> expiryTimeout = std::chrono::seconds(30);
int requestedMaxThreadCount = QThread::idealThreadCount(); // don't use this directly
int reservedThreads = 0;
int activeThreads = 0;
diff --git a/src/corelib/time/qdatetime.h b/src/corelib/time/qdatetime.h
index e1c0d29e2a..a9fefc4c22 100644
--- a/src/corelib/time/qdatetime.h
+++ b/src/corelib/time/qdatetime.h
@@ -529,7 +529,7 @@ public:
QT_POST_CXX17_API_IN_EXPORTED_CLASS
static QDateTime fromStdLocalTime(const std::chrono::local_time<std::chrono::milliseconds> &time)
{
- QDateTime result(QDate(1970, 1, 1), QTime(0, 0, 0));
+ QDateTime result(QDate(1970, 1, 1), QTime(0, 0, 0), TransitionResolution::LegacyBehavior);
return result.addMSecs(time.time_since_epoch().count());
}
diff --git a/src/corelib/time/qdatetimeparser.cpp b/src/corelib/time/qdatetimeparser.cpp
index 78520a51aa..cce32e7ad2 100644
--- a/src/corelib/time/qdatetimeparser.cpp
+++ b/src/corelib/time/qdatetimeparser.cpp
@@ -334,7 +334,7 @@ int QDateTimeParser::sectionPos(int sectionIndex) const
return sectionPos(sectionNode(sectionIndex));
}
-int QDateTimeParser::sectionPos(const SectionNode &sn) const
+int QDateTimeParser::sectionPos(SectionNode sn) const
{
switch (sn.type) {
case FirstSection: return 0;
@@ -2314,11 +2314,9 @@ QString QDateTimeParser::getAmPmText(AmPm ap, Case cs) const
/*
\internal
-
- I give arg2 preference because arg1 is always a QDateTime.
*/
-bool operator==(const QDateTimeParser::SectionNode &s1, const QDateTimeParser::SectionNode &s2)
+bool operator==(QDateTimeParser::SectionNode s1, QDateTimeParser::SectionNode s2)
{
return (s1.type == s2.type) && (s1.pos == s2.pos) && (s1.count == s2.count);
}
diff --git a/src/corelib/time/qdatetimeparser_p.h b/src/corelib/time/qdatetimeparser_p.h
index faf383f3d7..30e9e4d524 100644
--- a/src/corelib/time/qdatetimeparser_p.h
+++ b/src/corelib/time/qdatetimeparser_p.h
@@ -212,7 +212,7 @@ protected: // for the benefit of QDateTimeEditPrivate
int sectionSize(int index) const;
int sectionMaxSize(int index) const;
int sectionPos(int index) const;
- int sectionPos(const SectionNode &sn) const;
+ int sectionPos(SectionNode sn) const;
const SectionNode &sectionNode(int index) const;
Section sectionType(int index) const;
@@ -265,7 +265,7 @@ protected: // for the benefit of QDateTimeEditPrivate
};
Q_DECLARE_TYPEINFO(QDateTimeParser::SectionNode, Q_PRIMITIVE_TYPE);
-Q_CORE_EXPORT bool operator==(const QDateTimeParser::SectionNode &s1, const QDateTimeParser::SectionNode &s2);
+Q_CORE_EXPORT bool operator==(QDateTimeParser::SectionNode s1, QDateTimeParser::SectionNode s2);
Q_DECLARE_OPERATORS_FOR_FLAGS(QDateTimeParser::Sections)
Q_DECLARE_OPERATORS_FOR_FLAGS(QDateTimeParser::FieldInfo)
diff --git a/src/corelib/time/qtimezonelocale.cpp b/src/corelib/time/qtimezonelocale.cpp
index 5757e55d28..cf3a84b317 100644
--- a/src/corelib/time/qtimezonelocale.cpp
+++ b/src/corelib/time/qtimezonelocale.cpp
@@ -4,7 +4,9 @@
#include <private/qtimezonelocale_p.h>
#include <private/qtimezoneprivate_p.h>
-#if !QT_CONFIG(icu) // Use data generated from CLDR:
+#if !QT_CONFIG(icu)
+# include <private/qdatetime_p.h>
+// Use data generated from CLDR:
# include <private/qtimezonelocale_data_p.h>
# include <private/qtimezoneprivate_data_p.h>
#endif
@@ -12,9 +14,90 @@
QT_BEGIN_NAMESPACE
#if QT_CONFIG(icu) // Get data from ICU:
+namespace {
+
+// Convert TimeType and NameType into ICU UCalendarDisplayNameType
+constexpr UCalendarDisplayNameType ucalDisplayNameType(QTimeZone::TimeType timeType,
+ QTimeZone::NameType nameType)
+{
+ // TODO ICU C UCalendarDisplayNameType does not support full set of C++ TimeZone::EDisplayType
+ // For now, treat Generic as Standard
+ switch (nameType) {
+ case QTimeZone::OffsetName:
+ Q_UNREACHABLE(); // Callers of ucalTimeZoneDisplayName() should take care of OffsetName.
+ case QTimeZone::ShortName:
+ return timeType == QTimeZone::DaylightTime ? UCAL_SHORT_DST : UCAL_SHORT_STANDARD;
+ case QTimeZone::DefaultName:
+ case QTimeZone::LongName:
+ return timeType == QTimeZone::DaylightTime ? UCAL_DST : UCAL_STANDARD;
+ }
+ Q_UNREACHABLE_RETURN(UCAL_STANDARD);
+}
+
+} // nameless namespace
+
namespace QtTimeZoneLocale {
+// Qt wrapper around ucal_getTimeZoneDisplayName()
+// Used directly by ICU backend; indirectly by TZ (see below).
+QString ucalTimeZoneDisplayName(UCalendar *ucal,
+ QTimeZone::TimeType timeType,
+ QTimeZone::NameType nameType,
+ const QByteArray &localeCode)
+{
+ constexpr int32_t BigNameLength = 50;
+ int32_t size = BigNameLength;
+ QString result(size, Qt::Uninitialized);
+ auto dst = [&result]() { return reinterpret_cast<UChar *>(result.data()); };
+ UErrorCode status = U_ZERO_ERROR;
+ const UCalendarDisplayNameType utype = ucalDisplayNameType(timeType, nameType);
+
+ // size = ucal_getTimeZoneDisplayName(cal, type, locale, result, resultLength, status)
+ size = ucal_getTimeZoneDisplayName(ucal, utype, localeCode.constData(),
+ dst(), size, &status);
+
+ // If overflow, then resize and retry
+ if (size > BigNameLength || status == U_BUFFER_OVERFLOW_ERROR) {
+ result.resize(size);
+ status = U_ZERO_ERROR;
+ size = ucal_getTimeZoneDisplayName(ucal, utype, localeCode.constData(),
+ dst(), size, &status);
+ }
+
+ if (!U_SUCCESS(status))
+ return QString();
+
+ // Resize and return:
+ result.resize(size);
+ return result;
+}
+
} // QtTimeZoneLocale
+
+// Used by TZ backends when ICU is available:
+QString QTimeZonePrivate::localeName(qint64 atMSecsSinceEpoch, int offsetFromUtc,
+ QTimeZone::TimeType timeType,
+ QTimeZone::NameType nameType,
+ const QLocale &locale) const
+{
+ Q_UNUSED(atMSecsSinceEpoch);
+ // TODO: use CLDR data for the offset name.
+ // No ICU API for offset formats, so fall back to our ISO one, even if
+ // locale isn't C:
+ if (nameType == QTimeZone::OffsetName)
+ return isoOffsetFormat(offsetFromUtc);
+
+ const QString id = QString::fromUtf8(m_id);
+ const QByteArray loc = locale.name().toUtf8();
+ UErrorCode status = U_ZERO_ERROR;
+ UCalendar *ucal = ucal_open(reinterpret_cast<const UChar *>(id.data()), id.size(),
+ loc.constData(), UCAL_DEFAULT, &status);
+ if (ucal && U_SUCCESS(status)) {
+ auto tidier = qScopeGuard([ucal]() { ucal_close(ucal); });
+ return QtTimeZoneLocale::ucalTimeZoneDisplayName(ucal, timeType, nameType, loc);
+ }
+ return QString();
+}
#else // No ICU, use QTZ[LP}_data_p.h data for feature timezone_locale.
namespace {
using namespace QtTimeZoneLocale; // QTZL_data_p.h
@@ -24,6 +107,20 @@ using namespace QtTimeZoneCldr; // QTZP_data_p.h
// Accessors for the QTZP_data_p.h
} // nameless namespace
+
+QString QTimeZonePrivate::localeName(qint64 atMSecsSinceEpoch, int offsetFromUtc,
+ QTimeZone::TimeType timeType,
+ QTimeZone::NameType nameType,
+ const QLocale &locale) const
+{
+ Q_ASSERT(nameType != QTimeZone::OffsetName || locale.language() != QLocale::C);
+ // Get data from QTZ[LP]_data_p.h
+
+ Q_UNUSED(atMSecsSinceEpoch);
+ Q_UNUSED(offsetFromUtc);
+ Q_UNUSED(timeType);
+ return QString();
+}
#endif // ICU
QT_END_NAMESPACE
diff --git a/src/corelib/time/qtimezonelocale_p.h b/src/corelib/time/qtimezonelocale_p.h
index adc8e83b35..6e6c6b51fd 100644
--- a/src/corelib/time/qtimezonelocale_p.h
+++ b/src/corelib/time/qtimezonelocale_p.h
@@ -14,18 +14,31 @@
//
// We mean it.
//
+#include <private/qglobal_p.h>
+#include <QtCore/qstring.h>
#include <QtCore/qtimezone.h>
+#if QT_CONFIG(icu)
+#include <unicode/ucal.h>
+#endif
+
QT_REQUIRE_CONFIG(timezone);
QT_REQUIRE_CONFIG(timezone_locale);
+QT_BEGIN_NAMESPACE
+
namespace QtTimeZoneLocale {
#if QT_CONFIG(icu)
+QString ucalTimeZoneDisplayName(UCalendar *ucal, QTimeZone::TimeType timeType,
+ QTimeZone::NameType nameType,
+ const QString &localeCode);
#else
// Define data types for QTZL_data_p.h
#endif
}
+QT_END_NAMESPACE
+
#endif // QTIMEZONELOCALE_P_H
diff --git a/src/corelib/time/qtimezoneprivate.cpp b/src/corelib/time/qtimezoneprivate.cpp
index 2ad0d874b6..f3dc43df6a 100644
--- a/src/corelib/time/qtimezoneprivate.cpp
+++ b/src/corelib/time/qtimezoneprivate.cpp
@@ -5,6 +5,9 @@
#include "qtimezone.h"
#include "qtimezoneprivate_p.h"
+#if QT_CONFIG(timezone_locale)
+# include "qtimezonelocale_p.h"
+#endif
#include "qtimezoneprivate_data_p.h"
#include <qdatastream.h>
@@ -172,22 +175,37 @@ QString QTimeZonePrivate::displayName(qint64 atMSecsSinceEpoch,
QTimeZone::NameType nameType,
const QLocale &locale) const
{
- if (nameType == QTimeZone::OffsetName)
- return isoOffsetFormat(offsetFromUtc(atMSecsSinceEpoch));
-
- if (isDaylightTime(atMSecsSinceEpoch))
- return displayName(QTimeZone::DaylightTime, nameType, locale);
- else
- return displayName(QTimeZone::StandardTime, nameType, locale);
+ const Data tran = data(atMSecsSinceEpoch);
+ if (tran.atMSecsSinceEpoch != invalidMSecs()) {
+ if (nameType == QTimeZone::OffsetName && locale.language() == QLocale::C)
+ return isoOffsetFormat(tran.offsetFromUtc);
+ if (nameType == QTimeZone::ShortName && isDataLocale(locale))
+ return tran.abbreviation;
+
+ QTimeZone::TimeType timeType
+ = tran.daylightTimeOffset != 0 ? QTimeZone::DaylightTime : QTimeZone::StandardTime;
+#if QT_CONFIG(timezone_locale)
+ return localeName(atMSecsSinceEpoch, tran.offsetFromUtc, timeType, nameType, locale);
+#else
+ return displayName(timeType, nameType, locale);
+#endif
+ }
+ return QString();
}
QString QTimeZonePrivate::displayName(QTimeZone::TimeType timeType,
QTimeZone::NameType nameType,
const QLocale &locale) const
{
- Q_UNUSED(timeType);
- Q_UNUSED(nameType);
- Q_UNUSED(locale);
+ const Data tran = data(timeType);
+ if (tran.atMSecsSinceEpoch != invalidMSecs()) {
+ if (nameType == QTimeZone::OffsetName && isDataLocale(locale))
+ return isoOffsetFormat(tran.offsetFromUtc);
+
+#if QT_CONFIG(timezone_locale)
+ return localeName(tran.atMSecsSinceEpoch, tran.offsetFromUtc, timeType, nameType, locale);
+#endif
+ }
return QString();
}
@@ -227,6 +245,56 @@ bool QTimeZonePrivate::isDaylightTime(qint64 atMSecsSinceEpoch) const
return false;
}
+QTimeZonePrivate::Data QTimeZonePrivate::data(QTimeZone::TimeType timeType) const
+{
+ // True if tran is valid and has the DST-ness to match timeType:
+ const auto validMatch = [timeType](const QTimeZonePrivate::Data &tran) {
+ return tran.atMSecsSinceEpoch != invalidMSecs()
+ && ((timeType == QTimeZone::DaylightTime) != (tran.daylightTimeOffset == 0));
+ };
+
+ // Get current tran, use if suitable:
+ const qint64 currentMSecs = QDateTime::currentMSecsSinceEpoch();
+ QTimeZonePrivate::Data tran = data(currentMSecs);
+ if (validMatch(tran))
+ return tran;
+
+ if (hasTransitions()) {
+ // Otherwise, next tran probably flips DST-ness:
+ tran = nextTransition(currentMSecs);
+ if (validMatch(tran))
+ return tran;
+
+ // Failing that, prev (or present, if current MSecs is exactly a
+ // transition moment) tran defines what data() got us and the one before
+ // that probably flips DST-ness; failing that, keep marching backwards
+ // in search of a DST interval:
+ tran = previousTransition(currentMSecs + 1);
+ while (tran.atMSecsSinceEpoch != invalidMSecs()) {
+ tran = previousTransition(tran.atMSecsSinceEpoch);
+ if (validMatch(tran))
+ return tran;
+ }
+ }
+ return {};
+}
+
+/*!
+ \internal
+
+ Returns true if the abbreviation given in data()'s returns is appropriate
+ for use in the given \a locale.
+
+ Base implementation assumes data() corresponds to the system locale; derived
+ classes should override if their data() is something else (such as
+ C/English).
+*/
+bool QTimeZonePrivate::isDataLocale(const QLocale &locale) const
+{
+ // Guess data is for the system locale unless backend overrides that.
+ return locale == QLocale::system();
+}
+
QTimeZonePrivate::Data QTimeZonePrivate::data(qint64 forMSecsSinceEpoch) const
{
Q_UNUSED(forMSecsSinceEpoch);
@@ -906,6 +974,19 @@ QTimeZonePrivate::Data QUtcTimeZonePrivate::data(qint64 forMSecsSinceEpoch) cons
return d;
}
+// Override to shortcut past base's complications:
+QTimeZonePrivate::Data QUtcTimeZonePrivate::data(QTimeZone::TimeType timeType) const
+{
+ Q_UNUSED(timeType);
+ return data(QDateTime::currentMSecsSinceEpoch());
+}
+
+bool QUtcTimeZonePrivate::isDataLocale(const QLocale &locale) const
+{
+ // Officially only supports C locale names; these are surely also viable for English.
+ return locale.language() == QLocale::C || locale.language() == QLocale::English;
+}
+
void QUtcTimeZonePrivate::init(const QByteArray &zoneId)
{
m_id = zoneId;
@@ -933,6 +1014,15 @@ QString QUtcTimeZonePrivate::comment() const
return m_comment;
}
+// Override to bypass complications in base-class:
+QString QUtcTimeZonePrivate::displayName(qint64 atMSecsSinceEpoch,
+ QTimeZone::NameType nameType,
+ const QLocale &locale) const
+{
+ Q_UNUSED(atMSecsSinceEpoch);
+ return displayName(QTimeZone::StandardTime, nameType, locale);
+}
+
QString QUtcTimeZonePrivate::displayName(QTimeZone::TimeType timeType,
QTimeZone::NameType nameType,
const QLocale &locale) const
diff --git a/src/corelib/time/qtimezoneprivate_data_p.h b/src/corelib/time/qtimezoneprivate_data_p.h
index 659605b224..5174f06a0d 100644
--- a/src/corelib/time/qtimezoneprivate_data_p.h
+++ b/src/corelib/time/qtimezoneprivate_data_p.h
@@ -74,27 +74,6 @@ struct UtcData
constexpr QByteArrayView id() const; // Space-joined list of IANA IDs
};
-/*
- COPYRIGHT AND PERMISSION NOTICE
-
- Copyright © 1991-2012 Unicode, Inc. All rights reserved. Distributed under
- the Terms of Use in http://www.unicode.org/copyright.html.
-
- Permission is hereby granted, free of charge, to any person obtaining a
- copy of the Unicode data files and any associated documentation (the "Data
- Files") or Unicode software and any associated documentation (the "Software")
- to deal in the Data Files or Software without restriction, including without
- limitation the rights to use, copy, modify, merge, publish, distribute, and/or
- sell copies of the Data Files or Software, and to permit persons to whom the
- Data Files or Software are furnished to do so, provided that (a) the above
- copyright notice(s) and this permission notice appear with all copies of the
- Data Files or Software, (b) both the above copyright notice(s) and this
- permission notice appear in associated documentation, and (c) there is clear
- notice in each modified Data File or in the Software as well as in the
- documentation associated with the Data File(s) or Software that the data or
- software has been modified.
-*/
-
// GENERATED PART STARTS HERE
/*
diff --git a/src/corelib/time/qtimezoneprivate_icu.cpp b/src/corelib/time/qtimezoneprivate_icu.cpp
index 1a3baa70d0..a9fe68b83c 100644
--- a/src/corelib/time/qtimezoneprivate_icu.cpp
+++ b/src/corelib/time/qtimezoneprivate_icu.cpp
@@ -4,6 +4,7 @@
#include "qtimezone.h"
#include "qtimezoneprivate_p.h"
+#include "qtimezonelocale_p.h"
#include <unicode/ucal.h>
@@ -22,27 +23,6 @@ QT_BEGIN_NAMESPACE
// ICU utilities
-// Convert TimeType and NameType into ICU UCalendarDisplayNameType
-static UCalendarDisplayNameType ucalDisplayNameType(QTimeZone::TimeType timeType, QTimeZone::NameType nameType)
-{
- // TODO ICU C UCalendarDisplayNameType does not support full set of C++ TimeZone::EDisplayType
- switch (nameType) {
- case QTimeZone::ShortName :
- case QTimeZone::OffsetName :
- if (timeType == QTimeZone::DaylightTime)
- return UCAL_SHORT_DST;
- // Includes GenericTime
- return UCAL_SHORT_STANDARD;
- case QTimeZone::DefaultName :
- case QTimeZone::LongName :
- if (timeType == QTimeZone::DaylightTime)
- return UCAL_DST;
- // Includes GenericTime
- return UCAL_STANDARD;
- }
- return UCAL_STANDARD;
-}
-
// Qt wrapper around ucal_getDefaultTimeZone()
static QByteArray ucalDefaultTimeZoneId()
{
@@ -69,44 +49,6 @@ static QByteArray ucalDefaultTimeZoneId()
return QByteArray();
}
-// Qt wrapper around ucal_getTimeZoneDisplayName()
-static QString ucalTimeZoneDisplayName(UCalendar *ucal, QTimeZone::TimeType timeType,
- QTimeZone::NameType nameType,
- const QString &localeCode)
-{
- int32_t size = 50;
- QString result(size, Qt::Uninitialized);
- UErrorCode status = U_ZERO_ERROR;
-
- // size = ucal_getTimeZoneDisplayName(cal, type, locale, result, resultLength, status)
- size = ucal_getTimeZoneDisplayName(ucal,
- ucalDisplayNameType(timeType, nameType),
- localeCode.toUtf8(),
- reinterpret_cast<UChar *>(result.data()),
- size,
- &status);
-
- // If overflow, then resize and retry
- if (status == U_BUFFER_OVERFLOW_ERROR) {
- result.resize(size);
- status = U_ZERO_ERROR;
- size = ucal_getTimeZoneDisplayName(ucal,
- ucalDisplayNameType(timeType, nameType),
- localeCode.toUtf8(),
- reinterpret_cast<UChar *>(result.data()),
- size,
- &status);
- }
-
- // If successful on first or second go, resize and return
- if (U_SUCCESS(status)) {
- result.resize(size);
- return result;
- }
-
- return QString();
-}
-
// Qt wrapper around ucal_get() for offsets
static bool ucalOffsetsAtTime(UCalendar *m_ucal, qint64 atMSecsSinceEpoch,
int *utcOffset, int *dstOffset)
@@ -203,13 +145,11 @@ static QTimeZonePrivate::Data ucalTimeZoneTransition(UCalendar *m_ucal,
tran.offsetFromUtc = utc + dst;
tran.standardTimeOffset = utc;
tran.daylightTimeOffset = dst;
- // TODO No ICU API, use short name instead
- if (dst == 0)
- tran.abbreviation = ucalTimeZoneDisplayName(m_ucal, QTimeZone::StandardTime,
- QTimeZone::ShortName, QLocale().name());
- else
- tran.abbreviation = ucalTimeZoneDisplayName(m_ucal, QTimeZone::DaylightTime,
- QTimeZone::ShortName, QLocale().name());
+ // TODO No ICU API, use short name as abbreviation.
+ QTimeZone::TimeType timeType = dst == 0 ? QTimeZone::StandardTime : QTimeZone::DaylightTime;
+ using namespace QtTimeZoneLocale;
+ tran.abbreviation = ucalTimeZoneDisplayName(m_ucal, timeType,
+ QTimeZone::ShortName, QLocale().name());
return tran;
}
#endif // U_ICU_VERSION_SHORT
@@ -317,6 +257,7 @@ QString QIcuTimeZonePrivate::displayName(QTimeZone::TimeType timeType,
}
// Technically this may be suspect, if locale isn't QLocale(), since that's
// what we used when constructing m_ucal; does ICU cope with inconsistency ?
+ using namespace QtTimeZoneLocale;
return ucalTimeZoneDisplayName(m_ucal, timeType, nameType, locale.name());
}
diff --git a/src/corelib/time/qtimezoneprivate_p.h b/src/corelib/time/qtimezoneprivate_p.h
index 506acaa1f7..5d57ed7558 100644
--- a/src/corelib/time/qtimezoneprivate_p.h
+++ b/src/corelib/time/qtimezoneprivate_p.h
@@ -99,6 +99,8 @@ public:
virtual bool isDaylightTime(qint64 atMSecsSinceEpoch) const;
virtual Data data(qint64 forMSecsSinceEpoch) const;
+ virtual Data data(QTimeZone::TimeType timeType) const;
+ virtual bool isDataLocale(const QLocale &locale) const;
QDateTimePrivate::ZoneState stateAtZoneTime(qint64 forLocalMSecs,
QDateTimePrivate::TransitionOptions resolve) const;
@@ -150,6 +152,15 @@ public:
return QByteArrayLiteral("UTC");
}
+#if QT_CONFIG(timezone_locale)
+private:
+ // Defined in qtimezonelocale.cpp
+ QString localeName(qint64 atMSecsSinceEpoch, int offsetFromUtc,
+ QTimeZone::TimeType timeType,
+ QTimeZone::NameType nameType,
+ const QLocale &locale) const;
+#endif // L10n helpers.
+
protected:
QByteArray m_id;
};
@@ -179,11 +190,15 @@ public:
QUtcTimeZonePrivate *clone() const override;
Data data(qint64 forMSecsSinceEpoch) const override;
+ Data data(QTimeZone::TimeType timeType) const override;
+ bool isDataLocale(const QLocale &locale) const override;
QLocale::Territory territory() const override;
QString comment() const override;
- using QTimeZonePrivate::displayName;
+ QString displayName(qint64 atMSecsSinceEpoch,
+ QTimeZone::NameType nameType,
+ const QLocale &locale) const override;
QString displayName(QTimeZone::TimeType timeType,
QTimeZone::NameType nameType,
const QLocale &locale) const override;
@@ -217,7 +232,7 @@ private:
// TODO: shuffle (almost reverse) order of and rework #if-ery here to use #elif
// and match the #if-ery in each of QTZ's newBackendTimeZone() cascades for
// backend selection.
-#if QT_CONFIG(icu)
+#if QT_CONFIG(icu) && !defined(Q_OS_UNIX)
class Q_AUTOTEST_EXPORT QIcuTimeZonePrivate final : public QTimeZonePrivate
{
public:
@@ -241,6 +256,7 @@ public:
bool hasDaylightTime() const override;
bool isDaylightTime(qint64 atMSecsSinceEpoch) const override;
+ using QTimeZonePrivate::data;
Data data(qint64 forMSecsSinceEpoch) const override;
bool hasTransitions() const override;
@@ -259,7 +275,7 @@ private:
UCalendar *m_ucal;
};
-#endif // ICU
+#endif // ICU not on Unix.
#if defined(Q_OS_UNIX) && !defined(Q_OS_DARWIN) && !defined(Q_OS_ANDROID)
struct QTzTransitionTime
@@ -321,6 +337,8 @@ public:
bool isDaylightTime(qint64 atMSecsSinceEpoch) const override;
Data data(qint64 forMSecsSinceEpoch) const override;
+ Data data(QTimeZone::TimeType timeType) const override;
+ bool isDataLocale(const QLocale &locale) const override;
bool hasTransitions() const override;
Data nextTransition(qint64 afterMSecsSinceEpoch) const override;
@@ -338,14 +356,6 @@ private:
Data dataForTzTransition(QTzTransitionTime tran) const;
Data dataFromRule(QTzTransitionRule rule, qint64 msecsSinceEpoch) const;
-#if QT_CONFIG(icu)
-# ifdef __cpp_lib_is_final
- static_assert(std::is_final<QIcuTimeZonePrivate>::value,
- "if QIcuTimeZonePrivate isn't final, we may need to specialize "
- "QExplicitlySharedDataPointer::clone() to call QTimeZonePrivate::clone()");
-# endif
- mutable QExplicitlySharedDataPointer<const QIcuTimeZonePrivate> m_icu;
-#endif
QTzTimeZoneCacheEntry cached_data;
const QList<QTzTransitionTime> &tranCache() const { return cached_data.m_tranTimes; }
};
@@ -378,6 +388,7 @@ public:
bool hasDaylightTime() const override;
bool isDaylightTime(qint64 atMSecsSinceEpoch) const override;
+ using QTimeZonePrivate::data;
Data data(qint64 forMSecsSinceEpoch) const override;
bool hasTransitions() const override;
@@ -432,6 +443,7 @@ public:
bool hasDaylightTime() const override;
bool isDaylightTime(qint64 atMSecsSinceEpoch) const override;
+ using QTimeZonePrivate::data;
Data data(qint64 forMSecsSinceEpoch) const override;
bool hasTransitions() const override;
@@ -481,6 +493,7 @@ public:
bool hasDaylightTime() const override;
bool isDaylightTime(qint64 atMSecsSinceEpoch) const override;
+ using QTimeZonePrivate::data;
Data data(qint64 forMSecsSinceEpoch) const override;
QByteArray systemTimeZoneId() const override;
diff --git a/src/corelib/time/qtimezoneprivate_tz.cpp b/src/corelib/time/qtimezoneprivate_tz.cpp
index f6156fe93e..8d14e75193 100644
--- a/src/corelib/time/qtimezoneprivate_tz.cpp
+++ b/src/corelib/time/qtimezoneprivate_tz.cpp
@@ -33,10 +33,6 @@ QT_BEGIN_NAMESPACE
using namespace Qt::StringLiterals;
-#if QT_CONFIG(icu)
-Q_CONSTINIT static QBasicMutex s_icu_mutex;
-#endif
-
/*
Private
@@ -771,9 +767,6 @@ QTzTimeZonePrivate::~QTzTimeZonePrivate()
QTzTimeZonePrivate *QTzTimeZonePrivate::clone() const
{
-#if QT_CONFIG(icu)
- const auto lock = qt_scoped_lock(s_icu_mutex);
-#endif
return new QTzTimeZonePrivate(*this);
}
@@ -1007,15 +1000,7 @@ QTzTimeZonePrivate::QTzTimeZonePrivate(const QByteArray &ianaId)
if (m_id.isEmpty()) {
// This can only happen for the system zone, when we've read the
// contents of /etc/localtime because it wasn't a symlink.
-#if QT_CONFIG(icu)
- // Use ICU's system zone, if only to avoid using the abbreviation as ID
- // (ICU might mis-recognize it) in displayName().
- m_icu = new QIcuTimeZonePrivate();
- // Use its ID, as an alternate source of data:
- m_id = m_icu->id();
- if (!m_id.isEmpty())
- return;
-#endif
+ // TODO: use CLDR generic abbreviation for the zone.
m_id = abbreviation(QDateTime::currentMSecsSinceEpoch()).toUtf8();
}
}
@@ -1034,70 +1019,19 @@ QString QTzTimeZonePrivate::displayName(QTimeZone::TimeType timeType,
QTimeZone::NameType nameType,
const QLocale &locale) const
{
- // TZ DB lacks localized names (it only has IANA IDs), so delegate to ICU
- // for those, when available.
-#if QT_CONFIG(icu)
- {
- auto lock = qt_scoped_lock(s_icu_mutex);
- // TODO Some valid TZ names are not valid ICU names, use translation table?
- if (!m_icu)
- m_icu = new QIcuTimeZonePrivate(m_id);
- if (m_icu->isValid())
- return m_icu->displayName(timeType, nameType, locale);
- }
-#else
- Q_UNUSED(timeType);
- Q_UNUSED(nameType);
- Q_UNUSED(locale);
-#endif
- // If ICU is unavailable, fall back to abbreviations.
- // Abbreviations don't have GenericTime
- if (timeType == QTimeZone::GenericTime)
- timeType = QTimeZone::StandardTime;
-
- // Get current tran, if valid and is what we want, then use it
- const qint64 currentMSecs = QDateTime::currentMSecsSinceEpoch();
- QTimeZonePrivate::Data tran = data(currentMSecs);
- if (tran.atMSecsSinceEpoch != invalidMSecs()
- && ((timeType == QTimeZone::DaylightTime && tran.daylightTimeOffset != 0)
- || (timeType == QTimeZone::StandardTime && tran.daylightTimeOffset == 0))) {
- return tran.abbreviation;
- }
-
- // Otherwise get next tran and if valid and is what we want, then use it
- tran = nextTransition(currentMSecs);
- if (tran.atMSecsSinceEpoch != invalidMSecs()
- && ((timeType == QTimeZone::DaylightTime && tran.daylightTimeOffset != 0)
- || (timeType == QTimeZone::StandardTime && tran.daylightTimeOffset == 0))) {
- return tran.abbreviation;
- }
-
- // Otherwise get prev tran and if valid and is what we want, then use it
- tran = previousTransition(currentMSecs);
- if (tran.atMSecsSinceEpoch != invalidMSecs())
- tran = previousTransition(tran.atMSecsSinceEpoch);
- if (tran.atMSecsSinceEpoch != invalidMSecs()
- && ((timeType == QTimeZone::DaylightTime && tran.daylightTimeOffset != 0)
- || (timeType == QTimeZone::StandardTime && tran.daylightTimeOffset == 0))) {
- return tran.abbreviation;
- }
-
- // Otherwise is strange sequence, so work backwards through trans looking for first match, if any
- auto it = std::partition_point(tranCache().cbegin(), tranCache().cend(),
- [currentMSecs](const QTzTransitionTime &at) {
- return at.atMSecsSinceEpoch <= currentMSecs;
- });
-
- while (it != tranCache().cbegin()) {
- --it;
- tran = dataForTzTransition(*it);
- int offset = tran.daylightTimeOffset;
- if ((timeType == QTimeZone::DaylightTime) != (offset == 0))
- return tran.abbreviation;
+ // TZ only provides C-locale abbreviations and offset:
+ if (nameType != QTimeZone::LongName && isDataLocale(locale)) {
+ QTimeZonePrivate::Data tran = data(timeType);
+ if (tran.atMSecsSinceEpoch != invalidMSecs()) {
+ if (nameType == QTimeZone::ShortName)
+ return tran.abbreviation;
+ // Save base class repeating the data(timeType) query:
+ if (locale.language() == QLocale::C)
+ return isoOffsetFormat(tran.offsetFromUtc);
+ }
}
-
- // Otherwise if no match use current data
- return data(currentMSecs).abbreviation;
+ // Otherwise, fall back to base class (and qtimezonelocale.cpp):
+ return QTimeZonePrivate::displayName(timeType, nameType, locale);
}
QString QTzTimeZonePrivate::abbreviation(qint64 atMSecsSinceEpoch) const
@@ -1184,6 +1118,64 @@ QTimeZonePrivate::Data QTzTimeZonePrivate::data(qint64 forMSecsSinceEpoch) const
return dataFromRule(cached_data.m_tranRules.at(last->ruleIndex), forMSecsSinceEpoch);
}
+// Overridden because the final iteration over transitions only needs to look
+// forward and backwards one transition within the POSIX rule (when there is
+// one, as is common) to settle the whole period it covers, so we can then skip
+// all other transitions of the POSIX rule and iterate tranCache() backwards
+// from its most recent transition.
+QTimeZonePrivate::Data QTzTimeZonePrivate::data(QTimeZone::TimeType timeType) const
+{
+ // True if tran is valid and has the DST-ness to match timeType:
+ const auto validMatch = [timeType](const QTimeZonePrivate::Data &tran) {
+ return tran.atMSecsSinceEpoch != invalidMSecs()
+ && ((timeType == QTimeZone::DaylightTime) != (tran.daylightTimeOffset == 0));
+ };
+
+ // Get current tran, use if suitable:
+ const qint64 currentMSecs = QDateTime::currentMSecsSinceEpoch();
+ QTimeZonePrivate::Data tran = data(currentMSecs);
+ if (validMatch(tran))
+ return tran;
+
+ // Otherwise, next tran probably flips DST-ness:
+ tran = nextTransition(currentMSecs);
+ if (validMatch(tran))
+ return tran;
+
+ // Failing that, prev (or present, if current MSecs is eactly a transition
+ // moment) tran defines what data() got us and the one before that probably
+ // flips DST-ness:
+ tran = previousTransition(currentMSecs + 1);
+ if (tran.atMSecsSinceEpoch != invalidMSecs())
+ tran = previousTransition(tran.atMSecsSinceEpoch);
+ if (validMatch(tran))
+ return tran;
+
+ // Otherwise, we can look backwards through transitions for a match; if we
+ // have a POSIX rule, it clearly doesn't do DST (or we'd have hit it by
+ // now), so we only need to look in the tranCache() up to now.
+ const auto untilNow = [currentMSecs](const QTzTransitionTime &at) {
+ return at.atMSecsSinceEpoch <= currentMSecs;
+ };
+ auto it = std::partition_point(tranCache().cbegin(), tranCache().cend(), untilNow);
+ // That's the end or first future transition; we don't want to look at it,
+ // but at all those before it.
+ while (it != tranCache().cbegin()) {
+ --it;
+ tran = dataForTzTransition(*it);
+ if ((timeType == QTimeZone::DaylightTime) != (tran.daylightTimeOffset == 0))
+ return tran;
+ }
+
+ return {};
+}
+
+bool QTzTimeZonePrivate::isDataLocale(const QLocale &locale) const
+{
+ // TZ data uses English / C locale names:
+ return locale.language() == QLocale::C || locale.language() == QLocale::English;
+}
+
bool QTzTimeZonePrivate::hasTransitions() const
{
return true;
diff --git a/src/corelib/tools/qbitarray.cpp b/src/corelib/tools/qbitarray.cpp
index e311fee51f..e4276d383d 100644
--- a/src/corelib/tools/qbitarray.cpp
+++ b/src/corelib/tools/qbitarray.cpp
@@ -23,6 +23,8 @@ QT_BEGIN_NAMESPACE
\ingroup shared
\reentrant
+ \compares equality
+
A QBitArray is an array that gives access to individual bits and
provides operators (\l{operator&()}{AND}, \l{operator|()}{OR},
\l{operator^()}{XOR}, and \l{operator~()}{NOT}) that work on
@@ -499,17 +501,17 @@ quint32 QBitArray::toUInt32(QSysInfo::Endian endianness, bool *ok) const noexcep
fast and never fails.
*/
-/*! \fn bool QBitArray::operator==(const QBitArray &other) const
+/*! \fn bool QBitArray::operator==(const QBitArray &lhs, const QBitArray &rhs)
- Returns \c true if \a other is equal to this bit array; otherwise
+ Returns \c true if \a lhs is equal to \a rhs bit array; otherwise
returns \c false.
\sa operator!=()
*/
-/*! \fn bool QBitArray::operator!=(const QBitArray &other) const
+/*! \fn bool QBitArray::operator!=(const QBitArray &lhs, const QBitArray &rhs)
- Returns \c true if \a other is not equal to this bit array;
+ Returns \c true if \a lhs is not equal to \a rhs bit array;
otherwise returns \c false.
\sa operator==()
diff --git a/src/corelib/tools/qbitarray.h b/src/corelib/tools/qbitarray.h
index 4f99e693eb..b9c36b5320 100644
--- a/src/corelib/tools/qbitarray.h
+++ b/src/corelib/tools/qbitarray.h
@@ -116,8 +116,10 @@ public:
QBitArray operator~() const;
#endif
- inline bool operator==(const QBitArray &other) const { return d == other.d; }
- inline bool operator!=(const QBitArray &other) const { return d != other.d; }
+#if QT_CORE_REMOVED_SINCE(6, 8)
+ inline bool operator==(const QBitArray &other) const { return comparesEqual(d, other.d); }
+ inline bool operator!=(const QBitArray &other) const { return !operator==(other); }
+#endif
bool fill(bool aval, qsizetype asize = -1)
{ *this = QBitArray((asize < 0 ? this->size() : asize), aval); return true; }
@@ -134,6 +136,13 @@ public:
typedef QByteArray::DataPointer DataPtr;
inline DataPtr &data_ptr() { return d.data_ptr(); }
inline const DataPtr &data_ptr() const { return d.data_ptr(); }
+
+private:
+ friend bool comparesEqual(const QBitArray &lhs, const QBitArray &rhs) noexcept
+ {
+ return lhs.d == rhs.d;
+ }
+ Q_DECLARE_EQUALITY_COMPARABLE(QBitArray)
};
class QT6_ONLY(Q_CORE_EXPORT) QBitRef
diff --git a/src/corelib/tools/qeasingcurve.cpp b/src/corelib/tools/qeasingcurve.cpp
index d8b3367de3..52602a0256 100644
--- a/src/corelib/tools/qeasingcurve.cpp
+++ b/src/corelib/tools/qeasingcurve.cpp
@@ -1153,32 +1153,37 @@ QEasingCurve::~QEasingCurve()
*/
/*!
- Compare this easing curve with \a other and returns \c true if they are
- equal. It will also compare the properties of a curve.
+ \fn bool QEasingCurve::operator==(const QEasingCurve &lhs, const QEasingCurve &rhs)
+
+ Compares easing curve \a lhs with \a rhs and returns \c true if they are
+ equal; otherwise returns \c false.
+ It will also compare the properties of the curves.
*/
-bool QEasingCurve::operator==(const QEasingCurve &other) const
+bool comparesEqual(const QEasingCurve &lhs, const QEasingCurve &rhs) noexcept
{
- bool res = d_ptr->func == other.d_ptr->func
- && d_ptr->type == other.d_ptr->type;
+ bool res = lhs.d_ptr->func == rhs.d_ptr->func
+ && lhs.d_ptr->type == rhs.d_ptr->type;
if (res) {
- if (d_ptr->config && other.d_ptr->config) {
+ if (lhs.d_ptr->config && rhs.d_ptr->config) {
// catch the config content
- res = d_ptr->config->operator==(*(other.d_ptr->config));
+ res = lhs.d_ptr->config->operator==(*(rhs.d_ptr->config));
- } else if (d_ptr->config || other.d_ptr->config) {
+ } else if (lhs.d_ptr->config || rhs.d_ptr->config) {
// one one has a config object, which could contain default values
- res = qFuzzyCompare(amplitude(), other.amplitude())
- && qFuzzyCompare(period(), other.period())
- && qFuzzyCompare(overshoot(), other.overshoot());
+ res = qFuzzyCompare(lhs.amplitude(), rhs.amplitude())
+ && qFuzzyCompare(lhs.period(), rhs.period())
+ && qFuzzyCompare(lhs.overshoot(), rhs.overshoot());
}
}
return res;
}
/*!
- \fn bool QEasingCurve::operator!=(const QEasingCurve &other) const
- Compare this easing curve with \a other and returns \c true if they are not equal.
- It will also compare the properties of a curve.
+ \fn bool QEasingCurve::operator!=(const QEasingCurve &lhs, const QEasingCurve &rhs)
+
+ Compares easing curve \a lhs with \a rhs and returns \c true if they are
+ not equal; otherwise returns \c false.
+ It will also compare the properties of the curves.
\sa operator==()
*/
diff --git a/src/corelib/tools/qeasingcurve.h b/src/corelib/tools/qeasingcurve.h
index 5b112d7d7d..61e9aa247d 100644
--- a/src/corelib/tools/qeasingcurve.h
+++ b/src/corelib/tools/qeasingcurve.h
@@ -8,6 +8,7 @@
QT_REQUIRE_CONFIG(easingcurve);
+#include <QtCore/qcompare.h>
#include <QtCore/qlist.h>
#include <QtCore/qobjectdefs.h>
@@ -47,9 +48,11 @@ public:
void swap(QEasingCurve &other) noexcept { qt_ptr_swap(d_ptr, other.d_ptr); }
+#if QT_CORE_REMOVED_SINCE(6, 8)
bool operator==(const QEasingCurve &other) const;
inline bool operator!=(const QEasingCurve &other) const
{ return !(this->operator==(other)); }
+#endif
qreal amplitude() const;
void setAmplitude(qreal amplitude);
@@ -81,6 +84,11 @@ private:
friend Q_CORE_EXPORT QDataStream &operator<<(QDataStream &, const QEasingCurve &);
friend Q_CORE_EXPORT QDataStream &operator>>(QDataStream &, QEasingCurve &);
#endif
+ friend Q_CORE_EXPORT bool
+ comparesEqual(const QEasingCurve &lhs, const QEasingCurve &rhs) noexcept;
+#if !QT_CORE_REMOVED_SINCE(6, 8)
+ Q_DECLARE_EQUALITY_COMPARABLE(QEasingCurve)
+#endif
};
Q_DECLARE_SHARED(QEasingCurve)
diff --git a/src/corelib/tools/qline.cpp b/src/corelib/tools/qline.cpp
index 9216b8875b..e313b06aa9 100644
--- a/src/corelib/tools/qline.cpp
+++ b/src/corelib/tools/qline.cpp
@@ -14,6 +14,9 @@ QT_BEGIN_NAMESPACE
\class QLine
\inmodule QtCore
\ingroup painting
+ \compares equality
+ \compareswith equality QLineF
+ \endcompareswith
\brief The QLine class provides a two-dimensional vector using
integer precision.
@@ -133,18 +136,18 @@ QT_BEGIN_NAMESPACE
*/
/*!
- \fn bool QLine::operator!=(const QLine &line) const
+ \fn bool QLine::operator!=(const QLine &lhs, const QLine &rhs)
- Returns \c true if the given \a line is not the same as \e this line.
+ Returns \c true if the line \a lhs is not the same as line \a rhs.
A line is different from another line if any of their start or
end points differ, or the internal order of the points is different.
*/
/*!
- \fn bool QLine::operator==(const QLine &line) const
+ \fn bool QLine::operator==(const QLine &lhs, const QLine &rhs)
- Returns \c true if the given \a line is the same as \e this line.
+ Returns \c true if the line \a lhs is the same as line \a rhs.
A line is identical to another line if the start and end points
are identical, and the internal order of the points is the same.
@@ -288,6 +291,9 @@ QDataStream &operator>>(QDataStream &stream, QLine &line)
\class QLineF
\inmodule QtCore
\ingroup painting
+ \compares equality
+ \compareswith equality QLine
+ \endcompareswith
\brief The QLineF class provides a two-dimensional vector using
floating point precision.
@@ -508,18 +514,18 @@ QDataStream &operator>>(QDataStream &stream, QLine &line)
*/
/*!
- \fn bool QLineF::operator!=(const QLineF &line) const
+ \fn bool QLineF::operator!=(const QLineF &lhs, const QLineF &rhs)
- Returns \c true if the given \a line is not the same as \e this line.
+ Returns \c true if the line \a lhs is not the same as line \a rhs.
A line is different from another line if their start or end points
differ, or the internal order of the points is different.
*/
/*!
- \fn bool QLineF::operator==(const QLineF &line) const
+ \fn bool QLineF::operator==(const QLineF &lhs, const QLineF &rhs)
- Returns \c true if the given \a line is the same as this line.
+ Returns \c true if the line \a lhs is the same as line \a rhs.
A line is identical to another line if the start and end points
are identical, and the internal order of the points is the same.
@@ -781,6 +787,25 @@ qreal QLineF::angleTo(const QLineF &l) const
return delta_normalized;
}
+/*!
+ \fn bool QLineF::qFuzzyCompare(const QLineF &lhs, const QLineF &rhs)
+ \since 6.8
+
+ Returns \c true if line \a lhs is approximately equal to line \a rhs;
+ otherwise returns \c false.
+
+ The lines are considered approximately equal if their start and end
+ points are approximately equal.
+*/
+
+/*!
+ \fn bool QLineF::qFuzzyIsNull(const QLineF &line)
+ \since 6.8
+
+ Returns \c true if the start point of line \a line is approximately
+ equal to its end point; otherwise returns \c false.
+*/
+
#ifndef QT_NO_DEBUG_STREAM
QDebug operator<<(QDebug dbg, const QLineF &p)
{
diff --git a/src/corelib/tools/qline.h b/src/corelib/tools/qline.h
index e23ffbe9d5..03dac30e16 100644
--- a/src/corelib/tools/qline.h
+++ b/src/corelib/tools/qline.h
@@ -48,12 +48,20 @@ public:
inline void setPoints(const QPoint &p1, const QPoint &p2);
inline void setLine(int x1, int y1, int x2, int y2);
+#if QT_CORE_REMOVED_SINCE(6, 8)
constexpr inline bool operator==(const QLine &d) const noexcept;
- constexpr inline bool operator!=(const QLine &d) const noexcept { return !(*this == d); }
+ constexpr inline bool operator!=(const QLine &d) const noexcept { return !operator==(d); }
+#endif
[[nodiscard]] constexpr inline QLineF toLineF() const noexcept;
private:
+ friend constexpr bool comparesEqual(const QLine &lhs, const QLine &rhs) noexcept
+ { return lhs.pt1 == rhs.pt1 && lhs.pt2 == rhs.pt2; }
+#if !QT_CORE_REMOVED_SINCE(6, 8)
+ Q_DECLARE_EQUALITY_COMPARABLE_LITERAL_TYPE(QLine)
+#endif
+
QPoint pt1, pt2;
};
Q_DECLARE_TYPEINFO(QLine, Q_PRIMITIVE_TYPE);
@@ -161,10 +169,12 @@ inline void QLine::setLine(int aX1, int aY1, int aX2, int aY2)
pt2 = QPoint(aX2, aY2);
}
+#if QT_CORE_REMOVED_SINCE(6, 8)
constexpr inline bool QLine::operator==(const QLine &d) const noexcept
{
- return pt1 == d.pt1 && pt2 == d.pt2;
+ return comparesEqual(*this, d);
}
+#endif
#ifndef QT_NO_DEBUG_STREAM
Q_CORE_EXPORT QDebug operator<<(QDebug d, const QLine &p);
@@ -233,12 +243,30 @@ public:
inline void setPoints(const QPointF &p1, const QPointF &p2);
inline void setLine(qreal x1, qreal y1, qreal x2, qreal y2);
+#if QT_CORE_REMOVED_SINCE(6, 8)
constexpr inline bool operator==(const QLineF &d) const;
- constexpr inline bool operator!=(const QLineF &d) const { return !(*this == d); }
+ constexpr inline bool operator!=(const QLineF &d) const { return !operator==(d); }
+#endif
constexpr QLine toLine() const;
private:
+ friend constexpr bool comparesEqual(const QLineF &lhs, const QLineF &rhs) noexcept
+ { return lhs.pt1 == rhs.pt1 && lhs.pt2 == rhs.pt2; }
+#if !QT_CORE_REMOVED_SINCE(6, 8)
+ Q_DECLARE_EQUALITY_COMPARABLE_LITERAL_TYPE(QLineF)
+#endif
+
+ friend constexpr bool comparesEqual(const QLineF &lhs, const QLine &rhs) noexcept
+ { return comparesEqual(lhs, rhs.toLineF()); }
+ Q_DECLARE_EQUALITY_COMPARABLE_LITERAL_TYPE(QLineF, QLine)
+
+ friend constexpr bool qFuzzyCompare(const QLineF &lhs, const QLineF &rhs) noexcept
+ { return qFuzzyCompare(lhs.pt1, rhs.pt1) && qFuzzyCompare(lhs.pt2, rhs.pt2); }
+
+ friend constexpr bool qFuzzyIsNull(const QLineF &line) noexcept
+ { return qFuzzyCompare(line.pt1, line.pt2); }
+
QPointF pt1, pt2;
};
Q_DECLARE_TYPEINFO(QLineF, Q_PRIMITIVE_TYPE);
@@ -283,7 +311,7 @@ constexpr inline qreal QLineF::y2() const
constexpr inline bool QLineF::isNull() const
{
- return qFuzzyCompare(pt1.x(), pt2.x()) && qFuzzyCompare(pt1.y(), pt2.y());
+ return qFuzzyCompare(pt1, pt2);
}
constexpr inline QPointF QLineF::p1() const
@@ -383,12 +411,12 @@ inline void QLineF::setLine(qreal aX1, qreal aY1, qreal aX2, qreal aY2)
pt2 = QPointF(aX2, aY2);
}
-
+#if QT_CORE_REMOVED_SINCE(6, 8)
constexpr inline bool QLineF::operator==(const QLineF &d) const
{
- return pt1 == d.pt1 && pt2 == d.pt2;
+ return comparesEqual(*this, d);
}
-
+#endif
#ifndef QT_NO_DEBUG_STREAM
diff --git a/src/corelib/tools/qmargins.cpp b/src/corelib/tools/qmargins.cpp
index 1d2cb7d6e5..c4cd0da30d 100644
--- a/src/corelib/tools/qmargins.cpp
+++ b/src/corelib/tools/qmargins.cpp
@@ -14,6 +14,10 @@ QT_BEGIN_NAMESPACE
\ingroup painting
\since 4.6
+ \compares equality
+ \compareswith equality QMarginsF
+ \endcompareswith
+
\brief The QMargins class defines the four margins of a rectangle.
QMargin defines a set of four margins; left, top, right, and bottom,
@@ -107,15 +111,15 @@ QT_BEGIN_NAMESPACE
*/
/*!
- \fn bool QMargins::operator==(const QMargins &m1, const QMargins &m2)
+ \fn bool QMargins::operator==(const QMargins &lhs, const QMargins &rhs)
- Returns \c true if \a m1 and \a m2 are equal; otherwise returns \c false.
+ Returns \c true if \a lhs and \a rhs are equal; otherwise returns \c false.
*/
/*!
- \fn bool QMargins::operator!=(const QMargins &m1, const QMargins &m2)
+ \fn bool QMargins::operator!=(const QMargins &lhs, const QMargins &rhs)
- Returns \c true if \a m1 and \a m2 are different; otherwise returns \c false.
+ Returns \c true if \a lhs and \a rhs are different; otherwise returns \c false.
*/
/*!
@@ -438,6 +442,10 @@ QDebug operator<<(QDebug dbg, const QMargins &m)
\ingroup painting
\since 5.3
+ \compares equality
+ \compareswith equality QMargins
+ \endcompareswith
+
\brief The QMarginsF class defines the four margins of a rectangle.
QMarginsF defines a set of four margins; left, top, right, and bottom,
@@ -746,6 +754,22 @@ QDebug operator<<(QDebug dbg, const QMargins &m)
\sa QMarginsF(), QMargins::toMarginsF()
*/
+/*!
+ \fn bool QMarginsF::qFuzzyCompare(const QMarginsF &lhs, const QMarginsF &rhs)
+ \since 6.8
+
+ Returns \c true if \a lhs is approximately equal to \a rhs;
+ otherwise returns \c false.
+*/
+
+/*!
+ \fn bool QMarginsF::qFuzzyIsNull(const QMarginsF &margins)
+ \since 6.8
+
+ Returns \c true if all components of margsins \a margins are
+ approximately equal to zero; otherwise returns \c false.
+*/
+
/*****************************************************************************
QMarginsF stream functions
*****************************************************************************/
diff --git a/src/corelib/tools/qmargins.h b/src/corelib/tools/qmargins.h
index f8d7150dfd..3b29860d66 100644
--- a/src/corelib/tools/qmargins.h
+++ b/src/corelib/tools/qmargins.h
@@ -4,6 +4,7 @@
#ifndef QMARGINS_H
#define QMARGINS_H
+#include <QtCore/qcompare.h>
#include <QtCore/qnamespace.h>
#include <QtCore/q20type_traits.h>
@@ -54,19 +55,14 @@ private:
int m_right;
int m_bottom;
- friend constexpr inline bool operator==(const QMargins &m1, const QMargins &m2) noexcept
+ friend constexpr bool comparesEqual(const QMargins &lhs, const QMargins &rhs) noexcept
{
- return
- m1.m_left == m2.m_left &&
- m1.m_top == m2.m_top &&
- m1.m_right == m2.m_right &&
- m1.m_bottom == m2.m_bottom;
- }
-
- friend constexpr inline bool operator!=(const QMargins &m1, const QMargins &m2) noexcept
- {
- return !(m1 == m2);
+ return lhs.m_left == rhs.m_left
+ && lhs.m_top == rhs.m_top
+ && lhs.m_right == rhs.m_right
+ && lhs.m_bottom == rhs.m_bottom;
}
+ Q_DECLARE_EQUALITY_COMPARABLE_LITERAL_TYPE(QMargins)
template <std::size_t I,
typename M,
@@ -304,18 +300,35 @@ private:
qreal m_right;
qreal m_bottom;
- friend constexpr inline bool operator==(const QMarginsF &lhs, const QMarginsF &rhs) noexcept
+ QT_WARNING_PUSH
+ QT_WARNING_DISABLE_FLOAT_COMPARE
+ friend constexpr bool qFuzzyCompare(const QMarginsF &lhs, const QMarginsF &rhs) noexcept
{
- return qFuzzyCompare(lhs.left(), rhs.left())
- && qFuzzyCompare(lhs.top(), rhs.top())
- && qFuzzyCompare(lhs.right(), rhs.right())
- && qFuzzyCompare(lhs.bottom(), rhs.bottom());
+ 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));
+ }
+ QT_WARNING_POP
+ friend constexpr bool qFuzzyIsNull(const QMarginsF &m) noexcept
+ {
+ return qFuzzyIsNull(m.m_left) && qFuzzyIsNull(m.m_top)
+ && qFuzzyIsNull(m.m_right) && qFuzzyIsNull(m.m_bottom);
}
- friend constexpr inline bool operator!=(const QMarginsF &lhs, const QMarginsF &rhs) noexcept
+ friend constexpr bool comparesEqual(const QMarginsF &lhs, const QMarginsF &rhs) noexcept
{
- return !(lhs == rhs);
+ return qFuzzyCompare(lhs, rhs);
}
+ Q_DECLARE_EQUALITY_COMPARABLE_LITERAL_TYPE(QMarginsF)
+
+ friend constexpr bool comparesEqual(const QMarginsF &lhs, const QMargins &rhs) noexcept
+ { return comparesEqual(lhs, rhs.toMarginsF()); }
+ Q_DECLARE_EQUALITY_COMPARABLE_LITERAL_TYPE(QMarginsF, QMargins)
template <std::size_t I,
typename M,
diff --git a/src/corelib/tools/qpoint.cpp b/src/corelib/tools/qpoint.cpp
index d1f3b12a68..775a354469 100644
--- a/src/corelib/tools/qpoint.cpp
+++ b/src/corelib/tools/qpoint.cpp
@@ -15,6 +15,10 @@ QT_BEGIN_NAMESPACE
\ingroup painting
\reentrant
+ \compares equality
+ \compareswith equality QPointF
+ \endcompareswith
+
\brief The QPoint class defines a point in the plane using integer
precision.
@@ -208,16 +212,17 @@ QT_BEGIN_NAMESPACE
*/
/*!
- \fn bool QPoint::operator==(const QPoint &p1, const QPoint &p2)
+ \fn bool QPoint::operator==(const QPoint &lhs, const QPoint &rhs)
- Returns \c true if \a p1 and \a p2 are equal; otherwise returns
- false.
+ Returns \c true if \a lhs and \a rhs are equal; otherwise returns
+ \c false.
*/
/*!
- \fn bool QPoint::operator!=(const QPoint &p1, const QPoint &p2)
+ \fn bool QPoint::operator!=(const QPoint &lhs, const QPoint &rhs)
- Returns \c true if \a p1 and \a p2 are not equal; otherwise returns \c false.
+ Returns \c true if \a lhs and \a rhs are not equal; otherwise returns
+ \c false.
*/
/*!
@@ -463,6 +468,10 @@ size_t qHash(QPoint key, size_t seed) noexcept
\ingroup painting
\reentrant
+ \compares equality
+ \compareswith equality QPoint
+ \endcompareswith
+
\brief The QPointF class defines a point in the plane using
floating point precision.
@@ -730,9 +739,9 @@ size_t qHash(QPoint key, size_t seed) noexcept
*/
/*!
- \fn bool QPointF::operator==(const QPointF &p1, const QPointF &p2)
+ \fn bool QPointF::operator==(const QPointF &lhs, const QPointF &rhs)
- Returns \c true if \a p1 is approximately equal to \a p2; otherwise
+ Returns \c true if \a lhs is approximately equal to \a rhs; otherwise
returns \c false.
\warning This function does not check for strict equality; instead,
@@ -742,9 +751,9 @@ size_t qHash(QPoint key, size_t seed) noexcept
*/
/*!
- \fn bool QPointF::operator!=(const QPointF &p1, const QPointF &p2);
+ \fn bool QPointF::operator!=(const QPointF &lhs, const QPointF &rhs)
- Returns \c true if \a p1 is sufficiently different from \a p2;
+ Returns \c true if \a lhs is sufficiently different from \a rhs;
otherwise returns \c false.
\warning This function does not check for strict inequality; instead,
@@ -753,6 +762,26 @@ size_t qHash(QPoint key, size_t seed) noexcept
\sa qFuzzyCompare
*/
+/*!
+ \fn bool QPointF::qFuzzyCompare(const QPointF &p1, const QPointF &p2)
+ \since 6.8
+
+ Returns \c true if \a p1 is approximately equal to \a p2; otherwise
+ returns \c false.
+
+ \sa qFuzzyIsNull
+*/
+
+/*!
+ \fn bool QPointF::qFuzzyIsNull(const QPointF &point)
+ \since 6.8
+
+ Returns \c true if \a point is approximately equal to a point
+ \c {(0.0, 0.0)}.
+
+ \sa qFuzzyCompare
+*/
+
#ifndef QT_NO_DATASTREAM
/*!
\fn QDataStream &operator<<(QDataStream &stream, const QPointF &point)
diff --git a/src/corelib/tools/qpoint.h b/src/corelib/tools/qpoint.h
index 7df4d49005..50b4c864be 100644
--- a/src/corelib/tools/qpoint.h
+++ b/src/corelib/tools/qpoint.h
@@ -4,7 +4,9 @@
#ifndef QPOINT_H
#define QPOINT_H
+#include <QtCore/qcompare.h>
#include <QtCore/qnamespace.h>
+#include <QtCore/qnumeric.h>
#include <QtCore/q20type_traits.h>
#include <QtCore/q23utility.h>
@@ -51,10 +53,10 @@ public:
constexpr static inline int dotProduct(const QPoint &p1, const QPoint &p2)
{ return p1.xp * p2.xp + p1.yp * p2.yp; }
- friend constexpr inline bool operator==(const QPoint &p1, const QPoint &p2) noexcept
+private:
+ friend constexpr bool comparesEqual(const QPoint &p1, const QPoint &p2) noexcept
{ return p1.xp == p2.xp && p1.yp == p2.yp; }
- friend constexpr inline bool operator!=(const QPoint &p1, const QPoint &p2) noexcept
- { return p1.xp != p2.xp || p1.yp != p2.yp; }
+ Q_DECLARE_EQUALITY_COMPARABLE_LITERAL_TYPE(QPoint)
friend constexpr inline QPoint operator+(const QPoint &p1, const QPoint &p2) noexcept
{ return QPoint(p1.xp + p2.xp, p1.yp + p2.yp); }
friend constexpr inline QPoint operator-(const QPoint &p1, const QPoint &p2) noexcept
@@ -78,6 +80,7 @@ public:
friend constexpr inline QPoint operator/(const QPoint &p, qreal c)
{ return QPoint(qRound(p.xp / c), qRound(p.yp / c)); }
+public:
#if defined(Q_OS_DARWIN) || defined(Q_QDOC)
[[nodiscard]] Q_CORE_EXPORT CGPoint toCGPoint() const noexcept;
#endif
@@ -241,19 +244,25 @@ public:
return p1.xp * p2.xp + p1.yp * p2.yp;
}
+private:
QT_WARNING_PUSH
QT_WARNING_DISABLE_FLOAT_COMPARE
- friend constexpr inline bool operator==(const QPointF &p1, const QPointF &p2)
+ 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));
}
- friend constexpr inline bool operator!=(const QPointF &p1, const QPointF &p2)
+ QT_WARNING_POP
+ friend constexpr bool qFuzzyIsNull(const QPointF &point) noexcept
{
- return !(p1 == p2);
+ return qFuzzyIsNull(point.xp) && qFuzzyIsNull(point.yp);
}
- QT_WARNING_POP
-
+ friend constexpr bool comparesEqual(const QPointF &p1, const QPointF &p2) noexcept
+ { return qFuzzyCompare(p1, p2); }
+ Q_DECLARE_EQUALITY_COMPARABLE_LITERAL_TYPE(QPointF)
+ friend constexpr bool comparesEqual(const QPointF &p1, const QPoint &p2) noexcept
+ { return comparesEqual(p1, p2.toPointF()); }
+ Q_DECLARE_EQUALITY_COMPARABLE_LITERAL_TYPE(QPointF, QPoint)
friend constexpr inline QPointF operator+(const QPointF &p1, const QPointF &p2)
{ return QPointF(p1.xp + p2.xp, p1.yp + p2.yp); }
friend constexpr inline QPointF operator-(const QPointF &p1, const QPointF &p2)
@@ -272,6 +281,7 @@ public:
return QPointF(p.xp / divisor, p.yp / divisor);
}
+public:
constexpr QPoint toPoint() const;
#if defined(Q_OS_DARWIN) || defined(Q_QDOC)
diff --git a/src/corelib/tools/qrect.cpp b/src/corelib/tools/qrect.cpp
index 6d345ce543..ce28a6d887 100644
--- a/src/corelib/tools/qrect.cpp
+++ b/src/corelib/tools/qrect.cpp
@@ -15,6 +15,10 @@ QT_BEGIN_NAMESPACE
\ingroup painting
\reentrant
+ \compares equality
+ \compareswith equality QRectF
+ \endcompareswith
+
\brief The QRect class defines a rectangle in the plane using
integer precision.
@@ -1105,18 +1109,18 @@ bool QRect::intersects(const QRect &r) const noexcept
}
/*!
- \fn bool QRect::operator==(const QRect &r1, const QRect &r2)
+ \fn bool QRect::operator==(const QRect &lhs, const QRect &rhs)
- Returns \c true if the rectangles \a r1 and \a r2 are equal,
+ Returns \c true if the rectangles \a lhs and \a rhs are equal,
otherwise returns \c false.
*/
/*!
- \fn bool QRect::operator!=(const QRect &r1, const QRect &r2)
+ \fn bool QRect::operator!=(const QRect &lhs, const QRect &rhs)
- Returns \c true if the rectangles \a r1 and \a r2 are different, otherwise
- returns \c false.
+ Returns \c true if the rectangles \a lhs and \a rhs are different,
+ otherwise returns \c false.
*/
/*!
@@ -1279,6 +1283,10 @@ QDebug operator<<(QDebug dbg, const QRect &r)
\ingroup painting
\reentrant
+ \compares equality
+ \compareswith equality QRect
+ \endcompareswith
+
\brief The QRectF class defines a finite rectangle in the plane using
floating point precision.
@@ -2346,10 +2354,10 @@ QRect QRectF::toAlignedRect() const noexcept
*/
/*!
- \fn bool QRectF::operator==(const QRectF &r1, const QRectF &r2)
+ \fn bool QRectF::operator==(const QRectF &lhs, const QRectF &rhs)
- Returns \c true if the rectangles \a r1 and \a r2 are \b approximately equal,
- otherwise returns \c false.
+ Returns \c true if the rectangles \a lhs and \a rhs are \b approximately
+ equal, otherwise returns \c false.
\warning This function does not check for strict equality; instead,
it uses a fuzzy comparison to compare the rectangles' coordinates.
@@ -2359,9 +2367,9 @@ QRect QRectF::toAlignedRect() const noexcept
/*!
- \fn bool QRectF::operator!=(const QRectF &r1, const QRectF &r2)
+ \fn bool QRectF::operator!=(const QRectF &lhs, const QRectF &rhs)
- Returns \c true if the rectangles \a r1 and \a r2 are sufficiently
+ Returns \c true if the rectangles \a lhs and \a rhs are sufficiently
different, otherwise returns \c false.
\warning This function does not check for strict inequality; instead,
@@ -2429,6 +2437,22 @@ QRect QRectF::toAlignedRect() const noexcept
\sa marginsRemoved(), operator+=(), marginsAdded()
*/
+/*!
+ \fn bool QRectF::qFuzzyCompare(const QRectF &lhs, const QRectF &rhs)
+ \since 6.8
+
+ Returns \c true if the rectangle \a lhs is approximately equal to the
+ rectangle \a rhs; otherwise returns \c false.
+*/
+
+/*!
+ \fn bool QRectF::qFuzzyIsNull(const QRectF &rect)
+ \since 6.8
+
+ Returns \c true if both width and height of the rectangle \a rect are
+ approximately equal to zero; otherwise returns \c false.
+*/
+
/*****************************************************************************
QRectF stream functions
*****************************************************************************/
diff --git a/src/corelib/tools/qrect.h b/src/corelib/tools/qrect.h
index e69a217f48..fb938b0056 100644
--- a/src/corelib/tools/qrect.h
+++ b/src/corelib/tools/qrect.h
@@ -119,12 +119,13 @@ public:
[[nodiscard]] static constexpr inline QRect span(const QPoint &p1, const QPoint &p2) noexcept;
- friend constexpr inline bool operator==(const QRect &r1, const QRect &r2) noexcept
+private:
+ friend constexpr bool comparesEqual(const QRect &r1, const QRect &r2) noexcept
{ return r1.x1==r2.x1 && r1.x2==r2.x2 && r1.y1==r2.y1 && r1.y2==r2.y2; }
- friend constexpr inline bool operator!=(const QRect &r1, const QRect &r2) noexcept
- { return r1.x1!=r2.x1 || r1.x2!=r2.x2 || r1.y1!=r2.y1 || r1.y2!=r2.y2; }
+ Q_DECLARE_EQUALITY_COMPARABLE_LITERAL_TYPE(QRect)
friend constexpr inline size_t qHash(const QRect &, size_t) noexcept;
+public:
#if defined(Q_OS_DARWIN) || defined(Q_QDOC)
[[nodiscard]] CGRect toCGRect() const noexcept;
#endif
@@ -572,17 +573,30 @@ public:
constexpr inline QRectF &operator+=(const QMarginsF &margins) noexcept;
constexpr inline QRectF &operator-=(const QMarginsF &margins) noexcept;
- friend constexpr inline bool operator==(const QRectF &r1, const QRectF &r2) noexcept
+private:
+ friend constexpr bool comparesEqual(const QRectF &r1, const QRectF &r2) noexcept
{
return r1.topLeft() == r2.topLeft()
&& r1.size() == r2.size();
}
- friend constexpr inline bool operator!=(const QRectF &r1, const QRectF &r2) noexcept
+ Q_DECLARE_EQUALITY_COMPARABLE_LITERAL_TYPE(QRectF)
+
+ friend constexpr bool comparesEqual(const QRectF &r1, const QRect &r2) noexcept
+ { return r1.topLeft() == r2.topLeft() && r1.size() == r2.size(); }
+ Q_DECLARE_EQUALITY_COMPARABLE_LITERAL_TYPE(QRectF, QRect)
+
+ friend constexpr bool qFuzzyCompare(const QRectF &lhs, const QRectF &rhs) noexcept
{
- return r1.topLeft() != r2.topLeft()
- || r1.size() != r2.size();
+ return qFuzzyCompare(lhs.topLeft(), rhs.topLeft())
+ && qFuzzyCompare(lhs.bottomRight(), rhs.bottomRight());
}
+ friend constexpr bool qFuzzyIsNull(const QRectF &rect) noexcept
+ {
+ return qFuzzyIsNull(rect.w) && qFuzzyIsNull(rect.h);
+ }
+
+public:
[[nodiscard]] constexpr inline QRect toRect() const noexcept;
[[nodiscard]] QRect toAlignedRect() const noexcept;
diff --git a/src/corelib/tools/qsize.cpp b/src/corelib/tools/qsize.cpp
index d5e8e4c71b..27ff1d164d 100644
--- a/src/corelib/tools/qsize.cpp
+++ b/src/corelib/tools/qsize.cpp
@@ -266,15 +266,15 @@ QSize QSize::scaled(const QSize &s, Qt::AspectRatioMode mode) const noexcept
*/
/*!
- \fn bool QSize::operator==(const QSize &s1, const QSize &s2)
+ \fn bool QSize::operator==(const QSize &lhs, const QSize &rhs)
- Returns \c true if \a s1 and \a s2 are equal; otherwise returns \c false.
+ Returns \c true if \a lhs and \a rhs are equal; otherwise returns \c false.
*/
/*!
- \fn bool QSize::operator!=(const QSize &s1, const QSize &s2)
+ \fn bool QSize::operator!=(const QSize &lhs, const QSize &rhs)
- Returns \c true if \a s1 and \a s2 are different; otherwise returns \c false.
+ Returns \c true if \a lhs and \a rhs are different; otherwise returns \c false.
*/
/*!
@@ -714,9 +714,9 @@ QSizeF QSizeF::scaled(const QSizeF &s, Qt::AspectRatioMode mode) const noexcept
*/
/*!
- \fn bool QSizeF::operator==(const QSizeF &s1, const QSizeF &s2)
+ \fn bool QSizeF::operator==(const QSizeF &lhs, const QSizeF &rhs)
- Returns \c true if \a s1 and \a s2 are approximately equal; otherwise
+ Returns \c true if \a lhs and \a rhs are approximately equal; otherwise
returns false.
\warning This function does not check for strict equality; instead,
@@ -726,9 +726,9 @@ QSizeF QSizeF::scaled(const QSizeF &s, Qt::AspectRatioMode mode) const noexcept
*/
/*!
- \fn bool QSizeF::operator!=(const QSizeF &s1, const QSizeF &s2)
+ \fn bool QSizeF::operator!=(const QSizeF &lhs, const QSizeF &rhs)
- Returns \c true if \a s1 and \a s2 are sufficiently different; otherwise
+ Returns \c true if \a lhs and \a rhs are sufficiently different; otherwise
returns \c false.
\warning This function does not check for strict inequality; instead,
@@ -808,7 +808,24 @@ QSizeF QSizeF::scaled(const QSizeF &s, Qt::AspectRatioMode mode) const noexcept
\sa expandedTo(), scale()
*/
+/*!
+ \fn bool QSizeF::qFuzzyCompare(const QSizeF &lhs, const QSizeF &rhs)
+ \since 6.8
+
+ Returns \c true if the size \a lhs is approximately equal to the
+ size \a rhs; otherwise returns \c false.
+
+ The sizes are considered approximately equal if their width and
+ height are approximately equal.
+*/
+/*!
+ \fn bool QSizeF::qFuzzyIsNull(const QSizeF &size)
+ \since 6.8
+
+ Returns \c true if both width and height of the size \a size
+ are approximately equal to zero.
+*/
/*****************************************************************************
QSizeF stream functions
diff --git a/src/corelib/tools/qsize.h b/src/corelib/tools/qsize.h
index a5eaf34afe..67f7146201 100644
--- a/src/corelib/tools/qsize.h
+++ b/src/corelib/tools/qsize.h
@@ -59,10 +59,10 @@ public:
constexpr inline QSize &operator*=(qreal c) noexcept;
inline QSize &operator/=(qreal c);
- friend inline constexpr bool operator==(const QSize &s1, const QSize &s2) noexcept
+private:
+ friend constexpr bool comparesEqual(const QSize &s1, const QSize &s2) noexcept
{ return s1.wd == s2.wd && s1.ht == s2.ht; }
- friend inline constexpr bool operator!=(const QSize &s1, const QSize &s2) noexcept
- { return s1.wd != s2.wd || s1.ht != s2.ht; }
+ Q_DECLARE_EQUALITY_COMPARABLE_LITERAL_TYPE(QSize)
friend inline constexpr QSize operator+(const QSize &s1, const QSize &s2) noexcept
{ return QSize(s1.wd + s2.wd, s1.ht + s2.ht); }
friend inline constexpr QSize operator-(const QSize &s1, const QSize &s2) noexcept
@@ -75,6 +75,7 @@ public:
{ Q_ASSERT(!qFuzzyIsNull(c)); return QSize(qRound(s.wd / c), qRound(s.ht / c)); }
friend inline constexpr size_t qHash(const QSize &, size_t) noexcept;
+public:
#if defined(Q_OS_DARWIN) || defined(Q_QDOC)
[[nodiscard]] CGSize toCGSize() const noexcept;
#endif
@@ -242,16 +243,25 @@ public:
constexpr inline QSizeF &operator*=(qreal c) noexcept;
inline QSizeF &operator/=(qreal c);
+private:
QT_WARNING_PUSH
QT_WARNING_DISABLE_FLOAT_COMPARE
- friend constexpr inline bool operator==(const QSizeF &s1, const QSizeF &s2)
+ 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));
}
QT_WARNING_POP
- friend constexpr inline bool operator!=(const QSizeF &s1, const QSizeF &s2)
- { return !(s1 == s2); }
+ 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
+ { return qFuzzyCompare(lhs, rhs); }
+ Q_DECLARE_EQUALITY_COMPARABLE_LITERAL_TYPE(QSizeF)
+ friend constexpr bool comparesEqual(const QSizeF &lhs, const QSize &rhs) noexcept
+ { return comparesEqual(lhs, rhs.toSizeF()); }
+ Q_DECLARE_EQUALITY_COMPARABLE_LITERAL_TYPE(QSizeF, QSize)
friend constexpr inline QSizeF operator+(const QSizeF &s1, const QSizeF &s2) noexcept
{ return QSizeF(s1.wd + s2.wd, s1.ht + s2.ht); }
friend constexpr inline QSizeF operator-(const QSizeF &s1, const QSizeF &s2) noexcept
@@ -263,6 +273,7 @@ public:
friend inline QSizeF operator/(const QSizeF &s, qreal c)
{ Q_ASSERT(!qFuzzyIsNull(c)); return QSizeF(s.wd / c, s.ht / c); }
+public:
constexpr inline QSize toSize() const noexcept;
#if defined(Q_OS_DARWIN) || defined(Q_QDOC)
diff --git a/src/corelib/tools/qvarlengtharray.h b/src/corelib/tools/qvarlengtharray.h
index afc345d3be..0a579bf487 100644
--- a/src/corelib/tools/qvarlengtharray.h
+++ b/src/corelib/tools/qvarlengtharray.h
@@ -956,8 +956,8 @@ Q_OUTOFLINE_TEMPLATE auto QVLABase<T>::insert_impl(qsizetype prealloc, void *arr
template <class T>
Q_OUTOFLINE_TEMPLATE auto QVLABase<T>::erase(const_iterator abegin, const_iterator aend) -> iterator
{
- Q_ASSERT_X(isValidIterator(abegin), "QVarLengthArray::insert", "The specified const_iterator argument 'abegin' is invalid");
- Q_ASSERT_X(isValidIterator(aend), "QVarLengthArray::insert", "The specified const_iterator argument 'aend' is invalid");
+ Q_ASSERT_X(isValidIterator(abegin), "QVarLengthArray::erase", "The specified const_iterator argument 'abegin' is invalid");
+ Q_ASSERT_X(isValidIterator(aend), "QVarLengthArray::erase", "The specified const_iterator argument 'aend' is invalid");
qsizetype f = qsizetype(abegin - cbegin());
qsizetype l = qsizetype(aend - cbegin());
@@ -968,10 +968,11 @@ Q_OUTOFLINE_TEMPLATE auto QVLABase<T>::erase(const_iterator abegin, const_iterat
Q_ASSERT(n > 0); // aend must be reachable from abegin
- if constexpr (QTypeInfo<T>::isComplex) {
+ if constexpr (!QTypeInfo<T>::isRelocatable) {
std::move(begin() + l, end(), QT_MAKE_CHECKED_ARRAY_ITERATOR(begin() + f, size() - f));
std::destroy(end() - n, end());
} else {
+ std::destroy(abegin, aend);
memmove(static_cast<void *>(data() + f), static_cast<const void *>(data() + l), (size() - l) * sizeof(T));
}
this->s -= n;
diff --git a/src/corelib/tools/qversionnumber.cpp b/src/corelib/tools/qversionnumber.cpp
index 4b8ace71cc..af95875b44 100644
--- a/src/corelib/tools/qversionnumber.cpp
+++ b/src/corelib/tools/qversionnumber.cpp
@@ -30,6 +30,7 @@ QT_IMPL_METATYPE_EXTERN(QVersionNumber)
\brief The QVersionNumber class contains a version number with an arbitrary
number of segments.
+ \compares strong
\snippet qversionnumber/main.cpp 0
*/
diff --git a/src/corelib/tools/qversionnumber.h b/src/corelib/tools/qversionnumber.h
index 95217a6eff..e7ae107226 100644
--- a/src/corelib/tools/qversionnumber.h
+++ b/src/corelib/tools/qversionnumber.h
@@ -6,6 +6,7 @@
#ifndef QVERSIONNUMBER_H
#define QVERSIONNUMBER_H
+#include <QtCore/qcompare.h>
#include <QtCore/qcontainertools_impl.h>
#include <QtCore/qlist.h>
#include <QtCore/qmetatype.h>
@@ -355,25 +356,20 @@ public:
[[nodiscard]] Q_CORE_EXPORT static QVersionNumber fromString(QStringView string, int *suffixIndex);
#endif
- [[nodiscard]] friend bool operator> (const QVersionNumber &lhs, const QVersionNumber &rhs) noexcept
- { return compare(lhs, rhs) > 0; }
-
- [[nodiscard]] friend bool operator>=(const QVersionNumber &lhs, const QVersionNumber &rhs) noexcept
- { return compare(lhs, rhs) >= 0; }
-
- [[nodiscard]] friend bool operator< (const QVersionNumber &lhs, const QVersionNumber &rhs) noexcept
- { return compare(lhs, rhs) < 0; }
-
- [[nodiscard]] friend bool operator<=(const QVersionNumber &lhs, const QVersionNumber &rhs) noexcept
- { return compare(lhs, rhs) <= 0; }
-
- [[nodiscard]] friend bool operator==(const QVersionNumber &lhs, const QVersionNumber &rhs) noexcept
- { return compare(lhs, rhs) == 0; }
-
- [[nodiscard]] friend bool operator!=(const QVersionNumber &lhs, const QVersionNumber &rhs) noexcept
- { return compare(lhs, rhs) != 0; }
-
private:
+ [[nodiscard]] friend bool comparesEqual(const QVersionNumber &lhs,
+ const QVersionNumber &rhs) noexcept
+ {
+ return compare(lhs, rhs) == 0;
+ }
+ [[nodiscard]] friend Qt::strong_ordering compareThreeWay(const QVersionNumber &lhs,
+ const QVersionNumber &rhs) noexcept
+ {
+ int c = compare(lhs, rhs);
+ return Qt::compareThreeWay(c, 0);
+ }
+ Q_DECLARE_STRONGLY_ORDERED(QVersionNumber)
+
#ifndef QT_NO_DATASTREAM
friend Q_CORE_EXPORT QDataStream& operator>>(QDataStream &in, QVersionNumber &version);
#endif
diff --git a/src/dbus/qdbusextratypes.cpp b/src/dbus/qdbusextratypes.cpp
index 61f2075443..3354e76577 100644
--- a/src/dbus/qdbusextratypes.cpp
+++ b/src/dbus/qdbusextratypes.cpp
@@ -34,6 +34,8 @@ void QDBusSignature::doCheck()
if (!QDBusUtil::isValidSignature(m_signature)) {
qWarning("QDBusSignature: invalid signature \"%s\"", qPrintable(m_signature));
m_signature.clear();
+ } else if (m_signature.isEmpty()) {
+ m_signature.detach(); // we need it to not be null
}
}
diff --git a/src/dbus/qdbusextratypes.h b/src/dbus/qdbusextratypes.h
index 1bc0f3086d..1c0826b992 100644
--- a/src/dbus/qdbusextratypes.h
+++ b/src/dbus/qdbusextratypes.h
@@ -77,7 +77,10 @@ class Q_DBUS_EXPORT QDBusSignature
{
QString m_signature;
public:
- QDBusSignature() noexcept : m_signature() {}
+ QDBusSignature() noexcept
+ {
+ m_signature.detach(); // mark non-null (empty signatures are valid)
+ }
// compiler-generated copy/move constructor/assignment operators are ok!
// compiler-generated destructor is ok!
diff --git a/src/dbus/qdbusintegrator.cpp b/src/dbus/qdbusintegrator.cpp
index 89b9b2d17e..836562f496 100644
--- a/src/dbus/qdbusintegrator.cpp
+++ b/src/dbus/qdbusintegrator.cpp
@@ -151,7 +151,8 @@ static dbus_bool_t qDBusAddTimeout(DBusTimeout *timeout, void *data)
Q_ASSERT(d->timeouts.key(timeout, 0) == 0);
- int timerId = d->startTimer(std::chrono::milliseconds{q_dbus_timeout_get_interval(timeout)});
+ using namespace std::chrono_literals;
+ int timerId = d->startTimer(q_dbus_timeout_get_interval(timeout) * 1ms); // no overflow possible
if (!timerId)
return false;
diff --git a/src/dbus/qdbusmarshaller.cpp b/src/dbus/qdbusmarshaller.cpp
index b2ed2586fb..d1f1a41ab0 100644
--- a/src/dbus/qdbusmarshaller.cpp
+++ b/src/dbus/qdbusmarshaller.cpp
@@ -120,7 +120,7 @@ inline void QDBusMarshaller::append(const QDBusObjectPath &arg)
inline void QDBusMarshaller::append(const QDBusSignature &arg)
{
QByteArray data = arg.signature().toUtf8();
- if (!ba && data.isEmpty()) {
+ if (!ba && data.isNull()) {
error("Invalid signature passed in arguments"_L1);
} else {
const char *cdata = data.constData();
diff --git a/src/dbus/qdbusutil.cpp b/src/dbus/qdbusutil.cpp
index 827418c487..78338aa054 100644
--- a/src/dbus/qdbusutil.cpp
+++ b/src/dbus/qdbusutil.cpp
@@ -512,14 +512,14 @@ namespace QDBusUtil
bool isValidSignature(const QString &signature)
{
QByteArray ba = signature.toLatin1();
- const char *data = ba.constData();
- while (true) {
+ const char *data = ba.constBegin();
+ const char *end = ba.constEnd();
+ while (data != end) {
data = validateSingleType(data);
if (!data)
return false;
- if (*data == '\0')
- return true;
}
+ return true;
}
/*!
diff --git a/src/gui/CMakeLists.txt b/src/gui/CMakeLists.txt
index cef71318d8..aed66563a7 100644
--- a/src/gui/CMakeLists.txt
+++ b/src/gui/CMakeLists.txt
@@ -7,7 +7,9 @@ qt_find_package(WrapPNG PROVIDED_TARGETS WrapPNG::WrapPNG)
qt_find_package(WrapFreetype PROVIDED_TARGETS WrapFreetype::WrapFreetype)
if (QT_FEATURE_gui)
- if(WIN32)
+ if(QT_QPA_PLATFORMS)
+ list(GET QT_QPA_PLATFORMS 0 _default_platform)
+ elseif(WIN32)
set(_default_platform "windows")
elseif(ANDROID)
set(_default_platform "android")
@@ -30,6 +32,11 @@ if (QT_FEATURE_gui)
endif()
set(QT_QPA_DEFAULT_PLATFORM "${_default_platform}" CACHE STRING "QPA default platform")
+ if(NOT "${QT_QPA_DEFAULT_PLATFORM}" IN_LIST QT_QPA_PLATFORMS)
+ list(APPEND QT_QPA_PLATFORMS "${QT_QPA_DEFAULT_PLATFORM}")
+ set(QT_QPA_PLATFORMS "${QT_QPA_PLATFORMS}" CACHE STRING
+ "QPA platforms deployed by default" FORCE)
+ endif()
endif()
# Silence warnings in 3rdparty code
@@ -376,6 +383,11 @@ qt_internal_extend_target(Gui CONDITION MACOS
${FWAppKit}
)
+qt_internal_extend_target(Gui CONDITION UIKIT
+ SOURCES
+ platform/ios/qiosnativeinterface.cpp
+)
+
qt_internal_extend_target(Gui CONDITION WASM
SOURCES
platform/wasm/qwasmnativeinterface.cpp
@@ -402,6 +414,12 @@ qt_internal_extend_target(Gui CONDITION APPLE
${FWImageIO}
)
+qt_internal_extend_target(Gui CONDITION QNX
+ SOURCES
+ painting/qrasterbackingstore.cpp painting/qrasterbackingstore_p.h
+ painting/qrhibackingstore.cpp painting/qrhibackingstore_p.h
+)
+
qt_internal_extend_target(Gui CONDITION QT_FEATURE_animation
SOURCES
animation/qguivariantanimation.cpp
diff --git a/src/gui/accessible/linux/atspiadaptor.cpp b/src/gui/accessible/linux/atspiadaptor.cpp
index b3269a2a95..2cebad1453 100644
--- a/src/gui/accessible/linux/atspiadaptor.cpp
+++ b/src/gui/accessible/linux/atspiadaptor.cpp
@@ -35,6 +35,13 @@
#define ATSPI_COORD_TYPE_PARENT 2
#endif
+// ATSPI_*_VERSION defines were added in libatspi 2.50,
+// as was the AtspiLive enum; define values here for older versions
+#if !defined(ATSPI_MAJOR_VERSION) || !defined(ATSPI_MINOR_VERSION) || ATSPI_MAJOR_VERSION < 2 || ATSPI_MINOR_VERSION < 50
+#define ATSPI_LIVE_POLITE 1
+#define ATSPI_LIVE_ASSERTIVE 2
+#endif
+
QT_BEGIN_NAMESPACE
using namespace Qt::StringLiterals;
@@ -47,6 +54,7 @@ AtSpiAdaptor::AtSpiAdaptor(DBusConnection *connection, QObject *parent)
, sendFocus(0)
, sendObject(0)
, sendObject_active_descendant_changed(0)
+ , sendObject_announcement(0)
, sendObject_attributes_changed(0)
, sendObject_bounds_changed(0)
, sendObject_children_changed(0)
@@ -678,6 +686,8 @@ void AtSpiAdaptor::setBitFlag(const QString &flag)
if (false) {
} else if (right.startsWith("ActiveDescendantChanged"_L1)) {
sendObject_active_descendant_changed = 1;
+ } else if (right.startsWith("Announcement"_L1)) {
+ sendObject_announcement = 1;
} else if (right.startsWith("AttributesChanged"_L1)) {
sendObject_attributes_changed = 1;
} else if (right.startsWith("BoundsChanged"_L1)) {
@@ -929,6 +939,26 @@ void AtSpiAdaptor::notifyStateChange(QAccessibleInterface *interface, const QStr
sendDBusSignal(path, ATSPI_DBUS_INTERFACE_EVENT_OBJECT ""_L1, "StateChanged"_L1, stateArgs);
}
+void AtSpiAdaptor::sendAnnouncement(QAccessibleAnnouncementEvent *event)
+{
+ QAccessibleInterface *iface = event->accessibleInterface();
+ if (!iface) {
+ qCWarning(lcAccessibilityAtspi, "Announcement event has no accessible set.");
+ return;
+ }
+ if (!iface->isValid()) {
+ qCWarning(lcAccessibilityAtspi) << "Announcement event with invalid accessible: " << iface;
+ return;
+ }
+
+ const QString path = pathForInterface(iface);
+ const QString message = event->message();
+ const QAccessible::AnnouncementPriority prio = event->priority();
+ const int politeness = (prio == QAccessible::AnnouncementPriority::Assertive) ? ATSPI_LIVE_ASSERTIVE : ATSPI_LIVE_POLITE;
+
+ const QVariantList args = packDBusSignalArguments(QString(), politeness, 0, QVariant::fromValue(QDBusVariant(message)));
+ sendDBusSignal(path, ATSPI_DBUS_INTERFACE_EVENT_OBJECT ""_L1, "Announcement"_L1, args);
+}
/*!
This function gets called when Qt notifies about accessibility updates.
@@ -1003,6 +1033,14 @@ void AtSpiAdaptor::notify(QAccessibleEvent *event)
sendFocusChanged(event->accessibleInterface());
break;
}
+
+ case QAccessible::Announcement: {
+ if (sendObject || sendObject_announcement) {
+ QAccessibleAnnouncementEvent *announcementEvent = static_cast<QAccessibleAnnouncementEvent*>(event);
+ sendAnnouncement(announcementEvent);
+ }
+ break;
+ }
case QAccessible::TextInserted:
case QAccessible::TextRemoved:
case QAccessible::TextUpdated: {
diff --git a/src/gui/accessible/linux/atspiadaptor_p.h b/src/gui/accessible/linux/atspiadaptor_p.h
index 3a890f3d7d..aab15d4501 100644
--- a/src/gui/accessible/linux/atspiadaptor_p.h
+++ b/src/gui/accessible/linux/atspiadaptor_p.h
@@ -16,7 +16,7 @@
// We mean it.
//
-#include <atspi/atspi-constants.h>
+#include <atspi/atspi.h>
#include <QtGui/private/qtguiglobal_p.h>
#include <QtDBus/qdbusvirtualobject.h>
@@ -85,6 +85,8 @@ private:
void notifyStateChange(QAccessibleInterface *interface, const QString& state, int value);
+ void sendAnnouncement(QAccessibleAnnouncementEvent *event);
+
// accessible helper functions
AtspiRole getRole(QAccessibleInterface *interface) const;
QSpiRelationArray relationSet(QAccessibleInterface *interface, const QDBusConnection &connection) const;
@@ -130,6 +132,7 @@ private:
// all of object
uint sendObject : 1;
uint sendObject_active_descendant_changed : 1;
+ uint sendObject_announcement : 1;
uint sendObject_attributes_changed : 1;
uint sendObject_bounds_changed : 1;
uint sendObject_children_changed : 1;
diff --git a/src/gui/accessible/qaccessible.cpp b/src/gui/accessible/qaccessible.cpp
index 46bca16dad..b037279e8b 100644
--- a/src/gui/accessible/qaccessible.cpp
+++ b/src/gui/accessible/qaccessible.cpp
@@ -173,6 +173,7 @@ Q_LOGGING_CATEGORY(lcAccessibilityCore, "qt.accessibility.core");
\value ActionChanged An action has been changed.
\value ActiveDescendantChanged
\value Alert A system alert (e.g., a message from a QMessageBox)
+ \value [since 6.8] Announcement The announcement of a message is requested.
\value AttributeChanged
\value ContextHelpEnd Context help (QWhatsThis) for an object is finished.
\value ContextHelpStart Context help (QWhatsThis) for an object is initiated.
@@ -449,6 +450,30 @@ Q_LOGGING_CATEGORY(lcAccessibilityCore, "qt.accessibility.core");
\sa QAccessibleAttributesInterface
*/
+/*! \enum QAccessible::AnnouncementPriority
+ This enum describes the priority for announcements used by the
+ \l QAccessibleAnnouncementEvent.
+ \since 6.8
+
+ With \a QAccessible::AnouncementPriority::Polite, assistive technologies
+ should announce the message at the next graceful opportunity such as at the
+ end of speaking the current sentence or when the user pauses typing.
+
+ When specifying \a QAccessible::AnouncementPriority::Assertive, assistive
+ technologies should notify the user immediately.
+
+ Because an interruption might disorient users or cause them to not complete
+ their current task, \a QAccessible::AnouncementPriority::Assertive should
+ not be used unless the interruption is imperative.
+
+ \value Polite The announcement has normal priority.
+ \value Assertive The announcement has high priority and should notify
+ the user immediately, even if that means interrupting the user's
+ current task.
+
+ \sa QAccessibleAnnouncementEvent
+*/
+
/*!
\enum QAccessible::InterfaceType
@@ -1778,7 +1803,56 @@ QAccessibleTextSelectionEvent::~QAccessibleTextSelectionEvent()
{
}
+/*!
+ \since 6.8
+ \class QAccessibleAnnouncementEvent
+ \ingroup accessibility
+ \inmodule QtGui
+
+ \brief The QAccessibleAnnouncementEvent is used to request the announcement
+ of a given message by assistive technologies.
+
+ This class is used with \l QAccessible::updateAccessibility().
+*/
+
+/*! \fn QAccessibleAnnouncementEvent::QAccessibleAnnouncementEvent(QObject *object, const QString &message)
+
+ Constructs a new QAccessibleAnnouncementEvent event for \a object
+ to request the announcement of \a message with priority \l QAccessible::AnnouncementPriority::Polite.
+
+ \l QAccessibleAnnouncementEvent::setPriority can be used to adjust the priority.
+*/
+/*! \fn QAccessibleAnnouncementEvent::QAccessibleAnnouncementEvent(QAccessibleInterface *iface, const QString &message)
+
+ Constructs a new QAccessibleAnnouncementEvent event for \a iface
+ to request the announcement of \a message with priority \l QAccessible::AnnouncementPriority::Polite.
+
+ \l QAccessibleAnnouncementEvent::setPriority can be used to adjust the priority.
+*/
+
+/*! \fn QString QAccessibleAnnouncementEvent::message() const
+
+ Returns the message.
+*/
+
+/*! \fn QAccessible::AnnouncementPriority QAccessibleAnnouncementEvent::priority() const
+
+ Returns the priority.
+*/
+
+/*! \fn void QAccessibleAnnouncementEvent::setPriority(QAccessible::AnnouncementPriority priority)
+
+ Sets the priority with which the announcement will be requested to \a priority.
+*/
+
+
+/*!
+ \internal
+*/
+QAccessibleAnnouncementEvent::~QAccessibleAnnouncementEvent()
+{
+}
/*!
Returns the QAccessibleInterface associated with the event.
diff --git a/src/gui/accessible/qaccessible.h b/src/gui/accessible/qaccessible.h
index 0a92e76c73..3d8daa4b3c 100644
--- a/src/gui/accessible/qaccessible.h
+++ b/src/gui/accessible/qaccessible.h
@@ -316,6 +316,7 @@ public:
Q_ASSERT(m_type != QAccessible::TextRemoved);
Q_ASSERT(m_type != QAccessible::TextUpdated);
Q_ASSERT(m_type != QAccessible::TableModelChanged);
+ Q_ASSERT(m_type != QAccessible::Announcement);
}
inline QAccessibleEvent(QAccessibleInterface *iface, QAccessible::Event typ)
@@ -330,6 +331,7 @@ public:
Q_ASSERT(m_type != QAccessible::TextRemoved);
Q_ASSERT(m_type != QAccessible::TextUpdated);
Q_ASSERT(m_type != QAccessible::TableModelChanged);
+ Q_ASSERT(m_type != QAccessible::Announcement);
m_uniqueId = QAccessible::uniqueId(iface);
m_object = iface->object();
}
@@ -605,6 +607,36 @@ protected:
int m_lastColumn;
};
+class Q_GUI_EXPORT QAccessibleAnnouncementEvent : public QAccessibleEvent
+{
+public:
+ inline QAccessibleAnnouncementEvent(QObject *object, const QString &message)
+ : QAccessibleEvent(object, QAccessible::InvalidEvent)
+ , m_message(message)
+ , m_priority(QAccessible::AnnouncementPriority::Polite)
+ {
+ m_type = QAccessible::Announcement;
+ }
+
+ inline QAccessibleAnnouncementEvent(QAccessibleInterface *iface, const QString &message)
+ : QAccessibleEvent(iface, QAccessible::InvalidEvent)
+ , m_message(message)
+ , m_priority(QAccessible::AnnouncementPriority::Polite)
+ {
+ m_type = QAccessible::Announcement;
+ }
+
+ ~QAccessibleAnnouncementEvent();
+
+ QString message() const { return m_message; }
+ QAccessible::AnnouncementPriority priority() const { return m_priority; }
+ void setPriority(QAccessible::AnnouncementPriority priority) { m_priority = priority; };
+
+protected:
+ QString m_message;
+ QAccessible::AnnouncementPriority m_priority;
+};
+
#ifndef Q_QDOC
#define QAccessibleInterface_iid "org.qt-project.Qt.QAccessibleInterface"
Q_DECLARE_INTERFACE(QAccessibleInterface, QAccessibleInterface_iid)
diff --git a/src/gui/accessible/qaccessible_base.h b/src/gui/accessible/qaccessible_base.h
index 2d2b1de316..1a7b75c7f5 100644
--- a/src/gui/accessible/qaccessible_base.h
+++ b/src/gui/accessible/qaccessible_base.h
@@ -101,6 +101,7 @@ public:
HelpChanged = 0x80A0,
DefaultActionChanged = 0x80B0,
AcceleratorChanged = 0x80C0,
+ Announcement = 0x80D0,
InvalidEvent
};
@@ -367,6 +368,11 @@ public:
Level,
};
+ enum class AnnouncementPriority {
+ Polite,
+ Assertive
+ };
+
typedef QAccessibleInterface*(*InterfaceFactory)(const QString &key, QObject*);
typedef void(*UpdateHandler)(QAccessibleEvent *event);
typedef void(*RootObjectHandler)(QObject*);
diff --git a/src/gui/compat/removed_api.cpp b/src/gui/compat/removed_api.cpp
index 659a27ecb6..a642c33c42 100644
--- a/src/gui/compat/removed_api.cpp
+++ b/src/gui/compat/removed_api.cpp
@@ -7,6 +7,17 @@
QT_USE_NAMESPACE
+#if QT_GUI_REMOVED_SINCE(6, 4)
+
+#include "qpagesize.h" // removed duplicate declaration of op==
+ // (still caused an symbol on some platforms)
+
+// #include "qotherheader.h"
+// // implement removed functions from qotherheader.h
+// order sections alphabetically
+
+#endif // QT_GUI_REMOVED_SINCE(6, 4)
+
#if QT_GUI_REMOVED_SINCE(6, 6)
#include "qpixmapcache.h" // inlined API
diff --git a/src/gui/configure.cmake b/src/gui/configure.cmake
index e1d8efb292..da08863ac6 100644
--- a/src/gui/configure.cmake
+++ b/src/gui/configure.cmake
@@ -893,7 +893,7 @@ qt_feature("jpeg" PRIVATE
CONDITION QT_FEATURE_imageformatplugin
DISABLE INPUT_libjpeg STREQUAL 'no'
)
-qt_feature_definition("jpeg" "QT_NO_IMAGEFORMAT_JPEG" NEGATE)
+qt_feature_definition("jpeg" "QT_NO_IMAGEFORMAT_JPEG" NEGATE VALUE "1")
qt_feature("system-jpeg" PRIVATE
LABEL " Using system libjpeg"
CONDITION QT_FEATURE_jpeg AND JPEG_FOUND
diff --git a/src/gui/image/qicon.cpp b/src/gui/image/qicon.cpp
index 086ac37a07..3fc29ee10d 100644
--- a/src/gui/image/qicon.cpp
+++ b/src/gui/image/qicon.cpp
@@ -160,15 +160,16 @@ static inline int area(const QSize &s) { return s.width() * s.height(); }
// the 2x pixmaps then.)
static QPixmapIconEngineEntry *bestSizeScaleMatch(const QSize &size, qreal scale, QPixmapIconEngineEntry *pa, QPixmapIconEngineEntry *pb)
{
-
+ const auto scaleA = pa->pixmap.devicePixelRatio();
+ const auto scaleB = pb->pixmap.devicePixelRatio();
// scale: we can only differentiate on scale if the scale differs
- if (pa->scale != pb->scale) {
+ if (scaleA != scaleB) {
// Score the pixmaps: 0 is an exact scale match, positive
// scores have more detail than requested, negative scores
// have less detail than requested.
- qreal ascore = pa->scale - scale;
- qreal bscore = pb->scale - scale;
+ qreal ascore = scaleA - scale;
+ qreal bscore = scaleB - scale;
// always prefer positive scores to prevent upscaling
if ((ascore < 0) != (bscore < 0))
@@ -201,13 +202,14 @@ static QPixmapIconEngineEntry *bestSizeScaleMatch(const QSize &size, qreal scale
QPixmapIconEngineEntry *QPixmapIconEngine::tryMatch(const QSize &size, qreal scale, QIcon::Mode mode, QIcon::State state)
{
QPixmapIconEngineEntry *pe = nullptr;
- for (int i = 0; i < pixmaps.size(); ++i)
- if (pixmaps.at(i).mode == mode && pixmaps.at(i).state == state) {
+ for (auto &entry : pixmaps) {
+ if (entry.mode == mode && entry.state == state) {
if (pe)
- pe = bestSizeScaleMatch(size, scale, &pixmaps[i], pe);
+ pe = bestSizeScaleMatch(size, scale, &entry, pe);
else
- pe = &pixmaps[i];
+ pe = &entry;
}
+ }
return pe;
}
@@ -278,7 +280,7 @@ QPixmap QPixmapIconEngine::scaledPixmap(const QSize &size, QIcon::Mode mode, QIc
pm = pe->pixmap;
if (pm.isNull()) {
- int idx = pixmaps.size();
+ auto idx = pixmaps.size();
while (--idx >= 0) {
if (pe == &pixmaps.at(idx)) {
pixmaps.remove(idx);
@@ -369,7 +371,7 @@ void QPixmapIconEngine::addPixmap(const QPixmap &pixmap, QIcon::Mode mode, QIcon
{
if (!pixmap.isNull()) {
QPixmapIconEngineEntry *pe = tryMatch(pixmap.size(), pixmap.devicePixelRatio(), mode, state);
- if (pe && pe->size == pixmap.size() && pe->scale == pixmap.devicePixelRatio()) {
+ if (pe && pe->size == pixmap.size() && pe->pixmap.devicePixelRatio() == pixmap.devicePixelRatio()) {
pe->pixmap = pixmap;
pe->fileName.clear();
} else {
@@ -387,7 +389,7 @@ static inline int origIcoDepth(const QImage &image)
static inline int findBySize(const QList<QImage> &images, const QSize &size)
{
- for (int i = 0; i < images.size(); ++i) {
+ for (qsizetype i = 0; i < images.size(); ++i) {
if (images.at(i).size() == size)
return i;
}
diff --git a/src/gui/image/qicon_p.h b/src/gui/image/qicon_p.h
index c5bf120620..dfce2d5b53 100644
--- a/src/gui/image/qicon_p.h
+++ b/src/gui/image/qicon_p.h
@@ -49,24 +49,22 @@ public:
struct QPixmapIconEngineEntry
{
- QPixmapIconEngineEntry():scale(1), mode(QIcon::Normal), state(QIcon::Off){}
- QPixmapIconEngineEntry(const QPixmap &pm, QIcon::Mode m = QIcon::Normal, QIcon::State s = QIcon::Off)
- :pixmap(pm), size(pm.size()), scale(pm.devicePixelRatio()), mode(m), state(s){}
- QPixmapIconEngineEntry(const QString &file, const QSize &sz = QSize(), QIcon::Mode m = QIcon::Normal, QIcon::State s = QIcon::Off)
- :fileName(file), size(sz), scale(1), mode(m), state(s){}
- QPixmapIconEngineEntry(const QString &file, const QImage &image, QIcon::Mode m = QIcon::Normal, QIcon::State s = QIcon::Off);
+ QPixmapIconEngineEntry() = default;
+ QPixmapIconEngineEntry(const QPixmap &pm, QIcon::Mode m, QIcon::State s)
+ : pixmap(pm), size(pm.size()), mode(m), state(s) {}
+ QPixmapIconEngineEntry(const QString &file, const QSize &sz, QIcon::Mode m, QIcon::State s)
+ : fileName(file), size(sz), mode(m), state(s) {}
+ QPixmapIconEngineEntry(const QString &file, const QImage &image, QIcon::Mode m, QIcon::State s);
QPixmap pixmap;
QString fileName;
QSize size;
- qreal scale;
- QIcon::Mode mode;
- QIcon::State state;
- bool isNull() const {return (fileName.isEmpty() && pixmap.isNull()); }
+ QIcon::Mode mode = QIcon::Normal;
+ QIcon::State state = QIcon::Off;
};
Q_DECLARE_TYPEINFO(QPixmapIconEngineEntry, Q_RELOCATABLE_TYPE);
inline QPixmapIconEngineEntry::QPixmapIconEngineEntry(const QString &file, const QImage &image, QIcon::Mode m, QIcon::State s)
- : fileName(file), size(image.size()), scale(image.devicePixelRatio()), mode(m), state(s)
+ : fileName(file), size(image.size()), mode(m), state(s)
{
pixmap.convertFromImage(image);
}
diff --git a/src/gui/image/qiconengine.h b/src/gui/image/qiconengine.h
index 61411b0660..f5c5184608 100644
--- a/src/gui/image/qiconengine.h
+++ b/src/gui/image/qiconengine.h
@@ -18,6 +18,7 @@ public:
virtual ~QIconEngine();
virtual void paint(QPainter *painter, const QRect &rect, QIcon::Mode mode, QIcon::State state) = 0;
virtual QSize actualSize(const QSize &size, QIcon::Mode mode, QIcon::State state);
+ // ### Qt7: add qreal scale argument and remove scaledPixmap
virtual QPixmap pixmap(const QSize &size, QIcon::Mode mode, QIcon::State state);
virtual void addPixmap(const QPixmap &pixmap, QIcon::Mode mode, QIcon::State state);
diff --git a/src/gui/image/qimage.cpp b/src/gui/image/qimage.cpp
index 3bbf21320e..a24d7ccde7 100644
--- a/src/gui/image/qimage.cpp
+++ b/src/gui/image/qimage.cpp
@@ -1093,7 +1093,6 @@ QImage &QImage::operator=(const QImage &image)
/*!
\fn void QImage::swap(QImage &other)
- \since 4.8
Swaps image \a other with this image. This operation is very
fast and never fails.
@@ -1412,7 +1411,6 @@ int QImage::depth() const
}
/*!
- \since 4.6
\fn int QImage::colorCount() const
Returns the size of the color table for the image.
@@ -1676,7 +1674,6 @@ const uchar *QImage::scanLine(int i) const
shared pixel data, because the returned data is const.
\sa scanLine(), constBits()
- \since 4.7
*/
const uchar *QImage::constScanLine(int i) const
{
@@ -1732,7 +1729,6 @@ const uchar *QImage::bits() const
shared pixel data, because the returned data is const.
\sa bits(), constScanLine()
- \since 4.7
*/
const uchar *QImage::constBits() const
{
@@ -1839,7 +1835,6 @@ void QImage::fill(uint pixel)
/*!
\fn void QImage::fill(Qt::GlobalColor color)
\overload
- \since 4.8
Fills the image with the given \a color, described as a standard global
color.
@@ -1865,8 +1860,6 @@ void QImage::fill(Qt::GlobalColor color)
If the depth of the image is 8, the image will be filled with the
index corresponding the \a color in the color table if present; it
will otherwise be filled with 0.
-
- \since 4.8
*/
void QImage::fill(const QColor &color)
@@ -2123,7 +2116,6 @@ void QImage::invertPixels(InvertMode mode)
#endif
/*!
- \since 4.6
Resizes the color table to contain \a colorCount entries.
If the color table is expanded, all the extra colors will be set to
@@ -4607,7 +4599,6 @@ bool QImage::hasAlphaChannel() const
}
/*!
- \since 4.7
Returns the number of bit planes in the image.
The number of bit planes is the number of bits of color and
diff --git a/src/gui/image/qpixmap.cpp b/src/gui/image/qpixmap.cpp
index 89b8d5303b..afef16f867 100644
--- a/src/gui/image/qpixmap.cpp
+++ b/src/gui/image/qpixmap.cpp
@@ -289,7 +289,6 @@ QPixmap QPixmap::copy(const QRect &rect) const
/*!
\fn QPixmap::scroll(int dx, int dy, int x, int y, int width, int height, QRegion *exposed)
- \since 4.6
This convenience function is equivalent to calling QPixmap::scroll(\a dx,
\a dy, QRect(\a x, \a y, \a width, \a height), \a exposed).
@@ -298,8 +297,6 @@ QPixmap QPixmap::copy(const QRect &rect) const
*/
/*!
- \since 4.6
-
Scrolls the area \a rect of this pixmap by (\a dx, \a dy). The exposed
region is left unchanged. You can optionally pass a pointer to an empty
QRegion to get the region that is \a exposed by the scroll operation.
@@ -371,7 +368,6 @@ QPixmap &QPixmap::operator=(const QPixmap &pixmap)
/*!
\fn void QPixmap::swap(QPixmap &other)
- \since 4.8
Swaps pixmap \a other with this pixmap. This operation is very
fast and never fails.
@@ -970,12 +966,7 @@ bool QPixmap::isDetached() const
Passing 0 for \a flags sets all the default options. Returns \c true
if the result is that this pixmap is not null.
- Note: this function was part of Qt 3 support in Qt 4.6 and earlier.
- It has been promoted to official API status in 4.7 to support updating
- the pixmap's image without creating a new QPixmap as fromImage() would.
-
\sa fromImage()
- \since 4.7
*/
bool QPixmap::convertFromImage(const QImage &image, Qt::ImageConversionFlags flags)
{
diff --git a/src/gui/kernel/qguiapplication.cpp b/src/gui/kernel/qguiapplication.cpp
index 444091afef..5228ac9477 100644
--- a/src/gui/kernel/qguiapplication.cpp
+++ b/src/gui/kernel/qguiapplication.cpp
@@ -2643,28 +2643,18 @@ void QGuiApplicationPrivate::processThemeChanged(QWindowSystemInterfacePrivate::
QIconPrivate::clearIconCache();
- QStyleHintsPrivate::get(QGuiApplication::styleHints())->updateColorScheme(colorScheme());
-
QEvent themeChangeEvent(QEvent::ThemeChange);
const QWindowList windows = tce->window ? QWindowList{tce->window} : window_list;
for (auto *window : windows)
QGuiApplication::sendSpontaneousEvent(window, &themeChangeEvent);
}
-/*!
- \internal
- \brief QGuiApplicationPrivate::colorScheme
- \return the platform theme's color scheme
- or Qt::ColorScheme::Unknown if a platform theme cannot be established
- */
-Qt::ColorScheme QGuiApplicationPrivate::colorScheme()
-{
- return platformTheme() ? platformTheme()->colorScheme()
- : Qt::ColorScheme::Unknown;
-}
-
void QGuiApplicationPrivate::handleThemeChanged()
{
+ const auto newColorScheme = platformTheme() ? platformTheme()->colorScheme()
+ : Qt::ColorScheme::Unknown;
+ QStyleHintsPrivate::get(QGuiApplication::styleHints())->updateColorScheme(newColorScheme);
+
updatePalette();
QIconLoader::instance()->updateSystemTheme();
@@ -3457,6 +3447,15 @@ void QGuiApplicationPrivate::updatePalette()
}
}
+QEvent::Type QGuiApplicationPrivate::contextMenuEventType()
+{
+ switch (QGuiApplication::styleHints()->contextMenuTrigger()) {
+ case Qt::ContextMenuTrigger::Press: return QEvent::MouseButtonPress;
+ case Qt::ContextMenuTrigger::Release: return QEvent::MouseButtonRelease;
+ }
+ return QEvent::None;
+}
+
void QGuiApplicationPrivate::clearPalette()
{
delete app_pal;
@@ -3675,9 +3674,13 @@ void QGuiApplicationPrivate::notifyWindowIconChanged()
The default is \c true.
- If this property is \c true, the applications quits when the last visible
- \l{Primary and Secondary Windows}{primary window} (i.e. top level window
- with no transient parent) is closed.
+ If this property is \c true, the application will attempt to
+ quit when the last visible \l{Primary and Secondary Windows}{primary window}
+ (i.e. top level window with no transient parent) is closed.
+
+ Note that attempting a quit may not necessarily result in the
+ application quitting, for example if there still are active
+ QEventLoopLocker instances, or the QEvent::Quit event is ignored.
\sa quit(), QWindow::close()
*/
@@ -3733,7 +3736,13 @@ bool QGuiApplicationPrivate::lastWindowClosed() const
bool QGuiApplicationPrivate::canQuitAutomatically()
{
- if (quitOnLastWindowClosed && !lastWindowClosed())
+ // The automatic quit functionality is triggered by
+ // both QEventLoopLocker and maybeLastWindowClosed.
+ // Although the former is a QCoreApplication feature
+ // we don't want to quit the application when there
+ // are open windows, regardless of whether the app
+ // also quits automatically on maybeLastWindowClosed.
+ if (!lastWindowClosed())
return false;
return QCoreApplicationPrivate::canQuitAutomatically();
@@ -4392,6 +4401,9 @@ void *QGuiApplication::resolveInterface(const char *name, int revision) const
#if QT_CONFIG(wayland)
QT_NATIVE_INTERFACE_RETURN_IF(QWaylandApplication, platformNativeInterface());
#endif
+#if defined(Q_OS_VISIONOS)
+ QT_NATIVE_INTERFACE_RETURN_IF(QVisionOSApplication, platformIntegration);
+#endif
return QCoreApplication::resolveInterface(name, revision);
}
diff --git a/src/gui/kernel/qguiapplication.h b/src/gui/kernel/qguiapplication.h
index 14bce88c62..23d7fb3d65 100644
--- a/src/gui/kernel/qguiapplication.h
+++ b/src/gui/kernel/qguiapplication.h
@@ -42,7 +42,7 @@ class Q_GUI_EXPORT QGuiApplication : public QCoreApplication
Q_PROPERTY(QString desktopFileName READ desktopFileName WRITE setDesktopFileName)
Q_PROPERTY(Qt::LayoutDirection layoutDirection READ layoutDirection WRITE setLayoutDirection
NOTIFY layoutDirectionChanged)
- Q_PROPERTY(QString platformName READ platformName STORED false)
+ Q_PROPERTY(QString platformName READ platformName STORED false CONSTANT)
Q_PROPERTY(bool quitOnLastWindowClosed READ quitOnLastWindowClosed
WRITE setQuitOnLastWindowClosed)
Q_PROPERTY(QScreen *primaryScreen READ primaryScreen NOTIFY primaryScreenChanged STORED false)
diff --git a/src/gui/kernel/qguiapplication_p.h b/src/gui/kernel/qguiapplication_p.h
index 2bfcd87f3f..39c490c581 100644
--- a/src/gui/kernel/qguiapplication_p.h
+++ b/src/gui/kernel/qguiapplication_p.h
@@ -323,7 +323,7 @@ public:
static void updatePalette();
- static Qt::ColorScheme colorScheme();
+ static QEvent::Type contextMenuEventType();
protected:
virtual void handleThemeChanged();
diff --git a/src/gui/kernel/qguiapplication_platform.h b/src/gui/kernel/qguiapplication_platform.h
index 545bf75c84..d9ff01bf14 100644
--- a/src/gui/kernel/qguiapplication_platform.h
+++ b/src/gui/kernel/qguiapplication_platform.h
@@ -32,6 +32,22 @@ struct wl_pointer;
struct wl_touch;
#endif
+#if defined(Q_OS_VISIONOS) || defined(Q_QDOC)
+# ifdef __OBJC__
+Q_FORWARD_DECLARE_OBJC_CLASS(CP_OBJECT_cp_layer_renderer_capabilities);
+typedef CP_OBJECT_cp_layer_renderer_capabilities *cp_layer_renderer_capabilities_t;
+Q_FORWARD_DECLARE_OBJC_CLASS(CP_OBJECT_cp_layer_renderer_configuration);
+typedef CP_OBJECT_cp_layer_renderer_configuration *cp_layer_renderer_configuration_t;
+Q_FORWARD_DECLARE_OBJC_CLASS(CP_OBJECT_cp_layer_renderer);
+typedef CP_OBJECT_cp_layer_renderer *cp_layer_renderer_t;
+# else
+typedef struct cp_layer_renderer_capabilities_s *cp_layer_renderer_capabilities_t;
+typedef struct cp_layer_renderer_configuration_s *cp_layer_renderer_configuration_t;
+typedef struct cp_layer_renderer_s *cp_layer_renderer_t;
+# endif
+#endif
+
+
QT_BEGIN_NAMESPACE
namespace QNativeInterface
@@ -61,6 +77,20 @@ struct Q_GUI_EXPORT QWaylandApplication
};
#endif
+#if defined(Q_OS_VISIONOS) || defined(Q_QDOC)
+struct Q_GUI_EXPORT QVisionOSApplication
+{
+ QT_DECLARE_NATIVE_INTERFACE(QVisionOSApplication, 1, QGuiApplication)
+ struct ImmersiveSpaceCompositorLayer {
+ virtual void configure(cp_layer_renderer_capabilities_t, cp_layer_renderer_configuration_t) const {}
+ virtual void render(cp_layer_renderer_t) = 0;
+ };
+ virtual void setImmersiveSpaceCompositorLayer(ImmersiveSpaceCompositorLayer *layer) = 0;
+ virtual void openImmersiveSpace() = 0;
+ virtual void dismissImmersiveSpace() = 0;
+};
+#endif
+
} // QNativeInterface
QT_END_NAMESPACE
diff --git a/src/gui/kernel/qguivariant.cpp b/src/gui/kernel/qguivariant.cpp
index fe72e7782f..78a1660355 100644
--- a/src/gui/kernel/qguivariant.cpp
+++ b/src/gui/kernel/qguivariant.cpp
@@ -78,7 +78,9 @@ static constexpr struct : QMetaTypeModuleHelper
// either two nullptrs from canConvert, or two valid pointers
Q_ASSERT(onlyCheck || (bool(from) && bool(to)));
+#if QT_CONFIG(shortcut)
using Int = int;
+#endif
switch (makePair(toTypeId, fromTypeId)) {
QMETATYPE_CONVERTER(QByteArray, QColor,
result = source.name(source.alpha() != 255 ?
diff --git a/src/gui/kernel/qhighdpiscaling_p.h b/src/gui/kernel/qhighdpiscaling_p.h
index 189f31fd0a..d6deb8a72a 100644
--- a/src/gui/kernel/qhighdpiscaling_p.h
+++ b/src/gui/kernel/qhighdpiscaling_p.h
@@ -172,7 +172,7 @@ inline QMargins scale(const QMargins &margins, qreal scaleFactor, QPoint origin
template<typename T>
QList<T> scale(const QList<T> &list, qreal scaleFactor, QPoint origin = QPoint(0, 0))
{
- if (!QHighDpiScaling::isActive())
+ if (qFuzzyCompare(scaleFactor, qreal(1)))
return list;
QList<T> scaled;
@@ -184,7 +184,7 @@ QList<T> scale(const QList<T> &list, qreal scaleFactor, QPoint origin = QPoint(0
inline QRegion scale(const QRegion &region, qreal scaleFactor, QPoint origin = QPoint(0, 0))
{
- if (!QHighDpiScaling::isActive())
+ if (qFuzzyCompare(scaleFactor, qreal(1)))
return region;
QRegion scaled = region.translated(-origin);
diff --git a/src/gui/kernel/qplatformtheme.cpp b/src/gui/kernel/qplatformtheme.cpp
index 48978b849a..3d1319615e 100644
--- a/src/gui/kernel/qplatformtheme.cpp
+++ b/src/gui/kernel/qplatformtheme.cpp
@@ -447,6 +447,11 @@ Qt::ColorScheme QPlatformTheme::colorScheme() const
return Qt::ColorScheme::Unknown;
}
+void QPlatformTheme::requestColorScheme(Qt::ColorScheme scheme)
+{
+ Q_UNUSED(scheme);
+}
+
const QPalette *QPlatformTheme::palette(Palette type) const
{
Q_D(const QPlatformTheme);
diff --git a/src/gui/kernel/qplatformtheme.h b/src/gui/kernel/qplatformtheme.h
index c0193947b9..d007a19675 100644
--- a/src/gui/kernel/qplatformtheme.h
+++ b/src/gui/kernel/qplatformtheme.h
@@ -295,6 +295,7 @@ public:
#endif
virtual Qt::ColorScheme colorScheme() const;
+ virtual void requestColorScheme(Qt::ColorScheme scheme);
virtual const QPalette *palette(Palette type = SystemPalette) const;
diff --git a/src/gui/kernel/qstylehints.cpp b/src/gui/kernel/qstylehints.cpp
index 5029701f24..73c6199733 100644
--- a/src/gui/kernel/qstylehints.cpp
+++ b/src/gui/kernel/qstylehints.cpp
@@ -123,8 +123,29 @@ int QStyleHints::touchDoubleTapDistance() const
/*!
\property QStyleHints::colorScheme
- \brief the color scheme of the platform theme.
- \sa Qt::ColorScheme
+ \brief the color scheme used by the application.
+
+ By default, this follows the system's default color scheme (also known as appearance),
+ and changes when the system color scheme changes (e.g. during dusk or dawn).
+ Setting the color scheme to an explicit value will override the system setting and
+ ignore any changes to the system's color scheme. However, doing so is a hint to the
+ system, and overriding the color scheme is not supported on all platforms.
+
+ Resetting this property, or setting it to \l{Qt::ColorScheme::Unknown}, will remove
+ the override and make the application follow the system default again. The property
+ value will change to the color scheme the system currently has.
+
+ When this property changes, Qt will read the system palette and update the default
+ palette, but won't overwrite palette entries that have been explicitly set by the
+ application. When the colorSchemeChange() signal gets emitted, the old palette is
+ still in effect.
+
+ Application-specific colors should be selected to work well with the effective
+ palette, taking the current color scheme into account. To update application-
+ specific colors when the effective palette changes, handle
+ \l{QEvent::}{PaletteChange} or \l{QEvent::}{ApplicationPaletteChange} events.
+
+ \sa Qt::ColorScheme, QGuiApplication::palette(), QEvent::PaletteChange
\since 6.5
*/
Qt::ColorScheme QStyleHints::colorScheme() const
@@ -134,6 +155,30 @@ Qt::ColorScheme QStyleHints::colorScheme() const
}
/*!
+ \since 6.8
+
+ Sets the color scheme used by the application to an explicit \a scheme, or
+ revert to the system's current color scheme if \a scheme is Qt::ColorScheme::Unknown.
+*/
+void QStyleHints::setColorScheme(Qt::ColorScheme scheme)
+{
+ if (!QCoreApplication::instance()) {
+ qWarning("Must construct a QGuiApplication before accessing a platform theme hint.");
+ return;
+ }
+ if (QPlatformTheme *theme = QGuiApplicationPrivate::platformTheme())
+ theme->requestColorScheme(scheme);
+}
+
+/*!
+ \fn void QStyleHints::unsetColorScheme()
+ \since 6.8
+
+ Restores the color scheme to the system's current color scheme.
+*/
+
+
+/*!
Sets the \a mousePressAndHoldInterval.
\internal
\sa mousePressAndHoldInterval()
@@ -398,6 +443,40 @@ void QStyleHints::setShowShortcutsInContextMenus(bool s)
}
/*!
+ \property QStyleHints::contextMenuTrigger
+ \since 6.8
+ \brief mouse event used to trigger a context menu event.
+
+ The default on UNIX systems is to show context menu on mouse button press event, while on
+ Windows it is the mouse button release event. This property can be used to override the default
+ platform behavior.
+
+ \note Developers must use this property with great care, as it changes the default interaction
+ mode that their users will expect on the platform that they are running on.
+
+ \sa Qt::ContextMenuTrigger
+*/
+Qt::ContextMenuTrigger QStyleHints::contextMenuTrigger() const
+{
+ Q_D(const QStyleHints);
+ if (d->m_contextMenuTrigger == -1) {
+ return themeableHint(QPlatformTheme::ContextMenuOnMouseRelease).toBool()
+ ? Qt::ContextMenuTrigger::Release
+ : Qt::ContextMenuTrigger::Press;
+ }
+ return Qt::ContextMenuTrigger(d->m_contextMenuTrigger);
+}
+
+void QStyleHints::setContextMenuTrigger(Qt::ContextMenuTrigger contextMenuTrigger)
+{
+ Q_D(QStyleHints);
+ const Qt::ContextMenuTrigger currentTrigger = this->contextMenuTrigger();
+ d->m_contextMenuTrigger = int(contextMenuTrigger);
+ if (currentTrigger != contextMenuTrigger)
+ emit contextMenuTriggerChanged(contextMenuTrigger);
+}
+
+/*!
\property QStyleHints::passwordMaskDelay
\brief the time, in milliseconds, a typed letter is displayed unshrouded
in a text input field in password mode.
diff --git a/src/gui/kernel/qstylehints.h b/src/gui/kernel/qstylehints.h
index 969bec4b35..97ef59f3cf 100644
--- a/src/gui/kernel/qstylehints.h
+++ b/src/gui/kernel/qstylehints.h
@@ -36,6 +36,8 @@ class Q_GUI_EXPORT QStyleHints : public QObject
Q_PROPERTY(bool showIsMaximized READ showIsMaximized STORED false CONSTANT FINAL)
Q_PROPERTY(bool showShortcutsInContextMenus READ showShortcutsInContextMenus
WRITE setShowShortcutsInContextMenus NOTIFY showShortcutsInContextMenusChanged FINAL)
+ Q_PROPERTY(Qt::ContextMenuTrigger contextMenuTrigger READ contextMenuTrigger WRITE
+ setContextMenuTrigger NOTIFY contextMenuTriggerChanged FINAL)
Q_PROPERTY(int startDragDistance READ startDragDistance NOTIFY startDragDistanceChanged FINAL)
Q_PROPERTY(int startDragTime READ startDragTime NOTIFY startDragTimeChanged FINAL)
Q_PROPERTY(int startDragVelocity READ startDragVelocity STORED false CONSTANT FINAL)
@@ -52,7 +54,8 @@ class Q_GUI_EXPORT QStyleHints : public QObject
Q_PROPERTY(int mouseDoubleClickDistance READ mouseDoubleClickDistance STORED false CONSTANT
FINAL)
Q_PROPERTY(int touchDoubleTapDistance READ touchDoubleTapDistance STORED false CONSTANT FINAL)
- Q_PROPERTY(Qt::ColorScheme colorScheme READ colorScheme NOTIFY colorSchemeChanged FINAL)
+ Q_PROPERTY(Qt::ColorScheme colorScheme READ colorScheme WRITE setColorScheme
+ RESET unsetColorScheme NOTIFY colorSchemeChanged FINAL)
public:
void setMouseDoubleClickInterval(int mouseDoubleClickInterval);
@@ -79,6 +82,8 @@ public:
bool showIsMaximized() const;
bool showShortcutsInContextMenus() const;
void setShowShortcutsInContextMenus(bool showShortcutsInContextMenus);
+ Qt::ContextMenuTrigger contextMenuTrigger() const;
+ void setContextMenuTrigger(Qt::ContextMenuTrigger contextMenuTrigger);
int passwordMaskDelay() const;
QChar passwordMaskCharacter() const;
qreal fontSmoothingGamma() const;
@@ -94,6 +99,8 @@ public:
void setMouseQuickSelectionThreshold(int threshold);
int mouseQuickSelectionThreshold() const;
Qt::ColorScheme colorScheme() const;
+ void setColorScheme(Qt::ColorScheme scheme);
+ void unsetColorScheme() { setColorScheme(Qt::ColorScheme::Unknown); }
Q_SIGNALS:
void cursorFlashTimeChanged(int cursorFlashTime);
@@ -105,6 +112,7 @@ Q_SIGNALS:
void tabFocusBehaviorChanged(Qt::TabFocusBehavior tabFocusBehavior);
void useHoverEffectsChanged(bool useHoverEffects);
void showShortcutsInContextMenusChanged(bool);
+ void contextMenuTriggerChanged(Qt::ContextMenuTrigger contextMenuTrigger);
void wheelScrollLinesChanged(int scrollLines);
void mouseQuickSelectionThresholdChanged(int threshold);
void colorSchemeChanged(Qt::ColorScheme colorScheme);
diff --git a/src/gui/kernel/qstylehints_p.h b/src/gui/kernel/qstylehints_p.h
index 2b3979512a..497bf95cbf 100644
--- a/src/gui/kernel/qstylehints_p.h
+++ b/src/gui/kernel/qstylehints_p.h
@@ -35,6 +35,7 @@ public:
int m_tabFocusBehavior = -1;
int m_uiEffects = -1;
int m_showShortcutsInContextMenus = -1;
+ int m_contextMenuTrigger = -1;
int m_wheelScrollLines = -1;
int m_mouseQuickSelectionThreshold = -1;
int m_mouseDoubleClickDistance = -1;
diff --git a/src/gui/kernel/qwindow.cpp b/src/gui/kernel/qwindow.cpp
index b40fd7e8e8..7c885032c7 100644
--- a/src/gui/kernel/qwindow.cpp
+++ b/src/gui/kernel/qwindow.cpp
@@ -2654,16 +2654,14 @@ bool QWindow::event(QEvent *ev)
This logic could be simplified by always synthesizing events in
QGuiApplicationPrivate, or perhaps even in each QPA plugin. See QTBUG-93486.
*/
- static const QEvent::Type contextMenuTrigger =
- QGuiApplicationPrivate::platformTheme()->themeHint(QPlatformTheme::ContextMenuOnMouseRelease).toBool() ?
- QEvent::MouseButtonRelease : QEvent::MouseButtonPress;
auto asMouseEvent = [](QEvent *ev) {
const auto t = ev->type();
return t == QEvent::MouseButtonPress || t == QEvent::MouseButtonRelease
? static_cast<QMouseEvent *>(ev) : nullptr ;
};
- if (QMouseEvent *me = asMouseEvent(ev); me &&
- ev->type() == contextMenuTrigger && me->button() == Qt::RightButton) {
+ if (QMouseEvent *me = asMouseEvent(ev);
+ me && ev->type() == QGuiApplicationPrivate::contextMenuEventType()
+ && me->button() == Qt::RightButton) {
QContextMenuEvent e(QContextMenuEvent::Mouse, me->position().toPoint(),
me->globalPosition().toPoint(), me->modifiers());
QGuiApplication::sendEvent(this, &e);
diff --git a/src/gui/kernel/qwindow_p.h b/src/gui/kernel/qwindow_p.h
index a9716847a1..40ab06af8b 100644
--- a/src/gui/kernel/qwindow_p.h
+++ b/src/gui/kernel/qwindow_p.h
@@ -76,6 +76,16 @@ public:
void setTransientParent(QWindow *parent);
virtual void clearFocusObject();
+
+ enum class FocusTarget {
+ First,
+ Last,
+ Current,
+ Next,
+ Prev
+ };
+ virtual void setFocusToTarget(FocusTarget, Qt::FocusReason) {}
+
virtual QRectF closestAcceptableGeometry(const QRectF &rect) const;
void setMinOrMaxSize(QSize *oldSizeMember, const QSize &size,
diff --git a/src/gui/painting/qbackingstore.cpp b/src/gui/painting/qbackingstore.cpp
index 2304ee2256..3b709ec77b 100644
--- a/src/gui/painting/qbackingstore.cpp
+++ b/src/gui/painting/qbackingstore.cpp
@@ -50,6 +50,7 @@ public:
QScopedPointer<QImage> highDpiBackingstore;
QRegion staticContents;
QSize size;
+ QSize nativeSize;
bool downscale = qEnvironmentVariableIntValue("QT_WIDGETS_HIGHDPI_DOWNSCALE") > 0;
};
@@ -115,14 +116,13 @@ QWindow* QBackingStore::window() const
void QBackingStore::beginPaint(const QRegion &region)
{
- const qreal dpr = d_ptr->backingStoreDevicePixelRatio();
+ const qreal toNativeFactor = d_ptr->deviceIndependentToNativeFactor();
- if (d_ptr->highDpiBackingstore &&
- d_ptr->highDpiBackingstore->devicePixelRatio() != dpr)
+ if (d_ptr->nativeSize != QHighDpi::scale(size(), toNativeFactor))
resize(size());
QPlatformBackingStore *platformBackingStore = handle();
- platformBackingStore->beginPaint(QHighDpi::scale(region, d_ptr->deviceIndependentToNativeFactor()));
+ platformBackingStore->beginPaint(QHighDpi::scale(region, toNativeFactor));
// When QtGui is applying a high-dpi scale factor the backing store
// creates a "large" backing store image. This image needs to be
@@ -131,18 +131,20 @@ void QBackingStore::beginPaint(const QRegion &region)
// the image data to avoid having the new devicePixelRatio be propagated
// back to the platform plugin.
QPaintDevice *device = platformBackingStore->paintDevice();
- if (QHighDpiScaling::isActive() && device->devType() == QInternal::Image) {
+ if (!qFuzzyCompare(toNativeFactor, qreal(1)) && device->devType() == QInternal::Image) {
QImage *source = static_cast<QImage *>(device);
const bool needsNewImage = d_ptr->highDpiBackingstore.isNull()
- || source->data_ptr() != d_ptr->highDpiBackingstore->data_ptr()
+ || source->constBits() != d_ptr->highDpiBackingstore->constBits()
|| source->size() != d_ptr->highDpiBackingstore->size()
- || source->devicePixelRatio() != d_ptr->highDpiBackingstore->devicePixelRatio();
- if (needsNewImage) {
+ || source->bytesPerLine() != d_ptr->highDpiBackingstore->bytesPerLine()
+ || source->format() != d_ptr->highDpiBackingstore->format();
+ if (needsNewImage)
d_ptr->highDpiBackingstore.reset(
new QImage(source->bits(), source->width(), source->height(), source->bytesPerLine(), source->format()));
- d_ptr->highDpiBackingstore->setDevicePixelRatio(dpr);
- }
+ d_ptr->highDpiBackingstore->setDevicePixelRatio(d_ptr->backingStoreDevicePixelRatio());
+ } else {
+ d_ptr->highDpiBackingstore.reset();
}
}
@@ -156,7 +158,7 @@ QPaintDevice *QBackingStore::paintDevice()
{
QPaintDevice *device = handle()->paintDevice();
- if (QHighDpiScaling::isActive() && device->devType() == QInternal::Image)
+ if (!qFuzzyCompare(d_ptr->deviceIndependentToNativeFactor(), qreal(1)) && device->devType() == QInternal::Image)
return d_ptr->highDpiBackingstore.data();
return device;
@@ -229,9 +231,10 @@ void QBackingStore::flush(const QRegion &region, QWindow *window, const QPoint &
*/
void QBackingStore::resize(const QSize &size)
{
- d_ptr->size = size;
const qreal factor = d_ptr->deviceIndependentToNativeFactor();
- handle()->resize(QHighDpi::scale(size, factor), QHighDpi::scale(d_ptr->staticContents, factor));
+ d_ptr->size = size;
+ d_ptr->nativeSize = QHighDpi::scale(size, factor);
+ handle()->resize(d_ptr->nativeSize, QHighDpi::scale(d_ptr->staticContents, factor));
}
/*!
diff --git a/src/gui/painting/qcolorspace.cpp b/src/gui/painting/qcolorspace.cpp
index 7a1d34a408..f21e6ec738 100644
--- a/src/gui/painting/qcolorspace.cpp
+++ b/src/gui/painting/qcolorspace.cpp
@@ -448,11 +448,15 @@ QColorTransform QColorSpacePrivate::transformationToXYZ() const
transform.d = ptr;
ptr->colorSpaceIn = this;
ptr->colorSpaceOut = this;
- // Convert to XYZ relative to our white point, not the regular D50 white point.
if (isThreeComponentMatrix())
- ptr->colorMatrix = QColorMatrix::chromaticAdaptation(whitePoint).inverted() * toXyz;
+ ptr->colorMatrix = toXyz;
else
ptr->colorMatrix = QColorMatrix::identity();
+ // Convert to XYZ relative to our white point, not the regular D50 white point.
+ if (!chad.isNull())
+ ptr->colorMatrix = chad.inverted() * ptr->colorMatrix;
+ else if (!whitePoint.isNull())
+ ptr->colorMatrix = QColorMatrix::chromaticAdaptation(whitePoint).inverted() * ptr->colorMatrix;
return transform;
}
diff --git a/src/gui/painting/qcolortransform.cpp b/src/gui/painting/qcolortransform.cpp
index aac07bdc09..33b1dcdeb0 100644
--- a/src/gui/painting/qcolortransform.cpp
+++ b/src/gui/painting/qcolortransform.cpp
@@ -1640,7 +1640,7 @@ void QColorTransformPrivate::applyConvertIn(const S *src, QColorVector *buffer,
loadUnpremultiplied(buffer, src, len, this);
if (!colorSpaceOut->isThreeComponentMatrix())
- applyMatrix<DoClamp>(buffer, len, colorMatrix); // colorMatrix should have the first half only.
+ applyMatrix<DoClamp>(buffer, len, colorSpaceIn->toXyz);
return;
}
}
@@ -1666,7 +1666,7 @@ void QColorTransformPrivate::applyConvertOut(D *dst, const S *src, QColorVector
// Avoid compiling this part for D=QCmyk32:
if constexpr (!std::is_same_v<D, QCmyk32>) {
if (colorSpaceOut->isThreeComponentMatrix()) {
- applyMatrix<doClamp>(buffer, len, colorMatrix); // colorMatrix should have the latter half only.
+ applyMatrix<doClamp>(buffer, len, colorMatrix);
if constexpr (std::is_same_v<S, QCmyk32>) {
storeOpaque(dst, buffer, len, this);
@@ -1695,14 +1695,34 @@ void QColorTransformPrivate::applyConvertOut(D *dst, const S *src, QColorVector
storeUnpremultipliedLUT(dst, src, buffer, len);
}
-template<typename D, typename S>
-void QColorTransformPrivate::applyElementListTransform(D *dst, const S *src, qsizetype count, TransformFlags flags) const
+/*!
+ \internal
+ Adapt Profile Connecting Color spaces.
+*/
+void QColorTransformPrivate::pcsAdapt(QColorVector *buffer, qsizetype count) const
{
- Q_ASSERT(!colorSpaceIn->isThreeComponentMatrix() || !colorSpaceOut->isThreeComponentMatrix());
+ // Match Profile Connection Spaces (PCS):
+ if (colorSpaceOut->isPcsLab && !colorSpaceIn->isPcsLab) {
+ for (qsizetype j = 0; j < count; ++j)
+ buffer[j] = buffer[j].xyzToLab();
+ } else if (colorSpaceIn->isPcsLab && !colorSpaceOut->isPcsLab) {
+ for (qsizetype j = 0; j < count; ++j)
+ buffer[j] = buffer[j].labToXyz();
+ }
+}
- if (!colorMatrix.isValid())
- return;
+/*!
+ \internal
+ Applies the color transformation on \a count S pixels starting from
+ \a src and stores the result in \a dst as D pixels .
+ Assumes unpremultiplied data by default. Set \a flags to change defaults.
+
+ \sa prepare()
+*/
+template<typename D, typename S>
+void QColorTransformPrivate::apply(D *dst, const S *src, qsizetype count, TransformFlags flags) const
+{
if (colorSpaceIn->isThreeComponentMatrix())
updateLutsIn();
if (colorSpaceOut->isThreeComponentMatrix())
@@ -1715,14 +1735,7 @@ void QColorTransformPrivate::applyElementListTransform(D *dst, const S *src, qsi
applyConvertIn(src + i, buffer, len, flags);
- // Match Profile Connection Spaces (PCS):
- if (colorSpaceOut->isPcsLab && !colorSpaceIn->isPcsLab) {
- for (qsizetype j = 0; j < len; ++j)
- buffer[j] = buffer[j].xyzToLab();
- } else if (colorSpaceIn->isPcsLab && !colorSpaceOut->isPcsLab) {
- for (qsizetype j = 0; j < len; ++j)
- buffer[j] = buffer[j].labToXyz();
- }
+ pcsAdapt(buffer, len);
applyConvertOut(dst + i, src + i, buffer, len, flags);
@@ -1730,64 +1743,6 @@ void QColorTransformPrivate::applyElementListTransform(D *dst, const S *src, qsi
}
}
-template<typename D, typename S>
-void QColorTransformPrivate::applyThreeComponentMatrix(D *dst, const S *src, qsizetype count, TransformFlags flags) const
-{
- Q_ASSERT(colorSpaceIn->isThreeComponentMatrix() && colorSpaceOut->isThreeComponentMatrix());
-
- if (!colorMatrix.isValid())
- return;
-
- updateLutsIn();
- updateLutsOut();
-
- bool doApplyMatrix = !colorMatrix.isIdentity();
- constexpr ApplyMatrixForm doClamp = (std::is_same_v<D, QRgbaFloat16> || std::is_same_v<D, QRgbaFloat32>) ? DoNotClamp : DoClamp;
-
- QUninitialized<QColorVector, WorkBlockSize> buffer;
- qsizetype i = 0;
- while (i < count) {
- const qsizetype len = qMin(count - i, WorkBlockSize);
- if (flags & InputPremultiplied)
- loadPremultiplied(buffer, src + i, len, this);
- else
- loadUnpremultiplied(buffer, src + i, len, this);
-
- if (doApplyMatrix)
- applyMatrix<doClamp>(buffer, len, colorMatrix);
- else
- clampIfNeeded<doClamp>(buffer, len);
-
- if (flags & InputOpaque)
- storeOpaque(dst + i, buffer, len, this);
- else if (flags & OutputPremultiplied)
- storePremultiplied(dst + i, src + i, buffer, len, this);
- else
- storeUnpremultiplied(dst + i, src + i, buffer, len, this);
-
- i += len;
- }
-}
-
-/*!
- \internal
- Applies the color transformation on \a count S pixels starting from
- \a src and stores the result in \a dst as D pixels .
-
- Assumes unpremultiplied data by default. Set \a flags to change defaults.
-
- \sa prepare()
-*/
-template<typename D, typename S>
-void QColorTransformPrivate::apply(D *dst, const S *src, qsizetype count, TransformFlags flags) const
-{
- if constexpr (!std::is_same_v<D, QCmyk32> && !std::is_same_v<S, QCmyk32>) {
- if (isThreeComponentMatrix())
- return applyThreeComponentMatrix<D, S>(dst, src, count, flags);
- }
- applyElementListTransform<D, S>(dst, src, count, flags);
-}
-
/*!
\internal
Is to be called on a color-transform to XYZ, returns only luminance values.
@@ -1970,15 +1925,6 @@ template void QColorTransformPrivate::apply<QRgbaFloat32, QCmyk32>(QRgbaFloat32
template void QColorTransformPrivate::apply<QRgbaFloat32, QRgba64>(QRgbaFloat32 *dst, const QRgba64 *src, qsizetype count, TransformFlags flags) const;
template void QColorTransformPrivate::apply<QRgbaFloat32, QRgbaFloat32>(QRgbaFloat32 *dst, const QRgbaFloat32 *src, qsizetype count, TransformFlags flags) const;
-bool QColorTransformPrivate::isThreeComponentMatrix() const
-{
- if (colorSpaceIn && !colorSpaceIn->isThreeComponentMatrix())
- return false;
- if (colorSpaceOut && !colorSpaceOut->isThreeComponentMatrix())
- return false;
- return true;
-}
-
/*!
\internal
*/
@@ -1991,7 +1937,7 @@ bool QColorTransformPrivate::isIdentity() const
if (colorSpaceIn && colorSpaceOut) {
if (colorSpaceIn->equals(colorSpaceOut.constData()))
return true;
- if (!isThreeComponentMatrix())
+ if (!colorSpaceIn->isThreeComponentMatrix() || !colorSpaceOut->isThreeComponentMatrix())
return false;
if (colorSpaceIn->transferFunction != colorSpaceOut->transferFunction)
return false;
@@ -2001,7 +1947,9 @@ bool QColorTransformPrivate::isIdentity() const
&& colorSpaceIn->trc[2] == colorSpaceOut->trc[2];
}
} else {
- if (!isThreeComponentMatrix())
+ if (colorSpaceIn && !colorSpaceIn->isThreeComponentMatrix())
+ return false;
+ if (colorSpaceOut && !colorSpaceOut->isThreeComponentMatrix())
return false;
if (colorSpaceIn && colorSpaceIn->transferFunction != QColorSpace::TransferFunction::Linear)
return false;
diff --git a/src/gui/painting/qcolortransform_p.h b/src/gui/painting/qcolortransform_p.h
index 59ea6a2405..c74fe100eb 100644
--- a/src/gui/painting/qcolortransform_p.h
+++ b/src/gui/painting/qcolortransform_p.h
@@ -37,7 +37,6 @@ public:
void updateLutsIn() const;
void updateLutsOut() const;
bool isIdentity() const;
- bool isThreeComponentMatrix() const;
Q_GUI_EXPORT void prepare();
enum TransformFlag {
@@ -60,14 +59,11 @@ public:
void applyReturnGray(D *dst, const S *src, qsizetype count, TransformFlags flags) const;
private:
+ void pcsAdapt(QColorVector *buffer, qsizetype len) const;
template<typename S>
void applyConvertIn(const S *src, QColorVector *buffer, qsizetype len, TransformFlags flags) const;
template<typename D, typename S>
void applyConvertOut(D *dst, const S *src, QColorVector *buffer, qsizetype len, TransformFlags flags) const;
- template<typename D, typename S>
- void applyElementListTransform(D *dst, const S *src, qsizetype count, TransformFlags flags) const;
- template<typename D, typename S>
- void applyThreeComponentMatrix(D *dst, const S *src, qsizetype count, TransformFlags flags) const;
};
QT_END_NAMESPACE
diff --git a/src/gui/painting/qicc.cpp b/src/gui/painting/qicc.cpp
index c01fa433ea..a2786fbb8b 100644
--- a/src/gui/painting/qicc.cpp
+++ b/src/gui/painting/qicc.cpp
@@ -559,6 +559,8 @@ static bool parseXyzData(const QByteArray &data, const TagEntry &tagEntry, QColo
static quint32 parseTRC(const QByteArrayView &tagData, QColorTrc &gamma, QColorTransferTable::Type type = QColorTransferTable::TwoWay)
{
+ if (tagData.size() < 12)
+ return 0;
const GenericTagData trcData = qFromUnaligned<GenericTagData>(tagData.constData());
if (trcData.type == quint32(Tag::curv)) {
Q_STATIC_ASSERT(sizeof(CurvTagData) == 12);
@@ -1067,6 +1069,8 @@ static bool parseDesc(const QByteArray &data, const TagEntry &tagEntry, QString
// Either 'desc' (ICCv2) or 'mluc' (ICCv4)
if (tag.type == quint32(Tag::desc)) {
+ if (tagEntry.size < sizeof(DescTagData))
+ return false;
Q_STATIC_ASSERT(sizeof(DescTagData) == 12);
const DescTagData desc = qFromUnaligned<DescTagData>(data.constData() + tagEntry.offset);
const quint32 len = desc.asciiDescriptionLength;
@@ -1287,7 +1291,7 @@ bool fromIccProfile(const QByteArray &data, QColorSpace *colorSpace)
qCWarning(lcIcc) << "fromIccProfile: failed tag offset sanity 2";
return false;
}
- if (tagTable.size < 12) {
+ if (tagTable.size < 8) {
qCWarning(lcIcc) << "fromIccProfile: failed minimal tag size sanity";
return false;
}
diff --git a/src/gui/painting/qpagesize.h b/src/gui/painting/qpagesize.h
index b3629bb8d2..221863aad7 100644
--- a/src/gui/painting/qpagesize.h
+++ b/src/gui/painting/qpagesize.h
@@ -203,7 +203,9 @@ public:
void swap(QPageSize &other) noexcept { d.swap(other.d); }
+#if QT_GUI_REMOVED_SINCE(6, 4)
friend Q_GUI_EXPORT bool operator==(const QPageSize &lhs, const QPageSize &rhs);
+#endif
bool isEquivalentTo(const QPageSize &other) const;
bool isValid() const;
diff --git a/src/gui/platform/ios/qiosnativeinterface.cpp b/src/gui/platform/ios/qiosnativeinterface.cpp
new file mode 100644
index 0000000000..c942709e33
--- /dev/null
+++ b/src/gui/platform/ios/qiosnativeinterface.cpp
@@ -0,0 +1,26 @@
+// 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
+
+#include <QtGui/private/qguiapplication_p.h>
+
+QT_BEGIN_NAMESPACE
+
+using namespace QNativeInterface::Private;
+
+#if defined(Q_OS_VISIONOS)
+
+/*!
+ \class QNativeInterface::QVisionOSApplication
+ \since 6.8
+ \internal
+ \preliminary
+ \brief Native interface to QGuiApplication, to be retrieved from QPlatformIntegration.
+ \inmodule QtGui
+ \ingroup native-interfaces
+*/
+
+QT_DEFINE_NATIVE_INTERFACE(QVisionOSApplication);
+
+#endif // Q_OS_VISIONOS
+
+QT_END_NAMESPACE
diff --git a/src/gui/qt_cmdline.cmake b/src/gui/qt_cmdline.cmake
index 379cc417e7..446618ebc4 100644
--- a/src/gui/qt_cmdline.cmake
+++ b/src/gui/qt_cmdline.cmake
@@ -27,7 +27,8 @@ qt_commandline_option(opengl TYPE optionalString VALUES no yes desktop es2 dynam
qt_commandline_option(opengl-es-2 TYPE void NAME opengl VALUE es2)
qt_commandline_option(opengles3 TYPE boolean)
qt_commandline_option(openvg TYPE boolean)
-qt_commandline_option(qpa TYPE string NAME qpa_default_platform)
+qt_commandline_option(qpa TYPE string NAME qpa_platforms)
+qt_commandline_option(default-qpa TYPE string NAME qpa_default_platform)
qt_commandline_option(sm TYPE boolean NAME sessionmanager)
qt_commandline_option(tslib TYPE boolean)
qt_commandline_option(vulkan TYPE boolean)
diff --git a/src/gui/rhi/qrhigles2.cpp b/src/gui/rhi/qrhigles2.cpp
index 62830c291d..dcaa87a5ff 100644
--- a/src/gui/rhi/qrhigles2.cpp
+++ b/src/gui/rhi/qrhigles2.cpp
@@ -1096,6 +1096,8 @@ bool QRhiGles2::create(QRhi::Flags flags)
caps.glesMultiviewMultisampleRenderToTexture = false;
}
+ caps.unpackRowLength = !caps.gles || caps.ctxMajor >= 3;
+
nativeHandlesStruct.context = ctx;
contextLost = false;
@@ -1426,7 +1428,7 @@ bool QRhiGles2::isFeatureSupported(QRhi::Feature feature) const
case QRhi::PipelineCacheDataLoadSave:
return caps.programBinary;
case QRhi::ImageDataStride:
- return !caps.gles || caps.ctxMajor >= 3;
+ return caps.unpackRowLength;
case QRhi::RenderBufferImport:
return true;
case QRhi::ThreeDimensionalTextures:
@@ -2363,7 +2365,7 @@ void QRhiGles2::enqueueSubresUpload(QGles2Texture *texD, QGles2CommandBuffer *cb
dataStride = bytesPerLine;
cmd.args.subImage.rowStartAlign = (dataStride & 3) ? 1 : 4;
- cmd.args.subImage.rowLength = bytesPerPixel ? dataStride / bytesPerPixel : 0;
+ cmd.args.subImage.rowLength = caps.unpackRowLength ? (bytesPerPixel ? dataStride / bytesPerPixel : 0) : 0;
cmd.args.subImage.data = data;
};
@@ -2375,7 +2377,15 @@ void QRhiGles2::enqueueSubresUpload(QGles2Texture *texD, QGles2CommandBuffer *cb
const QPoint sp = subresDesc.sourceTopLeft();
if (!subresDesc.sourceSize().isEmpty())
size = subresDesc.sourceSize();
- img = img.copy(sp.x(), sp.y(), size.width(), size.height());
+
+ if (caps.unpackRowLength) {
+ cbD->retainImage(img);
+ // create a non-owning wrapper for the subimage
+ const uchar *data = img.constBits() + sp.y() * img.bytesPerLine() + sp.x() * (qMax(1, img.depth() / 8));
+ img = QImage(data, size.width(), size.height(), img.bytesPerLine(), img.format());
+ } else {
+ img = img.copy(sp.x(), sp.y(), size.width(), size.height());
+ }
}
setCmdByNotCompressedData(cbD->retainImage(img), size, img.bytesPerLine());
diff --git a/src/gui/rhi/qrhigles2_p.h b/src/gui/rhi/qrhigles2_p.h
index 4305186c02..4139579864 100644
--- a/src/gui/rhi/qrhigles2_p.h
+++ b/src/gui/rhi/qrhigles2_p.h
@@ -1014,7 +1014,10 @@ public:
halfAttributes(false),
multiView(false),
timestamps(false),
- objectLabel(false)
+ objectLabel(false),
+ glesMultisampleRenderToTexture(false),
+ glesMultiviewMultisampleRenderToTexture(false),
+ unpackRowLength(false)
{ }
int ctxMajor;
int ctxMinor;
@@ -1073,6 +1076,7 @@ public:
uint objectLabel : 1;
uint glesMultisampleRenderToTexture : 1;
uint glesMultiviewMultisampleRenderToTexture : 1;
+ uint unpackRowLength : 1;
} caps;
QGles2SwapChain *currentSwapChain = nullptr;
QSet<GLint> supportedCompressedFormats;
diff --git a/src/gui/rhi/qrhimetal.mm b/src/gui/rhi/qrhimetal.mm
index 1c7b397193..9fadfc15fa 100644
--- a/src/gui/rhi/qrhimetal.mm
+++ b/src/gui/rhi/qrhimetal.mm
@@ -2402,6 +2402,16 @@ QRhi::FrameOpResult QRhiMetal::endFrame(QRhiSwapChain *swapChain, QRhi::EndFrame
dispatch_semaphore_signal(swapChainD->d->sem[thisFrameSlot]);
}];
+#ifdef QRHI_METAL_COMMAND_BUFFERS_WITH_UNRETAINED_REFERENCES
+ // When Metal API validation diagnostics is enabled in Xcode the texture is
+ // released before the command buffer is done with it. Manually keep it alive
+ // to work around this.
+ id<MTLTexture> drawableTexture = [swapChainD->d->curDrawable.texture retain];
+ [swapChainD->cbWrapper.d->cb addCompletedHandler:^(id<MTLCommandBuffer>) {
+ [drawableTexture release];
+ }];
+#endif
+
const bool needsPresent = !flags.testFlag(QRhi::SkipPresent);
const bool presentsWithTransaction = swapChainD->d->layer.presentsWithTransaction;
if (!presentsWithTransaction && needsPresent) {
@@ -2579,7 +2589,6 @@ void QRhiMetal::enqueueSubresUpload(QMetalTexture *texD, void *mp, void *blitEnc
int w = img.width();
int h = img.height();
int bpl = img.bytesPerLine();
- int srcOffset = 0;
if (!subresDesc.sourceSize().isEmpty() || !subresDesc.sourceTopLeft().isNull()) {
const int sx = subresDesc.sourceTopLeft().x();
@@ -2588,10 +2597,12 @@ void QRhiMetal::enqueueSubresUpload(QMetalTexture *texD, void *mp, void *blitEnc
w = subresDesc.sourceSize().width();
h = subresDesc.sourceSize().height();
}
- if (img.depth() == 32) {
- memcpy(reinterpret_cast<char *>(mp) + *curOfs, img.constBits(), size_t(fullImageSizeBytes));
- srcOffset = sy * bpl + sx * 4;
- // bpl remains set to the original image's row stride
+ if (w == img.width()) {
+ const int bpc = qMax(1, img.depth() / 8);
+ Q_ASSERT(h * img.bytesPerLine() <= fullImageSizeBytes);
+ memcpy(reinterpret_cast<char *>(mp) + *curOfs,
+ img.constBits() + sy * img.bytesPerLine() + sx * bpc,
+ h * img.bytesPerLine());
} else {
img = img.copy(sx, sy, w, h);
bpl = img.bytesPerLine();
@@ -2603,7 +2614,7 @@ void QRhiMetal::enqueueSubresUpload(QMetalTexture *texD, void *mp, void *blitEnc
}
[blitEnc copyFromBuffer: texD->d->stagingBuf[currentFrameSlot]
- sourceOffset: NSUInteger(*curOfs + srcOffset)
+ sourceOffset: NSUInteger(*curOfs)
sourceBytesPerRow: NSUInteger(bpl)
sourceBytesPerImage: 0
sourceSize: MTLSizeMake(NSUInteger(w), NSUInteger(h), 1)
@@ -2971,9 +2982,11 @@ void QRhiMetal::beginPass(QRhiCommandBuffer *cb,
cbD->d->currentPassRpDesc.depthAttachment.loadAction = MTLLoadActionLoad;
cbD->d->currentPassRpDesc.stencilAttachment.loadAction = MTLLoadActionLoad;
}
+ int colorAttCount = 0;
for (auto it = rtTex->m_desc.cbeginColorAttachments(), itEnd = rtTex->m_desc.cendColorAttachments();
it != itEnd; ++it)
{
+ colorAttCount += 1;
if (it->texture()) {
QRHI_RES(QMetalTexture, it->texture())->lastActiveFrameSlot = currentFrameSlot;
if (it->multiViewCount() >= 2)
@@ -2986,8 +2999,12 @@ void QRhiMetal::beginPass(QRhiCommandBuffer *cb,
}
if (rtTex->m_desc.depthStencilBuffer())
QRHI_RES(QMetalRenderBuffer, rtTex->m_desc.depthStencilBuffer())->lastActiveFrameSlot = currentFrameSlot;
- if (rtTex->m_desc.depthTexture())
- QRHI_RES(QMetalTexture, rtTex->m_desc.depthTexture())->lastActiveFrameSlot = currentFrameSlot;
+ if (rtTex->m_desc.depthTexture()) {
+ QMetalTexture *depthTexture = QRHI_RES(QMetalTexture, rtTex->m_desc.depthTexture());
+ depthTexture->lastActiveFrameSlot = currentFrameSlot;
+ if (colorAttCount == 0 && depthTexture->arraySize() >= 2)
+ cbD->d->currentPassRpDesc.renderTargetArrayLength = NSUInteger(depthTexture->arraySize());
+ }
if (rtTex->m_desc.depthResolveTexture())
QRHI_RES(QMetalTexture, rtTex->m_desc.depthResolveTexture())->lastActiveFrameSlot = currentFrameSlot;
}
@@ -4840,7 +4857,7 @@ void QMetalGraphicsPipeline::setupAttachmentsInMetalRenderPassDescriptor(void *m
}
QRHI_RES_RHI(QRhiMetal);
- rpDesc.sampleCount = NSUInteger(rhiD->effectiveSampleCount(m_sampleCount));
+ rpDesc.rasterSampleCount = NSUInteger(rhiD->effectiveSampleCount(m_sampleCount));
}
void QMetalGraphicsPipeline::setupMetalDepthStencilDescriptor(void *metalDsDesc)
diff --git a/src/gui/rhi/qrhivulkan.cpp b/src/gui/rhi/qrhivulkan.cpp
index f0b51146cc..3dd3c57bd4 100644
--- a/src/gui/rhi/qrhivulkan.cpp
+++ b/src/gui/rhi/qrhivulkan.cpp
@@ -1598,8 +1598,8 @@ struct RenderPass2SetupHelper
#endif // VK_KHR_create_renderpass2
bool QRhiVulkan::createOffscreenRenderPass(QVkRenderPassDescriptor *rpD,
- const QRhiColorAttachment *firstColorAttachment,
- const QRhiColorAttachment *lastColorAttachment,
+ const QRhiColorAttachment *colorAttachmentsBegin,
+ const QRhiColorAttachment *colorAttachmentsEnd,
bool preserveColor,
bool preserveDs,
bool storeDs,
@@ -1610,7 +1610,7 @@ bool QRhiVulkan::createOffscreenRenderPass(QVkRenderPassDescriptor *rpD,
// attachment list layout is color (0-8), ds (0-1), resolve (0-8), ds resolve (0-1)
int multiViewCount = 0;
- for (auto it = firstColorAttachment; it != lastColorAttachment; ++it) {
+ for (auto it = colorAttachmentsBegin; it != colorAttachmentsEnd; ++it) {
QVkTexture *texD = QRHI_RES(QVkTexture, it->texture());
QVkRenderBuffer *rbD = QRHI_RES(QVkRenderBuffer, it->renderBuffer());
Q_ASSERT(texD || rbD);
@@ -1662,10 +1662,14 @@ bool QRhiVulkan::createOffscreenRenderPass(QVkRenderPassDescriptor *rpD,
attDesc.initialLayout = preserveDs ? VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL : VK_IMAGE_LAYOUT_UNDEFINED;
attDesc.finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
rpD->attDescs.append(attDesc);
+ if (depthTexture && depthTexture->arraySize() >= 2 && colorAttachmentsBegin == colorAttachmentsEnd) {
+ multiViewCount = depthTexture->arraySize();
+ rpD->multiViewCount = multiViewCount;
+ }
}
rpD->dsRef = { uint32_t(rpD->attDescs.size() - 1), VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL };
- for (auto it = firstColorAttachment; it != lastColorAttachment; ++it) {
+ for (auto it = colorAttachmentsBegin; it != colorAttachmentsEnd; ++it) {
if (it->resolveTexture()) {
QVkTexture *rtexD = QRHI_RES(QVkTexture, it->resolveTexture());
const VkFormat dstFormat = rtexD->vkformat;
@@ -3497,12 +3501,12 @@ void QRhiVulkan::prepareUploadSubres(QVkTexture *texD, int layer, int level,
const int sy = subresDesc.sourceTopLeft().y();
if (!subresDesc.sourceSize().isEmpty())
size = subresDesc.sourceSize();
- if (image.depth() == 32) {
- // The staging buffer will get the full image
- // regardless, just adjust the vk
- // buffer-to-image copy start offset.
- copyInfo.bufferOffset += VkDeviceSize(sy * image.bytesPerLine() + sx * 4);
- // bufferRowLength remains set to the original image's width
+
+ if (size.width() == image.width()) {
+ // No need to make a QImage copy here, can copy from the source
+ // QImage into staging directly.
+ src = image.constBits() + sy * image.bytesPerLine() + sx * bpc;
+ copySizeBytes = size.height() * image.bytesPerLine();
} else {
image = image.copy(sx, sy, size.width(), size.height());
src = image.constBits();
diff --git a/src/gui/rhi/qrhivulkan_p.h b/src/gui/rhi/qrhivulkan_p.h
index ad8687de5d..f23d8550f0 100644
--- a/src/gui/rhi/qrhivulkan_p.h
+++ b/src/gui/rhi/qrhivulkan_p.h
@@ -773,8 +773,8 @@ public:
VkSampleCountFlagBits samples,
VkFormat colorFormat);
bool createOffscreenRenderPass(QVkRenderPassDescriptor *rpD,
- const QRhiColorAttachment *firstColorAttachment,
- const QRhiColorAttachment *lastColorAttachment,
+ const QRhiColorAttachment *colorAttachmentsBegin,
+ const QRhiColorAttachment *colorAttachmentsEnd,
bool preserveColor,
bool preserveDs,
bool storeDs,
diff --git a/src/gui/text/qcssparser.cpp b/src/gui/text/qcssparser.cpp
index e5815ffce2..7886d9ba91 100644
--- a/src/gui/text/qcssparser.cpp
+++ b/src/gui/text/qcssparser.cpp
@@ -12,6 +12,7 @@
#include <qfontmetrics.h>
#include <qbrush.h>
#include <qimagereader.h>
+#include <qtextformat.h>
#include <algorithm>
@@ -35,6 +36,7 @@ struct QCssKnownValue
quint64 id;
};
+// This array is sorted alphabetically.
static const QCssKnownValue properties[NumProperties - 1] = {
{ "-qt-background-role", QtBackgroundRole },
{ "-qt-block-indent", QtBlockIndent },
@@ -46,6 +48,11 @@ static const QCssKnownValue properties[NumProperties - 1] = {
{ "-qt-list-number-suffix", QtListNumberSuffix },
{ "-qt-paragraph-type", QtParagraphType },
{ "-qt-stroke-color", QtStrokeColor },
+ { "-qt-stroke-dasharray", QtStrokeDashArray },
+ { "-qt-stroke-dashoffset", QtStrokeDashOffset },
+ { "-qt-stroke-linecap", QtStrokeLineCap },
+ { "-qt-stroke-linejoin", QtStrokeLineJoin },
+ { "-qt-stroke-miterlimit", QtStrokeMiterLimit },
{ "-qt-stroke-width", QtStrokeWidth },
{ "-qt-style-features", QtStyleFeatures },
{ "-qt-table-type", QtTableType },
@@ -159,6 +166,7 @@ static const QCssKnownValue values[NumKnownValues - 1] = {
{ "always", Value_Always },
{ "auto", Value_Auto },
{ "base", Value_Base },
+ { "beveljoin", Value_BevelJoin},
{ "bold", Value_Bold },
{ "bottom", Value_Bottom },
{ "bright-text", Value_BrightText },
@@ -175,6 +183,7 @@ static const QCssKnownValue values[NumKnownValues - 1] = {
{ "dot-dot-dash", Value_DotDotDash },
{ "dotted", Value_Dotted },
{ "double", Value_Double },
+ { "flatcap", Value_FlatCap},
{ "groove", Value_Groove },
{ "highlight", Value_Highlight },
{ "highlighted-text", Value_HighlightedText },
@@ -193,6 +202,7 @@ static const QCssKnownValue values[NumKnownValues - 1] = {
{ "mid", Value_Mid },
{ "middle", Value_Middle },
{ "midlight", Value_Midlight },
+ { "miterjoin", Value_MiterJoin},
{ "native", Value_Native },
{ "none", Value_None },
{ "normal", Value_Normal },
@@ -207,14 +217,18 @@ static const QCssKnownValue values[NumKnownValues - 1] = {
{ "pre-wrap", Value_PreWrap },
{ "ridge", Value_Ridge },
{ "right", Value_Right },
+ { "roundcap", Value_RoundCap},
+ { "roundjoin", Value_RoundJoin},
{ "selected", Value_Selected },
{ "shadow", Value_Shadow },
{ "small" , Value_Small },
{ "small-caps", Value_SmallCaps },
{ "solid", Value_Solid },
{ "square", Value_Square },
+ { "squarecap", Value_SquareCap},
{ "sub", Value_Sub },
{ "super", Value_Super },
+ { "svgmiterjoin", Value_SvgMiterJoin},
{ "text", Value_Text },
{ "top", Value_Top },
{ "transparent", Value_Transparent },
@@ -230,10 +244,10 @@ static const QCssKnownValue values[NumKnownValues - 1] = {
};
//Map id to strings as they appears in the 'values' array above
-static const short indexOfId[NumKnownValues] = { 0, 41, 48, 42, 49, 50, 55, 35, 26, 71, 72, 25, 43, 5, 64, 48,
- 29, 59, 60, 27, 52, 62, 6, 10, 39, 56, 19, 13, 17, 18, 20, 21, 51, 24, 46, 68, 37, 3, 2, 40, 63, 16,
- 11, 58, 14, 32, 65, 33, 66, 56, 67, 34, 70, 8, 28, 38, 12, 36, 61, 7, 9, 4, 69, 54, 22, 23, 30, 31,
- 1, 15, 0, 53, 45, 44 };
+static const short indexOfId[NumKnownValues] = { 0, 44, 51, 45, 52, 53, 60, 37, 28, 78, 79, 27, 46, 6, 71, 50,
+ 31, 65, 66, 29, 55, 69, 7, 11, 42, 62, 20, 14, 18, 19, 21, 23, 54, 26, 49, 75, 39, 3, 2, 43, 70, 17, 12,
+ 63, 15, 34, 72, 35, 73, 61, 74, 36, 64, 22, 56, 41, 5, 57, 67, 77, 9, 30, 40, 13, 38, 68, 8, 10, 4, 76,
+ 59, 24, 25, 32, 33, 1, 16, 0, 58, 48, 47 };
QString Value::toString() const
{
@@ -396,6 +410,8 @@ LengthData ValueExtractor::lengthValue(const Value& v)
if (data.unit != LengthData::None)
s.chop(2);
+ else if (v.type == Value::Percentage)
+ data.unit = LengthData::Percent;
data.number = s.toDouble();
return data;
@@ -409,6 +425,15 @@ static int lengthValueFromData(const LengthData& data, const QFont& f)
return qRound(qBound(double(INT_MIN) + 0.1, scale * data.number, double(INT_MAX)));
}
+QTextLength ValueExtractor::textLength(const Declaration &decl)
+{
+ const LengthData data = lengthValue(decl.d->values.at(0));
+ if (data.unit == LengthData::Percent)
+ return QTextLength(QTextLength::PercentageLength, data.number);
+
+ return QTextLength(QTextLength::FixedLength, lengthValueFromData(data, f));
+}
+
int ValueExtractor::lengthValue(const Declaration &decl)
{
if (decl.d->parsed.isValid())
@@ -1823,6 +1848,35 @@ bool Declaration::borderCollapseValue() const
return d->values.at(0).toString() == "collapse"_L1;
}
+QList<qreal> Declaration::dashArray() const
+{
+ if (d->propertyId != Property::QtStrokeDashArray || d->values.empty())
+ return QList<qreal>();
+
+ bool isValid = true;
+ QList<qreal> dashes;
+ for (int i = 0; i < d->values.size(); i++) {
+ Value v = d->values[i];
+ // Separators must be at odd indices and Numbers at even indices.
+ bool isValidSeparator = (i & 1) && v.type == Value::TermOperatorComma;
+ bool isValidNumber = !(i & 1) && v.type == Value::Number;
+ if (!isValidNumber && !isValidSeparator) {
+ isValid = false;
+ break;
+ } else if (isValidNumber) {
+ bool ok;
+ dashes.append(v.variant.toReal(&ok));
+ if (!ok) {
+ isValid = false;
+ break;
+ }
+ }
+ }
+
+ isValid &= !(dashes.size() & 1);
+ return isValid ? dashes : QList<qreal>();
+}
+
QIcon Declaration::iconValue() const
{
if (d->parsed.isValid())
diff --git a/src/gui/text/qcssparser_p.h b/src/gui/text/qcssparser_p.h
index c1cfb1ac9b..ba4a611df3 100644
--- a/src/gui/text/qcssparser_p.h
+++ b/src/gui/text/qcssparser_p.h
@@ -169,6 +169,11 @@ enum Property {
QtAccent,
QtStrokeWidth,
QtStrokeColor,
+ QtStrokeLineCap,
+ QtStrokeLineJoin,
+ QtStrokeMiterLimit,
+ QtStrokeDashArray,
+ QtStrokeDashOffset,
QtForeground,
NumProperties
};
@@ -226,6 +231,13 @@ enum KnownValue {
Value_SmallCaps,
Value_Uppercase,
Value_Lowercase,
+ Value_SquareCap,
+ Value_FlatCap,
+ Value_RoundCap,
+ Value_MiterJoin,
+ Value_BevelJoin,
+ Value_RoundJoin,
+ Value_SvgMiterJoin,
/* keep these in same order as QPalette::ColorRole */
Value_FirstColorRole,
@@ -392,7 +404,7 @@ QT_CSS_DECLARE_TYPEINFO(BackgroundData, Q_RELOCATABLE_TYPE)
struct LengthData {
qreal number;
- enum { None, Px, Ex, Em } unit;
+ enum { None, Px, Ex, Em, Percent } unit;
};
QT_CSS_DECLARE_TYPEINFO(LengthData, Q_PRIMITIVE_TYPE)
@@ -451,6 +463,8 @@ struct Q_GUI_EXPORT Declaration
void borderImageValue(QString *image, int *cuts, TileMode *h, TileMode *v) const;
bool borderCollapseValue() const;
+
+ QList<qreal> dashArray() const;
};
QT_CSS_DECLARE_TYPEINFO(Declaration, Q_RELOCATABLE_TYPE)
@@ -835,6 +849,7 @@ struct Q_GUI_EXPORT ValueExtractor
bool extractIcon(QIcon *icon, QSize *size);
void lengthValues(const Declaration &decl, int *m);
+ QTextLength textLength(const Declaration &decl);
private:
void extractFont();
diff --git a/src/gui/text/qfont.cpp b/src/gui/text/qfont.cpp
index f3b861957e..f3a35a4269 100644
--- a/src/gui/text/qfont.cpp
+++ b/src/gui/text/qfont.cpp
@@ -2435,9 +2435,7 @@ std::optional<QFont::Tag> QFont::Tag::fromString(QAnyStringView view) noexcept
By default, no variable axes are set.
- \note In order to use variable axes on Windows, the application has to run with either the
- FreeType or DirectWrite font databases. See the documentation for
- QGuiApplication::QGuiApplication() for more information on how to select these technologies.
+ \note On Windows, variable axes are not supported if the optional GDI font backend is in use.
\sa unsetVariableAxis
*/
diff --git a/src/gui/text/qtextdocument.cpp b/src/gui/text/qtextdocument.cpp
index 15a313e13d..31cb3a526a 100644
--- a/src/gui/text/qtextdocument.cpp
+++ b/src/gui/text/qtextdocument.cpp
@@ -2757,6 +2757,51 @@ bool QTextHtmlExporter::emitCharFormatStyle(const QTextCharFormat &format)
html += " -qt-stroke-width:"_L1;
html += QString::number(outlinePen.widthF());
html += "px;"_L1;
+
+ html += " -qt-stroke-linecap:"_L1;
+ if (outlinePen.capStyle() == Qt::SquareCap)
+ html += "squarecap;"_L1;
+ else if (outlinePen.capStyle() == Qt::FlatCap)
+ html += "flatcap;"_L1;
+ else if (outlinePen.capStyle() == Qt::RoundCap)
+ html += "roundcap;"_L1;
+
+ html += " -qt-stroke-linejoin:"_L1;
+ if (outlinePen.joinStyle() == Qt::MiterJoin)
+ html += "miterjoin;"_L1;
+ else if (outlinePen.joinStyle() == Qt::SvgMiterJoin)
+ html += "svgmiterjoin;"_L1;
+ else if (outlinePen.joinStyle() == Qt::BevelJoin)
+ html += "beveljoin;"_L1;
+ else if (outlinePen.joinStyle() == Qt::RoundJoin)
+ html += "roundjoin;"_L1;
+
+ if (outlinePen.joinStyle() == Qt::MiterJoin ||
+ outlinePen.joinStyle() == Qt::SvgMiterJoin) {
+ html += " -qt-stroke-miterlimit:"_L1;
+ html += QString::number(outlinePen.miterLimit());
+ html += u';';
+ }
+
+ if (outlinePen.style() == Qt::CustomDashLine && !outlinePen.dashPattern().empty()) {
+ html += " -qt-stroke-dasharray:"_L1;
+ QString dashArrayString;
+ QList<qreal> dashes = outlinePen.dashPattern();
+
+ for (int i = 0; i < dashes.length() - 1; i++) {
+ qreal dash = dashes[i];
+ dashArrayString += QString::number(dash) + u',';
+ }
+
+ dashArrayString += QString::number(dashes.last());
+ html += dashArrayString;
+ html += u';';
+
+ html += " -qt-stroke-dashoffset:"_L1;
+ html += QString::number(outlinePen.dashOffset());
+ html += u';';
+ }
+
attributesEmitted = true;
}
@@ -2944,6 +2989,17 @@ void QTextHtmlExporter::emitFragment(const QTextFragment &fragment)
html += "<img"_L1;
+ QString maxWidthCss;
+
+ if (imgFmt.hasProperty(QTextFormat::ImageMaxWidth)) {
+ auto length = imgFmt.lengthProperty(QTextFormat::ImageMaxWidth);
+ maxWidthCss += "max-width:"_L1;
+ if (length.type() == QTextLength::PercentageLength)
+ maxWidthCss += QString::number(length.rawValue()) + "%;"_L1;
+ else if (length.type() == QTextLength::FixedLength)
+ maxWidthCss += QString::number(length.rawValue()) + "px;"_L1;
+ }
+
if (imgFmt.hasProperty(QTextFormat::ImageName))
emitAttribute("src", imgFmt.name());
@@ -2960,9 +3016,11 @@ void QTextHtmlExporter::emitFragment(const QTextFragment &fragment)
emitAttribute("height", QString::number(imgFmt.height()));
if (imgFmt.verticalAlignment() == QTextCharFormat::AlignMiddle)
- html += " style=\"vertical-align: middle;\""_L1;
+ html += " style=\"vertical-align: middle;"_L1 + maxWidthCss + u'\"';
else if (imgFmt.verticalAlignment() == QTextCharFormat::AlignTop)
- html += " style=\"vertical-align: top;\""_L1;
+ html += " style=\"vertical-align: top;"_L1 + maxWidthCss + u'\"';
+ else if (!maxWidthCss.isEmpty())
+ html += " style=\""_L1 + maxWidthCss + u'\"';
if (QTextFrame *imageFrame = qobject_cast<QTextFrame *>(doc->objectForFormat(imgFmt)))
emitFloatStyle(imageFrame->frameFormat().position());
diff --git a/src/gui/text/qtextengine.cpp b/src/gui/text/qtextengine.cpp
index a18157ab9b..08512bead5 100644
--- a/src/gui/text/qtextengine.cpp
+++ b/src/gui/text/qtextengine.cpp
@@ -1396,7 +1396,9 @@ void QTextEngine::shapeText(int item) const
QFontEngine *fontEngine = this->fontEngine(si, &si.ascent, &si.descent, &si.leading);
+#if QT_CONFIG(harfbuzz)
bool kerningEnabled;
+#endif
bool letterSpacingIsAbsolute;
bool shapingEnabled = false;
QHash<QFont::Tag, quint32> features;
@@ -1405,8 +1407,8 @@ void QTextEngine::shapeText(int item) const
if (useRawFont) {
QTextCharFormat f = format(&si);
QFont font = f.font();
- kerningEnabled = font.kerning();
# if QT_CONFIG(harfbuzz)
+ kerningEnabled = font.kerning();
shapingEnabled = QFontEngine::scriptRequiresOpenType(QChar::Script(si.analysis.script))
|| (font.styleStrategy() & QFont::PreferNoShaping) == 0;
# endif
@@ -1418,8 +1420,8 @@ void QTextEngine::shapeText(int item) const
#endif
{
QFont font = this->font(si);
- kerningEnabled = font.d->kerning;
#if QT_CONFIG(harfbuzz)
+ kerningEnabled = font.d->kerning;
shapingEnabled = QFontEngine::scriptRequiresOpenType(QChar::Script(si.analysis.script))
|| (font.d->request.styleStrategy & QFont::PreferNoShaping) == 0;
#endif
diff --git a/src/gui/text/qtextformat.cpp b/src/gui/text/qtextformat.cpp
index 3e4bacf78c..dacef70812 100644
--- a/src/gui/text/qtextformat.cpp
+++ b/src/gui/text/qtextformat.cpp
@@ -745,6 +745,7 @@ Q_GUI_EXPORT QDataStream &operator>>(QDataStream &stream, QTextTableCellFormat &
\value ImageWidth
\value ImageHeight
\value ImageQuality
+ \value ImageMaxWidth This enum value has been added in Qt 6.8.
Selection properties
@@ -3156,7 +3157,8 @@ QTextTableFormat::QTextTableFormat()
: QTextFrameFormat()
{
setObjectType(TableObject);
- setCellSpacing(2);
+ setCellPadding(4);
+ setBorderCollapse(true);
setBorder(1);
}
@@ -3425,7 +3427,7 @@ QTextImageFormat::QTextImageFormat(const QTextFormat &fmt)
Sets the \a width of the rectangle occupied by the image.
- \sa width(), setHeight()
+ \sa width(), setHeight(), maximumWidth()
*/
@@ -3437,6 +3439,24 @@ QTextImageFormat::QTextImageFormat(const QTextFormat &fmt)
\sa height(), setWidth()
*/
+/*!
+ \fn void QTextImageFormat::setMaximumWidth(QTextLength maximumWidth)
+
+ Sets the \a maximumWidth of the rectangle occupied by the image. This
+ can be an absolute number or a percentage of the available document size.
+
+ \sa width(), setHeight()
+*/
+
+
+/*!
+ \fn QTextLength QTextImageFormat::maximumWidth() const
+
+ Returns the maximum width of the rectangle occupied by the image.
+
+ \sa width(), setMaximumWidth()
+*/
+
/*!
\fn void QTextImageFormat::setHeight(qreal height)
diff --git a/src/gui/text/qtextformat.h b/src/gui/text/qtextformat.h
index c009d328cb..2fa86ed0d1 100644
--- a/src/gui/text/qtextformat.h
+++ b/src/gui/text/qtextformat.h
@@ -241,6 +241,7 @@ public:
ImageWidth = 0x5010,
ImageHeight = 0x5011,
ImageQuality = 0x5014,
+ ImageMaxWidth = 0x5015,
// internal
/*
@@ -796,6 +797,10 @@ public:
inline qreal width() const
{ return doubleProperty(ImageWidth); }
+ inline void setMaximumWidth(QTextLength maxWidth);
+ inline QTextLength maximumWidth() const
+ { return lengthProperty(ImageMaxWidth); }
+
inline void setHeight(qreal height);
inline qreal height() const
{ return doubleProperty(ImageHeight); }
@@ -823,6 +828,9 @@ inline void QTextImageFormat::setName(const QString &aname)
inline void QTextImageFormat::setWidth(qreal awidth)
{ setProperty(ImageWidth, awidth); }
+inline void QTextImageFormat::setMaximumWidth(QTextLength maxWidth)
+{ setProperty(ImageMaxWidth, maxWidth); }
+
inline void QTextImageFormat::setHeight(qreal aheight)
{ setProperty(ImageHeight, aheight); }
diff --git a/src/gui/text/qtexthtmlparser.cpp b/src/gui/text/qtexthtmlparser.cpp
index ee92cece78..54c291b82e 100644
--- a/src/gui/text/qtexthtmlparser.cpp
+++ b/src/gui/text/qtexthtmlparser.cpp
@@ -1422,12 +1422,74 @@ void QTextHtmlParserNode::applyCssDeclarations(const QList<QCss::Declaration> &d
}
break;
}
+ case QCss::QtStrokeLineCap:
+ {
+ QPen pen = charFormat.textOutline();
+ switch (identifier) {
+ case QCss::Value_SquareCap: pen.setCapStyle(Qt::SquareCap); break;
+ case QCss::Value_FlatCap: pen.setCapStyle(Qt::FlatCap); break;
+ case QCss::Value_RoundCap: pen.setCapStyle(Qt::RoundCap); break;
+ default: break;
+ }
+ charFormat.setTextOutline(pen);
+ break;
+ }
+ case QCss::QtStrokeLineJoin:
+ {
+ QPen pen = charFormat.textOutline();
+ switch (identifier) {
+ case QCss::Value_MiterJoin: pen.setJoinStyle(Qt::MiterJoin); break;
+ case QCss::Value_BevelJoin: pen.setJoinStyle(Qt::BevelJoin); break;
+ case QCss::Value_RoundJoin: pen.setJoinStyle(Qt::RoundJoin); break;
+ case QCss::Value_SvgMiterJoin: pen.setJoinStyle(Qt::SvgMiterJoin); break;
+ default: break;
+ }
+ charFormat.setTextOutline(pen);
+ break;
+ }
+ case QCss::QtStrokeMiterLimit:
+ {
+ qreal miterLimit;
+ if (decl.realValue(&miterLimit)) {
+ QPen pen = charFormat.textOutline();
+ pen.setMiterLimit(miterLimit);
+ charFormat.setTextOutline(pen);
+ }
+ break;
+ }
+ case QCss::QtStrokeDashArray:
+ {
+ QList<qreal> dashes = decl.dashArray();
+ if (!dashes.empty()) {
+ QPen pen = charFormat.textOutline();
+ pen.setDashPattern(dashes);
+ charFormat.setTextOutline(pen);
+ }
+ break;
+ }
+ case QCss::QtStrokeDashOffset:
+ {
+ qreal dashOffset;
+ if (decl.realValue(&dashOffset)) {
+ QPen pen = charFormat.textOutline();
+ pen.setDashOffset(dashOffset);
+ charFormat.setTextOutline(pen);
+ }
+ break;
+ }
case QCss::QtForeground:
{
QBrush brush = decl.brushValue();
charFormat.setForeground(brush);
break;
}
+ case QCss::MaximumWidth:
+ if (id == Html_img) {
+ auto imageFormat = charFormat.toImageFormat();
+ imageFormat.setMaximumWidth(extractor.textLength(decl));
+ charFormat = imageFormat;
+ }
+ break;
default: break;
}
}
diff --git a/src/gui/text/qtextimagehandler.cpp b/src/gui/text/qtextimagehandler.cpp
index 5c56c30711..920e6c689c 100644
--- a/src/gui/text/qtextimagehandler.cpp
+++ b/src/gui/text/qtextimagehandler.cpp
@@ -12,6 +12,7 @@
#include <private/qtextengine_p.h>
#include <qpalette.h>
#include <qthread.h>
+#include <limits>
QT_BEGIN_NAMESPACE
@@ -72,21 +73,40 @@ template<typename T>
static QSize getSize(QTextDocument *doc, const QTextImageFormat &format)
{
const bool hasWidth = format.hasProperty(QTextFormat::ImageWidth);
- const int width = qRound(format.width());
+ int width = qRound(format.width());
const bool hasHeight = format.hasProperty(QTextFormat::ImageHeight);
const int height = qRound(format.height());
+ const bool hasMaxWidth = format.hasProperty(QTextFormat::ImageMaxWidth);
+ const auto maxWidth = format.maximumWidth();
+
+ int effectiveMaxWidth = std::numeric_limits<int>::max();
+ if (hasMaxWidth) {
+ if (maxWidth.type() == QTextLength::PercentageLength)
+ effectiveMaxWidth = (doc->pageSize().width() - 2 * doc->documentMargin()) * maxWidth.value(100) / 100;
+ else
+ effectiveMaxWidth = maxWidth.rawValue();
+
+ width = qMin(effectiveMaxWidth, width);
+ }
+
T source;
QSize size(width, height);
if (!hasWidth || !hasHeight) {
source = getAs<T>(doc, format);
- const QSizeF sourceSize = source.deviceIndependentSize();
+ QSizeF sourceSize = source.deviceIndependentSize();
+
+ if (sourceSize.width() > effectiveMaxWidth) {
+ // image is bigger than effectiveMaxWidth, scale it down
+ sourceSize.setHeight(effectiveMaxWidth * (sourceSize.height() / qreal(sourceSize.width())));
+ sourceSize.setWidth(effectiveMaxWidth);
+ }
if (!hasWidth) {
if (!hasHeight)
size.setWidth(sourceSize.width());
else
- size.setWidth(qRound(height * (sourceSize.width() / qreal(sourceSize.height()))));
+ size.setWidth(qMin(effectiveMaxWidth, qRound(height * (sourceSize.width() / qreal(sourceSize.height())))));
}
if (!hasHeight) {
if (!hasWidth)
diff --git a/src/gui/util/qdesktopservices.cpp b/src/gui/util/qdesktopservices.cpp
index 4a12f6db6f..4d98faf398 100644
--- a/src/gui/util/qdesktopservices.cpp
+++ b/src/gui/util/qdesktopservices.cpp
@@ -18,8 +18,6 @@
#include <qpa/qplatformintegration.h>
#include <qdir.h>
-#include <QtCore/private/qlocking_p.h>
-
QT_BEGIN_NAMESPACE
class QOpenUrlHandlerRegistry
@@ -36,36 +34,10 @@ public:
};
typedef QHash<QString, Handler> HandlerHash;
HandlerHash handlers;
-
-#if QT_VERSION < QT_VERSION_CHECK(6, 6, 0)
- QObject context;
-
- void handlerDestroyed(QObject *handler);
-#endif
-
};
Q_GLOBAL_STATIC(QOpenUrlHandlerRegistry, handlerRegistry)
-#if QT_VERSION < QT_VERSION_CHECK(6, 6, 0)
-void QOpenUrlHandlerRegistry::handlerDestroyed(QObject *handler)
-{
- const auto lock = qt_scoped_lock(mutex);
- HandlerHash::Iterator it = handlers.begin();
- while (it != handlers.end()) {
- if (it->receiver == handler) {
- it = handlers.erase(it);
- qWarning("Please call QDesktopServices::unsetUrlHandler() before destroying a "
- "registered URL handler object.\n"
- "Support for destroying a registered URL handler object is deprecated, "
- "and will be removed in Qt 6.6.");
- } else {
- ++it;
- }
- }
-}
-#endif
-
/*!
\class QDesktopServices
\brief The QDesktopServices class provides methods for accessing common desktop services.
@@ -238,10 +210,11 @@ bool QDesktopServices::openUrl(const QUrl &url)
the destruction of the handler object does not overlap with concurrent
invocations of openUrl() using it.
- \section1 iOS
+ \section1 iOS and \macos
- To use this function for receiving data from other apps on iOS you also need to
- add the custom scheme to the \c CFBundleURLSchemes list in your Info.plist file:
+ To use this function for receiving data from other apps on iOS/\macos
+ you also need to add the custom scheme to the \c CFBundleURLSchemes
+ list in your Info.plist file:
\snippet code/src_gui_util_qdesktopservices.cpp 4
@@ -256,7 +229,7 @@ bool QDesktopServices::openUrl(const QUrl &url)
\snippet code/src_gui_util_qdesktopservices.cpp 7
- iOS will search for /.well-known/apple-app-site-association on your domain,
+ iOS/\macos will search for /.well-known/apple-app-site-association on your domain,
when the application is installed. If you want to listen to
\c{https://your.domain.com/help?topic=ABCDEF} you need to provide the following
content there:
@@ -306,11 +279,6 @@ void QDesktopServices::setUrlHandler(const QString &scheme, QObject *receiver, c
h.receiver = receiver;
h.name = method;
registry->handlers.insert(scheme.toLower(), h);
-#if QT_VERSION < QT_VERSION_CHECK(6, 6, 0)
- QObject::connect(receiver, &QObject::destroyed, &registry->context,
- [registry](QObject *obj) { registry->handlerDestroyed(obj); },
- Qt::DirectConnection);
-#endif
}
/*!
diff --git a/src/network/CMakeLists.txt b/src/network/CMakeLists.txt
index 3f226a77dc..e977400245 100644
--- a/src/network/CMakeLists.txt
+++ b/src/network/CMakeLists.txt
@@ -22,6 +22,7 @@ qt_internal_add_module(Network
access/qnetworkfile.cpp access/qnetworkfile_p.h
access/qhttpheaders.cpp access/qhttpheaders.h
access/qhttpheaderparser.cpp access/qhttpheaderparser_p.h
+ access/qhttpheadershelper.cpp access/qhttpheadershelper_p.h
access/qnetworkreply.cpp access/qnetworkreply.h access/qnetworkreply_p.h
access/qnetworkreplydataimpl.cpp access/qnetworkreplydataimpl_p.h
access/qnetworkreplyfileimpl.cpp access/qnetworkreplyfileimpl_p.h
diff --git a/src/network/access/http2/http2protocol_p.h b/src/network/access/http2/http2protocol_p.h
index fb5ff199c5..f0f18d1dd5 100644
--- a/src/network/access/http2/http2protocol_p.h
+++ b/src/network/access/http2/http2protocol_p.h
@@ -117,7 +117,7 @@ const qint32 maxSessionReceiveWindowSize((quint32(1) << 31) - 1);
// Presumably, we never use up to 100 streams so let it be 10 simultaneous:
const qint32 qtDefaultStreamReceiveWindowSize = maxSessionReceiveWindowSize / 10;
-struct Frame configurationToSettingsFrame(const QHttp2Configuration &configuration);
+struct Frame Q_AUTOTEST_EXPORT configurationToSettingsFrame(const QHttp2Configuration &configuration);
QByteArray settingsFrameToBase64(const Frame &settingsFrame);
void appendProtocolUpgradeHeaders(const QHttp2Configuration &configuration, QHttpNetworkRequest *request);
std::vector<uchar> assemble_hpack_block(const std::vector<Frame> &frames);
diff --git a/src/network/access/qabstractnetworkcache.cpp b/src/network/access/qabstractnetworkcache.cpp
index c8b940d801..3cd55d46fa 100644
--- a/src/network/access/qabstractnetworkcache.cpp
+++ b/src/network/access/qabstractnetworkcache.cpp
@@ -3,6 +3,8 @@
#include "qabstractnetworkcache.h"
#include "qabstractnetworkcache_p.h"
+#include "qnetworkrequest_p.h"
+#include "qhttpheadershelper_p.h"
#include <qdatastream.h>
#include <qdatetime.h>
@@ -28,14 +30,14 @@ public:
url == other.url
&& lastModified == other.lastModified
&& expirationDate == other.expirationDate
- && headers == other.headers
- && saveToDisk == other.saveToDisk;
+ && saveToDisk == other.saveToDisk
+ && QHttpHeadersHelper::compareStrict(headers, other.headers);
}
QUrl url;
QDateTime lastModified;
QDateTime expirationDate;
- QNetworkCacheMetaData::RawHeaderList headers;
+ QHttpHeaders headers;
QNetworkCacheMetaData::AttributesMap attributes;
bool saveToDisk;
@@ -207,21 +209,45 @@ void QNetworkCacheMetaData::setUrl(const QUrl &url)
Returns a list of all raw headers that are set in this meta data.
The list is in the same order that the headers were set.
- \sa setRawHeaders()
+ \sa setRawHeaders(), headers()
*/
QNetworkCacheMetaData::RawHeaderList QNetworkCacheMetaData::rawHeaders() const
{
- return d->headers;
+ return QNetworkHeadersPrivate::fromHttpToRaw(d->headers);
}
/*!
Sets the raw headers to \a list.
- \sa rawHeaders()
+ \sa rawHeaders(), setHeaders()
*/
void QNetworkCacheMetaData::setRawHeaders(const RawHeaderList &list)
{
- d->headers = list;
+ d->headers = QNetworkHeadersPrivate::fromRawToHttp(list);
+}
+
+/*!
+ \since 6.8
+
+ Returns headers in form of QHttpHeaders that are set in this meta data.
+
+ \sa setHeaders()
+*/
+QHttpHeaders QNetworkCacheMetaData::headers() const
+{
+ return d->headers;
+}
+
+/*!
+ \since 6.8
+
+ Sets the headers of this network cache meta data to \a headers.
+
+ \sa headers()
+*/
+void QNetworkCacheMetaData::setHeaders(const QHttpHeaders &headers)
+{
+ d->headers = headers;
}
/*!
@@ -367,7 +393,8 @@ void QNetworkCacheMetaDataPrivate::load(QDataStream &in, QNetworkCacheMetaData &
in >> p->lastModified;
in >> p->saveToDisk;
in >> p->attributes;
- in >> p->headers;
+ QNetworkCacheMetaData::RawHeaderList list; in >> list;
+ metaData.setRawHeaders(list);
}
/*!
diff --git a/src/network/access/qabstractnetworkcache.h b/src/network/access/qabstractnetworkcache.h
index c70dcf737c..b12fd4f863 100644
--- a/src/network/access/qabstractnetworkcache.h
+++ b/src/network/access/qabstractnetworkcache.h
@@ -48,6 +48,9 @@ public:
RawHeaderList rawHeaders() const;
void setRawHeaders(const RawHeaderList &headers);
+ QHttpHeaders headers() const;
+ void setHeaders(const QHttpHeaders &headers);
+
QDateTime lastModified() const;
void setLastModified(const QDateTime &dateTime);
diff --git a/src/network/access/qabstractprotocolhandler_p.h b/src/network/access/qabstractprotocolhandler_p.h
index ad82aae66e..da5eaeeb74 100644
--- a/src/network/access/qabstractprotocolhandler_p.h
+++ b/src/network/access/qabstractprotocolhandler_p.h
@@ -23,7 +23,7 @@ QT_BEGIN_NAMESPACE
class QHttpNetworkConnectionChannel;
class QHttpNetworkReply;
-class QAbstractSocket;
+class QIODevice;
class QHttpNetworkConnection;
class QAbstractProtocolHandler {
@@ -39,7 +39,7 @@ public:
protected:
QHttpNetworkConnectionChannel *m_channel;
QHttpNetworkReply *m_reply;
- QAbstractSocket *m_socket;
+ QIODevice *m_socket;
QHttpNetworkConnection *m_connection;
};
diff --git a/src/network/access/qhttpheaders.h b/src/network/access/qhttpheaders.h
index 97dc415e55..260df1421b 100644
--- a/src/network/access/qhttpheaders.h
+++ b/src/network/access/qhttpheaders.h
@@ -4,7 +4,8 @@
#ifndef QHTTPHEADERS_H
#define QHTTPHEADERS_H
-#include <QtNetwork/qnetworkrequest.h>
+#include <QtNetwork/qtnetworkglobal.h>
+#include <QtCore/qmetaobject.h>
#include <QtCore/qshareddata.h>
#include <QtCore/qcontainerfwd.h>
diff --git a/src/network/access/qhttpheadershelper.cpp b/src/network/access/qhttpheadershelper.cpp
new file mode 100644
index 0000000000..d3cc9e439f
--- /dev/null
+++ b/src/network/access/qhttpheadershelper.cpp
@@ -0,0 +1,25 @@
+// 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
+
+#include "qhttpheadershelper_p.h"
+
+#include <QtNetwork/qhttpheaders.h>
+
+QT_BEGIN_NAMESPACE
+
+bool QHttpHeadersHelper::compareStrict(const QHttpHeaders &left, const QHttpHeaders &right)
+{
+ if (left.size() != right.size())
+ return false;
+
+ for (qsizetype i = 0; i < left.size(); ++i) {
+ if (left.nameAt(i) != right.nameAt(i))
+ return false;
+ if (left.valueAt(i) != right.valueAt(i))
+ return false;
+ }
+
+ return true;
+}
+
+QT_END_NAMESPACE
diff --git a/src/network/access/qhttpheadershelper_p.h b/src/network/access/qhttpheadershelper_p.h
new file mode 100644
index 0000000000..d1e38a1a8e
--- /dev/null
+++ b/src/network/access/qhttpheadershelper_p.h
@@ -0,0 +1,30 @@
+// 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
+
+#ifndef QHTTPHEADERSHELPER_H
+#define QHTTPHEADERSHELPER_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of the Network Access API. This header file may change from
+// version to version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtNetwork/private/qtnetworkglobal_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class QHttpHeaders;
+
+namespace QHttpHeadersHelper {
+ Q_NETWORK_EXPORT bool compareStrict(const QHttpHeaders &left, const QHttpHeaders &right);
+};
+
+QT_END_NAMESPACE
+
+#endif // QHTTPHEADERSHELPER_H
diff --git a/src/network/access/qhttpmultipart.cpp b/src/network/access/qhttpmultipart.cpp
index 6d81f1b957..a695969f00 100644
--- a/src/network/access/qhttpmultipart.cpp
+++ b/src/network/access/qhttpmultipart.cpp
@@ -386,9 +386,12 @@ void QHttpPartPrivate::checkHeaderCreated() const
{
if (!headerCreated) {
// copied from QHttpNetworkRequestPrivate::header() and adapted
- const auto fields = allRawHeaders();
- for (const auto &[name, value] : fields)
- header += name + ": " + value + "\r\n";
+ const auto h = headers();
+ for (qsizetype i = 0; i < h.size(); ++i) {
+ const auto name = h.nameAt(i);
+ header += QByteArrayView(name.data(), name.size()) + ": " + h.valueAt(i) + "\r\n";
+ }
+
header += "\r\n";
headerCreated = true;
}
@@ -511,6 +514,48 @@ qint64 QHttpMultiPartIODevice::writeData(const char *data, qint64 maxSize)
return -1;
}
+#ifndef QT_NO_DEBUG_STREAM
+
+/*!
+ \fn QDebug QHttpPart::operator<<(QDebug debug, const QHttpPart &part)
+
+ Writes the \a part into the \a debug object for debugging purposes.
+ Unless a device is set, the size of the body is shown.
+
+ \sa {Debugging Techniques}
+ \since 6.8
+*/
+
+QDebug operator<<(QDebug debug, const QHttpPart &part)
+{
+ const QDebugStateSaver saver(debug);
+ debug.resetFormat().nospace().noquote();
+
+ debug << "QHttpPart(headers = ["
+ << part.d->cookedHeaders
+ << "], http headers = ["
+ << part.d->httpHeaders
+ << "],";
+
+ if (part.d->bodyDevice) {
+ debug << " bodydevice = ["
+ << part.d->bodyDevice
+ << ", is open: "
+ << part.d->bodyDevice->isOpen()
+ << "]";
+ } else {
+ debug << " size of body = "
+ << part.d->body.size()
+ << " bytes";
+ }
+
+ debug << ")";
+
+ return debug;
+}
+
+#endif // QT_NO_DEBUG_STREAM
+
QT_END_NAMESPACE
diff --git a/src/network/access/qhttpmultipart.h b/src/network/access/qhttpmultipart.h
index 26e5fafdf2..9732bbd99d 100644
--- a/src/network/access/qhttpmultipart.h
+++ b/src/network/access/qhttpmultipart.h
@@ -19,6 +19,7 @@ QT_BEGIN_NAMESPACE
class QHttpPartPrivate;
class QHttpMultiPart;
+class QDebug;
class Q_NETWORK_EXPORT QHttpPart
{
@@ -45,6 +46,9 @@ private:
QSharedDataPointer<QHttpPartPrivate> d;
friend class QHttpMultiPartIODevice;
+#ifndef QT_NO_DEBUG_STREAM
+ friend Q_NETWORK_EXPORT QDebug operator<<(QDebug debug, const QHttpPart &httpPart);
+#endif
};
Q_DECLARE_SHARED(QHttpPart)
diff --git a/src/network/access/qhttpmultipart_p.h b/src/network/access/qhttpmultipart_p.h
index d485fcf5cd..7a12ce8424 100644
--- a/src/network/access/qhttpmultipart_p.h
+++ b/src/network/access/qhttpmultipart_p.h
@@ -18,6 +18,7 @@
#include <QtNetwork/private/qtnetworkglobal_p.h>
#include "QtCore/qshareddata.h"
#include "qnetworkrequest_p.h" // for deriving QHttpPartPrivate from QNetworkHeadersPrivate
+#include "qhttpheadershelper_p.h"
#include "private/qobject_p.h"
#ifndef Q_OS_WASM
@@ -47,8 +48,10 @@ public:
inline bool operator==(const QHttpPartPrivate &other) const
{
- return rawHeaders == other.rawHeaders && body == other.body &&
- bodyDevice == other.bodyDevice && readPointer == other.readPointer;
+ return QHttpHeadersHelper::compareStrict(httpHeaders, other.httpHeaders)
+ && body == other.body
+ && bodyDevice == other.bodyDevice
+ && readPointer == other.readPointer;
}
void setBodyDevice(QIODevice *device) {
diff --git a/src/network/access/qhttpnetworkconnection.cpp b/src/network/access/qhttpnetworkconnection.cpp
index 1897380e0e..501a410ae7 100644
--- a/src/network/access/qhttpnetworkconnection.cpp
+++ b/src/network/access/qhttpnetworkconnection.cpp
@@ -13,6 +13,7 @@
#include <qauthenticator.h>
#include <qcoreapplication.h>
#include <private/qdecompresshelper_p.h>
+#include <private/qsocketabstraction_p.h>
#include <qbuffer.h>
#include <qpair.h>
@@ -51,10 +52,11 @@ static int getPreferredActiveChannelCount(QHttpNetworkConnection::ConnectionType
QHttpNetworkConnectionPrivate::QHttpNetworkConnectionPrivate(
quint16 connectionCount, const QString &hostName, quint16 port, bool encrypt,
- QHttpNetworkConnection::ConnectionType type)
+ bool isLocalSocket, QHttpNetworkConnection::ConnectionType type)
: hostName(hostName),
port(port),
encrypt(encrypt),
+ isLocalSocket(isLocalSocket),
activeChannelCount(getPreferredActiveChannelCount(type, connectionCount)),
channelCount(connectionCount),
channels(new QHttpNetworkConnectionChannel[channelCount]),
@@ -63,6 +65,8 @@ QHttpNetworkConnectionPrivate::QHttpNetworkConnectionPrivate(
#endif
connectionType(type)
{
+ if (isLocalSocket) // Don't try to do host lookup for local sockets
+ networkLayerState = IPv4;
// We allocate all 6 channels even if it's an HTTP/2-enabled
// connection: in case the protocol negotiation via NPN/ALPN fails,
// we will have normally working HTTP/1.1.
@@ -101,13 +105,18 @@ void QHttpNetworkConnectionPrivate::pauseConnection()
// Disable all socket notifiers
for (int i = 0; i < activeChannelCount; i++) {
- if (channels[i].socket) {
+ if (auto *absSocket = qobject_cast<QAbstractSocket *>(channels[i].socket)) {
#ifndef QT_NO_SSL
if (encrypt)
- QSslSocketPrivate::pauseSocketNotifiers(static_cast<QSslSocket*>(channels[i].socket));
+ QSslSocketPrivate::pauseSocketNotifiers(static_cast<QSslSocket*>(absSocket));
else
#endif
- QAbstractSocketPrivate::pauseSocketNotifiers(channels[i].socket);
+ QAbstractSocketPrivate::pauseSocketNotifiers(absSocket);
+ } else if (qobject_cast<QLocalSocket *>(channels[i].socket)) {
+ // @todo how would we do this?
+#if 0 // @todo Enable this when there is a debug category for this
+ qDebug() << "Should pause socket but there is no way to do it for local sockets";
+#endif
}
}
}
@@ -117,17 +126,21 @@ void QHttpNetworkConnectionPrivate::resumeConnection()
state = RunningState;
// Enable all socket notifiers
for (int i = 0; i < activeChannelCount; i++) {
- if (channels[i].socket) {
+ if (auto *absSocket = qobject_cast<QAbstractSocket *>(channels[i].socket)) {
#ifndef QT_NO_SSL
if (encrypt)
- QSslSocketPrivate::resumeSocketNotifiers(static_cast<QSslSocket*>(channels[i].socket));
+ QSslSocketPrivate::resumeSocketNotifiers(static_cast<QSslSocket*>(absSocket));
else
#endif
- QAbstractSocketPrivate::resumeSocketNotifiers(channels[i].socket);
+ QAbstractSocketPrivate::resumeSocketNotifiers(absSocket);
// Resume pending upload if needed
if (channels[i].state == QHttpNetworkConnectionChannel::WritingState)
QMetaObject::invokeMethod(&channels[i], "_q_uploadDataReadyRead", Qt::QueuedConnection);
+ } else if (qobject_cast<QLocalSocket *>(channels[i].socket)) {
+#if 0 // @todo Enable this when there is a debug category for this
+ qDebug() << "Should resume socket but there is no way to do it for local sockets";
+#endif
}
}
@@ -319,7 +332,7 @@ void QHttpNetworkConnectionPrivate::prepareRequest(HttpMessagePair &messagePair)
-void QHttpNetworkConnectionPrivate::emitReplyError(QAbstractSocket *socket,
+void QHttpNetworkConnectionPrivate::emitReplyError(QIODevice *socket,
QHttpNetworkReply *reply,
QNetworkReply::NetworkError errorCode)
{
@@ -384,7 +397,7 @@ void QHttpNetworkConnectionPrivate::copyCredentials(int fromChannel, QAuthentica
// handles the authentication for one channel and eventually re-starts the other channels
-bool QHttpNetworkConnectionPrivate::handleAuthenticateChallenge(QAbstractSocket *socket, QHttpNetworkReply *reply,
+bool QHttpNetworkConnectionPrivate::handleAuthenticateChallenge(QIODevice *socket, QHttpNetworkReply *reply,
bool isProxy, bool &resend)
{
Q_ASSERT(socket);
@@ -486,7 +499,7 @@ bool QHttpNetworkConnectionPrivate::handleAuthenticateChallenge(QAbstractSocket
}
// Used by the HTTP1 code-path
-QUrl QHttpNetworkConnectionPrivate::parseRedirectResponse(QAbstractSocket *socket,
+QUrl QHttpNetworkConnectionPrivate::parseRedirectResponse(QIODevice *socket,
QHttpNetworkReply *reply)
{
ParseRedirectResult result = parseRedirectResponse(reply);
@@ -523,7 +536,9 @@ QHttpNetworkConnectionPrivate::parseRedirectResponse(QHttpNetworkReply *reply)
// Check redirect url protocol
const QUrl priorUrl(reply->request().url());
- if (redirectUrl.scheme() == "http"_L1 || redirectUrl.scheme() == "https"_L1) {
+ const QString targetUrlScheme = redirectUrl.scheme();
+ if (targetUrlScheme == "http"_L1 || targetUrlScheme == "https"_L1
+ || targetUrlScheme.startsWith("unix"_L1)) {
switch (reply->request().redirectPolicy()) {
case QNetworkRequest::NoLessSafeRedirectPolicy:
// Here we could handle https->http redirects as InsecureProtocolError.
@@ -534,7 +549,7 @@ QHttpNetworkConnectionPrivate::parseRedirectResponse(QHttpNetworkReply *reply)
break;
case QNetworkRequest::SameOriginRedirectPolicy:
if (priorUrl.host() != redirectUrl.host()
- || priorUrl.scheme() != redirectUrl.scheme()
+ || priorUrl.scheme() != targetUrlScheme
|| priorUrl.port() != redirectUrl.port()) {
return {{}, QNetworkReply::InsecureRedirectError};
}
@@ -740,7 +755,7 @@ QHttpNetworkReply* QHttpNetworkConnectionPrivate::predictNextRequestsReply() con
}
// this is called from _q_startNextRequest and when a request has been sent down a socket from the channel
-void QHttpNetworkConnectionPrivate::fillPipeline(QAbstractSocket *socket)
+void QHttpNetworkConnectionPrivate::fillPipeline(QIODevice *socket)
{
// return fast if there is nothing to pipeline
if (highPriorityQueue.isEmpty() && lowPriorityQueue.isEmpty())
@@ -768,7 +783,7 @@ void QHttpNetworkConnectionPrivate::fillPipeline(QAbstractSocket *socket)
return;
// check if socket is connected
- if (socket->state() != QAbstractSocket::ConnectedState)
+ if (QSocketAbstraction::socketState(socket) != QAbstractSocket::ConnectedState)
return;
// check for resendCurrent
@@ -862,16 +877,15 @@ bool QHttpNetworkConnectionPrivate::fillPipeline(QList<HttpMessagePair> &queue,
}
-QString QHttpNetworkConnectionPrivate::errorDetail(QNetworkReply::NetworkError errorCode, QAbstractSocket *socket, const QString &extraDetail)
+QString QHttpNetworkConnectionPrivate::errorDetail(QNetworkReply::NetworkError errorCode, QIODevice *socket, const QString &extraDetail)
{
QString errorString;
switch (errorCode) {
- case QNetworkReply::HostNotFoundError:
- if (socket)
- errorString = QCoreApplication::translate("QHttp", "Host %1 not found").arg(socket->peerName());
- else
- errorString = QCoreApplication::translate("QHttp", "Host %1 not found").arg(hostName);
+ case QNetworkReply::HostNotFoundError: {
+ const QString peerName = socket ? QSocketAbstraction::socketPeerName(socket) : hostName;
+ errorString = QCoreApplication::translate("QHttp", "Host %1 not found").arg(peerName);
break;
+ }
case QNetworkReply::ConnectionRefusedError:
errorString = QCoreApplication::translate("QHttp", "Connection refused");
break;
@@ -1024,7 +1038,7 @@ void QHttpNetworkConnectionPrivate::_q_startNextRequest()
for (int i = 0; i < activeChannelCount; ++i) {
if (channels[i].resendCurrent && (channels[i].state != QHttpNetworkConnectionChannel::ClosingState)) {
if (!channels[i].socket
- || channels[i].socket->state() == QAbstractSocket::UnconnectedState) {
+ || QSocketAbstraction::socketState(channels[i].socket) == QAbstractSocket::UnconnectedState) {
if (!channels[i].ensureConnection())
continue;
}
@@ -1048,7 +1062,9 @@ void QHttpNetworkConnectionPrivate::_q_startNextRequest()
// try to get a free AND connected socket
for (int i = 0; i < activeChannelCount; ++i) {
if (channels[i].socket) {
- if (!channels[i].reply && !channels[i].isSocketBusy() && channels[i].socket->state() == QAbstractSocket::ConnectedState) {
+ if (!channels[i].reply && !channels[i].isSocketBusy()
+ && QSocketAbstraction::socketState(channels[i].socket)
+ == QAbstractSocket::ConnectedState) {
if (dequeueRequest(channels[i].socket))
channels[i].sendRequest();
}
@@ -1068,7 +1084,8 @@ void QHttpNetworkConnectionPrivate::_q_startNextRequest()
else if (networkLayerState == IPv6)
channels[0].networkLayerPreference = QAbstractSocket::IPv6Protocol;
channels[0].ensureConnection();
- if (channels[0].socket && channels[0].socket->state() == QAbstractSocket::ConnectedState
+ if (auto *s = channels[0].socket; s
+ && QSocketAbstraction::socketState(s) == QAbstractSocket::ConnectedState
&& !channels[0].pendingEncrypt) {
if (channels[0].h2RequestsToSend.size()) {
channels[0].sendRequest();
@@ -1095,9 +1112,13 @@ void QHttpNetworkConnectionPrivate::_q_startNextRequest()
// return fast if there is nothing to pipeline
if (highPriorityQueue.isEmpty() && lowPriorityQueue.isEmpty())
return;
- for (int i = 0; i < activeChannelCount; i++)
- if (channels[i].socket && channels[i].socket->state() == QAbstractSocket::ConnectedState)
+ for (int i = 0; i < activeChannelCount; i++) {
+ if (channels[i].socket
+ && QSocketAbstraction::socketState(channels[i].socket)
+ == QAbstractSocket::ConnectedState) {
fillPipeline(channels[i].socket);
+ }
+ }
// If there is not already any connected channels we need to connect a new one.
// We do not pair the channel with the request until we know if it is
@@ -1122,15 +1143,16 @@ void QHttpNetworkConnectionPrivate::_q_startNextRequest()
if (!channels[i].socket)
continue;
- if ((channels[i].socket->state() == QAbstractSocket::ConnectingState)
- || (channels[i].socket->state() == QAbstractSocket::HostLookupState)
+ using State = QAbstractSocket::SocketState;
+ if ((QSocketAbstraction::socketState(channels[i].socket) == State::ConnectingState)
+ || (QSocketAbstraction::socketState(channels[i].socket) == State::HostLookupState)
|| channels[i].pendingEncrypt) { // pendingEncrypt == "EncryptingState"
neededOpenChannels--;
continue;
}
if (!channels[i].reply && !channels[i].isSocketBusy()
- && (channels[i].socket->state() == QAbstractSocket::UnconnectedState)) {
+ && (QSocketAbstraction::socketState(channels[i].socket) == State::UnconnectedState)) {
channelsToConnect.push_back(i);
neededOpenChannels--;
}
@@ -1329,9 +1351,9 @@ void QHttpNetworkConnectionPrivate::_q_connectDelayedChannel()
}
QHttpNetworkConnection::QHttpNetworkConnection(quint16 connectionCount, const QString &hostName,
- quint16 port, bool encrypt, QObject *parent,
+ quint16 port, bool encrypt, bool isLocalSocket, QObject *parent,
QHttpNetworkConnection::ConnectionType connectionType)
- : QObject(*(new QHttpNetworkConnectionPrivate(connectionCount, hostName, port, encrypt,
+ : QObject(*(new QHttpNetworkConnectionPrivate(connectionCount, hostName, port, encrypt, isLocalSocket,
connectionType)), parent)
{
Q_D(QHttpNetworkConnection);
diff --git a/src/network/access/qhttpnetworkconnection_p.h b/src/network/access/qhttpnetworkconnection_p.h
index 36234a24ba..5e4bce5eb0 100644
--- a/src/network/access/qhttpnetworkconnection_p.h
+++ b/src/network/access/qhttpnetworkconnection_p.h
@@ -64,7 +64,8 @@ public:
};
QHttpNetworkConnection(quint16 channelCount, const QString &hostName, quint16 port = 80,
- bool encrypt = false, QObject *parent = nullptr,
+ bool encrypt = false, bool isLocalSocket = false,
+ QObject *parent = nullptr,
ConnectionType connectionType = ConnectionTypeHTTP);
~QHttpNetworkConnection();
@@ -155,7 +156,8 @@ public:
};
QHttpNetworkConnectionPrivate(quint16 connectionCount, const QString &hostName, quint16 port,
- bool encrypt, QHttpNetworkConnection::ConnectionType type);
+ bool encrypt, bool isLocalSocket,
+ QHttpNetworkConnection::ConnectionType type);
~QHttpNetworkConnectionPrivate();
void init();
@@ -177,7 +179,7 @@ public:
QHttpNetworkRequest predictNextRequest() const;
QHttpNetworkReply* predictNextRequestsReply() const;
- void fillPipeline(QAbstractSocket *socket);
+ void fillPipeline(QIODevice *socket);
bool fillPipeline(QList<HttpMessagePair> &queue, QHttpNetworkConnectionChannel &channel);
// read more HTTP body after the next event loop spin
@@ -197,7 +199,7 @@ public:
void createAuthorization(QIODevice *socket, QHttpNetworkRequest &request);
- QString errorDetail(QNetworkReply::NetworkError errorCode, QAbstractSocket *socket,
+ QString errorDetail(QNetworkReply::NetworkError errorCode, QIODevice *socket,
const QString &extraDetail = QString());
void removeReply(QHttpNetworkReply *reply);
@@ -205,6 +207,7 @@ public:
QString hostName;
quint16 port;
bool encrypt;
+ bool isLocalSocket;
bool delayIpv4 = true;
// Number of channels we are trying to use at the moment:
@@ -219,15 +222,15 @@ public:
qint64 uncompressedBytesAvailableNextBlock(const QHttpNetworkReply &reply) const;
- void emitReplyError(QAbstractSocket *socket, QHttpNetworkReply *reply, QNetworkReply::NetworkError errorCode);
- bool handleAuthenticateChallenge(QAbstractSocket *socket, QHttpNetworkReply *reply, bool isProxy, bool &resend);
+ void emitReplyError(QIODevice *socket, QHttpNetworkReply *reply, QNetworkReply::NetworkError errorCode);
+ bool handleAuthenticateChallenge(QIODevice *socket, QHttpNetworkReply *reply, bool isProxy, bool &resend);
struct ParseRedirectResult {
QUrl redirectUrl;
QNetworkReply::NetworkError errorCode;
};
static ParseRedirectResult parseRedirectResponse(QHttpNetworkReply *reply);
// Used by the HTTP1 code-path
- QUrl parseRedirectResponse(QAbstractSocket *socket, QHttpNetworkReply *reply);
+ QUrl parseRedirectResponse(QIODevice *socket, QHttpNetworkReply *reply);
#ifndef QT_NO_NETWORKPROXY
QNetworkProxy networkProxy;
diff --git a/src/network/access/qhttpnetworkconnectionchannel.cpp b/src/network/access/qhttpnetworkconnectionchannel.cpp
index 6766989690..8688e4b8d7 100644
--- a/src/network/access/qhttpnetworkconnectionchannel.cpp
+++ b/src/network/access/qhttpnetworkconnectionchannel.cpp
@@ -12,6 +12,7 @@
#include <private/qhttp2protocolhandler_p.h>
#include <private/qhttpprotocolhandler_p.h>
#include <private/http2protocol_p.h>
+#include <private/qsocketabstraction_p.h>
#ifndef QT_NO_SSL
# include <private/qsslsocket_p.h>
@@ -78,6 +79,8 @@ void QHttpNetworkConnectionChannel::init()
#ifndef QT_NO_SSL
if (connection->d_func()->encrypt)
socket = new QSslSocket;
+ else if (connection->d_func()->isLocalSocket)
+ socket = new QLocalSocket;
else
socket = new QTcpSocket;
#else
@@ -85,58 +88,75 @@ void QHttpNetworkConnectionChannel::init()
#endif
#ifndef QT_NO_NETWORKPROXY
// Set by QNAM anyway, but let's be safe here
- socket->setProxy(QNetworkProxy::NoProxy);
+ if (auto s = qobject_cast<QAbstractSocket *>(socket))
+ s->setProxy(QNetworkProxy::NoProxy);
#endif
// After some back and forth in all the last years, this is now a DirectConnection because otherwise
// the state inside the *Socket classes gets messed up, also in conjunction with the socket notifiers
// which behave slightly differently on Windows vs Linux
- QObject::connect(socket, SIGNAL(bytesWritten(qint64)),
- this, SLOT(_q_bytesWritten(qint64)),
+ QObject::connect(socket, &QIODevice::bytesWritten,
+ this, &QHttpNetworkConnectionChannel::_q_bytesWritten,
Qt::DirectConnection);
- QObject::connect(socket, SIGNAL(connected()),
- this, SLOT(_q_connected()),
- Qt::DirectConnection);
- QObject::connect(socket, SIGNAL(readyRead()),
- this, SLOT(_q_readyRead()),
+ QObject::connect(socket, &QIODevice::readyRead,
+ this, &QHttpNetworkConnectionChannel::_q_readyRead,
Qt::DirectConnection);
- // The disconnected() and error() signals may already come
- // while calling connectToHost().
- // In case of a cached hostname or an IP this
- // will then emit a signal to the user of QNetworkReply
- // but cannot be caught because the user did not have a chance yet
- // to connect to QNetworkReply's signals.
- qRegisterMetaType<QAbstractSocket::SocketError>();
- QObject::connect(socket, SIGNAL(disconnected()),
- this, SLOT(_q_disconnected()),
- Qt::DirectConnection);
- QObject::connect(socket, SIGNAL(errorOccurred(QAbstractSocket::SocketError)),
- this, SLOT(_q_error(QAbstractSocket::SocketError)),
- Qt::DirectConnection);
+
+ QSocketAbstraction::visit([this](auto *socket){
+ using SocketType = std::remove_pointer_t<decltype(socket)>;
+ QObject::connect(socket, &SocketType::connected,
+ this, &QHttpNetworkConnectionChannel::_q_connected,
+ Qt::DirectConnection);
+
+ // The disconnected() and error() signals may already come
+ // while calling connectToHost().
+ // In case of a cached hostname or an IP this
+ // will then emit a signal to the user of QNetworkReply
+ // but cannot be caught because the user did not have a chance yet
+ // to connect to QNetworkReply's signals.
+ QObject::connect(socket, &SocketType::disconnected,
+ this, &QHttpNetworkConnectionChannel::_q_disconnected,
+ Qt::DirectConnection);
+ if constexpr (std::is_same_v<SocketType, QAbstractSocket>) {
+ QObject::connect(socket, &QAbstractSocket::errorOccurred,
+ this, &QHttpNetworkConnectionChannel::_q_error,
+ Qt::DirectConnection);
+ } else if constexpr (std::is_same_v<SocketType, QLocalSocket>) {
+ auto convertAndForward = [this](QLocalSocket::LocalSocketError error) {
+ _q_error(static_cast<QAbstractSocket::SocketError>(error));
+ };
+ QObject::connect(socket, &SocketType::errorOccurred,
+ this, std::move(convertAndForward),
+ Qt::DirectConnection);
+ }
+ }, socket);
+
#ifndef QT_NO_NETWORKPROXY
- QObject::connect(socket, SIGNAL(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*)),
- this, SLOT(_q_proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*)),
- Qt::DirectConnection);
+ if (auto *s = qobject_cast<QAbstractSocket *>(socket)) {
+ QObject::connect(s, &QAbstractSocket::proxyAuthenticationRequired,
+ this, &QHttpNetworkConnectionChannel::_q_proxyAuthenticationRequired,
+ Qt::DirectConnection);
+ }
#endif
#ifndef QT_NO_SSL
QSslSocket *sslSocket = qobject_cast<QSslSocket*>(socket);
if (sslSocket) {
// won't be a sslSocket if encrypt is false
- QObject::connect(sslSocket, SIGNAL(encrypted()),
- this, SLOT(_q_encrypted()),
+ QObject::connect(sslSocket, &QSslSocket::encrypted,
+ this, &QHttpNetworkConnectionChannel::_q_encrypted,
Qt::DirectConnection);
- QObject::connect(sslSocket, SIGNAL(sslErrors(QList<QSslError>)),
- this, SLOT(_q_sslErrors(QList<QSslError>)),
+ QObject::connect(sslSocket, &QSslSocket::sslErrors,
+ this, &QHttpNetworkConnectionChannel::_q_sslErrors,
Qt::DirectConnection);
- QObject::connect(sslSocket, SIGNAL(preSharedKeyAuthenticationRequired(QSslPreSharedKeyAuthenticator*)),
- this, SLOT(_q_preSharedKeyAuthenticationRequired(QSslPreSharedKeyAuthenticator*)),
+ QObject::connect(sslSocket, &QSslSocket::preSharedKeyAuthenticationRequired,
+ this, &QHttpNetworkConnectionChannel::_q_preSharedKeyAuthenticationRequired,
Qt::DirectConnection);
- QObject::connect(sslSocket, SIGNAL(encryptedBytesWritten(qint64)),
- this, SLOT(_q_encryptedBytesWritten(qint64)),
+ QObject::connect(sslSocket, &QSslSocket::encryptedBytesWritten,
+ this, &QHttpNetworkConnectionChannel::_q_encryptedBytesWritten,
Qt::DirectConnection);
if (ignoreAllSslErrors)
@@ -156,8 +176,10 @@ void QHttpNetworkConnectionChannel::init()
#endif
#ifndef QT_NO_NETWORKPROXY
- if (proxy.type() != QNetworkProxy::NoProxy)
- socket->setProxy(proxy);
+ if (auto *s = qobject_cast<QAbstractSocket *>(socket);
+ s && proxy.type() != QNetworkProxy::NoProxy) {
+ s->setProxy(proxy);
+ }
#endif
isInitialized = true;
}
@@ -170,7 +192,7 @@ void QHttpNetworkConnectionChannel::close()
if (!socket)
state = QHttpNetworkConnectionChannel::IdleState;
- else if (socket->state() == QAbstractSocket::UnconnectedState)
+ else if (QSocketAbstraction::socketState(socket) == QAbstractSocket::UnconnectedState)
state = QHttpNetworkConnectionChannel::IdleState;
else
state = QHttpNetworkConnectionChannel::ClosingState;
@@ -190,7 +212,7 @@ void QHttpNetworkConnectionChannel::abort()
{
if (!socket)
state = QHttpNetworkConnectionChannel::IdleState;
- else if (socket->state() == QAbstractSocket::UnconnectedState)
+ else if (QSocketAbstraction::socketState(socket) == QAbstractSocket::UnconnectedState)
state = QHttpNetworkConnectionChannel::IdleState;
else
state = QHttpNetworkConnectionChannel::ClosingState;
@@ -201,7 +223,10 @@ void QHttpNetworkConnectionChannel::abort()
if (socket) {
// socket can be 0 since the host lookup is done from qhttpnetworkconnection.cpp while
// there is no socket yet.
- socket->abort();
+ auto callAbort = [](auto *s) {
+ s->abort();
+ };
+ QSocketAbstraction::visit(callAbort, socket);
}
}
@@ -268,7 +293,7 @@ bool QHttpNetworkConnectionChannel::ensureConnection()
if (!isInitialized)
init();
- QAbstractSocket::SocketState socketState = socket->state();
+ QAbstractSocket::SocketState socketState = QSocketAbstraction::socketState(socket);
// resend this request after we receive the disconnected signal
// If !socket->isOpen() then we have already called close() on the socket, but there was still a
@@ -335,7 +360,8 @@ bool QHttpNetworkConnectionChannel::ensureConnection()
connectHost = connection->d_func()->networkProxy.hostName();
connectPort = connection->d_func()->networkProxy.port();
}
- if (socket->proxy().type() == QNetworkProxy::HttpProxy) {
+ if (auto *abSocket = qobject_cast<QAbstractSocket *>(socket);
+ abSocket && abSocket->proxy().type() == QNetworkProxy::HttpProxy) {
// Make user-agent field available to HTTP proxy socket engine (QTBUG-17223)
QByteArray value;
// ensureConnection is called before any request has been assigned, but can also be
@@ -353,9 +379,11 @@ bool QHttpNetworkConnectionChannel::ensureConnection()
value = request.headerField("user-agent");
}
if (!value.isEmpty()) {
- QNetworkProxy proxy(socket->proxy());
- proxy.setRawHeader("User-Agent", value); //detaches
- socket->setProxy(proxy);
+ QNetworkProxy proxy(abSocket->proxy());
+ auto h = proxy.headers();
+ h.replaceOrAppend(QHttpHeaders::WellKnownHeader::UserAgent, value);
+ proxy.setHeaders(std::move(h));
+ abSocket->setProxy(proxy);
}
}
#endif
@@ -378,7 +406,7 @@ bool QHttpNetworkConnectionChannel::ensureConnection()
// limit the socket read buffer size. we will read everything into
// the QHttpNetworkReply anyway, so let's grow only that and not
// here and there.
- socket->setReadBufferSize(64*1024);
+ sslSocket->setReadBufferSize(64*1024);
#else
// Need to dequeue the request so that we can emit the error.
if (!reply)
@@ -392,17 +420,24 @@ bool QHttpNetworkConnectionChannel::ensureConnection()
&& connection->cacheProxy().type() == QNetworkProxy::NoProxy
&& connection->transparentProxy().type() == QNetworkProxy::NoProxy) {
#endif
- socket->connectToHost(connectHost, connectPort, QIODevice::ReadWrite | QIODevice::Unbuffered, networkLayerPreference);
- // For an Unbuffered QTcpSocket, the read buffer size has a special meaning.
- socket->setReadBufferSize(1*1024);
+ if (auto *s = qobject_cast<QAbstractSocket *>(socket)) {
+ s->connectToHost(connectHost, connectPort,
+ QIODevice::ReadWrite | QIODevice::Unbuffered,
+ networkLayerPreference);
+ // For an Unbuffered QTcpSocket, the read buffer size has a special meaning.
+ s->setReadBufferSize(1 * 1024);
+ } else if (auto *s = qobject_cast<QLocalSocket *>(socket)) {
+ s->connectToServer(connectHost);
+ }
#ifndef QT_NO_NETWORKPROXY
} else {
- socket->connectToHost(connectHost, connectPort, QIODevice::ReadWrite, networkLayerPreference);
-
+ auto *s = qobject_cast<QAbstractSocket *>(socket);
+ Q_ASSERT(s);
// limit the socket read buffer size. we will read everything into
// the QHttpNetworkReply anyway, so let's grow only that and not
// here and there.
- socket->setReadBufferSize(64*1024);
+ s->connectToHost(connectHost, connectPort, QIODevice::ReadWrite, networkLayerPreference);
+ s->setReadBufferSize(64 * 1024);
}
#endif
}
@@ -505,7 +540,7 @@ void QHttpNetworkConnectionChannel::allDone()
// move next from pipeline to current request
if (!alreadyPipelinedRequests.isEmpty()) {
- if (resendCurrent || connectionCloseEnabled || socket->state() != QAbstractSocket::ConnectedState) {
+ if (resendCurrent || connectionCloseEnabled || QSocketAbstraction::socketState(socket) != QAbstractSocket::ConnectedState) {
// move the pipelined ones back to the main queue
requeueCurrentlyPipelinedRequests();
close();
@@ -536,7 +571,7 @@ void QHttpNetworkConnectionChannel::allDone()
QMetaObject::invokeMethod(connection, "_q_startNextRequest", Qt::QueuedConnection);
} else if (alreadyPipelinedRequests.isEmpty()) {
if (connectionCloseEnabled)
- if (socket->state() != QAbstractSocket::UnconnectedState)
+ if (QSocketAbstraction::socketState(socket) != QAbstractSocket::UnconnectedState)
close();
if (qobject_cast<QHttpNetworkConnection*>(connection))
QMetaObject::invokeMethod(connection, "_q_startNextRequest", Qt::QueuedConnection);
@@ -554,7 +589,7 @@ void QHttpNetworkConnectionChannel::detectPipeliningSupport()
// check for not having connection close
&& (!reply->d_func()->isConnectionCloseEnabled())
// check if it is still connected
- && (socket->state() == QAbstractSocket::ConnectedState)
+ && (QSocketAbstraction::socketState(socket) == QAbstractSocket::ConnectedState)
// check for broken servers in server reply header
// this is adapted from http://mxr.mozilla.org/firefox/ident?i=SupportsPipelining
&& (serverHeaderField = reply->headerField("Server"), !serverHeaderField.contains("Microsoft-IIS/4."))
@@ -677,8 +712,8 @@ bool QHttpNetworkConnectionChannel::resetUploadData()
void QHttpNetworkConnectionChannel::setProxy(const QNetworkProxy &networkProxy)
{
- if (socket)
- socket->setProxy(networkProxy);
+ if (auto *s = qobject_cast<QAbstractSocket *>(socket))
+ s->setProxy(networkProxy);
proxy = networkProxy;
}
@@ -839,7 +874,7 @@ void QHttpNetworkConnectionChannel::_q_disconnected()
}
-void QHttpNetworkConnectionChannel::_q_connected()
+void QHttpNetworkConnectionChannel::_q_connected_abstract_socket(QAbstractSocket *absSocket)
{
// For the Happy Eyeballs we need to check if this is the first channel to connect.
if (connection->d_func()->networkLayerState == QHttpNetworkConnectionPrivate::HostLookupPending || connection->d_func()->networkLayerState == QHttpNetworkConnectionPrivate::IPv4or6) {
@@ -850,7 +885,7 @@ void QHttpNetworkConnectionChannel::_q_connected()
else if (networkLayerPreference == QAbstractSocket::IPv6Protocol)
connection->d_func()->networkLayerState = QHttpNetworkConnectionPrivate::IPv6;
else {
- if (socket->peerAddress().protocol() == QAbstractSocket::IPv4Protocol)
+ if (absSocket->peerAddress().protocol() == QAbstractSocket::IPv4Protocol)
connection->d_func()->networkLayerState = QHttpNetworkConnectionPrivate::IPv4;
else
connection->d_func()->networkLayerState = QHttpNetworkConnectionPrivate::IPv6;
@@ -873,7 +908,7 @@ void QHttpNetworkConnectionChannel::_q_connected()
}
// improve performance since we get the request sent by the kernel ASAP
- //socket->setSocketOption(QAbstractSocket::LowDelayOption, 1);
+ //absSocket->setSocketOption(QAbstractSocket::LowDelayOption, 1);
// We have this commented out now. It did not have the effect we wanted. If we want to
// do this properly, Qt has to combine multiple HTTP requests into one buffer
// and send this to the kernel in one syscall and then the kernel immediately sends
@@ -882,7 +917,7 @@ void QHttpNetworkConnectionChannel::_q_connected()
// the requests into one TCP packet.
// not sure yet if it helps, but it makes sense
- socket->setSocketOption(QAbstractSocket::KeepAliveOption, 1);
+ absSocket->setSocketOption(QAbstractSocket::KeepAliveOption, 1);
pipeliningSupported = QHttpNetworkConnectionChannel::PipeliningSupportUnknown;
@@ -891,7 +926,7 @@ void QHttpNetworkConnectionChannel::_q_connected()
if (!connectionPrivate->connectionMonitor.isMonitoring()) {
// Now that we have a pair of addresses, we can start monitoring the
// connection status to handle its loss properly.
- if (connectionPrivate->connectionMonitor.setTargets(socket->localAddress(), socket->peerAddress()))
+ if (connectionPrivate->connectionMonitor.setTargets(absSocket->localAddress(), absSocket->peerAddress()))
connectionPrivate->connectionMonitor.startMonitoring();
}
}
@@ -903,7 +938,7 @@ void QHttpNetworkConnectionChannel::_q_connected()
if (!connection->sslContext()) {
// this socket is making the 1st handshake for this connection,
// we need to set the SSL context so new sockets can reuse it
- if (auto socketSslContext = QSslSocketPrivate::sslContext(static_cast<QSslSocket*>(socket)))
+ if (auto socketSslContext = QSslSocketPrivate::sslContext(static_cast<QSslSocket*>(absSocket)))
connection->setSslContext(std::move(socketSslContext));
}
#endif
@@ -925,7 +960,7 @@ void QHttpNetworkConnectionChannel::_q_connected()
switchedToHttp2 = false;
if (!reply)
- connection->d_func()->dequeueRequest(socket);
+ connection->d_func()->dequeueRequest(absSocket);
if (reply) {
if (tryProtocolUpgrade) {
@@ -938,6 +973,22 @@ void QHttpNetworkConnectionChannel::_q_connected()
}
}
+void QHttpNetworkConnectionChannel::_q_connected_local_socket(QLocalSocket *localSocket)
+{
+ state = QHttpNetworkConnectionChannel::IdleState;
+ if (!reply) // No reply object, try to dequeue a request (which is paired with a reply):
+ connection->d_func()->dequeueRequest(localSocket);
+ if (reply)
+ sendRequest();
+}
+
+void QHttpNetworkConnectionChannel::_q_connected()
+{
+ if (auto *s = qobject_cast<QAbstractSocket *>(socket))
+ _q_connected_abstract_socket(s);
+ else if (auto *s = qobject_cast<QLocalSocket *>(socket))
+ _q_connected_local_socket(s);
+}
void QHttpNetworkConnectionChannel::_q_error(QAbstractSocket::SocketError socketError)
{
@@ -1114,7 +1165,7 @@ void QHttpNetworkConnectionChannel::_q_error(QAbstractSocket::SocketError socket
//signal emission triggered event loop
if (!socket)
state = QHttpNetworkConnectionChannel::IdleState;
- else if (socket->state() == QAbstractSocket::UnconnectedState)
+ else if (QSocketAbstraction::socketState(socket) == QAbstractSocket::UnconnectedState)
state = QHttpNetworkConnectionChannel::IdleState;
else
state = QHttpNetworkConnectionChannel::ClosingState;
diff --git a/src/network/access/qhttpnetworkconnectionchannel_p.h b/src/network/access/qhttpnetworkconnectionchannel_p.h
index c42290feca..853b647ecc 100644
--- a/src/network/access/qhttpnetworkconnectionchannel_p.h
+++ b/src/network/access/qhttpnetworkconnectionchannel_p.h
@@ -19,6 +19,7 @@
#include <QtNetwork/qnetworkrequest.h>
#include <QtNetwork/qnetworkreply.h>
#include <QtNetwork/qabstractsocket.h>
+#include <QtNetwork/qlocalsocket.h>
#include <private/qobject_p.h>
#include <qauthenticator.h>
@@ -71,7 +72,7 @@ public:
ClosingState = 16,
BusyState = (ConnectingState|WritingState|WaitingState|ReadingState|ClosingState)
};
- QAbstractSocket *socket;
+ QIODevice *socket;
bool ssl;
bool isInitialized;
ChannelState state;
@@ -156,6 +157,8 @@ public:
void _q_bytesWritten(qint64 bytes); // proceed sending
void _q_readyRead(); // pending data to read
void _q_disconnected(); // disconnected from host
+ void _q_connected_abstract_socket(QAbstractSocket *socket);
+ void _q_connected_local_socket(QLocalSocket *socket);
void _q_connected(); // start sending request
void _q_error(QAbstractSocket::SocketError); // error from socket
#ifndef QT_NO_NETWORKPROXY
diff --git a/src/network/access/qhttpprotocolhandler.cpp b/src/network/access/qhttpprotocolhandler.cpp
index 28eab03890..fb584eb9cc 100644
--- a/src/network/access/qhttpprotocolhandler.cpp
+++ b/src/network/access/qhttpprotocolhandler.cpp
@@ -5,6 +5,7 @@
#include <private/qhttpprotocolhandler_p.h>
#include <private/qnoncontiguousbytedevice_p.h>
#include <private/qhttpnetworkconnectionchannel_p.h>
+#include <private/qsocketabstraction_p.h>
QT_BEGIN_NAMESPACE
@@ -34,10 +35,8 @@ void QHttpProtocolHandler::_q_receiveReply()
return;
}
- QAbstractSocket::SocketState socketState = m_socket->state();
-
// connection might be closed to signal the end of data
- if (socketState == QAbstractSocket::UnconnectedState) {
+ if (QSocketAbstraction::socketState(m_socket) == QAbstractSocket::UnconnectedState) {
if (m_socket->bytesAvailable() <= 0) {
if (m_reply->d_func()->state == QHttpNetworkReplyPrivate::ReadingDataState) {
// finish this reply. this case happens when the server did not send a content length
@@ -115,7 +114,7 @@ void QHttpProtocolHandler::_q_receiveReply()
}
case QHttpNetworkReplyPrivate::ReadingDataState: {
QHttpNetworkReplyPrivate *replyPrivate = m_reply->d_func();
- if (m_socket->state() == QAbstractSocket::ConnectedState &&
+ if (QSocketAbstraction::socketState(m_socket) == QAbstractSocket::ConnectedState &&
replyPrivate->downstreamLimited && !replyPrivate->responseData.isEmpty() && replyPrivate->shouldEmitSignals()) {
// (only do the following when still connected, not when we have already been disconnected and there is still data)
// We already have some HTTP body data. We don't read more from the socket until
@@ -193,7 +192,8 @@ void QHttpProtocolHandler::_q_receiveReply()
void QHttpProtocolHandler::_q_readyRead()
{
- if (m_socket->state() == QAbstractSocket::ConnectedState && m_socket->bytesAvailable() == 0) {
+ if (QSocketAbstraction::socketState(m_socket) == QAbstractSocket::ConnectedState
+ && m_socket->bytesAvailable() == 0) {
// We got a readyRead but no bytes are available..
// This happens for the Unbuffered QTcpSocket
// Also check if socket is in ConnectedState since
@@ -201,7 +201,7 @@ void QHttpProtocolHandler::_q_readyRead()
char c;
qint64 ret = m_socket->peek(&c, 1);
if (ret < 0) {
- m_channel->_q_error(m_socket->error());
+ m_channel->_q_error(QSocketAbstraction::socketError(m_socket));
// We still need to handle the reply so it emits its signals etc.
if (m_reply)
_q_receiveReply();
diff --git a/src/network/access/qhttpthreaddelegate.cpp b/src/network/access/qhttpthreaddelegate.cpp
index b0ae0dcf44..7dddd1369e 100644
--- a/src/network/access/qhttpthreaddelegate.cpp
+++ b/src/network/access/qhttpthreaddelegate.cpp
@@ -95,7 +95,9 @@ static QByteArray makeCacheKey(QUrl &url, QNetworkProxy *proxy, const QString &p
QUrl copy = url;
QString scheme = copy.scheme();
bool isEncrypted = scheme == "https"_L1 || scheme == "preconnect-https"_L1;
- copy.setPort(copy.port(isEncrypted ? 443 : 80));
+ const bool isLocalSocket = scheme.startsWith("unix"_L1);
+ if (!isLocalSocket)
+ copy.setPort(copy.port(isEncrypted ? 443 : 80));
if (scheme == "preconnect-http"_L1)
copy.setScheme("http"_L1);
else if (scheme == "preconnect-https"_L1)
@@ -145,9 +147,9 @@ class QNetworkAccessCachedHttpConnection: public QHttpNetworkConnection,
{
// Q_OBJECT
public:
- QNetworkAccessCachedHttpConnection(quint16 connectionCount, const QString &hostName, quint16 port, bool encrypt,
+ QNetworkAccessCachedHttpConnection(quint16 connectionCount, const QString &hostName, quint16 port, bool encrypt, bool isLocalSocket,
QHttpNetworkConnection::ConnectionType connectionType)
- : QHttpNetworkConnection(connectionCount, hostName, port, encrypt, /*parent=*/nullptr, connectionType)
+ : QHttpNetworkConnection(connectionCount, hostName, port, encrypt, isLocalSocket, /*parent=*/nullptr, connectionType)
{
setExpires(true);
setShareable(true);
@@ -244,7 +246,9 @@ void QHttpThreadDelegate::startRequest()
// check if we have an open connection to this host
QUrl urlCopy = httpRequest.url();
- urlCopy.setPort(urlCopy.port(ssl ? 443 : 80));
+ const bool isLocalSocket = urlCopy.scheme().startsWith("unix"_L1);
+ if (!isLocalSocket)
+ urlCopy.setPort(urlCopy.port(ssl ? 443 : 80));
QHttpNetworkConnection::ConnectionType connectionType
= httpRequest.isHTTP2Allowed() ? QHttpNetworkConnection::ConnectionTypeHTTP2
@@ -279,7 +283,10 @@ void QHttpThreadDelegate::startRequest()
} else
#endif // QT_CONFIG(ssl)
{
- urlCopy.setScheme(QStringLiteral("h2"));
+ if (isLocalSocket)
+ urlCopy.setScheme(QStringLiteral("unix+h2"));
+ else
+ urlCopy.setScheme(QStringLiteral("h2"));
}
}
@@ -297,8 +304,9 @@ void QHttpThreadDelegate::startRequest()
if (!httpConnection) {
// no entry in cache; create an object
// the http object is actually a QHttpNetworkConnection
- httpConnection = new QNetworkAccessCachedHttpConnection(http1Parameters.numberOfConnectionsPerHost(), urlCopy.host(), urlCopy.port(), ssl,
- connectionType);
+ httpConnection = new QNetworkAccessCachedHttpConnection(
+ http1Parameters.numberOfConnectionsPerHost(), urlCopy.host(), urlCopy.port(), ssl,
+ isLocalSocket, connectionType);
if (connectionType == QHttpNetworkConnection::ConnectionTypeHTTP2
|| connectionType == QHttpNetworkConnection::ConnectionTypeHTTP2Direct) {
httpConnection->setHttp2Parameters(http2Parameters);
diff --git a/src/network/access/qnetworkaccessbackend.cpp b/src/network/access/qnetworkaccessbackend.cpp
index 5dbcef4bbe..3c7fee567d 100644
--- a/src/network/access/qnetworkaccessbackend.cpp
+++ b/src/network/access/qnetworkaccessbackend.cpp
@@ -568,6 +568,43 @@ void QNetworkAccessBackend::setRawHeader(const QByteArray &header, const QByteAr
}
/*!
+ \since 6.8
+
+ Returns headers that are set in this QNetworkAccessBackend instance.
+
+ \sa setHeaders()
+*/
+QHttpHeaders QNetworkAccessBackend::headers() const
+{
+ return d_func()->m_reply->headers();
+}
+
+/*!
+ \since 6.8
+
+ Sets \a newHeaders as headers, overriding any previously set headers.
+
+ These headers are accessible on the QNetworkReply instance which was
+ returned when calling one of the appropriate functions on
+ QNetworkAccessManager.
+
+ \sa headers()
+*/
+void QNetworkAccessBackend::setHeaders(QHttpHeaders &&newHeaders)
+{
+ d_func()->m_reply->setHeaders(std::move(newHeaders));
+}
+
+/*!
+ \overload
+ \since 6.8
+*/
+void QNetworkAccessBackend::setHeaders(const QHttpHeaders &newHeaders)
+{
+ d_func()->m_reply->setHeaders(newHeaders);
+}
+
+/*!
Returns the operation which was requested when calling
QNetworkAccessManager.
*/
diff --git a/src/network/access/qnetworkaccessbackend_p.h b/src/network/access/qnetworkaccessbackend_p.h
index 799ae3faad..4f8d7e4372 100644
--- a/src/network/access/qnetworkaccessbackend_p.h
+++ b/src/network/access/qnetworkaccessbackend_p.h
@@ -104,6 +104,9 @@ public:
void setHeader(QNetworkRequest::KnownHeaders header, const QVariant &value);
QByteArray rawHeader(const QByteArray &header) const;
void setRawHeader(const QByteArray &header, const QByteArray &value);
+ QHttpHeaders headers() const;
+ void setHeaders(const QHttpHeaders &newHeaders);
+ void setHeaders(QHttpHeaders &&newHeaders);
QNetworkAccessManager::Operation operation() const;
bool isCachingEnabled() const;
diff --git a/src/network/access/qnetworkaccesscachebackend.cpp b/src/network/access/qnetworkaccesscachebackend.cpp
index fd8174c143..ead5fe2ef5 100644
--- a/src/network/access/qnetworkaccesscachebackend.cpp
+++ b/src/network/access/qnetworkaccesscachebackend.cpp
@@ -47,21 +47,21 @@ bool QNetworkAccessCacheBackend::sendCacheContents()
return false;
QNetworkCacheMetaData::AttributesMap attributes = item.attributes();
- setAttribute(QNetworkRequest::HttpStatusCodeAttribute, attributes.value(QNetworkRequest::HttpStatusCodeAttribute));
- setAttribute(QNetworkRequest::HttpReasonPhraseAttribute, attributes.value(QNetworkRequest::HttpReasonPhraseAttribute));
-
- // set the raw headers
- const QNetworkCacheMetaData::RawHeaderList rawHeaders = item.rawHeaders();
- for (const auto &header : rawHeaders) {
- if (header.first.compare("cache-control", Qt::CaseInsensitive) == 0) {
- const QLatin1StringView cacheControlValue(header.second);
- if (cacheControlValue.contains("must-revalidate"_L1, Qt::CaseInsensitive)
- || cacheControlValue.contains("no-cache"_L1, Qt::CaseInsensitive)) {
- return false;
- }
- }
- setRawHeader(header.first, header.second);
+ setAttribute(QNetworkRequest::HttpStatusCodeAttribute,
+ attributes.value(QNetworkRequest::HttpStatusCodeAttribute));
+ setAttribute(QNetworkRequest::HttpReasonPhraseAttribute,
+ attributes.value(QNetworkRequest::HttpReasonPhraseAttribute));
+
+ // set the headers
+ auto headers = item.headers();
+ const auto cacheControlValue = QLatin1StringView(
+ headers.value(QHttpHeaders::WellKnownHeader::CacheControl));
+ // RFC 9111 Section 5.2 Cache Control
+ if (cacheControlValue.contains("must-revalidate"_L1, Qt::CaseInsensitive)
+ || cacheControlValue.contains("no-cache"_L1, Qt::CaseInsensitive)) {
+ return false;
}
+ setHeaders(std::move(headers));
// handle a possible redirect
QVariant redirectionTarget = attributes.value(QNetworkRequest::RedirectionTargetAttribute);
diff --git a/src/network/access/qnetworkaccessdebugpipebackend.cpp b/src/network/access/qnetworkaccessdebugpipebackend.cpp
index 4b12f9fe31..e0a89e3646 100644
--- a/src/network/access/qnetworkaccessdebugpipebackend.cpp
+++ b/src/network/access/qnetworkaccessdebugpipebackend.cpp
@@ -97,7 +97,9 @@ qint64 QNetworkAccessDebugPipeBackend::read(char *data, qint64 maxlen)
if (haveRead == -1) {
hasDownloadFinished = true;
// this ensures a good last downloadProgress is emitted
- setHeader(QNetworkRequest::ContentLengthHeader, QVariant());
+ auto h = headers();
+ h.removeAll(QHttpHeaders::WellKnownHeader::ContentLength);
+ setHeaders(std::move(h));
possiblyFinish();
return haveRead;
}
diff --git a/src/network/access/qnetworkaccessmanager.cpp b/src/network/access/qnetworkaccessmanager.cpp
index 4e13c9924b..ae99721758 100644
--- a/src/network/access/qnetworkaccessmanager.cpp
+++ b/src/network/access/qnetworkaccessmanager.cpp
@@ -1220,6 +1220,13 @@ QNetworkReply *QNetworkAccessManager::createRequest(QNetworkAccessManager::Opera
bool isLocalFile = req.url().isLocalFile();
QString scheme = req.url().scheme();
+ // Remap local+http to unix+http to make further processing easier
+ if (scheme == "local+http"_L1) {
+ scheme = u"unix+http"_s;
+ QUrl url = req.url();
+ url.setScheme(scheme);
+ req.setUrl(url);
+ }
// fast path for GET on file:// URLs
// The QNetworkAccessFileBackend will right now only be used for PUT
@@ -1255,12 +1262,14 @@ QNetworkReply *QNetworkAccessManager::createRequest(QNetworkAccessManager::Opera
}
}
QNetworkRequest request = req;
+ auto h = request.headers();
#ifndef Q_OS_WASM // Content-length header is not allowed to be set by user in wasm
- if (!request.header(QNetworkRequest::ContentLengthHeader).isValid() &&
+ if (!h.contains(QHttpHeaders::WellKnownHeader::ContentLength) &&
outgoingData && !outgoingData->isSequential()) {
// request has no Content-Length
// but the data that is outgoing is random-access
- request.setHeader(QNetworkRequest::ContentLengthHeader, outgoingData->size());
+ h.append(QHttpHeaders::WellKnownHeader::ContentLength,
+ QByteArray::number(outgoingData->size()));
}
#endif
if (static_cast<QNetworkRequest::LoadControl>
@@ -1269,9 +1278,11 @@ QNetworkReply *QNetworkAccessManager::createRequest(QNetworkAccessManager::Opera
if (d->cookieJar) {
QList<QNetworkCookie> cookies = d->cookieJar->cookiesForUrl(request.url());
if (!cookies.isEmpty())
- request.setHeader(QNetworkRequest::CookieHeader, QVariant::fromValue(cookies));
+ h.replaceOrAppend(QHttpHeaders::WellKnownHeader::Cookie,
+ QNetworkHeadersPrivate::fromCookieList(cookies));
}
}
+ request.setHeaders(std::move(h));
#ifdef Q_OS_WASM
Q_UNUSED(isLocalFile);
// Support http, https, and relative urls
@@ -1292,11 +1303,15 @@ QNetworkReply *QNetworkAccessManager::createRequest(QNetworkAccessManager::Opera
u"https",
u"preconnect-https",
#endif
+ u"unix+http",
};
// Since Qt 5 we use the new QNetworkReplyHttpImpl
if (std::find(std::begin(httpSchemes), std::end(httpSchemes), scheme) != std::end(httpSchemes)) {
+
#ifndef QT_NO_SSL
- if (isStrictTransportSecurityEnabled() && d->stsCache.isKnownHost(request.url())) {
+ const bool isLocalSocket = scheme.startsWith("unix"_L1);
+ if (!isLocalSocket && isStrictTransportSecurityEnabled()
+ && d->stsCache.isKnownHost(request.url())) {
QUrl stsUrl(request.url());
// RFC6797, 8.3:
// The UA MUST replace the URI scheme with "https" [RFC2818],
@@ -1387,6 +1402,8 @@ QStringList QNetworkAccessManager::supportedSchemesImplementation() const
// Those ones don't exist in backends
#if QT_CONFIG(http)
schemes << QStringLiteral("http");
+ schemes << QStringLiteral("unix+http");
+ schemes << QStringLiteral("local+http");
#ifndef QT_NO_SSL
if (QSslSocket::supportsSsl())
schemes << QStringLiteral("https");
@@ -1746,9 +1763,10 @@ QNetworkRequest QNetworkAccessManagerPrivate::prepareMultipart(const QNetworkReq
{
// copy the request, we probably need to add some headers
QNetworkRequest newRequest(request);
+ auto h = newRequest.headers();
// add Content-Type header if not there already
- if (!request.header(QNetworkRequest::ContentTypeHeader).isValid()) {
+ if (!h.contains(QHttpHeaders::WellKnownHeader::ContentType)) {
QByteArray contentType;
contentType.reserve(34 + multiPart->d_func()->boundary.size());
contentType += "multipart/";
@@ -1768,14 +1786,15 @@ QNetworkRequest QNetworkAccessManagerPrivate::prepareMultipart(const QNetworkReq
}
// putting the boundary into quotes, recommended in RFC 2046 section 5.1.1
contentType += "; boundary=\"" + multiPart->d_func()->boundary + '"';
- newRequest.setHeader(QNetworkRequest::ContentTypeHeader, QVariant(contentType));
+ h.append(QHttpHeaders::WellKnownHeader::ContentType, contentType);
}
// add MIME-Version header if not there already (we must include the header
// if the message conforms to RFC 2045, see section 4 of that RFC)
- auto mimeHeader = "MIME-Version"_ba;
- if (!request.hasRawHeader(mimeHeader))
- newRequest.setRawHeader(mimeHeader, "1.0"_ba);
+ if (!h.contains(QHttpHeaders::WellKnownHeader::MIMEVersion))
+ h.append(QHttpHeaders::WellKnownHeader::MIMEVersion, "1.0"_ba);
+
+ newRequest.setHeaders(std::move(h));
QIODevice *device = multiPart->d_func()->device;
if (!device->isReadable()) {
diff --git a/src/network/access/qnetworkdiskcache.cpp b/src/network/access/qnetworkdiskcache.cpp
index c883a61886..b39924025e 100644
--- a/src/network/access/qnetworkdiskcache.cpp
+++ b/src/network/access/qnetworkdiskcache.cpp
@@ -154,15 +154,11 @@ QIODevice *QNetworkDiskCache::prepare(const QNetworkCacheMetaData &metaData)
return nullptr;
}
- const auto headers = metaData.rawHeaders();
- for (const auto &header : headers) {
- if (header.first.compare("content-length", Qt::CaseInsensitive) == 0) {
- const qint64 size = header.second.toLongLong();
- if (size > (maximumCacheSize() * 3)/4)
- return nullptr;
- break;
- }
- }
+ const auto sizeValue = metaData.headers().value(QHttpHeaders::WellKnownHeader::ContentLength);
+ const qint64 size = sizeValue.toLongLong();
+ if (size > (maximumCacheSize() * 3)/4)
+ return nullptr;
+
std::unique_ptr<QCacheItem> cacheItem = std::make_unique<QCacheItem>();
cacheItem->metaData = metaData;
@@ -578,31 +574,27 @@ QString QNetworkDiskCachePrivate::cacheFileName(const QUrl &url) const
*/
bool QCacheItem::canCompress() const
{
- bool sizeOk = false;
- bool typeOk = false;
- const auto headers = metaData.rawHeaders();
- for (const auto &header : headers) {
- if (header.first.compare("content-length", Qt::CaseInsensitive) == 0) {
- qint64 size = header.second.toLongLong();
- if (size > MAX_COMPRESSION_SIZE)
- return false;
- else
- sizeOk = true;
- }
+ const auto h = metaData.headers();
- if (header.first.compare("content-type", Qt::CaseInsensitive) == 0) {
- QByteArray type = header.second;
- if (type.startsWith("text/")
- || (type.startsWith("application/")
- && (type.endsWith("javascript") || type.endsWith("ecmascript"))))
- typeOk = true;
- else
- return false;
- }
- if (sizeOk && typeOk)
- return true;
+ const auto sizeValue = h.value(QHttpHeaders::WellKnownHeader::ContentLength);
+ if (sizeValue.empty())
+ return false;
+
+ qint64 size = sizeValue.toLongLong();
+ if (size > MAX_COMPRESSION_SIZE)
+ return false;
+
+ const auto type = h.value(QHttpHeaders::WellKnownHeader::ContentType);
+ if (!type.empty())
+ return false;
+
+ if (!type.startsWith("text/")
+ && !(type.startsWith("application/")
+ && (type.endsWith("javascript") || type.endsWith("ecmascript")))) {
+ return false;
}
- return false;
+
+ return true;
}
enum
@@ -673,7 +665,7 @@ bool QCacheItem::read(QFileDevice *device, bool readData)
if (!device->fileName().endsWith(expectedFilename))
return false;
- return metaData.isValid() && !metaData.rawHeaders().isEmpty();
+ return metaData.isValid() && !metaData.headers().isEmpty();
}
QT_END_NAMESPACE
diff --git a/src/network/access/qnetworkfile.cpp b/src/network/access/qnetworkfile.cpp
index bfedf044de..fb9ce8232d 100644
--- a/src/network/access/qnetworkfile.cpp
+++ b/src/network/access/qnetworkfile.cpp
@@ -2,6 +2,7 @@
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qnetworkfile_p.h"
+#include "qnetworkrequest_p.h"
#include <QtCore/QDebug>
#include <QNetworkReply>
@@ -31,8 +32,10 @@ void QNetworkFile::open()
"Cannot open %1: Path is a directory").arg(fileName());
emit error(QNetworkReply::ContentOperationNotPermittedError, msg);
} else {
- emit headerRead(QNetworkRequest::LastModifiedHeader, QVariant::fromValue(fi.lastModified()));
- emit headerRead(QNetworkRequest::ContentLengthHeader, QVariant::fromValue(fi.size()));
+ emit headerRead(QHttpHeaders::WellKnownHeader::LastModified,
+ QNetworkHeadersPrivate::toHttpDate(fi.lastModified()));
+ emit headerRead(QHttpHeaders::WellKnownHeader::ContentLength,
+ QByteArray::number(fi.size()));
opened = QFile::open(QIODevice::ReadOnly | QIODevice::Unbuffered);
if (!opened) {
QString msg = QCoreApplication::translate("QNetworkAccessFileBackend",
diff --git a/src/network/access/qnetworkfile_p.h b/src/network/access/qnetworkfile_p.h
index df772251b4..9dcb63711e 100644
--- a/src/network/access/qnetworkfile_p.h
+++ b/src/network/access/qnetworkfile_p.h
@@ -35,7 +35,7 @@ public Q_SLOTS:
Q_SIGNALS:
void finished(bool ok);
- void headerRead(QNetworkRequest::KnownHeaders header, const QVariant &value);
+ void headerRead(QHttpHeaders::WellKnownHeader, const QByteArray &value);
void error(QNetworkReply::NetworkError error, const QString &message);
};
diff --git a/src/network/access/qnetworkreply.cpp b/src/network/access/qnetworkreply.cpp
index 9334b01de6..3301ae85d2 100644
--- a/src/network/access/qnetworkreply.cpp
+++ b/src/network/access/qnetworkreply.cpp
@@ -617,7 +617,7 @@ QVariant QNetworkReply::header(QNetworkRequest::KnownHeaders header) const
bool QNetworkReply::hasRawHeader(QAnyStringView headerName) const
{
Q_D(const QNetworkReply);
- return d->findRawHeader(headerName) != d->rawHeaders.constEnd();
+ return d->headers().contains(headerName);
}
/*!
@@ -633,9 +633,7 @@ bool QNetworkReply::hasRawHeader(QAnyStringView headerName) const
QByteArray QNetworkReply::rawHeader(QAnyStringView headerName) const
{
Q_D(const QNetworkReply);
- if (const auto it = d->findRawHeader(headerName); it != d->rawHeaders.constEnd())
- return it->second;
- return QByteArray();
+ return d->rawHeader(headerName);
}
/*! \typedef QNetworkReply::RawHeaderPair
@@ -650,7 +648,20 @@ QByteArray QNetworkReply::rawHeader(QAnyStringView headerName) const
const QList<QNetworkReply::RawHeaderPair>& QNetworkReply::rawHeaderPairs() const
{
Q_D(const QNetworkReply);
- return d->rawHeaders;
+ return d->allRawHeaders();
+}
+
+/*!
+ \since 6.8
+
+ Returns headers that were sent by the remote server.
+
+ \sa setHeaders(), QNetworkRequest::setAttribute(), QNetworkRequest::Attribute
+*/
+QHttpHeaders QNetworkReply::headers() const
+{
+ Q_D(const QNetworkReply);
+ return d->headers();
}
/*!
@@ -888,6 +899,45 @@ void QNetworkReply::setUrl(const QUrl &url)
}
/*!
+ \since 6.8
+
+ Sets \a newHeaders as headers in this network reply, overriding
+ any previously set headers.
+
+ If some headers correspond to the known headers, they will be
+ parsed and the corresponding parsed form will also be set.
+
+ \sa headers(), QNetworkRequest::KnownHeaders
+*/
+void QNetworkReply::setHeaders(const QHttpHeaders &newHeaders)
+{
+ Q_D(QNetworkReply);
+ d->setHeaders(newHeaders);
+}
+
+/*!
+ \overload
+ \since 6.8
+*/
+void QNetworkReply::setHeaders(QHttpHeaders &&newHeaders)
+{
+ Q_D(QNetworkReply);
+ d->setHeaders(std::move(newHeaders));
+}
+
+/*!
+ \since 6.8
+
+ Sets the header \a name to be of value \a value. If \a
+ name was previously set, it is overridden.
+*/
+void QNetworkReply::setWellKnownHeader(QHttpHeaders::WellKnownHeader name, const QByteArray &value)
+{
+ Q_D(QNetworkReply);
+ d->setHeader(name, value);
+}
+
+/*!
Sets the known header \a header to be of value \a value. The
corresponding raw form of the header will be set as well.
diff --git a/src/network/access/qnetworkreply.h b/src/network/access/qnetworkreply.h
index 390a6f2f51..48ec0397c6 100644
--- a/src/network/access/qnetworkreply.h
+++ b/src/network/access/qnetworkreply.h
@@ -109,6 +109,7 @@ public:
typedef QPair<QByteArray, QByteArray> RawHeaderPair;
const QList<RawHeaderPair>& rawHeaderPairs() const;
+ QHttpHeaders headers() const;
// attributes
QVariant attribute(QNetworkRequest::Attribute code) const;
@@ -152,6 +153,9 @@ protected:
void setUrl(const QUrl &url);
void setHeader(QNetworkRequest::KnownHeaders header, const QVariant &value);
void setRawHeader(const QByteArray &headerName, const QByteArray &value);
+ void setHeaders(const QHttpHeaders &newHeaders);
+ void setHeaders(QHttpHeaders &&newHeaders);
+ void setWellKnownHeader(QHttpHeaders::WellKnownHeader name, const QByteArray &value);
void setAttribute(QNetworkRequest::Attribute code, const QVariant &value);
#if QT_CONFIG(ssl)
diff --git a/src/network/access/qnetworkreplydataimpl.cpp b/src/network/access/qnetworkreplydataimpl.cpp
index 7cb7621bca..006cfd57cb 100644
--- a/src/network/access/qnetworkreplydataimpl.cpp
+++ b/src/network/access/qnetworkreplydataimpl.cpp
@@ -36,8 +36,11 @@ QNetworkReplyDataImpl::QNetworkReplyDataImpl(QObject *parent, const QNetworkRequ
QByteArray payload;
if (qDecodeDataUrl(url, mimeType, payload)) {
qint64 size = payload.size();
- setHeader(QNetworkRequest::ContentTypeHeader, mimeType);
- setHeader(QNetworkRequest::ContentLengthHeader, size);
+ auto h = headers();
+ h.replaceOrAppend(QHttpHeaders::WellKnownHeader::ContentType, mimeType);
+ h.replaceOrAppend(QHttpHeaders::WellKnownHeader::ContentLength, QByteArray::number(size));
+ setHeaders(std::move(h));
+
QMetaObject::invokeMethod(this, "metaDataChanged", Qt::QueuedConnection);
d->decodedData.setData(payload);
diff --git a/src/network/access/qnetworkreplyfileimpl.cpp b/src/network/access/qnetworkreplyfileimpl.cpp
index e6208a5c85..bad0bb7b0a 100644
--- a/src/network/access/qnetworkreplyfileimpl.cpp
+++ b/src/network/access/qnetworkreplyfileimpl.cpp
@@ -85,7 +85,7 @@ QNetworkReplyFileImpl::QNetworkReplyFileImpl(QNetworkAccessManager *manager, con
if (req.attribute(QNetworkRequest::BackgroundRequestAttribute).toBool()) { // Asynchronous open
auto realFile = new QNetworkFile(fileName);
- connect(realFile, &QNetworkFile::headerRead, this, &QNetworkReplyFileImpl::setHeader,
+ connect(realFile, &QNetworkFile::headerRead, this, &QNetworkReplyFileImpl::setWellKnownHeader,
Qt::QueuedConnection);
connect(realFile, &QNetworkFile::error, this, &QNetworkReplyFileImpl::setError,
Qt::QueuedConnection);
@@ -128,8 +128,12 @@ QNetworkReplyFileImpl::QNetworkReplyFileImpl(QNetworkAccessManager *manager, con
QMetaObject::invokeMethod(this, "finished", Qt::QueuedConnection);
return;
}
- setHeader(QNetworkRequest::LastModifiedHeader, fi.lastModified());
- setHeader(QNetworkRequest::ContentLengthHeader, fi.size());
+ auto h = headers();
+ h.replaceOrAppend(QHttpHeaders::WellKnownHeader::LastModified,
+ QNetworkHeadersPrivate::toHttpDate(fi.lastModified()));
+ h.replaceOrAppend(QHttpHeaders::WellKnownHeader::ContentLength,
+ QByteArray::number(fi.size()));
+ setHeaders(std::move(h));
QMetaObject::invokeMethod(this, "metaDataChanged", Qt::QueuedConnection);
QMetaObject::invokeMethod(this, "downloadProgress", Qt::QueuedConnection,
@@ -171,9 +175,9 @@ bool QNetworkReplyFileImpl::isSequential () const
qint64 QNetworkReplyFileImpl::size() const
{
- bool ok;
- int size = header(QNetworkRequest::ContentLengthHeader).toInt(&ok);
- return ok ? size : 0;
+ const auto totalSizeOpt = QNetworkHeadersPrivate::toInt(
+ headers().value(QHttpHeaders::WellKnownHeader::ContentLength));
+ return totalSizeOpt.value_or(0);
}
/*!
diff --git a/src/network/access/qnetworkreplyhttpimpl.cpp b/src/network/access/qnetworkreplyhttpimpl.cpp
index 1eee98f834..b4aca940a7 100644
--- a/src/network/access/qnetworkreplyhttpimpl.cpp
+++ b/src/network/access/qnetworkreplyhttpimpl.cpp
@@ -202,7 +202,10 @@ QNetworkReplyHttpImpl::QNetworkReplyHttpImpl(QNetworkAccessManager* const manage
if (bufferingDisallowed) {
// if a valid content-length header for the request was supplied, we can disable buffering
// if not, we will buffer anyway
- if (request.header(QNetworkRequest::ContentLengthHeader).isValid()) {
+
+ const auto sizeOpt = QNetworkHeadersPrivate::toInt(
+ request.headers().value(QHttpHeaders::WellKnownHeader::ContentLength));
+ if (sizeOpt) {
QMetaObject::invokeMethod(this, "_q_startOperation", Qt::QueuedConnection);
// FIXME make direct call?
} else {
@@ -478,10 +481,12 @@ bool QNetworkReplyHttpImplPrivate::loadFromCacheIfAllowed(QHttpNetworkRequest &h
{
QNetworkRequest::CacheLoadControl CacheLoadControlAttribute =
(QNetworkRequest::CacheLoadControl)request.attribute(QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::PreferNetwork).toInt();
+
+ auto requestHeaders = request.headers();
if (CacheLoadControlAttribute == QNetworkRequest::AlwaysNetwork) {
// If the request does not already specify preferred cache-control
// force reload from the network and tell any caching proxy servers to reload too
- if (!request.rawHeaderList().contains(cacheControlName())) {
+ if (!requestHeaders.contains(QHttpHeaders::WellKnownHeader::CacheControl)) {
const auto noCache = "no-cache"_ba;
httpRequest.setHeaderField(cacheControlName(), noCache);
httpRequest.setHeaderField("Pragma"_ba, noCache);
@@ -491,7 +496,7 @@ bool QNetworkReplyHttpImplPrivate::loadFromCacheIfAllowed(QHttpNetworkRequest &h
// The disk cache API does not currently support partial content retrieval.
// That is why we don't use the disk cache for any such requests.
- if (request.hasRawHeader(rangeName()))
+ if (requestHeaders.contains(QHttpHeaders::WellKnownHeader::Range))
return false;
QAbstractNetworkCache *nc = managerPrivate->networkCache;
@@ -505,28 +510,27 @@ bool QNetworkReplyHttpImplPrivate::loadFromCacheIfAllowed(QHttpNetworkRequest &h
if (!metaData.saveToDisk())
return false;
- QNetworkHeadersPrivate cacheHeaders;
- QNetworkHeadersPrivate::RawHeadersList::ConstIterator it;
- cacheHeaders.setAllRawHeaders(metaData.rawHeaders());
+ QHttpHeaders cacheHeaders = metaData.headers();
- it = cacheHeaders.findRawHeader("content-length");
- if (it != cacheHeaders.rawHeaders.constEnd()) {
+ const auto sizeOpt = QNetworkHeadersPrivate::toInt(
+ cacheHeaders.value(QHttpHeaders::WellKnownHeader::ContentLength));
+ if (sizeOpt) {
std::unique_ptr<QIODevice> data(nc->data(httpRequest.url()));
- if (!data || data->size() < it->second.toLongLong())
+ if (!data || data->size() < sizeOpt.value())
return false; // The data is smaller than the content-length specified
}
- it = cacheHeaders.findRawHeader("etag");
- if (it != cacheHeaders.rawHeaders.constEnd())
- httpRequest.setHeaderField("If-None-Match"_ba, it->second);
+ auto value = cacheHeaders.value(QHttpHeaders::WellKnownHeader::ETag);
+ if (!value.empty())
+ httpRequest.setHeaderField("If-None-Match"_ba, value.toByteArray());
QDateTime lastModified = metaData.lastModified();
if (lastModified.isValid())
httpRequest.setHeaderField("If-Modified-Since"_ba, QNetworkHeadersPrivate::toHttpDate(lastModified));
- it = cacheHeaders.findRawHeader(cacheControlName());
- if (it != cacheHeaders.rawHeaders.constEnd()) {
- QHash<QByteArray, QByteArray> cacheControl = parseHttpOptionHeader(it->second);
+ value = cacheHeaders.value(QHttpHeaders::WellKnownHeader::CacheControl);
+ if (!value.empty()) {
+ QHash<QByteArray, QByteArray> cacheControl = parseHttpOptionHeader(value);
if (cacheControl.contains("must-revalidate"_ba))
return false;
if (cacheControl.contains("no-cache"_ba))
@@ -553,16 +557,15 @@ bool QNetworkReplyHttpImplPrivate::loadFromCacheIfAllowed(QHttpNetworkRequest &h
* now
* is the current (local) time
*/
- qint64 age_value = 0;
- it = cacheHeaders.findRawHeader("age");
- if (it != cacheHeaders.rawHeaders.constEnd())
- age_value = it->second.toLongLong();
+ const auto ageOpt = QNetworkHeadersPrivate::toInt(
+ cacheHeaders.value(QHttpHeaders::WellKnownHeader::Age));
+ const qint64 age_value = ageOpt.value_or(0);
QDateTime dateHeader;
qint64 date_value = 0;
- it = cacheHeaders.findRawHeader("date");
- if (it != cacheHeaders.rawHeaders.constEnd()) {
- dateHeader = QNetworkHeadersPrivate::fromHttpDate(it->second);
+ value = cacheHeaders.value(QHttpHeaders::WellKnownHeader::Date);
+ if (!value.empty()) {
+ dateHeader = QNetworkHeadersPrivate::fromHttpDate(value);
date_value = dateHeader.toSecsSinceEpoch();
}
@@ -744,18 +747,17 @@ void QNetworkReplyHttpImplPrivate::postRequest(const QNetworkRequest &newHttpReq
break; // can't happen
}
- QList<QByteArray> headers = newHttpRequest.rawHeaderList();
+ QHttpHeaders newRequestHeaders = newHttpRequest.headers();
if (resumeOffset != 0) {
- const int rangeIndex = headers.indexOf(rangeName());
- if (rangeIndex != -1) {
+ if (newRequestHeaders.contains(QHttpHeaders::WellKnownHeader::Range)) {
// Need to adjust resume offset for user specified range
- headers.removeAt(rangeIndex);
-
// We've already verified that requestRange starts with "bytes=", see canResume.
- const auto rangeHeader = newHttpRequest.rawHeader(rangeName());
+ const auto rangeHeader = newRequestHeaders.value(QHttpHeaders::WellKnownHeader::Range);
const auto requestRange = QByteArrayView(rangeHeader).mid(bytesEqualPrefix().size());
+ newRequestHeaders.removeAll(QHttpHeaders::WellKnownHeader::Range);
+
int index = requestRange.indexOf('-');
quint64 requestStartOffset = requestRange.left(index).toULongLong();
@@ -771,8 +773,11 @@ void QNetworkReplyHttpImplPrivate::postRequest(const QNetworkRequest &newHttpReq
}
}
- for (const QByteArray &header : std::as_const(headers))
- httpRequest.setHeaderField(header, newHttpRequest.rawHeader(header));
+ for (int i = 0; i < newRequestHeaders.size(); i++) {
+ const auto name = newRequestHeaders.nameAt(i);
+ const auto value = newRequestHeaders.valueAt(i);
+ httpRequest.setHeaderField(QByteArray(name.data(), name.size()), value.toByteArray());
+ }
if (newHttpRequest.attribute(QNetworkRequest::HttpPipeliningAllowedAttribute).toBool())
httpRequest.setPipeliningAllowed(true);
@@ -1151,7 +1156,8 @@ void QNetworkReplyHttpImplPrivate::replyDownloadData(QByteArray d)
}
lastReadyReadEmittedSize = bytesDownloaded;
- QVariant totalSize = cookedHeaders.value(QNetworkRequest::ContentLengthHeader);
+ const auto totalSizeOpt = QNetworkHeadersPrivate::toInt(
+ headers().value(QHttpHeaders::WellKnownHeader::ContentLength));
emit q->readyRead();
// emit readyRead before downloadProgress in case this will cause events to be
@@ -1159,8 +1165,7 @@ void QNetworkReplyHttpImplPrivate::replyDownloadData(QByteArray d)
if (downloadProgressSignalChoke.elapsed() >= progressSignalInterval
&& (!decompressHelper.isValid() || decompressHelper.isCountingBytes())) {
downloadProgressSignalChoke.restart();
- emit q->downloadProgress(bytesDownloaded,
- totalSize.isNull() ? Q_INT64_C(-1) : totalSize.toLongLong());
+ emit q->downloadProgress(bytesDownloaded, totalSizeOpt.value_or(-1));
}
}
@@ -1223,7 +1228,8 @@ void QNetworkReplyHttpImplPrivate::onRedirected(const QUrl &redirectUrl, int htt
if (httpRequest.isFollowRedirects()) // update the reply's url as it could've changed
url = redirectUrl;
- if (managerPrivate->stsEnabled && managerPrivate->stsCache.isKnownHost(url)) {
+ const bool wasLocalSocket = schemeBefore.startsWith("unix"_L1);
+ if (!wasLocalSocket && managerPrivate->stsEnabled && managerPrivate->stsCache.isKnownHost(url)) {
// RFC6797, 8.3:
// The UA MUST replace the URI scheme with "https" [RFC2818],
// and if the URI contains an explicit port component of "80",
@@ -1237,9 +1243,12 @@ void QNetworkReplyHttpImplPrivate::onRedirected(const QUrl &redirectUrl, int htt
url.setPort(443);
}
- const bool isLessSafe = schemeBefore == "https"_L1 && url.scheme() == "http"_L1;
- if (httpRequest.redirectPolicy() == QNetworkRequest::NoLessSafeRedirectPolicy
- && isLessSafe) {
+ // Just to be on the safe side for local sockets, any changes to the scheme
+ // are considered less safe
+ const bool changingLocalScheme = wasLocalSocket && url.scheme() != schemeBefore;
+ const bool isLessSafe = changingLocalScheme
+ || (schemeBefore == "https"_L1 && url.scheme() == "http"_L1);
+ if (httpRequest.redirectPolicy() == QNetworkRequest::NoLessSafeRedirectPolicy && isLessSafe) {
error(QNetworkReply::InsecureRedirectError,
QCoreApplication::translate("QHttp", "Insecure redirect"));
return;
@@ -1255,6 +1264,7 @@ void QNetworkReplyHttpImplPrivate::onRedirected(const QUrl &redirectUrl, int htt
// Clear stale headers, the relevant ones get set again later
httpRequest.clearHeaders();
+ auto newHeaders = redirectRequest.headers();
if ((operation == QNetworkAccessManager::GetOperation
|| operation == QNetworkAccessManager::HeadOperation) && !getOperationKeepsBody) {
// possibly changed from not-GET/HEAD to GET/HEAD, make sure to get rid of upload device
@@ -1269,18 +1279,20 @@ void QNetworkReplyHttpImplPrivate::onRedirected(const QUrl &redirectUrl, int htt
outgoingData = nullptr;
outgoingDataBuffer.reset();
// We need to explicitly unset these headers so they're not reapplied to the httpRequest
- redirectRequest.setHeader(QNetworkRequest::ContentLengthHeader, QVariant());
- redirectRequest.setHeader(QNetworkRequest::ContentTypeHeader, QVariant());
+ newHeaders.removeAll(QHttpHeaders::WellKnownHeader::ContentLength);
+ newHeaders.removeAll(QHttpHeaders::WellKnownHeader::ContentType);
}
if (const QNetworkCookieJar *const cookieJar = manager->cookieJar()) {
auto cookies = cookieJar->cookiesForUrl(url);
if (!cookies.empty()) {
- redirectRequest.setHeader(QNetworkRequest::KnownHeaders::CookieHeader,
- QVariant::fromValue(cookies));
+ auto cookieHeader = QNetworkHeadersPrivate::fromCookieList(cookies);
+ newHeaders.replaceOrAppend(QHttpHeaders::WellKnownHeader::Cookie, cookieHeader);
}
}
+ redirectRequest.setHeaders(std::move(newHeaders));
+
if (httpRequest.redirectPolicy() != QNetworkRequest::UserVerifiedRedirectPolicy)
followRedirect();
@@ -1293,8 +1305,7 @@ void QNetworkReplyHttpImplPrivate::followRedirect()
Q_ASSERT(managerPrivate);
decompressHelper.clear();
- rawHeaders.clear();
- cookedHeaders.clear();
+ clearHeaders();
if (managerPrivate->thread)
managerPrivate->thread->disconnect();
@@ -1317,7 +1328,7 @@ void QNetworkReplyHttpImplPrivate::checkForRedirect(const int statusCode)
// What do we do about the caching of the HTML note?
// The response to a 303 MUST NOT be cached, while the response to
// all of the others is cacheable if the headers indicate it to be
- QByteArray header = q->rawHeader(locationHeader());
+ QByteArrayView header = q->headers().value(locationHeader());
QUrl url = QUrl(QString::fromUtf8(header));
if (!url.isValid())
url = QUrl(QLatin1StringView(header));
@@ -1361,20 +1372,19 @@ void QNetworkReplyHttpImplPrivate::replyDownloadMetaData(const QHttpHeaders &hm,
// A user having manually defined which encodings they accept is, for
// somwehat unknown (presumed legacy compatibility) reasons treated as
// disabling our decompression:
- const bool autoDecompress = request.rawHeader("accept-encoding").isEmpty();
+ const bool autoDecompress = !request.headers().contains(QHttpHeaders::WellKnownHeader::AcceptEncoding);
const bool shouldDecompress = isCompressed && autoDecompress;
// reconstruct the HTTP header
+ auto h = q->headers();
for (qsizetype i = 0; i < hm.size(); ++i) {
const auto key = hm.nameAt(i);
const auto originValue = hm.valueAt(i);
- QByteArray value = q->rawHeader(key);
-
// Reset any previous "location" header set in the reply. In case of
// redirects, we don't want to 'append' multiple location header values,
// rather we keep only the latest one
- if (key == locationHeader())
- value.clear();
+ if (key.compare(locationHeader(), Qt::CaseInsensitive) == 0)
+ h.removeAll(key);
if (shouldDecompress && !decompressHelper.isValid() && key == "content-encoding"_L1) {
if (!synchronous) // with synchronous all the data is expected to be handled at once
@@ -1390,17 +1400,9 @@ void QNetworkReplyHttpImplPrivate::replyDownloadMetaData(const QHttpHeaders &hm,
request.decompressedSafetyCheckThreshold());
}
- if (!value.isEmpty()) {
- // Why are we appending values for headers which are already
- // present?
- if (key == "set-cookie"_L1)
- value += '\n';
- else
- value += ", ";
- }
- value += originValue;
- q->setRawHeader({key.data(), key.size()}, value);
+ h.append(key, originValue);
}
+ q->setHeaders(std::move(h));
q->setAttribute(QNetworkRequest::HttpStatusCodeAttribute, statusCode);
q->setAttribute(QNetworkRequest::HttpReasonPhraseAttribute, reasonPhrase);
@@ -1415,13 +1417,10 @@ void QNetworkReplyHttpImplPrivate::replyDownloadMetaData(const QHttpHeaders &hm,
QAbstractNetworkCache *nc = managerPrivate->networkCache;
if (nc) {
QNetworkCacheMetaData metaData = nc->metaData(httpRequest.url());
- QNetworkHeadersPrivate cacheHeaders;
- cacheHeaders.setAllRawHeaders(metaData.rawHeaders());
- QNetworkHeadersPrivate::RawHeadersList::ConstIterator it;
- it = cacheHeaders.findRawHeader(cacheControlName());
+ auto value = metaData.headers().value(QHttpHeaders::WellKnownHeader::CacheControl);
bool mustReValidate = false;
- if (it != cacheHeaders.rawHeaders.constEnd()) {
- QHash<QByteArray, QByteArray> cacheControl = parseHttpOptionHeader(it->second);
+ if (!value.empty()) {
+ QHash<QByteArray, QByteArray> cacheControl = parseHttpOptionHeader(value);
if (cacheControl.contains("must-revalidate"_ba))
mustReValidate = true;
}
@@ -1660,16 +1659,21 @@ bool QNetworkReplyHttpImplPrivate::sendCacheContents(const QNetworkCacheMetaData
q->setAttribute(QNetworkRequest::HttpReasonPhraseAttribute, attributes.value(QNetworkRequest::HttpReasonPhraseAttribute));
q->setAttribute(QNetworkRequest::SourceIsFromCacheAttribute, true);
- QNetworkCacheMetaData::RawHeaderList rawHeaders = metaData.rawHeaders();
- QNetworkCacheMetaData::RawHeaderList::ConstIterator it = rawHeaders.constBegin(),
- end = rawHeaders.constEnd();
+ QHttpHeaders cachedHeaders = metaData.headers();
+ QHttpHeaders h = headers();
QUrl redirectUrl;
- for ( ; it != end; ++it) {
- if (httpRequest.isFollowRedirects() &&
- !it->first.compare(locationHeader(), Qt::CaseInsensitive))
- redirectUrl = QUrl::fromEncoded(it->second);
- setRawHeader(it->first, it->second);
+ for (qsizetype i = 0; i < cachedHeaders.size(); ++i) {
+ const auto name = cachedHeaders.nameAt(i);
+ const auto value = cachedHeaders.valueAt(i);
+
+ if (httpRequest.isFollowRedirects()
+ && !name.compare(locationHeader(), Qt::CaseInsensitive)) {
+ redirectUrl = QUrl::fromEncoded(value);
+ }
+
+ h.replaceOrAppend(name, value);
}
+ setHeaders(std::move(h));
if (!isHttpRedirectResponse())
checkForRedirect(status);
@@ -1729,17 +1733,17 @@ QNetworkCacheMetaData QNetworkReplyHttpImplPrivate::fetchCacheMetaData(const QNe
Q_Q(const QNetworkReplyHttpImpl);
QNetworkCacheMetaData metaData = oldMetaData;
+ QHttpHeaders cacheHeaders = metaData.headers();
- QNetworkHeadersPrivate cacheHeaders;
- cacheHeaders.setAllRawHeaders(metaData.rawHeaders());
- QNetworkHeadersPrivate::RawHeadersList::ConstIterator it;
+ const auto newHeaders = q->headers();
+ for (qsizetype i = 0; i < newHeaders.size(); ++i) {
+ const auto name = newHeaders.nameAt(i);
+ const auto value = newHeaders.valueAt(i);
- const QList<QByteArray> newHeaders = q->rawHeaderList();
- for (const QByteArray& header : newHeaders) {
- if (isHopByHop(header))
+ if (isHopByHop(name))
continue;
- if (header.compare("set-cookie", Qt::CaseInsensitive) == 0)
+ if (name.compare("set-cookie", Qt::CaseInsensitive) == 0)
continue;
// for 4.6.0, we were planning to not store the date header in the
@@ -1752,50 +1756,46 @@ QNetworkCacheMetaData QNetworkReplyHttpImplPrivate::fetchCacheMetaData(const QNe
//continue;
// Don't store Warning 1xx headers
- if (header.compare("warning", Qt::CaseInsensitive) == 0) {
- const QByteArray v = q->rawHeader(header);
- if (v.size() == 3
- && v[0] == '1'
- && isAsciiDigit(v[1])
- && isAsciiDigit(v[2]))
+ if (name.compare("warning", Qt::CaseInsensitive) == 0) {
+ if (value.size() == 3
+ && value[0] == '1'
+ && isAsciiDigit(value[1])
+ && isAsciiDigit(value[2]))
continue;
}
- it = cacheHeaders.findRawHeader(header);
- if (it != cacheHeaders.rawHeaders.constEnd()) {
+ if (cacheHeaders.contains(name)) {
// Match the behavior of Firefox and assume Cache-Control: "no-transform"
constexpr QByteArrayView headers[]=
{"content-encoding", "content-range", "content-type"};
- if (std::any_of(std::begin(headers), std::end(headers), caseInsensitiveCompare(header)))
+ if (std::any_of(std::begin(headers), std::end(headers), caseInsensitiveCompare(name)))
continue;
}
// IIS has been known to send "Content-Length: 0" on 304 responses, so
// ignore this too
- if (statusCode == 304 && header.compare("content-length", Qt::CaseInsensitive) == 0)
+ if (statusCode == 304 && name.compare("content-length", Qt::CaseInsensitive) == 0)
continue;
#if defined(QNETWORKACCESSHTTPBACKEND_DEBUG)
- QByteArray n = q->rawHeader(header);
- QByteArray o;
- if (it != cacheHeaders.rawHeaders.constEnd())
- o = (*it).second;
- if (n != o && headerheader.compare("date", Qt::CaseInsensitive) != 0) {
- qDebug() << "replacing" << header;
+ QByteArrayView n = newHeaders.value(name);
+ QByteArrayView o = cacheHeaders.value(name);
+ if (n != o && name.compare("date", Qt::CaseInsensitive) != 0) {
+ qDebug() << "replacing" << name;
qDebug() << "new" << n;
qDebug() << "old" << o;
}
#endif
- cacheHeaders.setRawHeader(header, q->rawHeader(header));
+ cacheHeaders.replaceOrAppend(name, value);
}
- metaData.setRawHeaders(cacheHeaders.rawHeaders);
+ metaData.setHeaders(cacheHeaders);
bool checkExpired = true;
QHash<QByteArray, QByteArray> cacheControl;
- it = cacheHeaders.findRawHeader(cacheControlName());
- if (it != cacheHeaders.rawHeaders.constEnd()) {
- cacheControl = parseHttpOptionHeader(it->second);
+ auto value = cacheHeaders.value(QHttpHeaders::WellKnownHeader::CacheControl);
+ if (!value.empty()) {
+ cacheControl = parseHttpOptionHeader(value);
QByteArray maxAge = cacheControl.value("max-age"_ba);
if (!maxAge.isEmpty()) {
checkExpired = false;
@@ -1805,16 +1805,18 @@ QNetworkCacheMetaData QNetworkReplyHttpImplPrivate::fetchCacheMetaData(const QNe
}
}
if (checkExpired) {
- it = cacheHeaders.findRawHeader("expires");
- if (it != cacheHeaders.rawHeaders.constEnd()) {
- QDateTime expiredDateTime = QNetworkHeadersPrivate::fromHttpDate(it->second);
+ if (const auto value = cacheHeaders.value(
+ QHttpHeaders::WellKnownHeader::Expires); !value.isEmpty()) {
+ QDateTime expiredDateTime = QNetworkHeadersPrivate::fromHttpDate(value);
metaData.setExpirationDate(expiredDateTime);
}
}
- it = cacheHeaders.findRawHeader("last-modified");
- if (it != cacheHeaders.rawHeaders.constEnd())
- metaData.setLastModified(QNetworkHeadersPrivate::fromHttpDate(it->second));
+ if (const auto value = cacheHeaders.value(
+ QHttpHeaders::WellKnownHeader::LastModified); !value.isEmpty()) {
+ metaData.setLastModified(QNetworkHeadersPrivate::fromHttpDate(value));
+ }
+
bool canDiskCache;
// only cache GET replies by default, all other replies (POST, PUT, DELETE)
@@ -1862,14 +1864,16 @@ bool QNetworkReplyHttpImplPrivate::canResume() const
if (operation != QNetworkAccessManager::GetOperation)
return false;
+ const auto h = q->headers();
+
// Can only resume if server/resource supports Range header.
- constexpr auto acceptRangesheaderName = QByteArrayView("Accept-Ranges");
- if (!q->hasRawHeader(acceptRangesheaderName) || q->rawHeader(acceptRangesheaderName) == "none")
+ const auto acceptRanges = h.value(QHttpHeaders::WellKnownHeader::AcceptRanges);
+ if (acceptRanges.empty() || acceptRanges == "none")
return false;
// We only support resuming for byte ranges.
- if (request.hasRawHeader(rangeName())) {
- QByteArray range = request.rawHeader(rangeName());
+ const auto range = h.value(QHttpHeaders::WellKnownHeader::Range);
+ if (!range.empty()) {
if (!range.startsWith(bytesEqualPrefix()))
return false;
}
@@ -1918,8 +1922,8 @@ void QNetworkReplyHttpImplPrivate::_q_cacheLoadReadyRead()
// Needs to be done where sendCacheContents() (?) of HTTP is emitting
// metaDataChanged ?
-
- QVariant totalSize = cookedHeaders.value(QNetworkRequest::ContentLengthHeader);
+ const auto totalSizeOpt = QNetworkHeadersPrivate::toInt(
+ headers().value(QHttpHeaders::WellKnownHeader::ContentLength));
// emit readyRead before downloadProgress in case this will cause events to be
// processed and we get into a recursive call (as in QProgressDialog).
@@ -1930,8 +1934,7 @@ void QNetworkReplyHttpImplPrivate::_q_cacheLoadReadyRead()
if (downloadProgressSignalChoke.elapsed() >= progressSignalInterval) {
downloadProgressSignalChoke.restart();
- emit q->downloadProgress(bytesDownloaded,
- totalSize.isNull() ? Q_INT64_C(-1) : totalSize.toLongLong());
+ emit q->downloadProgress(bytesDownloaded, totalSizeOpt.value_or(-1));
}
}
@@ -2118,11 +2121,13 @@ void QNetworkReplyHttpImplPrivate::finished()
if (state == Finished || state == Aborted)
return;
- QVariant totalSize = cookedHeaders.value(QNetworkRequest::ContentLengthHeader);
+ const auto totalSizeOpt = QNetworkHeadersPrivate::toInt(
+ headers().value(QHttpHeaders::WellKnownHeader::ContentLength));
+ const qint64 totalSize = totalSizeOpt.value_or(-1);
// if we don't know the total size of or we received everything save the cache.
// If the data is compressed then this is done in readData()
- if ((totalSize.isNull() || totalSize == -1 || bytesDownloaded == totalSize)
+ if ((totalSize == -1 || bytesDownloaded == totalSize)
&& !decompressHelper.isValid()) {
completeCacheSave();
}
@@ -2135,10 +2140,10 @@ void QNetworkReplyHttpImplPrivate::finished()
state = Finished;
q->setFinished(true);
- if (totalSize.isNull() || totalSize == -1) {
+ if (totalSize == -1) {
emit q->downloadProgress(bytesDownloaded, bytesDownloaded);
} else {
- emit q->downloadProgress(bytesDownloaded, totalSize.toLongLong());
+ emit q->downloadProgress(bytesDownloaded, totalSize);
}
if (bytesUploaded == -1 && (outgoingData || outgoingDataBuffer))
@@ -2182,14 +2187,15 @@ void QNetworkReplyHttpImplPrivate::_q_metaDataChanged()
// 1. do we have cookies?
// 2. are we allowed to set them?
Q_ASSERT(manager);
- const auto it = cookedHeaders.constFind(QNetworkRequest::SetCookieHeader);
- if (it != cookedHeaders.cend()
+
+ const auto cookiesOpt = QNetworkHeadersPrivate::toSetCookieList(
+ headers().values(QHttpHeaders::WellKnownHeader::SetCookie));
+ const auto cookies = cookiesOpt.value_or(QList<QNetworkCookie>());
+ if (!cookies.empty()
&& request.attribute(QNetworkRequest::CookieSaveControlAttribute,
QNetworkRequest::Automatic).toInt() == QNetworkRequest::Automatic) {
QNetworkCookieJar *jar = manager->cookieJar();
if (jar) {
- QList<QNetworkCookie> cookies =
- qvariant_cast<QList<QNetworkCookie> >(it.value());
jar->setCookiesFromUrl(cookies, url);
}
}
diff --git a/src/network/access/qnetworkreplyimpl.cpp b/src/network/access/qnetworkreplyimpl.cpp
index 8b2acfdb4e..b90ec1cc4c 100644
--- a/src/network/access/qnetworkreplyimpl.cpp
+++ b/src/network/access/qnetworkreplyimpl.cpp
@@ -118,15 +118,16 @@ void QNetworkReplyImplPrivate::_q_copyReadyRead()
return;
}
- QVariant totalSize = cookedHeaders.value(QNetworkRequest::ContentLengthHeader);
+ const auto totalSizeOpt = QNetworkHeadersPrivate::toInt(
+ headers().value(QHttpHeaders::WellKnownHeader::ContentLength));
+
pauseNotificationHandling();
// emit readyRead before downloadProgress in case this will cause events to be
// processed and we get into a recursive call (as in QProgressDialog).
emit q->readyRead();
if (downloadProgressSignalChoke.elapsed() >= progressSignalInterval) {
downloadProgressSignalChoke.restart();
- emit q->downloadProgress(bytesDownloaded,
- totalSize.isNull() ? Q_INT64_C(-1) : totalSize.toLongLong());
+ emit q->downloadProgress(bytesDownloaded, totalSizeOpt.value_or(-1));
}
resumeNotificationHandling();
}
@@ -243,7 +244,10 @@ void QNetworkReplyImplPrivate::setup(QNetworkAccessManager::Operation op, const
if (bufferingDisallowed) {
// if a valid content-length header for the request was supplied, we can disable buffering
// if not, we will buffer anyway
- if (req.header(QNetworkRequest::ContentLengthHeader).isValid()) {
+ const auto sizeOpt = QNetworkHeadersPrivate::toInt(
+ headers().value(QHttpHeaders::WellKnownHeader::ContentLength));
+
+ if (sizeOpt) {
QMetaObject::invokeMethod(q, "_q_startOperation", Qt::QueuedConnection);
} else {
state = Buffering;
@@ -478,7 +482,8 @@ void QNetworkReplyImplPrivate::appendDownstreamDataSignalEmissions()
{
Q_Q(QNetworkReplyImpl);
- QVariant totalSize = cookedHeaders.value(QNetworkRequest::ContentLengthHeader);
+ const auto totalSizeOpt = QNetworkHeadersPrivate::toInt(
+ headers().value(QHttpHeaders::WellKnownHeader::ContentLength));
pauseNotificationHandling();
// important: At the point of this readyRead(), the data parameter list must be empty,
// else implicit sharing will trigger memcpy when the user is reading data!
@@ -487,8 +492,7 @@ void QNetworkReplyImplPrivate::appendDownstreamDataSignalEmissions()
// processed and we get into a recursive call (as in QProgressDialog).
if (downloadProgressSignalChoke.elapsed() >= progressSignalInterval) {
downloadProgressSignalChoke.restart();
- emit q->downloadProgress(bytesDownloaded,
- totalSize.isNull() ? Q_INT64_C(-1) : totalSize.toLongLong());
+ emit q->downloadProgress(bytesDownloaded, totalSizeOpt.value_or(-1));
}
resumeNotificationHandling();
@@ -589,7 +593,9 @@ void QNetworkReplyImplPrivate::finished()
return;
pauseNotificationHandling();
- QVariant totalSize = cookedHeaders.value(QNetworkRequest::ContentLengthHeader);
+ const auto totalSizeOpt = QNetworkHeadersPrivate::toInt(
+ headers().value(QHttpHeaders::WellKnownHeader::ContentLength));
+ const auto totalSize = totalSizeOpt.value_or(-1);
resumeNotificationHandling();
@@ -599,10 +605,10 @@ void QNetworkReplyImplPrivate::finished()
pendingNotifications.clear();
pauseNotificationHandling();
- if (totalSize.isNull() || totalSize == -1) {
+ if (totalSize == -1) {
emit q->downloadProgress(bytesDownloaded, bytesDownloaded);
} else {
- emit q->downloadProgress(bytesDownloaded, totalSize.toLongLong());
+ emit q->downloadProgress(bytesDownloaded, totalSize);
}
if (bytesUploaded == -1 && (outgoingData || outgoingDataBuffer))
@@ -610,7 +616,7 @@ void QNetworkReplyImplPrivate::finished()
resumeNotificationHandling();
// if we don't know the total size of or we received everything save the cache
- if (totalSize.isNull() || totalSize == -1 || bytesDownloaded == totalSize)
+ if (totalSize == -1 || bytesDownloaded == totalSize)
completeCacheSave();
// note: might not be a good idea, since users could decide to delete us
@@ -646,14 +652,14 @@ void QNetworkReplyImplPrivate::metaDataChanged()
// 1. do we have cookies?
// 2. are we allowed to set them?
if (!manager.isNull()) {
- const auto it = cookedHeaders.constFind(QNetworkRequest::SetCookieHeader);
- if (it != cookedHeaders.cend()
+ const auto cookiesOpt = QNetworkHeadersPrivate::toSetCookieList(
+ headers().values(QHttpHeaders::WellKnownHeader::SetCookie));
+ const auto cookies = cookiesOpt.value_or(QList<QNetworkCookie>());
+ if (!cookies.empty()
&& request.attribute(QNetworkRequest::CookieSaveControlAttribute,
QNetworkRequest::Automatic).toInt() == QNetworkRequest::Automatic) {
QNetworkCookieJar *jar = manager->cookieJar();
if (jar) {
- QList<QNetworkCookie> cookies =
- qvariant_cast<QList<QNetworkCookie> >(it.value());
jar->setCookiesFromUrl(cookies, url);
}
}
@@ -857,9 +863,10 @@ qint64 QNetworkReplyImpl::readData(char *data, qint64 maxlen)
break;
}
}
- QVariant totalSize = d->cookedHeaders.value(QNetworkRequest::ContentLengthHeader);
- emit downloadProgress(bytesRead,
- totalSize.isNull() ? Q_INT64_C(-1) : totalSize.toLongLong());
+
+ const auto totalSizeOpt = QNetworkHeadersPrivate::toInt(
+ headers().value(QHttpHeaders::WellKnownHeader::ContentLength));
+ emit downloadProgress(bytesRead, totalSizeOpt.value_or(-1));
return bytesRead;
} else if (d->backend && d->backend->bytesAvailable()) {
return d->backend->read(data, maxlen);
diff --git a/src/network/access/qnetworkreplywasmimpl.cpp b/src/network/access/qnetworkreplywasmimpl.cpp
index c02f0b4e61..7d2b6a701e 100644
--- a/src/network/access/qnetworkreplywasmimpl.cpp
+++ b/src/network/access/qnetworkreplywasmimpl.cpp
@@ -9,6 +9,7 @@
#include <QtCore/qcoreapplication.h>
#include <QtCore/qfileinfo.h>
#include <QtCore/qthread.h>
+#include <QtCore/private/qeventdispatcher_wasm_p.h>
#include <QtCore/private/qoffsetstringarray_p.h>
#include <QtCore/private/qtools_p.h>
@@ -63,11 +64,27 @@ QNetworkReplyWasmImplPrivate::QNetworkReplyWasmImplPrivate()
, totalDownloadSize(0)
, percentFinished(0)
, m_fetch(nullptr)
+ , m_fetchContext(nullptr)
{
}
QNetworkReplyWasmImplPrivate::~QNetworkReplyWasmImplPrivate()
{
+
+ if (m_fetchContext) { // fetch has been initiated
+ std::unique_lock lock{ m_fetchContext->mutex };
+
+ if (m_fetchContext->state == FetchContext::State::SCHEDULED
+ || m_fetchContext->state == FetchContext::State::SENT
+ || m_fetchContext->state == FetchContext::State::CANCELED) {
+ m_fetchContext->reply = nullptr;
+ m_fetchContext->state = FetchContext::State::TO_BE_DESTROYED;
+ } else if (m_fetchContext->state == FetchContext::State::FINISHED) {
+ lock.unlock();
+ delete m_fetchContext;
+ }
+ }
+
}
QNetworkReplyWasmImpl::QNetworkReplyWasmImpl(QObject *parent)
@@ -116,7 +133,7 @@ void QNetworkReplyWasmImpl::close()
d->state = QNetworkReplyPrivate::Finished;
d->setCanceled();
}
-
+ emscripten_fetch_close(d->m_fetch);
QNetworkReply::close();
}
@@ -134,8 +151,14 @@ void QNetworkReplyWasmImpl::abort()
void QNetworkReplyWasmImplPrivate::setCanceled()
{
Q_Q(QNetworkReplyWasmImpl);
- if (m_fetch)
- m_fetch->userData = nullptr;
+ {
+ if (m_fetchContext) {
+ std::scoped_lock lock{ m_fetchContext->mutex };
+ if (m_fetchContext->state == FetchContext::State::SCHEDULED
+ || m_fetchContext->state == FetchContext::State::SENT)
+ m_fetchContext->state = FetchContext::State::CANCELED;
+ }
+ }
emitReplyError(QNetworkReply::OperationCanceledError, QStringLiteral("Operation canceled"));
q->setFinished(true);
@@ -227,48 +250,7 @@ void QNetworkReplyWasmImplPrivate::doSendRequest()
emscripten_fetch_attr_t attr;
emscripten_fetch_attr_init(&attr);
- strcpy(attr.requestMethod, q->methodName().constData());
-
- QList<QByteArray> headersData = request.rawHeaderList();
- int arrayLength = getArraySize(headersData.count());
- const char *customHeaders[arrayLength];
- QStringList trimmedHeaders;
-
- if (headersData.count() > 0) {
- int i = 0;
- for (const auto &headerName : headersData) {
- if (isUnsafeHeader(QLatin1StringView(headerName.constData()))) {
- trimmedHeaders.push_back(QString::fromLatin1(headerName));
- } else {
- customHeaders[i++] = headerName.constData();
- customHeaders[i++] = request.rawHeader(headerName).constData();
- }
- }
- if (!trimmedHeaders.isEmpty()) {
- qWarning() << "Qt has trimmed the following forbidden headers from the request:"
- << trimmedHeaders.join(QLatin1StringView(", "));
- }
- customHeaders[i] = nullptr;
- attr.requestHeaders = customHeaders;
- }
-
- if (outgoingData) { // data from post request
- // handle extra data
- requestData = outgoingData->readAll(); // is there a size restriction here?
- if (!requestData.isEmpty()) {
- attr.requestData = requestData.data();
- attr.requestDataSize = requestData.size();
- }
- }
-
- QByteArray userName, password;
- // username & password
- if (!request.url().userInfo().isEmpty()) {
- userName = request.url().userName().toUtf8();
- password = request.url().password().toUtf8();
- attr.userName = userName.constData();
- attr.password = password.constData();
- }
+ qstrncpy(attr.requestMethod, q->methodName().constData(), 32); // requestMethod is char[32] in emscripten
attr.attributes = EMSCRIPTEN_FETCH_LOAD_TO_MEMORY;
@@ -293,13 +275,67 @@ void QNetworkReplyWasmImplPrivate::doSendRequest()
attr.onprogress = QNetworkReplyWasmImplPrivate::downloadProgress;
attr.onreadystatechange = QNetworkReplyWasmImplPrivate::stateChange;
attr.timeoutMSecs = request.transferTimeout();
- attr.userData = reinterpret_cast<void *>(this);
- QString dPath = "/home/web_user/"_L1 + request.url().fileName();
- QByteArray destinationPath = dPath.toUtf8();
- attr.destinationPath = destinationPath.constData();
+ m_fetchContext = new FetchContext(this);;
+ attr.userData = static_cast<void *>(m_fetchContext);
+ if (outgoingData) { // data from post request
+ m_fetchContext->requestData = outgoingData->readAll(); // is there a size restriction here?
+ if (!m_fetchContext->requestData.isEmpty()) {
+ attr.requestData = m_fetchContext->requestData.data();
+ attr.requestDataSize = m_fetchContext->requestData.size();
+ }
+ }
- m_fetch = emscripten_fetch(&attr, request.url().toString().toUtf8().constData());
+ QEventDispatcherWasm::runOnMainThread([attr, fetchContext = m_fetchContext]() mutable {
+ std::unique_lock lock{ fetchContext->mutex };
+ if (fetchContext->state == FetchContext::State::CANCELED) {
+ fetchContext->state = FetchContext::State::FINISHED;
+ return;
+ } else if (fetchContext->state == FetchContext::State::TO_BE_DESTROYED) {
+ lock.unlock();
+ delete fetchContext;
+ return;
+ }
+ const auto reply = fetchContext->reply;
+ const auto &request = reply->request;
+
+ QByteArray userName, password;
+ if (!request.url().userInfo().isEmpty()) {
+ userName = request.url().userName().toUtf8();
+ password = request.url().password().toUtf8();
+ attr.userName = userName.constData();
+ attr.password = password.constData();
+ }
+
+ QList<QByteArray> headersData = request.rawHeaderList();
+ int arrayLength = getArraySize(headersData.count());
+ const char *customHeaders[arrayLength];
+ QStringList trimmedHeaders;
+ if (headersData.count() > 0) {
+ int i = 0;
+ for (const auto &headerName : headersData) {
+ if (isUnsafeHeader(QLatin1StringView(headerName.constData()))) {
+ trimmedHeaders.push_back(QString::fromLatin1(headerName));
+ } else {
+ customHeaders[i++] = headerName.constData();
+ customHeaders[i++] = request.rawHeader(headerName).constData();
+ }
+ }
+ if (!trimmedHeaders.isEmpty()) {
+ qWarning() << "Qt has trimmed the following forbidden headers from the request:"
+ << trimmedHeaders.join(QLatin1StringView(", "));
+ }
+ customHeaders[i] = nullptr;
+ attr.requestHeaders = customHeaders;
+ }
+
+ auto url = request.url().toString().toUtf8();
+ QString dPath = "/home/web_user/"_L1 + request.url().fileName();
+ QByteArray destinationPath = dPath.toUtf8();
+ attr.destinationPath = destinationPath.constData();
+ reply->m_fetch = emscripten_fetch(&attr, url.constData());
+ fetchContext->state = FetchContext::State::SENT;
+ });
state = Working;
}
@@ -477,8 +513,18 @@ void QNetworkReplyWasmImplPrivate::_q_bufferOutgoingData()
void QNetworkReplyWasmImplPrivate::downloadSucceeded(emscripten_fetch_t *fetch)
{
- auto reply = reinterpret_cast<QNetworkReplyWasmImplPrivate*>(fetch->userData);
- if (reply) {
+ auto fetchContext = static_cast<FetchContext *>(fetch->userData);
+ std::unique_lock lock{ fetchContext->mutex };
+
+ if (fetchContext->state == FetchContext::State::TO_BE_DESTROYED) {
+ lock.unlock();
+ delete fetchContext;
+ return;
+ } else if (fetchContext->state == FetchContext::State::CANCELED) {
+ fetchContext->state = FetchContext::State::FINISHED;
+ return;
+ } else if (fetchContext->state == FetchContext::State::SENT) {
+ const auto reply = fetchContext->reply;
if (reply->state != QNetworkReplyPrivate::Aborted) {
QByteArray buffer(fetch->data, fetch->numBytes);
reply->dataReceived(buffer);
@@ -487,8 +533,8 @@ void QNetworkReplyWasmImplPrivate::downloadSucceeded(emscripten_fetch_t *fetch)
reply->setReplyFinished();
}
reply->m_fetch = nullptr;
+ fetchContext->state = FetchContext::State::FINISHED;
}
- emscripten_fetch_close(fetch);
}
void QNetworkReplyWasmImplPrivate::setReplyFinished()
@@ -509,7 +555,8 @@ void QNetworkReplyWasmImplPrivate::setStatusCode(int status, const QByteArray &s
void QNetworkReplyWasmImplPrivate::stateChange(emscripten_fetch_t *fetch)
{
- auto reply = reinterpret_cast<QNetworkReplyWasmImplPrivate*>(fetch->userData);
+ const auto fetchContext = static_cast<FetchContext*>(fetch->userData);
+ const auto reply = fetchContext->reply;
if (reply && reply->state != QNetworkReplyPrivate::Aborted) {
if (fetch->readyState == /*HEADERS_RECEIVED*/ 2) {
size_t headerLength = emscripten_fetch_get_response_headers_length(fetch);
@@ -522,7 +569,8 @@ void QNetworkReplyWasmImplPrivate::stateChange(emscripten_fetch_t *fetch)
void QNetworkReplyWasmImplPrivate::downloadProgress(emscripten_fetch_t *fetch)
{
- auto reply = reinterpret_cast<QNetworkReplyWasmImplPrivate*>(fetch->userData);
+ const auto fetchContext = static_cast<FetchContext*>(fetch->userData);
+ const auto reply = fetchContext->reply;
if (reply && reply->state != QNetworkReplyPrivate::Aborted) {
if (fetch->status < 400) {
uint64_t bytes = fetch->dataOffset + fetch->numBytes;
@@ -536,8 +584,18 @@ void QNetworkReplyWasmImplPrivate::downloadProgress(emscripten_fetch_t *fetch)
void QNetworkReplyWasmImplPrivate::downloadFailed(emscripten_fetch_t *fetch)
{
- auto reply = reinterpret_cast<QNetworkReplyWasmImplPrivate*>(fetch->userData);
- if (reply) {
+ const auto fetchContext = static_cast<FetchContext*>(fetch->userData);
+ std::unique_lock lock{ fetchContext->mutex };
+
+ if (fetchContext->state == FetchContext::State::TO_BE_DESTROYED) {
+ lock.unlock();
+ delete fetchContext;
+ return;
+ } else if (fetchContext->state == FetchContext::State::CANCELED) {
+ fetchContext->state = FetchContext::State::FINISHED;
+ return;
+ } else if (fetchContext->state == FetchContext::State::SENT) {
+ const auto reply = fetchContext->reply;
if (reply->state != QNetworkReplyPrivate::Aborted) {
QString reasonStr;
if (fetch->status > 600)
@@ -548,12 +606,13 @@ void QNetworkReplyWasmImplPrivate::downloadFailed(emscripten_fetch_t *fetch)
reply->dataReceived(buffer);
QByteArray statusText(fetch->statusText);
reply->setStatusCode(fetch->status, statusText);
- reply->emitReplyError(reply->statusCodeFromHttp(fetch->status, reply->request.url()), reasonStr);
+ reply->emitReplyError(reply->statusCodeFromHttp(fetch->status, reply->request.url()),
+ reasonStr);
reply->setReplyFinished();
}
reply->m_fetch = nullptr;
+ fetchContext->state = FetchContext::State::FINISHED;
}
- emscripten_fetch_close(fetch);
}
//taken from qhttpthreaddelegate.cpp
diff --git a/src/network/access/qnetworkreplywasmimpl_p.h b/src/network/access/qnetworkreplywasmimpl_p.h
index ae167799d7..4b00bb09ea 100644
--- a/src/network/access/qnetworkreplywasmimpl_p.h
+++ b/src/network/access/qnetworkreplywasmimpl_p.h
@@ -28,6 +28,7 @@
#include <emscripten/fetch.h>
#include <memory>
+#include <mutex>
QT_BEGIN_NAMESPACE
@@ -63,6 +64,29 @@ private:
QByteArray methodName() const;
};
+class QNetworkReplyWasmImplPrivate;
+
+/*!
+ The FetchContext class ensures the requestData object remains valid
+ while a fetch operation is pending. Since Emscripten fetch is asynchronous,
+ requestData must persist until one of the final callbacks is invoked.
+ Additionally, there's a potential race condition between the thread
+ scheduling the fetch operation and the one executing it. Since fetch must
+ occur on the main thread due to browser limitations,
+ a mutex safeguards the FetchContext to ensure atomic state transitions.
+*/
+struct FetchContext
+{
+ enum class State { SCHEDULED, SENT, FINISHED, CANCELED, TO_BE_DESTROYED };
+
+ FetchContext(QNetworkReplyWasmImplPrivate *networkReply) : reply(networkReply) { }
+
+ QNetworkReplyWasmImplPrivate *reply{ nullptr };
+ std::mutex mutex;
+ QByteArray requestData;
+ State state{ State::SCHEDULED };
+};
+
class QNetworkReplyWasmImplPrivate: public QNetworkReplyPrivate
{
public:
@@ -101,7 +125,6 @@ public:
QIODevice *outgoingData;
std::shared_ptr<QRingBuffer> outgoingDataBuffer;
- QByteArray requestData;
static void downloadProgress(emscripten_fetch_t *fetch);
static void downloadFailed(emscripten_fetch_t *fetch);
@@ -111,6 +134,7 @@ public:
static QNetworkReply::NetworkError statusCodeFromHttp(int httpStatusCode, const QUrl &url);
emscripten_fetch_t *m_fetch;
+ FetchContext *m_fetchContext;
void setReplyFinished();
void setCanceled();
diff --git a/src/network/access/qnetworkrequest.cpp b/src/network/access/qnetworkrequest.cpp
index 6f5a7ff19a..7a1b5426d2 100644
--- a/src/network/access/qnetworkrequest.cpp
+++ b/src/network/access/qnetworkrequest.cpp
@@ -6,6 +6,7 @@
#include "qplatformdefs.h"
#include "qnetworkcookie.h"
#include "qsslconfiguration.h"
+#include "qhttpheadershelper_p.h"
#if QT_CONFIG(http)
#include "qhttp1configuration.h"
#include "qhttp2configuration.h"
@@ -16,6 +17,7 @@
#include "QtCore/qlocale.h"
#include "QtCore/qshareddata.h"
#include "QtCore/qtimezone.h"
+#include "QtCore/private/qduplicatetracker_p.h"
#include "QtCore/private/qtools_p.h"
#include <ctype.h>
@@ -24,6 +26,7 @@
#endif
#include <algorithm>
+#include <q20algorithm.h>
QT_BEGIN_NAMESPACE
@@ -109,6 +112,8 @@ QT_IMPL_METATYPE_EXTERN_TAGGED(QNetworkRequest::RedirectPolicy, QNetworkRequest_
\value ServerHeader The Server header received by HTTP clients.
+ \omitvalue NumKnownHeaders
+
\sa header(), setHeader(), rawHeader(), setRawHeader()
*/
@@ -465,7 +470,6 @@ public:
{
return url == other.url &&
priority == other.priority &&
- rawHeaders == other.rawHeaders &&
attributes == other.attributes &&
maxRedirectsAllowed == other.maxRedirectsAllowed &&
peerVerifyName == other.peerVerifyName
@@ -475,6 +479,7 @@ public:
&& decompressedSafetyCheckThreshold == other.decompressedSafetyCheckThreshold
#endif
&& transferTimeout == other.transferTimeout
+ && QHttpHeadersHelper::compareStrict(httpHeaders, other.httpHeaders)
;
// don't compare cookedHeaders
}
@@ -601,6 +606,43 @@ void QNetworkRequest::setUrl(const QUrl &url)
}
/*!
+ \since 6.8
+
+ Returns headers that are set in this network request.
+
+ \sa setHeaders()
+*/
+QHttpHeaders QNetworkRequest::headers() const
+{
+ return d->headers();
+}
+
+/*!
+ \since 6.8
+
+ Sets \a newHeaders as headers in this network request, overriding
+ any previously set headers.
+
+ If some headers correspond to the known headers, the values will
+ be parsed and the corresponding parsed form will also be set.
+
+ \sa headers(), KnownHeaders
+*/
+void QNetworkRequest::setHeaders(QHttpHeaders &&newHeaders)
+{
+ d->setHeaders(std::move(newHeaders));
+}
+
+/*!
+ \overload
+ \since 6.8
+*/
+void QNetworkRequest::setHeaders(const QHttpHeaders &newHeaders)
+{
+ d->setHeaders(newHeaders);
+}
+
+/*!
Returns the value of the known network header \a header if it is
present in this request. If it is not present, returns QVariant()
(i.e., an invalid variant).
@@ -633,7 +675,7 @@ void QNetworkRequest::setHeader(KnownHeaders header, const QVariant &value)
*/
bool QNetworkRequest::hasRawHeader(QAnyStringView headerName) const
{
- return d->findRawHeader(headerName) != d->rawHeaders.constEnd();
+ return d->headers().contains(headerName);
}
/*!
@@ -649,9 +691,7 @@ bool QNetworkRequest::hasRawHeader(QAnyStringView headerName) const
*/
QByteArray QNetworkRequest::rawHeader(QAnyStringView headerName) const
{
- if (const auto it = d->findRawHeader(headerName); it != d->rawHeaders.constEnd())
- return it->second;
- return QByteArray();
+ return d->rawHeader(headerName);
}
/*!
@@ -1038,63 +1078,72 @@ void QNetworkRequest::setTransferTimeout(std::chrono::milliseconds duration)
}
#endif // QT_CONFIG(http) || defined (Q_OS_WASM)
-static QByteArray headerName(QNetworkRequest::KnownHeaders header)
-{
- switch (header) {
- case QNetworkRequest::ContentTypeHeader:
- return "Content-Type"_ba;
-
- case QNetworkRequest::ContentLengthHeader:
- return "Content-Length"_ba;
-
- case QNetworkRequest::LocationHeader:
- return "Location"_ba;
-
- case QNetworkRequest::LastModifiedHeader:
- return "Last-Modified"_ba;
-
- case QNetworkRequest::IfModifiedSinceHeader:
- return "If-Modified-Since"_ba;
+namespace {
- case QNetworkRequest::ETagHeader:
- return "ETag"_ba;
-
- case QNetworkRequest::IfMatchHeader:
- return "If-Match"_ba;
-
- case QNetworkRequest::IfNoneMatchHeader:
- return "If-None-Match"_ba;
-
- case QNetworkRequest::CookieHeader:
- return "Cookie"_ba;
+struct HeaderPair {
+ QHttpHeaders::WellKnownHeader wellKnownHeader;
+ QNetworkRequest::KnownHeaders knownHeader;
+};
- case QNetworkRequest::SetCookieHeader:
- return "Set-Cookie"_ba;
+constexpr bool operator<(const HeaderPair &lhs, const HeaderPair &rhs)
+{
+ return lhs.wellKnownHeader < rhs.wellKnownHeader;
+}
- case QNetworkRequest::ContentDispositionHeader:
- return "Content-Disposition"_ba;
+constexpr bool operator<(const HeaderPair &lhs, QHttpHeaders::WellKnownHeader rhs)
+{
+ return lhs.wellKnownHeader < rhs;
+}
- case QNetworkRequest::UserAgentHeader:
- return "User-Agent"_ba;
+constexpr bool operator<(QHttpHeaders::WellKnownHeader lhs, const HeaderPair &rhs)
+{
+ return lhs < rhs.wellKnownHeader;
+}
- case QNetworkRequest::ServerHeader:
- return "Server"_ba;
+} // anonymous namespace
+
+static constexpr HeaderPair knownHeadersArr[] = {
+ { QHttpHeaders::WellKnownHeader::ContentDisposition, QNetworkRequest::KnownHeaders::ContentDispositionHeader },
+ { QHttpHeaders::WellKnownHeader::ContentLength, QNetworkRequest::KnownHeaders::ContentLengthHeader },
+ { QHttpHeaders::WellKnownHeader::ContentType, QNetworkRequest::KnownHeaders::ContentTypeHeader },
+ { QHttpHeaders::WellKnownHeader::Cookie, QNetworkRequest::KnownHeaders::CookieHeader },
+ { QHttpHeaders::WellKnownHeader::ETag, QNetworkRequest::KnownHeaders::ETagHeader },
+ { QHttpHeaders::WellKnownHeader::IfMatch , QNetworkRequest::KnownHeaders::IfMatchHeader },
+ { QHttpHeaders::WellKnownHeader::IfModifiedSince, QNetworkRequest::KnownHeaders::IfModifiedSinceHeader },
+ { QHttpHeaders::WellKnownHeader::IfNoneMatch, QNetworkRequest::KnownHeaders::IfNoneMatchHeader },
+ { QHttpHeaders::WellKnownHeader::LastModified, QNetworkRequest::KnownHeaders::LastModifiedHeader},
+ { QHttpHeaders::WellKnownHeader::Location, QNetworkRequest::KnownHeaders::LocationHeader},
+ { QHttpHeaders::WellKnownHeader::Server, QNetworkRequest::KnownHeaders::ServerHeader },
+ { QHttpHeaders::WellKnownHeader::SetCookie, QNetworkRequest::KnownHeaders::SetCookieHeader },
+ { QHttpHeaders::WellKnownHeader::UserAgent, QNetworkRequest::KnownHeaders::UserAgentHeader }
+};
- // no default:
- // if new values are added, this will generate a compiler warning
- }
+static_assert(std::size(knownHeadersArr) == size_t(QNetworkRequest::KnownHeaders::NumKnownHeaders));
+static_assert(q20::is_sorted(std::begin(knownHeadersArr), std::end(knownHeadersArr)));
- return QByteArray();
+static std::optional<QNetworkRequest::KnownHeaders> toKnownHeader(QHttpHeaders::WellKnownHeader key)
+{
+ const auto it = std::lower_bound(std::begin(knownHeadersArr), std::end(knownHeadersArr), key);
+ if (it == std::end(knownHeadersArr) || key < *it)
+ return std::nullopt;
+ return it->knownHeader;
}
-static QByteArray makeCookieHeader(const QVariant &value, QNetworkCookie::RawForm type, QByteArrayView separator)
+static std::optional<QHttpHeaders::WellKnownHeader> toWellKnownHeader(QNetworkRequest::KnownHeaders key)
{
- QList<QNetworkCookie> cookies = qvariant_cast<QList<QNetworkCookie> >(value);
- if (cookies.isEmpty() && value.userType() == qMetaTypeId<QNetworkCookie>())
- cookies << qvariant_cast<QNetworkCookie>(value);
+ auto pred = [key](const HeaderPair &pair) { return pair.knownHeader == key; };
+ const auto it = std::find_if(std::begin(knownHeadersArr), std::end(knownHeadersArr), pred);
+ if (it == std::end(knownHeadersArr))
+ return std::nullopt;
+ return it->wellKnownHeader;
+}
+static QByteArray makeCookieHeader(const QList<QNetworkCookie> &cookies,
+ QNetworkCookie::RawForm type,
+ QByteArrayView separator)
+{
QByteArray result;
- for (const QNetworkCookie &cookie : std::as_const(cookies)) {
+ for (const QNetworkCookie &cookie : cookies) {
result += cookie.toRawForm(type);
result += separator;
}
@@ -1103,6 +1152,15 @@ static QByteArray makeCookieHeader(const QVariant &value, QNetworkCookie::RawFor
return result;
}
+static QByteArray makeCookieHeader(const QVariant &value, QNetworkCookie::RawForm type,
+ QByteArrayView separator)
+{
+ const QList<QNetworkCookie> *cookies = get_if<QList<QNetworkCookie>>(&value);
+ if (!cookies)
+ return {};
+ return makeCookieHeader(*cookies, type, separator);
+}
+
static QByteArray headerValue(QNetworkRequest::KnownHeaders header, const QVariant &value)
{
switch (header) {
@@ -1143,9 +1201,10 @@ static QByteArray headerValue(QNetworkRequest::KnownHeaders header, const QVaria
case QNetworkRequest::SetCookieHeader:
return makeCookieHeader(value, QNetworkCookie::Full, ", ");
- }
- return QByteArray();
+ default:
+ Q_UNREACHABLE_RETURN({});
+ }
}
static int parseHeaderName(QByteArrayView headerName)
@@ -1206,7 +1265,7 @@ static int parseHeaderName(QByteArrayView headerName)
return -1; // nothing found
}
-static QVariant parseHttpDate(const QByteArray &raw)
+static QVariant parseHttpDate(QByteArrayView raw)
{
QDateTime dt = QNetworkHeadersPrivate::fromHttpDate(raw);
if (dt.isValid())
@@ -1214,18 +1273,18 @@ static QVariant parseHttpDate(const QByteArray &raw)
return QVariant(); // transform an invalid QDateTime into a null QVariant
}
-static QVariant parseCookieHeader(QByteArrayView raw)
+static QList<QNetworkCookie> parseCookieHeader(QByteArrayView raw)
{
QList<QNetworkCookie> result;
for (auto cookie : QLatin1StringView(raw).tokenize(';'_L1)) {
QList<QNetworkCookie> parsed = QNetworkCookie::parseCookies(cookie.trimmed());
if (parsed.size() != 1)
- return QVariant(); // invalid Cookie: header
+ return {}; // invalid Cookie: header
result += parsed;
}
- return QVariant::fromValue(result);
+ return result;
}
static QVariant parseETag(QByteArrayView raw)
@@ -1241,7 +1300,7 @@ static QVariant parseETag(QByteArrayView raw)
}
template<typename T>
-static QVariant parseMatchImpl(QByteArrayView raw, T op)
+static QStringList parseMatchImpl(QByteArrayView raw, T op)
{
const QByteArrayView trimmedRaw = raw.trimmed();
if (trimmedRaw == "*")
@@ -1256,14 +1315,14 @@ static QVariant parseMatchImpl(QByteArrayView raw, T op)
}
-static QVariant parseIfMatch(QByteArrayView raw)
+static QStringList parseIfMatch(QByteArrayView raw)
{
return parseMatchImpl(raw, [](QByteArrayView element) {
return element.startsWith('"') && element.endsWith('"');
});
}
-static QVariant parseIfNoneMatch(QByteArrayView raw)
+static QStringList parseIfNoneMatch(QByteArrayView raw)
{
return parseMatchImpl(raw, [](QByteArrayView element) {
return (element.startsWith('"') || element.startsWith(R"(W/")")) && element.endsWith('"');
@@ -1271,7 +1330,7 @@ static QVariant parseIfNoneMatch(QByteArrayView raw)
}
-static QVariant parseHeaderValue(QNetworkRequest::KnownHeaders header, const QByteArray &value)
+static QVariant parseHeaderValue(QNetworkRequest::KnownHeaders header, QByteArrayView value)
{
// header is always a valid value
switch (header) {
@@ -1311,40 +1370,127 @@ static QVariant parseHeaderValue(QNetworkRequest::KnownHeaders header, const QBy
return parseIfNoneMatch(value);
case QNetworkRequest::CookieHeader:
- return parseCookieHeader(value);
+ return QVariant::fromValue(parseCookieHeader(value));
case QNetworkRequest::SetCookieHeader:
return QVariant::fromValue(QNetworkCookie::parseCookies(value));
default:
- Q_ASSERT(0);
+ Q_UNREACHABLE_RETURN({});
+ }
+}
+
+static QVariant parseHeaderValue(QNetworkRequest::KnownHeaders header, QList<QByteArray> values)
+{
+ if (values.empty())
+ return QVariant();
+
+ // header is always a valid value
+ switch (header) {
+ case QNetworkRequest::IfMatchHeader: {
+ QStringList res;
+ for (const auto &val : values)
+ res << parseIfMatch(val);
+ return res;
+ }
+ case QNetworkRequest::IfNoneMatchHeader: {
+ QStringList res;
+ for (const auto &val : values)
+ res << parseIfNoneMatch(val);
+ return res;
+ }
+ case QNetworkRequest::CookieHeader: {
+ auto listOpt = QNetworkHeadersPrivate::toCookieList(values);
+ return listOpt.has_value() ? QVariant::fromValue(listOpt.value()) : QVariant();
+ }
+ case QNetworkRequest::SetCookieHeader: {
+ QList<QNetworkCookie> res;
+ for (const auto &val : values)
+ res << QNetworkCookie::parseCookies(val);
+ return QVariant::fromValue(res);
+ }
+ default:
+ return parseHeaderValue(header, values.first());
}
return QVariant();
}
-QNetworkHeadersPrivate::RawHeadersList::ConstIterator
-QNetworkHeadersPrivate::findRawHeader(QAnyStringView key) const
+static bool isSetCookie(QByteArrayView name)
{
- auto isKeyEqual = [key](const auto &headerPair)
- {
- QLatin1StringView name{headerPair.first};
- return QAnyStringView::compare(name, key, Qt::CaseInsensitive) == 0;
- };
- return std::find_if(rawHeaders.begin(), rawHeaders.end(), isKeyEqual);
+ return name.compare(QHttpHeaders::wellKnownHeaderName(QHttpHeaders::WellKnownHeader::SetCookie),
+ Qt::CaseInsensitive) == 0;
+}
+
+static bool isSetCookie(QHttpHeaders::WellKnownHeader name)
+{
+ return name == QHttpHeaders::WellKnownHeader::SetCookie;
}
-QNetworkHeadersPrivate::RawHeadersList QNetworkHeadersPrivate::allRawHeaders() const
+template<class HeaderName>
+static void setFromRawHeader(QHttpHeaders &headers, HeaderName header,
+ QByteArrayView value)
{
- return rawHeaders;
+ headers.removeAll(header);
+
+ if (value.isNull())
+ // only wanted to erase key
+ return;
+
+ if (isSetCookie(header)) {
+ for (auto cookie : QLatin1StringView(value).tokenize('\n'_L1))
+ headers.append(QHttpHeaders::WellKnownHeader::SetCookie, cookie);
+ } else {
+ headers.append(header, value);
+ }
+}
+
+const QNetworkHeadersPrivate::RawHeadersList &QNetworkHeadersPrivate::allRawHeaders() const
+{
+ if (rawHeaderCache.isCached)
+ return rawHeaderCache.headersList;
+
+ rawHeaderCache.headersList = fromHttpToRaw(httpHeaders);
+ rawHeaderCache.isCached = true;
+ return rawHeaderCache.headersList;
}
QList<QByteArray> QNetworkHeadersPrivate::rawHeadersKeys() const
{
+ if (httpHeaders.isEmpty())
+ return {};
+
QList<QByteArray> result;
- result.reserve(rawHeaders.size());
- for (const auto &pair : rawHeaders)
- result << pair.first;
+ result.reserve(httpHeaders.size());
+ QDuplicateTracker<QByteArray> seen(httpHeaders.size());
+ for (qsizetype i = 0; i < httpHeaders.size(); i++) {
+ const auto nameL1 = httpHeaders.nameAt(i);
+ const auto name = QByteArray(nameL1.data(), nameL1.size());
+ if (seen.hasSeen(name))
+ continue;
+
+ result << name;
+ }
+
+ return result;
+}
+
+QByteArray QNetworkHeadersPrivate::rawHeader(QAnyStringView headerName) const
+{
+ QByteArrayView setCookieStr = QHttpHeaders::wellKnownHeaderName(
+ QHttpHeaders::WellKnownHeader::SetCookie);
+ if (QAnyStringView::compare(headerName, setCookieStr, Qt::CaseInsensitive) != 0)
+ return httpHeaders.combinedValue(headerName);
+
+ QByteArray result;
+ const char* separator = "";
+ for (qsizetype i = 0; i < httpHeaders.size(); ++i) {
+ if (QAnyStringView::compare(httpHeaders.nameAt(i), headerName, Qt::CaseInsensitive) == 0) {
+ result.append(separator);
+ result.append(httpHeaders.valueAt(i));
+ separator = "\n";
+ }
+ }
return result;
}
@@ -1354,86 +1500,101 @@ void QNetworkHeadersPrivate::setRawHeader(const QByteArray &key, const QByteArra
// refuse to accept an empty raw header
return;
- setRawHeaderInternal(key, value);
+ setFromRawHeader(httpHeaders, key, value);
parseAndSetHeader(key, value);
-}
-/*!
- \internal
- Sets the internal raw headers list to match \a list. The cooked headers
- will also be updated.
-
- If \a list contains duplicates, they will be stored, but only the first one
- is usually accessed.
-*/
-void QNetworkHeadersPrivate::setAllRawHeaders(const RawHeadersList &list)
-{
- cookedHeaders.clear();
- rawHeaders = list;
-
- for (const auto &[key, value] : std::as_const(rawHeaders))
- parseAndSetHeader(key, value);
+ invalidateHeaderCache();
}
void QNetworkHeadersPrivate::setCookedHeader(QNetworkRequest::KnownHeaders header,
const QVariant &value)
{
- QByteArray name = headerName(header);
- if (name.isEmpty()) {
- // headerName verifies that \a header is a known value
+ const auto wellKnownOpt = toWellKnownHeader(header);
+ if (!wellKnownOpt) {
+ // verifies that \a header is a known value
qWarning("QNetworkRequest::setHeader: invalid header value KnownHeader(%d) received", header);
return;
}
if (value.isNull()) {
- setRawHeaderInternal(name, QByteArray());
+ httpHeaders.removeAll(wellKnownOpt.value());
cookedHeaders.remove(header);
} else {
QByteArray rawValue = headerValue(header, value);
if (rawValue.isEmpty()) {
qWarning("QNetworkRequest::setHeader: QVariant of type %s cannot be used with header %s",
- value.typeName(), name.constData());
+ value.typeName(),
+ QHttpHeaders::wellKnownHeaderName(wellKnownOpt.value()).constData());
return;
}
- setRawHeaderInternal(name, rawValue);
+ setFromRawHeader(httpHeaders, wellKnownOpt.value(), rawValue);
cookedHeaders.insert(header, value);
}
+
+ invalidateHeaderCache();
}
-void QNetworkHeadersPrivate::setRawHeaderInternal(const QByteArray &key, const QByteArray &value)
+QHttpHeaders QNetworkHeadersPrivate::headers() const
{
- auto firstEqualsKey = [&key](const RawHeaderPair &header) {
- return header.first.compare(key, Qt::CaseInsensitive) == 0;
- };
- rawHeaders.removeIf(firstEqualsKey);
+ return httpHeaders;
+}
- if (value.isNull())
- return; // only wanted to erase key
+void QNetworkHeadersPrivate::setHeaders(const QHttpHeaders &newHeaders)
+{
+ httpHeaders = newHeaders;
+ setCookedFromHttp(httpHeaders);
+ invalidateHeaderCache();
+}
+
+void QNetworkHeadersPrivate::setHeaders(QHttpHeaders &&newHeaders)
+{
+ httpHeaders = std::move(newHeaders);
+ setCookedFromHttp(httpHeaders);
+ invalidateHeaderCache();
+}
+
+void QNetworkHeadersPrivate::setHeader(QHttpHeaders::WellKnownHeader name, QByteArrayView value)
+{
+ httpHeaders.replaceOrAppend(name, value);
- RawHeaderPair pair;
- pair.first = key;
- pair.second = value;
- rawHeaders.append(pair);
+ // set cooked header
+ const auto knownHeaderOpt = toKnownHeader(name);
+ if (knownHeaderOpt)
+ parseAndSetHeader(knownHeaderOpt.value(), value);
+
+ invalidateHeaderCache();
+}
+
+void QNetworkHeadersPrivate::clearHeaders()
+{
+ httpHeaders.clear();
+ cookedHeaders.clear();
+ invalidateHeaderCache();
}
-void QNetworkHeadersPrivate::parseAndSetHeader(const QByteArray &key, const QByteArray &value)
+void QNetworkHeadersPrivate::parseAndSetHeader(QByteArrayView key, QByteArrayView value)
{
// is it a known header?
const int parsedKeyAsInt = parseHeaderName(key);
if (parsedKeyAsInt != -1) {
const QNetworkRequest::KnownHeaders parsedKey
= static_cast<QNetworkRequest::KnownHeaders>(parsedKeyAsInt);
- if (value.isNull()) {
- cookedHeaders.remove(parsedKey);
- } else if (parsedKey == QNetworkRequest::ContentLengthHeader
- && cookedHeaders.contains(QNetworkRequest::ContentLengthHeader)) {
- // Only set the cooked header "Content-Length" once.
- // See bug QTBUG-15311
- } else {
- cookedHeaders.insert(parsedKey, parseHeaderValue(parsedKey, value));
- }
+ parseAndSetHeader(parsedKey, value);
+ }
+}
+void QNetworkHeadersPrivate::parseAndSetHeader(QNetworkRequest::KnownHeaders key,
+ QByteArrayView value)
+{
+ if (value.isNull()) {
+ cookedHeaders.remove(key);
+ } else if (key == QNetworkRequest::ContentLengthHeader
+ && cookedHeaders.contains(QNetworkRequest::ContentLengthHeader)) {
+ // Only set the cooked header "Content-Length" once.
+ // See bug QTBUG-15311
+ } else {
+ cookedHeaders.insert(key, parseHeaderValue(key, value));
}
}
@@ -1487,7 +1648,7 @@ static int name_to_month(const char* month_str)
return 0;
}
-QDateTime QNetworkHeadersPrivate::fromHttpDate(const QByteArray &value)
+QDateTime QNetworkHeadersPrivate::fromHttpDate(QByteArrayView value)
{
// HTTP dates have three possible formats:
// RFC 1123/822 - ddd, dd MMM yyyy hh:mm:ss "GMT"
@@ -1536,6 +1697,137 @@ QByteArray QNetworkHeadersPrivate::toHttpDate(const QDateTime &dt)
return QLocale::c().toString(dt.toUTC(), u"ddd, dd MMM yyyy hh:mm:ss 'GMT'").toLatin1();
}
+QNetworkHeadersPrivate::RawHeadersList QNetworkHeadersPrivate::fromHttpToRaw(
+ const QHttpHeaders &headers)
+{
+ if (headers.isEmpty())
+ return {};
+
+ QNetworkHeadersPrivate::RawHeadersList list;
+ QHash<QByteArray, qsizetype> nameToIndex;
+ list.reserve(headers.size());
+ nameToIndex.reserve(headers.size());
+
+ for (qsizetype i = 0; i < headers.size(); ++i) {
+ const auto nameL1 = headers.nameAt(i);
+ const auto value = headers.valueAt(i);
+
+ const bool isSetCookie = nameL1 == QHttpHeaders::wellKnownHeaderName(
+ QHttpHeaders::WellKnownHeader::SetCookie);
+
+ const auto name = QByteArray(nameL1.data(), nameL1.size());
+ if (auto it = nameToIndex.find(name); it != nameToIndex.end()) {
+ list[it.value()].second += isSetCookie ? "\n" : ", ";
+ list[it.value()].second += value;
+ } else {
+ nameToIndex[name] = list.size();
+ list.emplaceBack(name, value.toByteArray());
+ }
+ }
+
+ return list;
+}
+
+QHttpHeaders QNetworkHeadersPrivate::fromRawToHttp(const RawHeadersList &raw)
+{
+ if (raw.empty())
+ return {};
+
+ QHttpHeaders headers;
+ headers.reserve(raw.size());
+
+ for (const auto &[key, value] : raw) {
+ const bool isSetCookie = key.compare(QHttpHeaders::wellKnownHeaderName(
+ QHttpHeaders::WellKnownHeader::SetCookie),
+ Qt::CaseInsensitive) == 0;
+ if (isSetCookie) {
+ for (auto header : QLatin1StringView(value).tokenize('\n'_L1))
+ headers.append(key, header);
+ } else {
+ headers.append(key, value);
+ }
+ }
+
+ return headers;
+}
+
+std::optional<qint64> QNetworkHeadersPrivate::toInt(QByteArrayView value)
+{
+ if (value.empty())
+ return std::nullopt;
+
+ bool ok;
+ qint64 res = value.toLongLong(&ok);
+ if (ok)
+ return res;
+ return std::nullopt;
+}
+
+std::optional<QNetworkHeadersPrivate::NetworkCookieList> QNetworkHeadersPrivate::toSetCookieList(
+ const QList<QByteArray> &values)
+{
+ if (values.empty())
+ return std::nullopt;
+
+ QList<QNetworkCookie> cookies;
+ for (const auto &s : values)
+ cookies += QNetworkCookie::parseCookies(s);
+
+ if (cookies.empty())
+ return std::nullopt;
+ return cookies;
+}
+
+QByteArray QNetworkHeadersPrivate::fromCookieList(const QList<QNetworkCookie> &cookies)
+{
+ return makeCookieHeader(cookies, QNetworkCookie::NameAndValueOnly, "; ");
+}
+
+std::optional<QNetworkHeadersPrivate::NetworkCookieList> QNetworkHeadersPrivate::toCookieList(
+ const QList<QByteArray> &values)
+{
+ if (values.empty())
+ return std::nullopt;
+
+ QList<QNetworkCookie> cookies;
+ for (const auto &s : values)
+ cookies += parseCookieHeader(s);
+
+ if (cookies.empty())
+ return std::nullopt;
+ return cookies;
+}
+
+void QNetworkHeadersPrivate::invalidateHeaderCache()
+{
+ rawHeaderCache.headersList.clear();
+ rawHeaderCache.isCached = false;
+}
+
+void QNetworkHeadersPrivate::setCookedFromHttp(const QHttpHeaders &newHeaders)
+{
+ cookedHeaders.clear();
+
+ QMap<QNetworkRequest::KnownHeaders, QList<QByteArray>> multipleHeadersMap;
+ for (int i = 0; i < newHeaders.size(); ++i) {
+ const auto name = newHeaders.nameAt(i);
+ const auto value = newHeaders.valueAt(i);
+
+ const int parsedKeyAsInt = parseHeaderName(name);
+ if (parsedKeyAsInt == -1)
+ continue;
+
+ const QNetworkRequest::KnownHeaders parsedKey
+ = static_cast<QNetworkRequest::KnownHeaders>(parsedKeyAsInt);
+
+ auto &list = multipleHeadersMap[parsedKey];
+ list.append(value.toByteArray());
+ }
+
+ for (auto i = multipleHeadersMap.cbegin(), end = multipleHeadersMap.cend(); i != end; ++i)
+ cookedHeaders.insert(i.key(), parseHeaderValue(i.key(), i.value()));
+}
+
QT_END_NAMESPACE
#include "moc_qnetworkrequest.cpp"
diff --git a/src/network/access/qnetworkrequest.h b/src/network/access/qnetworkrequest.h
index 3ca61a2ee3..e281c74834 100644
--- a/src/network/access/qnetworkrequest.h
+++ b/src/network/access/qnetworkrequest.h
@@ -5,6 +5,7 @@
#define QNETWORKREQUEST_H
#include <QtNetwork/qtnetworkglobal.h>
+#include <QtNetwork/qhttpheaders.h>
#include <QtCore/QSharedDataPointer>
#include <QtCore/QString>
#include <QtCore/QUrl>
@@ -34,7 +35,8 @@ public:
IfModifiedSinceHeader,
ETagHeader,
IfMatchHeader,
- IfNoneMatchHeader
+ IfNoneMatchHeader,
+ NumKnownHeaders
};
Q_ENUM(KnownHeaders)
@@ -119,6 +121,10 @@ public:
QUrl url() const;
void setUrl(const QUrl &url);
+ QHttpHeaders headers() const;
+ void setHeaders(const QHttpHeaders &newHeaders);
+ void setHeaders(QHttpHeaders &&newHeaders);
+
// "cooked" headers
QVariant header(KnownHeaders header) const;
void setHeader(KnownHeaders header, const QVariant &value);
diff --git a/src/network/access/qnetworkrequest_p.h b/src/network/access/qnetworkrequest_p.h
index 48fcdcf1ed..7268e1a4aa 100644
--- a/src/network/access/qnetworkrequest_p.h
+++ b/src/network/access/qnetworkrequest_p.h
@@ -16,6 +16,7 @@
//
#include <QtNetwork/private/qtnetworkglobal_p.h>
+#include <QtNetwork/qhttpheaders.h>
#include "qnetworkrequest.h"
#include "QtCore/qbytearray.h"
#include "QtCore/qlist.h"
@@ -26,6 +27,8 @@
QT_BEGIN_NAMESPACE
+class QNetworkCookie;
+
// this is the common part between QNetworkRequestPrivate, QNetworkReplyPrivate and QHttpPartPrivate
class QNetworkHeadersPrivate
{
@@ -35,24 +38,49 @@ public:
typedef QHash<QNetworkRequest::KnownHeaders, QVariant> CookedHeadersMap;
typedef QHash<QNetworkRequest::Attribute, QVariant> AttributesMap;
- RawHeadersList rawHeaders;
+ mutable struct {
+ RawHeadersList headersList;
+ bool isCached = false;
+ } rawHeaderCache;
+
+ QHttpHeaders httpHeaders;
CookedHeadersMap cookedHeaders;
AttributesMap attributes;
QPointer<QObject> originatingObject;
- RawHeadersList::ConstIterator findRawHeader(QAnyStringView key) const;
- RawHeadersList allRawHeaders() const;
+ const RawHeadersList &allRawHeaders() const;
QList<QByteArray> rawHeadersKeys() const;
+ QByteArray rawHeader(QAnyStringView headerName) const;
void setRawHeader(const QByteArray &key, const QByteArray &value);
- void setAllRawHeaders(const RawHeadersList &list);
void setCookedHeader(QNetworkRequest::KnownHeaders header, const QVariant &value);
- static QDateTime fromHttpDate(const QByteArray &value);
+ QHttpHeaders headers() const;
+ void setHeaders(const QHttpHeaders &newHeaders);
+ void setHeaders(QHttpHeaders &&newHeaders);
+ void setHeader(QHttpHeaders::WellKnownHeader name, QByteArrayView value);
+
+ void clearHeaders();
+
+ static QDateTime fromHttpDate(QByteArrayView value);
static QByteArray toHttpDate(const QDateTime &dt);
+ static std::optional<qint64> toInt(QByteArrayView value);
+
+ typedef QList<QNetworkCookie> NetworkCookieList;
+ static QByteArray fromCookieList(const NetworkCookieList &cookies);
+ static std::optional<NetworkCookieList> toSetCookieList(const QList<QByteArray> &values);
+ static std::optional<NetworkCookieList> toCookieList(const QList<QByteArray> &values);
+
+ static RawHeadersList fromHttpToRaw(const QHttpHeaders &headers);
+ static QHttpHeaders fromRawToHttp(const RawHeadersList &raw);
+
private:
- void setRawHeaderInternal(const QByteArray &key, const QByteArray &value);
- void parseAndSetHeader(const QByteArray &key, const QByteArray &value);
+ void invalidateHeaderCache();
+
+ void setCookedFromHttp(const QHttpHeaders &newHeaders);
+ void parseAndSetHeader(QByteArrayView key, QByteArrayView value);
+ void parseAndSetHeader(QNetworkRequest::KnownHeaders key, QByteArrayView value);
+
};
Q_DECLARE_TYPEINFO(QNetworkHeadersPrivate::RawHeaderPair, Q_RELOCATABLE_TYPE);
diff --git a/src/network/access/qnetworkrequestfactory.cpp b/src/network/access/qnetworkrequestfactory.cpp
index 87738d9086..d9c536cef2 100644
--- a/src/network/access/qnetworkrequestfactory.cpp
+++ b/src/network/access/qnetworkrequestfactory.cpp
@@ -615,17 +615,11 @@ QNetworkRequest QNetworkRequestFactoryPrivate::newRequest(const QUrl &url) const
if (!sslConfig.isNull())
request.setSslConfiguration(sslConfig);
#endif
- // Set the header entries to the request. Combine values as there
- // may be multiple values per name. Note: this would not necessarily
- // produce right result for 'Set-Cookie' header if it has multiple values,
- // but since it is a purely server-side (response) header, not relevant here.
- const auto headerNames = headers.toMultiMap().uniqueKeys(); // ### fixme: port QNR to QHH
- for (const auto &name : headerNames)
- request.setRawHeader(name, headers.combinedValue(name));
-
+ auto h = headers;
constexpr char Bearer[] = "Bearer ";
if (!bearerToken.isEmpty())
- request.setRawHeader("Authorization"_ba, Bearer + bearerToken);
+ h.replaceOrAppend(QHttpHeaders::WellKnownHeader::Authorization, Bearer + bearerToken);
+ request.setHeaders(std::move(h));
request.setTransferTimeout(transferTimeout);
request.setPriority(priority);
diff --git a/src/network/access/qrestaccessmanager_p.h b/src/network/access/qrestaccessmanager_p.h
index 51af299f5c..2e6c1afb90 100644
--- a/src/network/access/qrestaccessmanager_p.h
+++ b/src/network/access/qrestaccessmanager_p.h
@@ -61,10 +61,12 @@ public:
return warnNoAccessManager();
verifyThreadAffinity(context);
QNetworkRequest req(request);
- if (!request.header(QNetworkRequest::ContentTypeHeader).isValid()) {
- req.setHeader(QNetworkRequest::ContentTypeHeader,
- QLatin1StringView{"application/json"});
+ auto h = req.headers();
+ if (!h.contains(QHttpHeaders::WellKnownHeader::ContentType)) {
+ h.append(QHttpHeaders::WellKnownHeader::ContentType,
+ QLatin1StringView{"application/json"});
}
+ req.setHeaders(std::move(h));
QNetworkReply *reply = requestOperation(qnam, req, jsonDoc.toJson(QJsonDocument::Compact));
return createActiveRequest(reply, context, slot);
}
diff --git a/src/network/access/qrestreply.cpp b/src/network/access/qrestreply.cpp
index 155e5877fd..2d8d101084 100644
--- a/src/network/access/qrestreply.cpp
+++ b/src/network/access/qrestreply.cpp
@@ -335,7 +335,7 @@ QDebug operator<<(QDebug debug, const QRestReply &reply)
<< ", bytesAvailable = " << reply.networkReply()->bytesAvailable()
<< ", url " << reply.networkReply()->url()
<< ", operation = " << operationName(reply.networkReply()->operation())
- << ", reply headers = " << reply.networkReply()->rawHeaderPairs()
+ << ", reply headers = " << reply.networkReply()->headers()
<< ")";
return debug;
}
@@ -420,6 +420,54 @@ static constexpr bool is_tchar(char ch) noexcept
}
}
+static constexpr auto parse_comment(QByteArrayView data) noexcept
+{
+ struct R {
+ QByteArrayView comment, tail;
+ constexpr explicit operator bool() const noexcept { return !comment.isEmpty(); }
+ };
+
+ const auto invalid = R{{}, data}; // preserves original `data`
+
+ // comment = "(" *( ctext / quoted-pair / comment ) ")"
+ // ctext = HTAB / SP / %x21-27 / %x2A-5B / %x5D-7E / obs-text
+
+ if (!data.startsWith('('))
+ return invalid;
+
+ qsizetype i = 1;
+ qsizetype level = 1;
+ while (i < data.size()) {
+ switch (data[i++]) {
+ case '(': // nested comment
+ ++level;
+ break;
+ case ')': // end of comment
+ if (--level == 0)
+ return R{data.first(i), data.sliced(i)};
+ break;
+ case '\\': // quoted-pair
+ if (i == data.size())
+ return invalid; // premature end
+ ++i; // eat escaped character
+ break;
+ default:
+ ; // don't validate ctext - accept everything (Postel's Law)
+ }
+ }
+
+ return invalid; // premature end / unbalanced nesting levels
+}
+
+static constexpr void eat_CWS(QByteArrayView &data) noexcept
+{
+ eat_OWS(data);
+ while (const auto comment = parse_comment(data)) {
+ data = comment.tail;
+ eat_OWS(data);
+ }
+}
+
static constexpr auto parse_token(QByteArrayView data) noexcept
{
struct R {
@@ -452,13 +500,13 @@ static constexpr auto parse_parameter(QByteArrayView data, qxp::function_ref<voi
return invalid;
data = name.tail;
- eat_OWS(data); // not in the grammar, but accepted under Postel's Law
+ eat_CWS(data); // not in the grammar, but accepted under Postel's Law
if (!data.startsWith('='))
return invalid;
data = data.sliced(1);
- eat_OWS(data); // not in the grammar, but accepted under Postel's Law
+ eat_CWS(data); // not in the grammar, but accepted under Postel's Law
if (Q_UNLIKELY(data.startsWith('"'))) { // value is a quoted-string
@@ -488,27 +536,27 @@ static auto parse_content_type(QByteArrayView data)
constexpr explicit operator bool() const noexcept { return !type.isEmpty(); }
};
- eat_OWS(data); // not in the grammar, but accepted under Postel's Law
+ eat_CWS(data); // not in the grammar, but accepted under Postel's Law
const auto type = parse_token(data);
if (!type)
return R{};
data = type.tail;
- eat_OWS(data); // not in the grammar, but accepted under Postel's Law
+ eat_CWS(data); // not in the grammar, but accepted under Postel's Law
if (!data.startsWith('/'))
return R{};
data = data.sliced(1);
- eat_OWS(data); // not in the grammar, but accepted under Postel's Law
+ eat_CWS(data); // not in the grammar, but accepted under Postel's Law
const auto subtype = parse_token(data);
if (!subtype)
return R{};
data = subtype.tail;
- eat_OWS(data);
+ eat_CWS(data);
auto r = R{QLatin1StringView{type.token}, QLatin1StringView{subtype.token}, {}};
@@ -516,7 +564,7 @@ static auto parse_content_type(QByteArrayView data)
data = data.sliced(1); // eat ';'
- eat_OWS(data);
+ eat_CWS(data);
const auto param = parse_parameter(data, [&](char ch) { r.charset.append(1, ch); });
if (param.name.compare("charset"_L1, Qt::CaseInsensitive) == 0) {
@@ -530,7 +578,7 @@ static auto parse_content_type(QByteArrayView data)
// otherwise, continue (accepting e.g. `;;`)
data = param.tail;
- eat_OWS(data);
+ eat_CWS(data);
}
return r; // no charset found
@@ -548,7 +596,7 @@ QByteArray QRestReplyPrivate::contentCharset(const QNetworkReply* reply)
// text/plain; charset ="utf-8"
const QByteArray contentTypeValue =
- reply->header(QNetworkRequest::KnownHeaders::ContentTypeHeader).toByteArray();
+ reply->headers().value(QHttpHeaders::WellKnownHeader::ContentType).toByteArray();
const auto r = parse_content_type(contentTypeValue);
if (r && !r.charset.empty())
diff --git a/src/network/compat/removed_api.cpp b/src/network/compat/removed_api.cpp
index 86951d9222..ceda117538 100644
--- a/src/network/compat/removed_api.cpp
+++ b/src/network/compat/removed_api.cpp
@@ -58,6 +58,9 @@ QList<QNetworkCookie> QNetworkCookie::parseCookies(const QByteArray &cookieStrin
#if QT_NETWORK_REMOVED_SINCE(6, 8)
+#if QT_CONFIG(dnslookup)
+# include "qdnslookup.h" // inlined API
+#endif
#include "qnetworkrequest.h" // inlined API
// #include "qotherheader.h"
diff --git a/src/network/kernel/qdnslookup.cpp b/src/network/kernel/qdnslookup.cpp
index c310c7e28e..1b4db7130b 100644
--- a/src/network/kernel/qdnslookup.cpp
+++ b/src/network/kernel/qdnslookup.cpp
@@ -8,14 +8,22 @@
#include <qapplicationstatic.h>
#include <qcoreapplication.h>
#include <qdatetime.h>
+#include <qendian.h>
#include <qloggingcategory.h>
#include <qrandom.h>
+#include <qspan.h>
#include <qurl.h>
+#if QT_CONFIG(ssl)
+# include <qsslsocket.h>
+#endif
+
#include <algorithm>
QT_BEGIN_NAMESPACE
+using namespace Qt::StringLiterals;
+
static Q_LOGGING_CATEGORY(lcDnsLookup, "qt.network.dnslookup", QtCriticalMsg)
namespace {
@@ -159,6 +167,42 @@ static void qt_qdnsservicerecord_sort(QList<QDnsServiceRecord> &records)
\note If you simply want to find the IP address(es) associated with a host
name, or the host name associated with an IP address you should use
QHostInfo instead.
+
+ \section1 DNS-over-TLS and Authentic Data
+
+ QDnsLookup supports DNS-over-TLS (DoT, as specified by \l{RFC 7858}) on
+ some platforms. That currently includes all Unix platforms where regular
+ queries are supported, if \l QSslSocket support is present in Qt. To query
+ if support is present at runtime, use isProtocolSupported().
+
+ When using DNS-over-TLS, QDnsLookup only implements the "Opportunistic
+ Privacy Profile" method of authentication, as described in \l{RFC 7858}
+ section 4.1. In this mode, QDnsLookup (through \l QSslSocket) only
+ validates that the server presents a certificate that is valid for the
+ server being connected to. Clients may use setSslConfiguration() to impose
+ additional restrictions and sslConfiguration() to obtain information after
+ the query is complete.
+
+ QDnsLookup will request DNS servers queried over TLS to perform
+ authentication on the data they return. If they confirm the data is valid,
+ the \l authenticData property will be set to true. QDnsLookup does not
+ verify the integrity of the data by itself, so applications should only
+ trust this property on servers they have confirmed through other means to
+ be trustworthy.
+
+ \section2 Authentic Data without TLS
+
+ QDnsLookup request Authentic Data for any server set with setNameserver(),
+ even if TLS encryption is not required. This is useful when querying a
+ caching nameserver on the same host as the application or on a trusted
+ network. Though similar to the TLS case, the application is responsible for
+ determining if the server it chose to use is trustworthy, and if the
+ unencrypted connection cannot be tampered with.
+
+ QDnsLookup obeys the system configuration to request Authentic Data on the
+ default nameserver (that is, if setNameserver() is not called). This is
+ currently only supported on Linux systems using glibc 2.31 or later. On any
+ other systems, QDnsLookup will ignore the AD bit in the query header.
*/
/*!
@@ -213,10 +257,72 @@ static void qt_qdnsservicerecord_sort(QList<QDnsServiceRecord> &records)
\value SRV service records.
+ \value[since 6.8] TLSA TLS association records.
+
\value TXT text records.
*/
/*!
+ \enum QDnsLookup::Protocol
+
+ Indicates the type of DNS server that is being queried.
+
+ \value Standard
+ Regular, unencrypted DNS, using UDP and falling back to TCP as necessary
+ (default port: 53)
+
+ \value DnsOverTls
+ Encrypted DNS over TLS (DoT, as specified by \l{RFC 7858}), over TCP
+ (default port: 853)
+
+ \sa isProtocolSupported(), nameserverProtocol, setNameserver()
+*/
+
+/*!
+ \since 6.8
+
+ Returns true if DNS queries using \a protocol are supported with QDnsLookup.
+
+ \sa nameserverProtocol
+*/
+bool QDnsLookup::isProtocolSupported(Protocol protocol)
+{
+#if QT_CONFIG(libresolv) || defined(Q_OS_WIN)
+ switch (protocol) {
+ case QDnsLookup::Standard:
+ return true;
+ case QDnsLookup::DnsOverTls:
+# if QT_CONFIG(ssl)
+ if (QSslSocket::supportsSsl())
+ return true;
+# endif
+ return false;
+ }
+#else
+ Q_UNUSED(protocol)
+#endif
+ return false;
+}
+
+/*!
+ \since 6.8
+
+ Returns the standard (default) port number for the protocol \a protocol.
+
+ \sa isProtocolSupported()
+*/
+quint16 QDnsLookup::defaultPortForProtocol(Protocol protocol) noexcept
+{
+ switch (protocol) {
+ case QDnsLookup::Standard:
+ return DnsPort;
+ case QDnsLookup::DnsOverTls:
+ return DnsOverTlsPort;
+ }
+ return 0; // will probably fail somewhere
+}
+
+/*!
\fn void QDnsLookup::finished()
This signal is emitted when the reply has finished processing.
@@ -270,7 +376,7 @@ QDnsLookup::QDnsLookup(Type type, const QString &name, QObject *parent)
*/
QDnsLookup::QDnsLookup(Type type, const QString &name, const QHostAddress &nameserver, QObject *parent)
- : QDnsLookup(type, name, nameserver, DnsPort, parent)
+ : QDnsLookup(type, name, nameserver, 0, parent)
{
}
@@ -285,8 +391,9 @@ QDnsLookup::QDnsLookup(Type type, const QString &name, const QHostAddress &names
//! [nameserver-port]
\note Setting the port number to any value other than the default (53) can
cause the name resolution to fail, depending on the operating system
- limitations and firewalls. Notably, the Windows API used by QDnsLookup is
- unable to handle alternate port numbers.
+ limitations and firewalls, if the nameserverProtocol() to be used
+ QDnsLookup::Standard. Notably, the Windows API used by QDnsLookup is unable
+ to handle alternate port numbers.
//! [nameserver-port]
*/
QDnsLookup::QDnsLookup(Type type, const QString &name, const QHostAddress &nameserver, quint16 port, QObject *parent)
@@ -300,6 +407,30 @@ QDnsLookup::QDnsLookup(Type type, const QString &name, const QHostAddress &names
}
/*!
+ \since 6.8
+
+ Constructs a QDnsLookup object to issue a query for \a name of record type
+ \a type, using the DNS server \a nameserver running on port \a port, and
+ sets \a parent as the parent object.
+
+ The query will be sent using \a protocol, if supported. Use
+ isProtocolSupported() to check if it is supported.
+
+ \include qdnslookup.cpp nameserver-port
+*/
+QDnsLookup::QDnsLookup(Type type, const QString &name, Protocol protocol,
+ const QHostAddress &nameserver, quint16 port, QObject *parent)
+ : QObject(*new QDnsLookupPrivate, parent)
+{
+ Q_D(QDnsLookup);
+ d->name = name;
+ d->type = type;
+ d->nameserver = nameserver;
+ d->port = port;
+ d->protocol = protocol;
+}
+
+/*!
Destroys the QDnsLookup object.
It is safe to delete a QDnsLookup object even if it is not finished, you
@@ -311,6 +442,28 @@ QDnsLookup::~QDnsLookup()
}
/*!
+ \since 6.8
+ \property QDnsLookup::authenticData
+ \brief whether the reply was authenticated by the resolver.
+
+ QDnsLookup does not perform the authentication itself. Instead, it trusts
+ the name server that was queried to perform the authentication and report
+ it. The application is responsible for determining if any servers it
+ configured with setNameserver() are trustworthy; if no server was set,
+ QDnsLookup obeys system configuration on whether responses should be
+ trusted.
+
+ This property may be set even if error() indicates a resolver error
+ occurred.
+
+ \sa setNameserver(), nameserverProtocol()
+*/
+bool QDnsLookup::isAuthenticData() const
+{
+ return d_func()->reply.authenticData;
+}
+
+/*!
\property QDnsLookup::error
\brief the type of error that occurred if the DNS lookup failed, or NoError.
*/
@@ -416,6 +569,10 @@ QBindable<QHostAddress> QDnsLookup::bindableNameserver()
\property QDnsLookup::nameserverPort
\since 6.6
\brief the port number of nameserver to use for DNS lookup.
+
+ The value of 0 indicates that QDnsLookup should use the default port for
+ the nameserverProtocol().
+
\include qdnslookup.cpp nameserver-port
*/
@@ -437,18 +594,44 @@ QBindable<quint16> QDnsLookup::bindableNameserverPort()
}
/*!
+ \property QDnsLookup::nameserverProtocol
+ \since 6.8
+ \brief the protocol to use when sending the DNS query
+
+ \sa isProtocolSupported()
+*/
+QDnsLookup::Protocol QDnsLookup::nameserverProtocol() const
+{
+ return d_func()->protocol;
+}
+
+void QDnsLookup::setNameserverProtocol(Protocol protocol)
+{
+ d_func()->protocol = protocol;
+}
+
+QBindable<QDnsLookup::Protocol> QDnsLookup::bindableNameserverProtocol()
+{
+ return &d_func()->protocol;
+}
+
+/*!
+ \fn void QDnsLookup::setNameserver(const QHostAddress &nameserver, quint16 port)
\since 6.6
+
Sets the nameserver to \a nameserver and the port to \a port.
\include qdnslookup.cpp nameserver-port
\sa QDnsLookup::nameserver, QDnsLookup::nameserverPort
*/
-void QDnsLookup::setNameserver(const QHostAddress &nameserver, quint16 port)
+
+void QDnsLookup::setNameserver(Protocol protocol, const QHostAddress &nameserver, quint16 port)
{
Qt::beginPropertyUpdateGroup();
setNameserver(nameserver);
setNameserverPort(port);
+ setNameserverProtocol(protocol);
Qt::endPropertyUpdateGroup();
}
@@ -524,6 +707,46 @@ QList<QDnsTextRecord> QDnsLookup::textRecords() const
}
/*!
+ \since 6.8
+ Returns the list of TLS association records associated with this lookup.
+
+ According to the standards relating to DNS-based Authentication of Named
+ Entities (DANE), this field should be ignored and must not be used for
+ verifying the authentity of a given server if the authenticity of the DNS
+ reply cannot itself be confirmed. See isAuthenticData() for more
+ information.
+ */
+QList<QDnsTlsAssociationRecord> QDnsLookup::tlsAssociationRecords() const
+{
+ return d_func()->reply.tlsAssociationRecords;
+}
+
+#if QT_CONFIG(ssl)
+/*!
+ \since 6.8
+ Sets the \a sslConfiguration to use for outgoing DNS-over-TLS connections.
+
+ \sa sslConfiguration(), QSslSocket::setSslConfiguration()
+*/
+void QDnsLookup::setSslConfiguration(const QSslConfiguration &sslConfiguration)
+{
+ Q_D(QDnsLookup);
+ d->sslConfiguration.emplace(sslConfiguration);
+}
+
+/*!
+ Returns the current SSL configuration.
+
+ \sa setSslConfiguration()
+*/
+QSslConfiguration QDnsLookup::sslConfiguration() const
+{
+ const Q_D(QDnsLookup);
+ return d->sslConfiguration.value_or(QSslConfiguration::defaultConfiguration());
+}
+#endif
+
+/*!
Aborts the DNS lookup operation.
If the lookup is already finished, does nothing.
@@ -565,6 +788,9 @@ void QDnsLookup::lookup()
#ifdef QDNSLOOKUP_DEBUG
qDebug("DNS reply for %s: %i (%s)", qPrintable(d->name), reply.error, qPrintable(reply.errorString));
#endif
+#if QT_CONFIG(ssl)
+ d->sslConfiguration = std::move(reply.sslConfiguration);
+#endif
d->reply = reply;
d->runnable = nullptr;
d->isFinished = true;
@@ -1052,6 +1278,223 @@ QDnsTextRecord &QDnsTextRecord::operator=(const QDnsTextRecord &other)
very fast and never fails.
*/
+/*!
+ \class QDnsTlsAssociationRecord
+ \since 6.8
+ \brief The QDnsTlsAssociationRecord class stores information about a DNS TLSA record.
+
+ \inmodule QtNetwork
+ \ingroup network
+ \ingroup shared
+
+ When performing a text lookup, zero or more records will be returned. Each
+ record is represented by a QDnsTlsAssociationRecord instance.
+
+ The meaning of the fields is defined in \l{RFC 6698}.
+
+ \sa QDnsLookup
+*/
+
+QT_DEFINE_QSDP_SPECIALIZATION_DTOR(QDnsTlsAssociationRecordPrivate)
+
+/*!
+ \enum QDnsTlsAssociationRecord::CertificateUsage
+
+ This enumeration contains valid values for the certificate usage field of
+ TLS Association queries. The following list is up-to-date with \l{RFC 6698}
+ section 2.1.1 and RFC 7218 section 2.1. Please refer to those documents for
+ authoritative instructions on interpreting this enumeration.
+
+ \value CertificateAuthorityConstrait
+ Indicates the record includes an association to a specific Certificate
+ Authority that must be found in the TLS server's certificate chain and
+ must pass PKIX validation.
+
+ \value ServiceCertificateConstraint
+ Indicates the record includes an association to a certificate that must
+ match the end entity certificate provided by the TLS server and must
+ pass PKIX validation.
+
+ \value TrustAnchorAssertion
+ Indicates the record includes an association to a certificate that MUST
+ be used as the ultimate trust anchor to validate the TLS server's
+ certificate and must pass PKIX validation.
+
+ \value DomainIssuedCertificate
+ Indicates the record includes an association to a certificate that must
+ match the end entity certificate provided by the TLS server. PKIX
+ validation is not tested.
+
+ \value PrivateUse
+ No standard meaning applied.
+
+ \value PKIX_TA
+ Alias; mnemonic for Public Key Infrastructure Trust Anchor
+
+ \value PKIX_EE
+ Alias; mnemonic for Public Key Infrastructure End Entity
+
+ \value DANE_TA
+ Alias; mnemonic for DNS-based Authentication of Named Entities Trust Anchor
+
+ \value DANE_EE
+ Alias; mnemonic for DNS-based Authentication of Named Entities End Entity
+
+ \value PrivCert
+ Alias
+
+ Other values are currently reserved, but may be unreserved by future
+ standards. This enumeration can be used for those values even if no
+ enumerator is provided.
+
+ \sa certificateUsage()
+*/
+
+/*!
+ \enum QDnsTlsAssociationRecord::Selector
+
+ This enumeration contains valid values for the selector field of TLS
+ Association queries. The following list is up-to-date with \l{RFC 6698}
+ section 2.1.2 and RFC 7218 section 2.2. Please refer to those documents for
+ authoritative instructions on interpreting this enumeration.
+
+ \value FullCertificate
+ Indicates this record refers to the full certificate in its binary
+ structure form.
+
+ \value SubjectPublicKeyInfo
+ Indicates the record refers to the certificate's subject and public
+ key information, in DER-encoded binary structure form.
+
+ \value PrivateUse
+ No standard meaning applied.
+
+ \value Cert
+ Alias
+
+ \value SPKI
+ Alias
+
+ \value PrivSel
+ Alias
+
+ Other values are currently reserved, but may be unreserved by future
+ standards. This enumeration can be used for those values even if no
+ enumerator is provided.
+
+ \sa selector()
+*/
+
+/*!
+ \enum QDnsTlsAssociationRecord::MatchingType
+
+ This enumeration contains valid values for the matching type field of TLS
+ Association queries. The following list is up-to-date with \l{RFC 6698}
+ section 2.1.3 and RFC 7218 section 2.3. Please refer to those documents for
+ authoritative instructions on interpreting this enumeration.
+
+ \value Exact
+ Indicates this the certificate or SPKI data is stored verbatim in this
+ record.
+
+ \value Sha256
+ Indicates this a SHA-256 checksum of the the certificate or SPKI data
+ present in this record.
+
+ \value Sha512
+ Indicates this a SHA-512 checksum of the the certificate or SPKI data
+ present in this record.
+
+ \value PrivateUse
+ No standard meaning applied.
+
+ \value PrivMatch
+ Alias
+
+ Other values are currently reserved, but may be unreserved by future
+ standards. This enumeration can be used for those values even if no
+ enumerator is provided.
+
+ \sa matchingType()
+*/
+
+/*!
+ Constructs an empty TLS Association record.
+ */
+QDnsTlsAssociationRecord::QDnsTlsAssociationRecord()
+ : d(new QDnsTlsAssociationRecordPrivate)
+{
+}
+
+/*!
+ Constructs a copy of \a other.
+ */
+QDnsTlsAssociationRecord::QDnsTlsAssociationRecord(const QDnsTlsAssociationRecord &other) = default;
+
+/*!
+ Moves the content of \a other into this object.
+ */
+QDnsTlsAssociationRecord &
+QDnsTlsAssociationRecord::operator=(const QDnsTlsAssociationRecord &other) = default;
+
+/*!
+ Destroys this TLS Association record object.
+ */
+QDnsTlsAssociationRecord::~QDnsTlsAssociationRecord() = default;
+
+/*!
+ Returns the name of this record.
+*/
+QString QDnsTlsAssociationRecord::name() const
+{
+ return d->name;
+}
+
+/*!
+ Returns the duration in seconds for which this record is valid.
+*/
+quint32 QDnsTlsAssociationRecord::timeToLive() const
+{
+ return d->timeToLive;
+}
+
+/*!
+ Returns the certificate usage field for this record.
+ */
+QDnsTlsAssociationRecord::CertificateUsage QDnsTlsAssociationRecord::usage() const
+{
+ return d->usage;
+}
+
+/*!
+ Returns the selector field for this record.
+ */
+QDnsTlsAssociationRecord::Selector QDnsTlsAssociationRecord::selector() const
+{
+ return d->selector;
+}
+
+/*!
+ Returns the match type field for this record.
+ */
+QDnsTlsAssociationRecord::MatchingType QDnsTlsAssociationRecord::matchType() const
+{
+ return d->matchType;
+}
+
+/*!
+ Returns the binary data field for this record. The interpretation of this
+ binary data depends on the three numeric fields provided by
+ certificateUsage(), selector(), and matchType().
+
+ Do note this is a binary field, even for the checksums, similar to what
+ QCyrptographicHash::result() returns.
+ */
+QByteArray QDnsTlsAssociationRecord::value() const
+{
+ return d->value;
+}
+
static QDnsLookupRunnable::EncodedLabel encodeLabel(const QString &label)
{
QDnsLookupRunnable::EncodedLabel::value_type rootDomain = u'.';
@@ -1070,8 +1513,14 @@ inline QDnsLookupRunnable::QDnsLookupRunnable(const QDnsLookupPrivate *d)
: requestName(encodeLabel(d->name)),
nameserver(d->nameserver),
requestType(d->type),
- port(d->port)
+ port(d->port),
+ protocol(d->protocol)
{
+ if (port == 0)
+ port = QDnsLookup::defaultPortForProtocol(protocol);
+#if QT_CONFIG(ssl)
+ sslConfiguration = d->sslConfiguration;
+#endif
}
void QDnsLookupRunnable::run()
@@ -1120,12 +1569,103 @@ inline QDebug operator<<(QDebug &d, QDnsLookupRunnable *r)
if (r->requestName.size() > MaxDomainNameLength)
d << "... (truncated)";
d << " type " << r->requestType;
- if (!r->nameserver.isNull())
+ if (!r->nameserver.isNull()) {
d << " to nameserver " << qUtf16Printable(r->nameserver.toString())
- << " port " << (r->port ? r->port : DnsPort);
+ << " port " << (r->port ? r->port : QDnsLookup::defaultPortForProtocol(r->protocol));
+ switch (r->protocol) {
+ case QDnsLookup::Standard:
+ break;
+ case QDnsLookup::DnsOverTls:
+ d << " (TLS)";
+ }
+ }
return d;
}
+#if QT_CONFIG(ssl)
+static constexpr std::chrono::milliseconds DnsOverTlsConnectTimeout(15'000);
+static constexpr std::chrono::milliseconds DnsOverTlsTimeout(120'000);
+static constexpr quint8 DnsAuthenticDataBit = 0x20;
+
+static int makeReplyErrorFromSocket(QDnsLookupReply *reply, const QAbstractSocket *socket)
+{
+ QDnsLookup::Error error = [&] {
+ switch (socket->error()) {
+ case QAbstractSocket::SocketTimeoutError:
+ case QAbstractSocket::ProxyConnectionTimeoutError:
+ return QDnsLookup::TimeoutError;
+ default:
+ return QDnsLookup::ResolverError;
+ }
+ }();
+ reply->setError(error, socket->errorString());
+ return false;
+}
+
+bool QDnsLookupRunnable::sendDnsOverTls(QDnsLookupReply *reply, QSpan<unsigned char> query,
+ ReplyBuffer &response)
+{
+ QSslSocket socket;
+ socket.setSslConfiguration(sslConfiguration.value_or(QSslConfiguration::defaultConfiguration()));
+
+# if QT_CONFIG(networkproxy)
+ socket.setProtocolTag("domain-s"_L1);
+# endif
+
+ // Request the name server attempt to authenticate the reply.
+ query[3] |= DnsAuthenticDataBit;
+
+ do {
+ quint16 size = qToBigEndian<quint16>(query.size());
+ QDeadlineTimer timeout(DnsOverTlsTimeout);
+
+ socket.connectToHostEncrypted(nameserver.toString(), port);
+ socket.write(reinterpret_cast<const char *>(&size), sizeof(size));
+ socket.write(reinterpret_cast<const char *>(query.data()), query.size());
+ if (!socket.waitForEncrypted(DnsOverTlsConnectTimeout.count()))
+ break;
+
+ reply->sslConfiguration = socket.sslConfiguration();
+
+ // accumulate reply
+ auto waitForBytes = [&](void *buffer, int count) {
+ int remaining = timeout.remainingTime();
+ while (remaining >= 0 && socket.bytesAvailable() < count) {
+ if (!socket.waitForReadyRead(remaining))
+ return false;
+ }
+ return socket.read(static_cast<char *>(buffer), count) == count;
+ };
+ if (!waitForBytes(&size, sizeof(size)))
+ break;
+
+ // note: strictly speaking, we're allocating memory based on untrusted data
+ // but in practice, due to limited range of the data type (16 bits),
+ // the maximum allocation is small.
+ size = qFromBigEndian(size);
+ response.resize(size);
+ if (waitForBytes(response.data(), size)) {
+ // check if the AD bit is set; we'll trust it over TLS requests
+ if (size >= 4)
+ reply->authenticData = response[3] & DnsAuthenticDataBit;
+ return true;
+ }
+ } while (false);
+
+ // handle errors
+ return makeReplyErrorFromSocket(reply, &socket);
+}
+#else
+bool QDnsLookupRunnable::sendDnsOverTls(QDnsLookupReply *reply, QSpan<unsigned char> query,
+ ReplyBuffer &response)
+{
+ Q_UNUSED(query)
+ Q_UNUSED(response)
+ reply->setError(QDnsLookup::ResolverError, QDnsLookup::tr("SSL/TLS support not present"));
+ return false;
+}
+#endif
+
QT_END_NAMESPACE
#include "moc_qdnslookup.cpp"
diff --git a/src/network/kernel/qdnslookup.h b/src/network/kernel/qdnslookup.h
index ae89a0a11f..8d21e99c84 100644
--- a/src/network/kernel/qdnslookup.h
+++ b/src/network/kernel/qdnslookup.h
@@ -22,6 +22,10 @@ class QDnsHostAddressRecordPrivate;
class QDnsMailExchangeRecordPrivate;
class QDnsServiceRecordPrivate;
class QDnsTextRecordPrivate;
+class QDnsTlsAssociationRecordPrivate;
+class QSslConfiguration;
+
+QT_DECLARE_QSDP_SPECIALIZATION_DTOR(QDnsTlsAssociationRecordPrivate)
class Q_NETWORK_EXPORT QDnsDomainNameRecord
{
@@ -137,10 +141,83 @@ private:
Q_DECLARE_SHARED(QDnsTextRecord)
+class Q_NETWORK_EXPORT QDnsTlsAssociationRecord
+{
+ Q_GADGET
+public:
+ enum class CertificateUsage : quint8 {
+ // https://www.iana.org/assignments/dane-parameters/dane-parameters.xhtml#certificate-usages
+ // RFC 6698
+ CertificateAuthorityConstrait = 0,
+ ServiceCertificateConstraint = 1,
+ TrustAnchorAssertion = 2,
+ DomainIssuedCertificate = 3,
+ PrivateUse = 255,
+
+ // Aliases by RFC 7218
+ PKIX_TA = 0,
+ PKIX_EE = 1,
+ DANE_TA = 2,
+ DANE_EE = 3,
+ PrivCert = 255,
+ };
+ Q_ENUM(CertificateUsage)
+
+ enum class Selector : quint8 {
+ // https://www.iana.org/assignments/dane-parameters/dane-parameters.xhtml#selectors
+ // RFC 6698
+ FullCertificate = 0,
+ SubjectPublicKeyInfo = 1,
+ PrivateUse = 255,
+
+ // Aliases by RFC 7218
+ Cert = FullCertificate,
+ SPKI = SubjectPublicKeyInfo,
+ PrivSel = PrivateUse,
+ };
+ Q_ENUM(Selector)
+
+ enum class MatchingType : quint8 {
+ // https://www.iana.org/assignments/dane-parameters/dane-parameters.xhtml#matching-types
+ // RFC 6698
+ Exact = 0,
+ Sha256 = 1,
+ Sha512 = 2,
+ PrivateUse = 255,
+ PrivMatch = PrivateUse,
+ };
+ Q_ENUM(MatchingType)
+
+ QDnsTlsAssociationRecord();
+ QDnsTlsAssociationRecord(const QDnsTlsAssociationRecord &other);
+ QDnsTlsAssociationRecord(QDnsTlsAssociationRecord &&other)
+ : d(std::move(other.d))
+ {}
+ QDnsTlsAssociationRecord &operator=(QDnsTlsAssociationRecord &&other) noexcept { swap(other); return *this; }
+ QDnsTlsAssociationRecord &operator=(const QDnsTlsAssociationRecord &other);
+ ~QDnsTlsAssociationRecord();
+
+ void swap(QDnsTlsAssociationRecord &other) noexcept { d.swap(other.d); }
+
+ QString name() const;
+ quint32 timeToLive() const;
+ CertificateUsage usage() const;
+ Selector selector() const;
+ MatchingType matchType() const;
+ QByteArray value() const;
+
+private:
+ QSharedDataPointer<QDnsTlsAssociationRecordPrivate> d;
+ friend class QDnsLookupRunnable;
+};
+
+Q_DECLARE_SHARED(QDnsTlsAssociationRecord)
+
class Q_NETWORK_EXPORT QDnsLookup : public QObject
{
Q_OBJECT
Q_PROPERTY(Error error READ error NOTIFY finished)
+ Q_PROPERTY(bool authenticData READ isAuthenticData NOTIFY finished)
Q_PROPERTY(QString errorString READ errorString NOTIFY finished)
Q_PROPERTY(QString name READ name WRITE setName NOTIFY nameChanged BINDABLE bindableName)
Q_PROPERTY(Type type READ type WRITE setType NOTIFY typeChanged BINDABLE bindableType)
@@ -148,6 +225,8 @@ class Q_NETWORK_EXPORT QDnsLookup : public QObject
BINDABLE bindableNameserver)
Q_PROPERTY(quint16 nameserverPort READ nameserverPort WRITE setNameserverPort
NOTIFY nameserverPortChanged BINDABLE bindableNameserverPort)
+ Q_PROPERTY(Protocol nameserverProtocol READ nameserverProtocol WRITE setNameserverProtocol
+ NOTIFY nameserverProtocolChanged BINDABLE bindableNameserverProtocol)
public:
enum Error
@@ -174,17 +253,27 @@ public:
NS = 2,
PTR = 12,
SRV = 33,
+ TLSA = 52,
TXT = 16
};
Q_ENUM(Type)
+ enum Protocol : quint8 {
+ Standard = 0,
+ DnsOverTls,
+ };
+ Q_ENUM(Protocol)
+
explicit QDnsLookup(QObject *parent = nullptr);
QDnsLookup(Type type, const QString &name, QObject *parent = nullptr);
QDnsLookup(Type type, const QString &name, const QHostAddress &nameserver, QObject *parent = nullptr);
QDnsLookup(Type type, const QString &name, const QHostAddress &nameserver, quint16 port,
QObject *parent = nullptr);
+ QDnsLookup(Type type, const QString &name, Protocol protocol, const QHostAddress &nameserver,
+ quint16 port = 0, QObject *parent = nullptr);
~QDnsLookup();
+ bool isAuthenticData() const;
Error error() const;
QString errorString() const;
bool isFinished() const;
@@ -203,6 +292,11 @@ public:
quint16 nameserverPort() const;
void setNameserverPort(quint16 port);
QBindable<quint16> bindableNameserverPort();
+ Protocol nameserverProtocol() const;
+ void setNameserverProtocol(Protocol protocol);
+ QBindable<Protocol> bindableNameserverProtocol();
+ void setNameserver(Protocol protocol, const QHostAddress &nameserver, quint16 port = 0);
+ QT_NETWORK_INLINE_SINCE(6, 8)
void setNameserver(const QHostAddress &nameserver, quint16 port);
QList<QDnsDomainNameRecord> canonicalNameRecords() const;
@@ -212,7 +306,15 @@ public:
QList<QDnsDomainNameRecord> pointerRecords() const;
QList<QDnsServiceRecord> serviceRecords() const;
QList<QDnsTextRecord> textRecords() const;
+ QList<QDnsTlsAssociationRecord> tlsAssociationRecords() const;
+
+#if QT_CONFIG(ssl)
+ void setSslConfiguration(const QSslConfiguration &sslConfiguration);
+ QSslConfiguration sslConfiguration() const;
+#endif
+ static bool isProtocolSupported(Protocol protocol);
+ static quint16 defaultPortForProtocol(Protocol protocol) noexcept Q_DECL_CONST_FUNCTION;
public Q_SLOTS:
void abort();
@@ -224,11 +326,19 @@ Q_SIGNALS:
void typeChanged(Type type);
void nameserverChanged(const QHostAddress &nameserver);
void nameserverPortChanged(quint16 port);
+ void nameserverProtocolChanged(Protocol protocol);
private:
Q_DECLARE_PRIVATE(QDnsLookup)
};
+#if QT_NETWORK_INLINE_IMPL_SINCE(6, 8)
+void QDnsLookup::setNameserver(const QHostAddress &nameserver, quint16 port)
+{
+ setNameserver(Standard, nameserver, port);
+}
+#endif
+
QT_END_NAMESPACE
#endif // QDNSLOOKUP_H
diff --git a/src/network/kernel/qdnslookup_p.h b/src/network/kernel/qdnslookup_p.h
index da4721411b..1f32b4ee4f 100644
--- a/src/network/kernel/qdnslookup_p.h
+++ b/src/network/kernel/qdnslookup_p.h
@@ -27,6 +27,10 @@
#include "private/qobject_p.h"
#include "private/qurl_p.h"
+#if QT_CONFIG(ssl)
+# include "qsslconfiguration.h"
+#endif
+
QT_REQUIRE_CONFIG(dnslookup);
QT_BEGIN_NAMESPACE
@@ -35,6 +39,7 @@ QT_BEGIN_NAMESPACE
constexpr qsizetype MaxDomainNameLength = 255;
constexpr quint16 DnsPort = 53;
+constexpr quint16 DnsOverTlsPort = 853;
class QDnsLookupRunnable;
QDebug operator<<(QDebug &, QDnsLookupRunnable *);
@@ -43,6 +48,7 @@ class QDnsLookupReply
{
public:
QDnsLookup::Error error = QDnsLookup::NoError;
+ bool authenticData = false;
QString errorString;
QList<QDnsDomainNameRecord> canonicalNameRecords;
@@ -51,8 +57,13 @@ public:
QList<QDnsDomainNameRecord> nameServerRecords;
QList<QDnsDomainNameRecord> pointerRecords;
QList<QDnsServiceRecord> serviceRecords;
+ QList<QDnsTlsAssociationRecord> tlsAssociationRecords;
QList<QDnsTextRecord> textRecords;
+#if QT_CONFIG(ssl)
+ std::optional<QSslConfiguration> sslConfiguration;
+#endif
+
// helper methods
void setError(QDnsLookup::Error err, QString &&msg)
{
@@ -120,6 +131,7 @@ private:
&& nameServerRecords.isEmpty()
&& pointerRecords.isEmpty()
&& serviceRecords.isEmpty()
+ && tlsAssociationRecords.isEmpty()
&& textRecords.isEmpty();
}
};
@@ -129,7 +141,8 @@ class QDnsLookupPrivate : public QObjectPrivate
public:
QDnsLookupPrivate()
: type(QDnsLookup::A)
- , port(DnsPort)
+ , port(0)
+ , protocol(QDnsLookup::Standard)
{ }
void nameChanged()
@@ -162,11 +175,22 @@ public:
Q_OBJECT_BINDABLE_PROPERTY(QDnsLookupPrivate, quint16,
port, &QDnsLookupPrivate::nameserverPortChanged);
+ void nameserverProtocolChanged()
+ {
+ emit q_func()->nameserverProtocolChanged(protocol);
+ }
+
+ Q_OBJECT_BINDABLE_PROPERTY(QDnsLookupPrivate, QDnsLookup::Protocol,
+ protocol, &QDnsLookupPrivate::nameserverProtocolChanged);
QDnsLookupReply reply;
QDnsLookupRunnable *runnable = nullptr;
bool isFinished = false;
+#if QT_CONFIG(ssl)
+ std::optional<QSslConfiguration> sslConfiguration;
+#endif
+
Q_DECLARE_PUBLIC(QDnsLookup)
};
@@ -180,9 +204,13 @@ public:
#else
using EncodedLabel = QByteArray;
#endif
+ // minimum IPv6 MTU (1280) minus the IPv6 (40) and UDP headers (8)
+ static constexpr qsizetype ReplyBufferSize = 1280 - 40 - 8;
+ using ReplyBuffer = QVarLengthArray<unsigned char, ReplyBufferSize>;
QDnsLookupRunnable(const QDnsLookupPrivate *d);
void run() override;
+ bool sendDnsOverTls(QDnsLookupReply *reply, QSpan<unsigned char> query, ReplyBuffer &response);
signals:
void finished(const QDnsLookupReply &reply);
@@ -198,6 +226,11 @@ private:
QHostAddress nameserver;
QDnsLookup::Type requestType;
quint16 port;
+ QDnsLookup::Protocol protocol;
+
+#if QT_CONFIG(ssl)
+ std::optional<QSslConfiguration> sslConfiguration;
+#endif
friend QDebug operator<<(QDebug &, QDnsLookupRunnable *);
};
@@ -265,6 +298,15 @@ public:
QList<QByteArray> values;
};
+class QDnsTlsAssociationRecordPrivate : public QDnsRecordPrivate
+{
+public:
+ QDnsTlsAssociationRecord::CertificateUsage usage;
+ QDnsTlsAssociationRecord::Selector selector;
+ QDnsTlsAssociationRecord::MatchingType matchType;
+ QByteArray value;
+};
+
QT_END_NAMESPACE
#endif // QDNSLOOKUP_P_H
diff --git a/src/network/kernel/qdnslookup_unix.cpp b/src/network/kernel/qdnslookup_unix.cpp
index 5696a3ca70..9de073b781 100644
--- a/src/network/kernel/qdnslookup_unix.cpp
+++ b/src/network/kernel/qdnslookup_unix.cpp
@@ -6,6 +6,7 @@
#include <qendian.h>
#include <qscopedpointer.h>
+#include <qspan.h>
#include <qurl.h>
#include <qvarlengtharray.h>
#include <private/qnativesocketengine_p.h> // for setSockAddr
@@ -32,15 +33,13 @@ QT_REQUIRE_CONFIG(libresolv);
QT_BEGIN_NAMESPACE
using namespace Qt::StringLiterals;
-
-// minimum IPv6 MTU (1280) minus the IPv6 (40) and UDP headers (8)
-static constexpr qsizetype ReplyBufferSize = 1280 - 40 - 8;
+using ReplyBuffer = QDnsLookupRunnable::ReplyBuffer;
// https://www.rfc-editor.org/rfc/rfc6891
static constexpr unsigned char Edns0Record[] = {
0x00, // root label
T_OPT >> 8, T_OPT & 0xff, // type OPT
- ReplyBufferSize >> 8, ReplyBufferSize & 0xff, // payload size
+ ReplyBuffer::PreallocatedSize >> 8, ReplyBuffer::PreallocatedSize & 0xff, // payload size
NOERROR, // extended rcode
0, // version
0x00, 0x00, // flags
@@ -68,11 +67,9 @@ using Cache = QList<QDnsCachedName>; // QHash or QMap are overkill
// https://docs.oracle.com/cd/E86824_01/html/E54774/res-setservers-3resolv.html
static bool applyNameServer(res_state state, const QHostAddress &nameserver, quint16 port)
{
- if (!nameserver.isNull()) {
- union res_sockaddr_union u;
- setSockaddr(reinterpret_cast<sockaddr *>(&u.sin), nameserver, port);
- res_setservers(state, &u, 1);
- }
+ union res_sockaddr_union u;
+ setSockaddr(reinterpret_cast<sockaddr *>(&u.sin), nameserver, port);
+ res_setservers(state, &u, 1);
return true;
}
#else
@@ -123,9 +120,6 @@ template <typename State> bool setIpv6NameServer(State *, const void *, quint16)
static bool applyNameServer(res_state state, const QHostAddress &nameserver, quint16 port)
{
- if (nameserver.isNull())
- return true;
-
state->nscount = 1;
state->nsaddr_list[0].sin_family = AF_UNSPEC;
if (nameserver.protocol() == QAbstractSocket::IPv6Protocol)
@@ -153,36 +147,30 @@ prepareQueryBuffer(res_state state, QueryBuffer &buffer, const char *label, ns_r
return queryLength + sizeof(Edns0Record);
}
-void QDnsLookupRunnable::query(QDnsLookupReply *reply)
+static int sendStandardDns(QDnsLookupReply *reply, res_state state, QSpan<unsigned char> qbuffer,
+ ReplyBuffer &buffer, const QHostAddress &nameserver, quint16 port)
{
- // Initialize state.
- std::remove_pointer_t<res_state> state = {};
- if (res_ninit(&state) < 0) {
- int error = errno;
- qErrnoWarning(error, "QDnsLookup: Resolver initialization failed");
- return reply->makeResolverSystemError(error);
- }
- auto guard = qScopeGuard([&] { res_nclose(&state); });
+ // Check if a nameserver was set. If so, use it.
+ if (!nameserver.isNull()) {
+ if (!applyNameServer(state, nameserver, port)) {
+ reply->setError(QDnsLookup::ResolverError,
+ QDnsLookup::tr("IPv6 nameservers are currently not supported on this OS"));
+ return -1;
+ }
- //Check if a nameserver was set. If so, use it
- if (!applyNameServer(&state, nameserver, port))
- return reply->setError(QDnsLookup::ResolverError,
- QDnsLookup::tr("IPv6 nameservers are currently not supported on this OS"));
-#ifdef QDNSLOOKUP_DEBUG
- state.options |= RES_DEBUG;
-#endif
+ // Request the name server attempt to authenticate the reply.
+ reinterpret_cast<HEADER *>(buffer.data())->ad = true;
- // Prepare the DNS query.
- QueryBuffer qbuffer;
- int queryLength = prepareQueryBuffer(&state, qbuffer, requestName.constData(), ns_rcode(requestType));
- if (Q_UNLIKELY(queryLength < 0))
- return reply->makeResolverSystemError();
+#ifdef RES_TRUSTAD
+ // Need to set this option even though we set the AD bit, otherwise
+ // glibc turns it off.
+ state->options |= RES_TRUSTAD;
+#endif
+ }
- // Perform DNS query.
- QVarLengthArray<unsigned char, ReplyBufferSize> buffer(ReplyBufferSize);
auto attemptToSend = [&]() {
std::memset(buffer.data(), 0, HFIXEDSZ); // the header is enough
- int responseLength = res_nsend(&state, qbuffer.data(), queryLength, buffer.data(), buffer.size());
+ int responseLength = res_nsend(state, qbuffer.data(), qbuffer.size(), buffer.data(), buffer.size());
if (responseLength >= 0)
return responseLength; // success
@@ -202,10 +190,10 @@ void QDnsLookupRunnable::query(QDnsLookupReply *reply)
};
// strictly use UDP, we'll deal with truncated replies ourselves
- state.options |= RES_IGNTC;
+ state->options |= RES_IGNTC;
int responseLength = attemptToSend();
if (responseLength < 0)
- return;
+ return responseLength;
// check if we need to use the virtual circuit (TCP)
auto header = reinterpret_cast<HEADER *>(buffer.data());
@@ -216,17 +204,65 @@ void QDnsLookupRunnable::query(QDnsLookupReply *reply)
// remove the EDNS record in the query
reinterpret_cast<HEADER *>(qbuffer.data())->arcount = 0;
- queryLength -= sizeof(Edns0Record);
+ qbuffer = qbuffer.first(qbuffer.size() - sizeof(Edns0Record));
// send using the virtual circuit
- state.options |= RES_USEVC;
+ state->options |= RES_USEVC;
responseLength = attemptToSend();
if (Q_UNLIKELY(responseLength > buffer.size())) {
// Ok, we give up.
- return reply->setError(QDnsLookup::ResolverError,
- QDnsLookup::tr("Reply was too large"));
+ reply->setError(QDnsLookup::ResolverError, QDnsLookup::tr("Reply was too large"));
+ return -1;
}
}
+
+ // We only trust the AD bit in the reply if we're querying a custom name
+ // server or if we can tell the system administrator configured the resolver
+ // to trust replies.
+#ifndef RES_TRUSTAD
+ if (nameserver.isNull())
+ header->ad = false;
+#endif
+ reply->authenticData = header->ad;
+
+ return responseLength;
+}
+
+void QDnsLookupRunnable::query(QDnsLookupReply *reply)
+{
+ // Initialize state.
+ std::remove_pointer_t<res_state> state = {};
+ if (res_ninit(&state) < 0) {
+ int error = errno;
+ qErrnoWarning(error, "QDnsLookup: Resolver initialization failed");
+ return reply->makeResolverSystemError(error);
+ }
+ auto guard = qScopeGuard([&] { res_nclose(&state); });
+
+#ifdef QDNSLOOKUP_DEBUG
+ state.options |= RES_DEBUG;
+#endif
+
+ // Prepare the DNS query.
+ QueryBuffer qbuffer;
+ int queryLength = prepareQueryBuffer(&state, qbuffer, requestName.constData(), ns_rcode(requestType));
+ if (Q_UNLIKELY(queryLength < 0))
+ return reply->makeResolverSystemError();
+
+ // Perform DNS query.
+ ReplyBuffer buffer(ReplyBufferSize);
+ int responseLength = -1;
+ switch (protocol) {
+ case QDnsLookup::Standard:
+ responseLength = sendStandardDns(reply, &state, qbuffer, buffer, nameserver, port);
+ break;
+ case QDnsLookup::DnsOverTls:
+ if (!sendDnsOverTls(reply, qbuffer, buffer))
+ return;
+ responseLength = buffer.size();
+ break;
+ }
+
if (responseLength < 0)
return;
@@ -235,6 +271,7 @@ void QDnsLookupRunnable::query(QDnsLookupReply *reply)
return reply->makeInvalidReplyError();
// Parse the reply.
+ auto header = reinterpret_cast<HEADER *>(buffer.data());
if (header->rcode)
return reply->makeDnsRcodeError(header->rcode);
@@ -273,7 +310,7 @@ void QDnsLookupRunnable::query(QDnsLookupReply *reply)
expandHost(offset);
if (status < 0)
return;
- if (offset + status + 4 >= responseLength)
+ if (offset + status + 4 > responseLength)
header->qdcount = 0xffff; // invalid reply below
else
offset += status + 4;
@@ -356,6 +393,8 @@ void QDnsLookupRunnable::query(QDnsLookupReply *reply)
return reply->makeInvalidReplyError(QDnsLookup::tr("Invalid mail exchange record"));
reply->mailExchangeRecords.append(record);
} else if (type == QDnsLookup::SRV) {
+ if (size < 7)
+ return reply->makeInvalidReplyError(QDnsLookup::tr("Invalid service record"));
const quint16 priority = qFromBigEndian<quint16>(response + offset);
const quint16 weight = qFromBigEndian<quint16>(response + offset + 2);
const quint16 port = qFromBigEndian<quint16>(response + offset + 4);
@@ -369,6 +408,23 @@ void QDnsLookupRunnable::query(QDnsLookupReply *reply)
if (status < 0)
return reply->makeInvalidReplyError(QDnsLookup::tr("Invalid service record"));
reply->serviceRecords.append(record);
+ } else if (type == QDnsLookup::TLSA) {
+ // https://datatracker.ietf.org/doc/html/rfc6698#section-2.1
+ if (size < 3)
+ return reply->makeInvalidReplyError(QDnsLookup::tr("Invalid TLS association record"));
+
+ const quint8 usage = response[offset];
+ const quint8 selector = response[offset + 1];
+ const quint8 matchType = response[offset + 2];
+
+ QDnsTlsAssociationRecord record;
+ record.d->name = name;
+ record.d->timeToLive = ttl;
+ record.d->usage = QDnsTlsAssociationRecord::CertificateUsage(usage);
+ record.d->selector = QDnsTlsAssociationRecord::Selector(selector);
+ record.d->matchType = QDnsTlsAssociationRecord::MatchingType(matchType);
+ record.d->value.assign(response + offset + 3, response + offset + size);
+ reply->tlsAssociationRecords.append(std::move(record));
} else if (type == QDnsLookup::TXT) {
QDnsTextRecord record;
record.d->name = name;
diff --git a/src/network/kernel/qdnslookup_win.cpp b/src/network/kernel/qdnslookup_win.cpp
index 72d5ae5c86..1b07776db9 100644
--- a/src/network/kernel/qdnslookup_win.cpp
+++ b/src/network/kernel/qdnslookup_win.cpp
@@ -5,9 +5,11 @@
#include <winsock2.h>
#include "qdnslookup_p.h"
-#include <qurl.h>
+#include <qendian.h>
#include <private/qnativesocketengine_p.h>
#include <private/qsystemerror_p.h>
+#include <qurl.h>
+#include <qspan.h>
#include <qt_windows.h>
#include <windns.h>
@@ -63,6 +65,58 @@ DNS_STATUS WINAPI DnsQueryEx(PDNS_QUERY_REQUEST pQueryRequest,
QT_BEGIN_NAMESPACE
+static DNS_STATUS sendAlternate(QDnsLookupRunnable *self, QDnsLookupReply *reply,
+ PDNS_QUERY_REQUEST request, PDNS_QUERY_RESULT results)
+{
+ // WinDNS wants MTU - IP Header - UDP header for some reason, in spite
+ // of never needing that much
+ QVarLengthArray<unsigned char, 1472> query(1472);
+
+ auto dnsBuffer = new (query.data()) DNS_MESSAGE_BUFFER;
+ DWORD dnsBufferSize = query.size();
+ WORD xid = 0;
+ bool recursionDesired = true;
+
+ SetLastError(ERROR_SUCCESS);
+
+ // MinGW winheaders incorrectly declare the third parameter as LPWSTR
+ if (!DnsWriteQuestionToBuffer_W(dnsBuffer, &dnsBufferSize,
+ const_cast<LPWSTR>(request->QueryName), request->QueryType,
+ xid, recursionDesired)) {
+ // let's try reallocating
+ query.resize(dnsBufferSize);
+ if (!DnsWriteQuestionToBuffer_W(dnsBuffer, &dnsBufferSize,
+ const_cast<LPWSTR>(request->QueryName), request->QueryType,
+ xid, recursionDesired)) {
+ return GetLastError();
+ }
+ }
+
+ // set AD bit: we want to trust this server
+ dnsBuffer->MessageHead.AuthenticatedData = true;
+
+ QDnsLookupRunnable::ReplyBuffer replyBuffer;
+ if (!self->sendDnsOverTls(reply, { query.data(), qsizetype(dnsBufferSize) }, replyBuffer))
+ return DNS_STATUS(-1); // error set in reply
+
+ // interpret the RCODE in the reply
+ auto response = reinterpret_cast<PDNS_MESSAGE_BUFFER>(replyBuffer.data());
+ DNS_HEADER *header = &response->MessageHead;
+ if (!header->IsResponse)
+ return DNS_ERROR_BAD_PACKET; // not a reply
+
+ // Convert the byte order for the 16-bit quantities in the header, so
+ // DnsExtractRecordsFromMessage can parse the contents.
+ //header->Xid = qFromBigEndian(header->Xid);
+ header->QuestionCount = qFromBigEndian(header->QuestionCount);
+ header->AnswerCount = qFromBigEndian(header->AnswerCount);
+ header->NameServerCount = qFromBigEndian(header->NameServerCount);
+ header->AdditionalCount = qFromBigEndian(header->AdditionalCount);
+
+ results->QueryOptions = request->QueryOptions;
+ return DnsExtractRecordsFromMessage_W(response, replyBuffer.size(), &results->pQueryRecords);
+}
+
void QDnsLookupRunnable::query(QDnsLookupReply *reply)
{
// Perform DNS query.
@@ -73,7 +127,7 @@ void QDnsLookupRunnable::query(QDnsLookupReply *reply)
request.QueryType = requestType;
request.QueryOptions = DNS_QUERY_STANDARD | DNS_QUERY_TREAT_AS_FQDN;
- if (!nameserver.isNull()) {
+ if (protocol == QDnsLookup::Standard && !nameserver.isNull()) {
memset(dnsAddresses, 0, sizeof(dnsAddresses));
request.pDnsServerList = new (dnsAddresses) DNS_ADDR_ARRAY;
auto addr = new (request.pDnsServerList->AddrArray) DNS_ADDR[1];
@@ -87,7 +141,18 @@ void QDnsLookupRunnable::query(QDnsLookupReply *reply)
DNS_QUERY_RESULT results = {};
results.Version = 1;
- const DNS_STATUS status = DnsQueryEx(&request, &results, nullptr);
+ DNS_STATUS status = ERROR_INVALID_PARAMETER;
+ switch (protocol) {
+ case QDnsLookup::Standard:
+ status = DnsQueryEx(&request, &results, nullptr);
+ break;
+ case QDnsLookup::DnsOverTls:
+ status = sendAlternate(this, reply, &request, &results);
+ break;
+ }
+
+ if (status == DNS_STATUS(-1))
+ return; // error already set in reply
if (status >= DNS_ERROR_RCODE_FORMAT_ERROR && status <= DNS_ERROR_RCODE_LAST)
return reply->makeDnsRcodeError(status - DNS_ERROR_RCODE_FORMAT_ERROR + 1);
else if (status == ERROR_TIMEOUT)
@@ -159,6 +224,25 @@ void QDnsLookupRunnable::query(QDnsLookupReply *reply)
record.d->timeToLive = ptr->dwTtl;
record.d->weight = ptr->Data.Srv.wWeight;
reply->serviceRecords.append(record);
+ } else if (ptr->wType == QDnsLookup::TLSA) {
+ // Note: untested, because the DNS_RECORD reply appears to contain
+ // no records relating to TLSA. Maybe WinDNS filters them out of
+ // zones without DNSSEC.
+ QDnsTlsAssociationRecord record;
+ record.d->name = name;
+ record.d->timeToLive = ptr->dwTtl;
+
+ const auto &tlsa = ptr->Data.Tlsa;
+ const quint8 usage = tlsa.bCertUsage;
+ const quint8 selector = tlsa.bSelector;
+ const quint8 matchType = tlsa.bMatchingType;
+
+ record.d->usage = QDnsTlsAssociationRecord::CertificateUsage(usage);
+ record.d->selector = QDnsTlsAssociationRecord::Selector(selector);
+ record.d->matchType = QDnsTlsAssociationRecord::MatchingType(matchType);
+ record.d->value.assign(tlsa.bCertificateAssociationData,
+ tlsa.bCertificateAssociationData + tlsa.bCertificateAssociationDataLength);
+ reply->tlsAssociationRecords.append(std::move(record));
} else if (ptr->wType == QDnsLookup::TXT) {
QDnsTextRecord record;
record.d->name = name;
diff --git a/src/network/kernel/qnetworkinformation.h b/src/network/kernel/qnetworkinformation.h
index 4e70a7faf2..57a49f23c8 100644
--- a/src/network/kernel/qnetworkinformation.h
+++ b/src/network/kernel/qnetworkinformation.h
@@ -83,7 +83,6 @@ Q_SIGNALS:
private:
friend struct QNetworkInformationDeleter;
- friend class QNetworkInformationPrivate;
QNetworkInformation(QNetworkInformationBackend *backend);
~QNetworkInformation() override;
diff --git a/src/network/kernel/qnetworkproxy.cpp b/src/network/kernel/qnetworkproxy.cpp
index 62bba24bce..9b91b11d6b 100644
--- a/src/network/kernel/qnetworkproxy.cpp
+++ b/src/network/kernel/qnetworkproxy.cpp
@@ -752,6 +752,53 @@ QNetworkProxy QNetworkProxy::applicationProxy()
}
/*!
+ \since 6.8
+
+ Returns headers that are set in this network request.
+
+ If the proxy is not of type HttpProxy or HttpCachingProxy,
+ default constructed QHttpHeaders is returned.
+
+ \sa setHeaders()
+*/
+QHttpHeaders QNetworkProxy::headers() const
+{
+ if (d->type != HttpProxy && d->type != HttpCachingProxy)
+ return {};
+ return d->headers.headers();
+}
+
+/*!
+ \since 6.8
+
+ Sets \a newHeaders as headers in this network request, overriding
+ any previously set headers.
+
+ If some headers correspond to the known headers, the values will
+ be parsed and the corresponding parsed form will also be set.
+
+ If the proxy is not of type HttpProxy or HttpCachingProxy this has no
+ effect.
+
+ \sa headers(), QNetworkRequest::KnownHeaders
+*/
+void QNetworkProxy::setHeaders(QHttpHeaders &&newHeaders)
+{
+ if (d->type == HttpProxy || d->type == HttpCachingProxy)
+ d->headers.setHeaders(std::move(newHeaders));
+}
+
+/*!
+ \overload
+ \since 6.8
+*/
+void QNetworkProxy::setHeaders(const QHttpHeaders &newHeaders)
+{
+ if (d->type == HttpProxy || d->type == HttpCachingProxy)
+ d->headers.setHeaders(newHeaders);
+}
+
+/*!
\since 5.0
Returns the value of the known network header \a header if it is
in use for this proxy. If it is not present, returns QVariant()
@@ -795,7 +842,7 @@ bool QNetworkProxy::hasRawHeader(const QByteArray &headerName) const
{
if (d->type != HttpProxy && d->type != HttpCachingProxy)
return false;
- return d->headers.findRawHeader(headerName) != d->headers.rawHeaders.constEnd();
+ return d->headers.headers().contains(headerName);
}
/*!
@@ -814,11 +861,7 @@ QByteArray QNetworkProxy::rawHeader(const QByteArray &headerName) const
{
if (d->type != HttpProxy && d->type != HttpCachingProxy)
return QByteArray();
- QNetworkHeadersPrivate::RawHeadersList::ConstIterator it =
- d->headers.findRawHeader(headerName);
- if (it != d->headers.rawHeaders.constEnd())
- return it->second;
- return QByteArray();
+ return d->headers.rawHeader(headerName);
}
/*!
diff --git a/src/network/kernel/qnetworkproxy.h b/src/network/kernel/qnetworkproxy.h
index d04bd9ee13..9f92ffeb12 100644
--- a/src/network/kernel/qnetworkproxy.h
+++ b/src/network/kernel/qnetworkproxy.h
@@ -136,6 +136,10 @@ public:
static void setApplicationProxy(const QNetworkProxy &proxy);
static QNetworkProxy applicationProxy();
+ QHttpHeaders headers() const;
+ void setHeaders(const QHttpHeaders &newHeaders);
+ void setHeaders(QHttpHeaders &&newHeaders);
+
// "cooked" headers
QVariant header(QNetworkRequest::KnownHeaders header) const;
void setHeader(QNetworkRequest::KnownHeaders header, const QVariant &value);
diff --git a/src/network/kernel/qnetworkproxy_libproxy.cpp b/src/network/kernel/qnetworkproxy_libproxy.cpp
index 248a8d2456..da1e8fdbd4 100644
--- a/src/network/kernel/qnetworkproxy_libproxy.cpp
+++ b/src/network/kernel/qnetworkproxy_libproxy.cpp
@@ -166,13 +166,15 @@ QList<QNetworkProxy> QNetworkProxyFactory::systemProxyForQuery(const QNetworkPro
break;
// fake URLs to get libproxy to tell us the SOCKS proxy
case QNetworkProxyQuery::TcpSocket:
- queryUrl.setScheme(QStringLiteral("tcp"));
+ if (queryUrl.scheme().isEmpty())
+ queryUrl.setScheme(QStringLiteral("tcp"));
queryUrl.setHost(query.peerHostName());
queryUrl.setPort(query.peerPort());
requiredCapabilities |= QNetworkProxy::TunnelingCapability;
break;
case QNetworkProxyQuery::UdpSocket:
- queryUrl.setScheme(QStringLiteral("udp"));
+ if (queryUrl.scheme().isEmpty())
+ queryUrl.setScheme(QStringLiteral("udp"));
queryUrl.setHost(query.peerHostName());
queryUrl.setPort(query.peerPort());
requiredCapabilities |= QNetworkProxy::UdpTunnelingCapability;
diff --git a/src/network/socket/qhttpsocketengine.cpp b/src/network/socket/qhttpsocketengine.cpp
index a700023bd5..725bc21359 100644
--- a/src/network/socket/qhttpsocketengine.cpp
+++ b/src/network/socket/qhttpsocketengine.cpp
@@ -467,11 +467,14 @@ void QHttpSocketEngine::slotSocketConnected()
data += " HTTP/1.1\r\n";
data += "Proxy-Connection: keep-alive\r\n";
data += "Host: " + peerAddress + "\r\n";
- if (!d->proxy.hasRawHeader("User-Agent"))
+ const auto headers = d->proxy.headers();
+ if (!headers.contains(QHttpHeaders::WellKnownHeader::UserAgent))
data += "User-Agent: Mozilla/5.0\r\n";
- const auto headers = d->proxy.rawHeaderList();
- for (const QByteArray &header : headers)
- data += header + ": " + d->proxy.rawHeader(header) + "\r\n";
+ for (qsizetype i = 0; i < headers.size(); ++i) {
+ const auto name = headers.nameAt(i);
+ data += QByteArrayView(name.data(), name.size()) + ": "
+ + headers.valueAt(i) + "\r\n";
+ }
QAuthenticatorPrivate *priv = QAuthenticatorPrivate::getPrivate(d->authenticator);
//qDebug() << "slotSocketConnected: priv=" << priv << (priv ? (int)priv->method : -1);
if (priv && priv->method != QAuthenticatorPrivate::None) {
diff --git a/src/network/socket/qsocks5socketengine.cpp b/src/network/socket/qsocks5socketengine.cpp
index 0564ad7a33..b0fdc63d66 100644
--- a/src/network/socket/qsocks5socketengine.cpp
+++ b/src/network/socket/qsocks5socketengine.cpp
@@ -1460,7 +1460,7 @@ qint64 QSocks5SocketEngine::read(char *data, qint64 maxlen)
//imitate remote closed
close();
setError(QAbstractSocket::RemoteHostClosedError,
- "Remote host closed connection###"_L1);
+ "Remote host closed connection"_L1);
setState(QAbstractSocket::UnconnectedState);
return -1;
} else {
diff --git a/src/plugins/platforms/android/CMakeLists.txt b/src/plugins/platforms/android/CMakeLists.txt
index d5a275a76c..1e22a5c1cc 100644
--- a/src/plugins/platforms/android/CMakeLists.txt
+++ b/src/plugins/platforms/android/CMakeLists.txt
@@ -9,7 +9,7 @@ qt_find_package(EGL)
qt_internal_add_plugin(QAndroidIntegrationPlugin
OUTPUT_NAME qtforandroid
PLUGIN_TYPE platforms
- DEFAULT_IF ${QT_QPA_DEFAULT_PLATFORM} MATCHES android
+ DEFAULT_IF "android" IN_LIST QT_QPA_PLATFORMS
SOURCES
androidcontentfileengine.cpp androidcontentfileengine.h
androiddeadlockprotector.h
diff --git a/src/plugins/platforms/android/androidjnimain.cpp b/src/plugins/platforms/android/androidjnimain.cpp
index 960f8ffc9c..9fdcf3936b 100644
--- a/src/plugins/platforms/android/androidjnimain.cpp
+++ b/src/plugins/platforms/android/androidjnimain.cpp
@@ -441,7 +441,7 @@ static void waitForServiceSetup(JNIEnv *env, jclass /*clazz*/)
Q_UNUSED(env);
// The service must wait until the QCoreApplication starts otherwise onBind will be
// called too early
- if (QtAndroidPrivate::service().isValid())
+ if (QtAndroidPrivate::service().isValid() && QtAndroid::isQtApplication())
QtAndroidPrivate::waitForServiceSetup();
}
diff --git a/src/plugins/platforms/android/androidwindowembedding.cpp b/src/plugins/platforms/android/androidwindowembedding.cpp
index 230776f571..65dabcac66 100644
--- a/src/plugins/platforms/android/androidwindowembedding.cpp
+++ b/src/plugins/platforms/android/androidwindowembedding.cpp
@@ -12,7 +12,6 @@
QT_BEGIN_NAMESPACE
Q_DECLARE_JNI_CLASS(QtView, "org/qtproject/qt/android/QtView");
-Q_DECLARE_JNI_CLASS(QtEmbeddedDelegate, "org/qtproject/qt/android/QtEmbeddedDelegate");
namespace QtAndroidWindowEmbedding {
void createRootWindow(JNIEnv *, jclass, QtJniTypes::View rootView,
@@ -59,16 +58,12 @@ namespace QtAndroidWindowEmbedding {
}
bool registerNatives(QJniEnvironment& env) {
- using namespace QtJniTypes;
- bool success = env.registerNativeMethods(Traits<QtEmbeddedDelegate>::className(),
- {Q_JNI_NATIVE_SCOPED_METHOD(createRootWindow, QtAndroidWindowEmbedding),
- Q_JNI_NATIVE_SCOPED_METHOD(deleteWindow, QtAndroidWindowEmbedding)});
-
- success &= env.registerNativeMethods(Traits<QtView>::className(),
- {Q_JNI_NATIVE_SCOPED_METHOD(setWindowVisible, QtAndroidWindowEmbedding),
- Q_JNI_NATIVE_SCOPED_METHOD(resizeWindow, QtAndroidWindowEmbedding)});
- return success;
-
+ return env.registerNativeMethods(
+ QtJniTypes::Traits<QtJniTypes::QtView>::className(),
+ { Q_JNI_NATIVE_SCOPED_METHOD(createRootWindow, QtAndroidWindowEmbedding),
+ Q_JNI_NATIVE_SCOPED_METHOD(deleteWindow, QtAndroidWindowEmbedding),
+ Q_JNI_NATIVE_SCOPED_METHOD(setWindowVisible, QtAndroidWindowEmbedding),
+ Q_JNI_NATIVE_SCOPED_METHOD(resizeWindow, QtAndroidWindowEmbedding) });
}
}
diff --git a/src/plugins/platforms/android/qandroidplatformintegration.cpp b/src/plugins/platforms/android/qandroidplatformintegration.cpp
index 4d9e6fa704..6efd3fc631 100644
--- a/src/plugins/platforms/android/qandroidplatformintegration.cpp
+++ b/src/plugins/platforms/android/qandroidplatformintegration.cpp
@@ -72,6 +72,12 @@ QAndroidPlatformScreen* createScreenForDisplayId(int displayId)
return new QAndroidPlatformScreen(display);
}
+static bool isValidAndroidContextForRendering()
+{
+ return QtAndroid::isQtApplication() ? QtAndroidPrivate::activity().isValid()
+ : QtAndroidPrivate::context().isValid();
+}
+
} // anonymous namespace
void *QAndroidPlatformNativeInterface::nativeResourceForIntegration(const QByteArray &resource)
@@ -317,9 +323,12 @@ bool QAndroidPlatformIntegration::hasCapability(Capability cap) const
case ApplicationState: return true;
case ThreadedPixmaps: return true;
case NativeWidgets: return QtAndroidPrivate::activity().isValid();
- case OpenGL: return QtAndroidPrivate::activity().isValid();
- case ForeignWindows: return QtAndroidPrivate::activity().isValid();
- case ThreadedOpenGL: return !needsBasicRenderloopWorkaround() && QtAndroidPrivate::activity().isValid();
+ case OpenGL:
+ return isValidAndroidContextForRendering();
+ case ForeignWindows:
+ return isValidAndroidContextForRendering();
+ case ThreadedOpenGL:
+ return !needsBasicRenderloopWorkaround() && isValidAndroidContextForRendering();
case RasterGLSurface: return QtAndroidPrivate::activity().isValid();
case TopStackedNativeChildWindows: return false;
case MaximizeUsingFullscreenGeometry: return true;
@@ -341,7 +350,7 @@ QPlatformBackingStore *QAndroidPlatformIntegration::createPlatformBackingStore(Q
QPlatformOpenGLContext *QAndroidPlatformIntegration::createPlatformOpenGLContext(QOpenGLContext *context) const
{
- if (!QtAndroidPrivate::activity().isValid())
+ if (!isValidAndroidContextForRendering())
return nullptr;
QSurfaceFormat format(context->format());
format.setAlphaBufferSize(8);
@@ -384,7 +393,7 @@ QOffscreenSurface *QAndroidPlatformIntegration::createOffscreenSurface(ANativeWi
QPlatformWindow *QAndroidPlatformIntegration::createPlatformWindow(QWindow *window) const
{
- if (!QtAndroidPrivate::activity().isValid())
+ if (!isValidAndroidContextForRendering())
return nullptr;
#if QT_CONFIG(vulkan)
diff --git a/src/plugins/platforms/android/qandroidplatformservices.cpp b/src/plugins/platforms/android/qandroidplatformservices.cpp
index f43e7cdd6a..39287aa905 100644
--- a/src/plugins/platforms/android/qandroidplatformservices.cpp
+++ b/src/plugins/platforms/android/qandroidplatformservices.cpp
@@ -24,15 +24,18 @@ QAndroidPlatformServices::QAndroidPlatformServices()
QtAndroidPrivate::registerNewIntentListener(this);
- QMetaObject::invokeMethod(
- this,
- [this] {
- QJniObject context = QJniObject(QtAndroidPrivate::context());
- QJniObject intent =
- context.callObjectMethod("getIntent", "()Landroid/content/Intent;");
- handleNewIntent(nullptr, intent.object());
- },
- Qt::QueuedConnection);
+ // Qt applications without Activity contexts cannot retrieve intents from the Activity.
+ if (QNativeInterface::QAndroidApplication::isActivityContext()) {
+ QMetaObject::invokeMethod(
+ this,
+ [this] {
+ QJniObject context = QJniObject(QtAndroidPrivate::context());
+ QJniObject intent =
+ context.callObjectMethod("getIntent", "()Landroid/content/Intent;");
+ handleNewIntent(nullptr, intent.object());
+ },
+ Qt::QueuedConnection);
+ }
}
Q_DECLARE_JNI_CLASS(UriType, "android/net/Uri")
diff --git a/src/plugins/platforms/android/qandroidplatformtheme.cpp b/src/plugins/platforms/android/qandroidplatformtheme.cpp
index 7b9072df69..99eeabac1d 100644
--- a/src/plugins/platforms/android/qandroidplatformtheme.cpp
+++ b/src/plugins/platforms/android/qandroidplatformtheme.cpp
@@ -161,7 +161,10 @@ QJsonObject AndroidStyle::loadStyleData()
if (!stylePath.isEmpty() && !stylePath.endsWith(slashChar))
stylePath += slashChar;
- if (QAndroidPlatformIntegration::colorScheme() == Qt::ColorScheme::Dark)
+ const Qt::ColorScheme colorScheme = QAndroidPlatformTheme::instance()
+ ? QAndroidPlatformTheme::instance()->colorScheme()
+ : QAndroidPlatformIntegration::colorScheme();
+ if (colorScheme == Qt::ColorScheme::Dark)
stylePath += "darkUiMode/"_L1;
Q_ASSERT(!stylePath.isEmpty());
@@ -423,9 +426,19 @@ void QAndroidPlatformTheme::showPlatformMenuBar()
Qt::ColorScheme QAndroidPlatformTheme::colorScheme() const
{
+ if (m_colorSchemeOverride != Qt::ColorScheme::Unknown)
+ return m_colorSchemeOverride;
return QAndroidPlatformIntegration::colorScheme();
}
+void QAndroidPlatformTheme::requestColorScheme(Qt::ColorScheme scheme)
+{
+ m_colorSchemeOverride = scheme;
+ QMetaObject::invokeMethod(qGuiApp, [this]{
+ updateColorScheme();
+ });
+}
+
static inline int paletteType(QPlatformTheme::Palette type)
{
switch (type) {
diff --git a/src/plugins/platforms/android/qandroidplatformtheme.h b/src/plugins/platforms/android/qandroidplatformtheme.h
index ce3d6d5f73..1b4ab5664d 100644
--- a/src/plugins/platforms/android/qandroidplatformtheme.h
+++ b/src/plugins/platforms/android/qandroidplatformtheme.h
@@ -40,6 +40,8 @@ public:
QPlatformMenuItem *createPlatformMenuItem() const override;
void showPlatformMenuBar() override;
Qt::ColorScheme colorScheme() const override;
+ void requestColorScheme(Qt::ColorScheme scheme) override;
+
const QPalette *palette(Palette type = SystemPalette) const override;
const QFont *font(Font type = SystemFont) const override;
QIconEngine *createIconEngine(const QString &iconName) const override;
@@ -57,6 +59,7 @@ private:
std::shared_ptr<AndroidStyle> m_androidStyleData;
QPalette m_defaultPalette;
QFont m_systemFont;
+ Qt::ColorScheme m_colorSchemeOverride = Qt::ColorScheme::Unknown;
};
QT_END_NAMESPACE
diff --git a/src/plugins/platforms/cocoa/CMakeLists.txt b/src/plugins/platforms/cocoa/CMakeLists.txt
index 92e681d8fb..491c61703f 100644
--- a/src/plugins/platforms/cocoa/CMakeLists.txt
+++ b/src/plugins/platforms/cocoa/CMakeLists.txt
@@ -7,7 +7,7 @@
qt_internal_add_plugin(QCocoaIntegrationPlugin
OUTPUT_NAME qcocoa
- DEFAULT_IF ${QT_QPA_DEFAULT_PLATFORM} MATCHES cocoa
+ DEFAULT_IF "cocoa" IN_LIST QT_QPA_PLATFORMS
PLUGIN_TYPE platforms
SOURCES
main.mm
diff --git a/src/plugins/platforms/cocoa/qcocoaaccessibility.mm b/src/plugins/platforms/cocoa/qcocoaaccessibility.mm
index c5e40a4087..40c1e90511 100644
--- a/src/plugins/platforms/cocoa/qcocoaaccessibility.mm
+++ b/src/plugins/platforms/cocoa/qcocoaaccessibility.mm
@@ -36,6 +36,23 @@ void QCocoaAccessibility::notifyAccessibilityUpdate(QAccessibleEvent *event)
}
switch (event->type()) {
+ case QAccessible::Announcement: {
+ auto *announcementEvent = static_cast<QAccessibleAnnouncementEvent *>(event);
+ auto priorityLevel = (announcementEvent->priority() == QAccessible::AnnouncementPriority::Assertive)
+ ? NSAccessibilityPriorityHigh
+ : NSAccessibilityPriorityMedium;
+ NSDictionary *announcementInfo = @{
+ NSAccessibilityPriorityKey: [NSNumber numberWithInt:priorityLevel],
+ NSAccessibilityAnnouncementKey: announcementEvent->message().toNSString()
+ };
+ // post event for application element, as the comment for
+ // NSAccessibilityAnnouncementRequestedNotification in the
+ // NSAccessibilityConstants.h header says
+ NSAccessibilityPostNotificationWithUserInfo(NSApp,
+ NSAccessibilityAnnouncementRequestedNotification,
+ announcementInfo);
+ break;
+ }
case QAccessible::Focus: {
NSAccessibilityPostNotification(element, NSAccessibilityFocusedUIElementChangedNotification);
break;
diff --git a/src/plugins/platforms/cocoa/qcocoaapplicationdelegate.mm b/src/plugins/platforms/cocoa/qcocoaapplicationdelegate.mm
index d7f8a1665e..d642115926 100644
--- a/src/plugins/platforms/cocoa/qcocoaapplicationdelegate.mm
+++ b/src/plugins/platforms/cocoa/qcocoaapplicationdelegate.mm
@@ -334,16 +334,50 @@ QT_USE_NAMESPACE
[self doesNotRecognizeSelector:invocationSelector];
}
+- (BOOL)application:(NSApplication *)application continueUserActivity:(NSUserActivity *)userActivity
+ restorationHandler:(void(^)(NSArray<id<NSUserActivityRestoring>> *restorableObjects))restorationHandler
+{
+ // Check if eg. user has installed an app delegate capable of handling this
+ if ([reflectionDelegate respondsToSelector:_cmd]
+ && [reflectionDelegate application:application continueUserActivity:userActivity
+ restorationHandler:restorationHandler] == YES) {
+ return YES;
+ }
+
+ if (!QGuiApplication::instance())
+ return NO;
+
+ if ([userActivity.activityType isEqualToString:NSUserActivityTypeBrowsingWeb]) {
+ QCocoaIntegration *cocoaIntegration = QCocoaIntegration::instance();
+ Q_ASSERT(cocoaIntegration);
+ return cocoaIntegration->services()->handleUrl(QUrl::fromNSURL(userActivity.webpageURL));
+ }
+
+ return NO;
+}
+
- (void)getUrl:(NSAppleEventDescriptor *)event withReplyEvent:(NSAppleEventDescriptor *)replyEvent
{
Q_UNUSED(replyEvent);
+
NSString *urlString = [[event paramDescriptorForKeyword:keyDirectObject] stringValue];
+ const QString qurlString = QString::fromNSString(urlString);
+
+ if (event.eventClass == kInternetEventClass && event.eventID == kAEGetURL) {
+ // 'GURL' (Get URL) event this application should handle
+ if (!QGuiApplication::instance())
+ return;
+ QCocoaIntegration *cocoaIntegration = QCocoaIntegration::instance();
+ Q_ASSERT(cocoaIntegration);
+ cocoaIntegration->services()->handleUrl(QUrl(qurlString));
+ return;
+ }
+
// The string we get from the requesting application might not necessarily meet
// QUrl's requirement for a IDN-compliant host. So if we can't parse into a QUrl,
// then we pass the string on to the application as the name of a file (and
// QFileOpenEvent::file is not guaranteed to be the path to a local, open'able
// file anyway).
- const QString qurlString = QString::fromNSString(urlString);
if (const QUrl url(qurlString); url.isValid())
QWindowSystemInterface::handleFileOpenEvent(url);
else
diff --git a/src/plugins/platforms/cocoa/qcocoaservices.h b/src/plugins/platforms/cocoa/qcocoaservices.h
index a0aec6f16b..b6299570e8 100644
--- a/src/plugins/platforms/cocoa/qcocoaservices.h
+++ b/src/plugins/platforms/cocoa/qcocoaservices.h
@@ -4,6 +4,8 @@
#ifndef QCOCOADESKTOPSERVICES_H
#define QCOCOADESKTOPSERVICES_H
+#include <QtCore/qurl.h>
+
#include <qpa/qplatformservices.h>
QT_BEGIN_NAMESPACE
@@ -15,8 +17,12 @@ public:
bool openUrl(const QUrl &url) override;
bool openDocument(const QUrl &url) override;
+ bool handleUrl(const QUrl &url);
QPlatformServiceColorPicker *colorPicker(QWindow *parent) override;
+
+private:
+ QUrl m_handlingUrl;
};
QT_END_NAMESPACE
diff --git a/src/plugins/platforms/cocoa/qcocoaservices.mm b/src/plugins/platforms/cocoa/qcocoaservices.mm
index 4566cbb8f7..87212c265c 100644
--- a/src/plugins/platforms/cocoa/qcocoaservices.mm
+++ b/src/plugins/platforms/cocoa/qcocoaservices.mm
@@ -8,12 +8,19 @@
#include <Foundation/NSURL.h>
#include <QtCore/QUrl>
+#include <QtCore/qscopedvaluerollback.h>
+
+#include <QtGui/qdesktopservices.h>
#include <QtGui/private/qcoregraphics_p.h>
QT_BEGIN_NAMESPACE
bool QCocoaServices::openUrl(const QUrl &url)
{
+ // avoid recursing back into self
+ if (url == m_handlingUrl)
+ return false;
+
return [[NSWorkspace sharedWorkspace] openURL:url.toNSURL()];
}
@@ -22,6 +29,16 @@ bool QCocoaServices::openDocument(const QUrl &url)
return openUrl(url);
}
+/* Callback from macOS that the application should handle a URL */
+bool QCocoaServices::handleUrl(const QUrl &url)
+{
+ QScopedValueRollback<QUrl> rollback(m_handlingUrl, url);
+ // FIXME: Add platform services callback from QDesktopServices::setUrlHandler
+ // so that we can warn the user if calling setUrlHandler without also setting
+ // up the matching keys in the Info.plist file (CFBundleURLTypes and friends).
+ return QDesktopServices::openUrl(url);
+}
+
class QCocoaColorPicker : public QPlatformServiceColorPicker
{
public:
diff --git a/src/plugins/platforms/cocoa/qcocoatheme.h b/src/plugins/platforms/cocoa/qcocoatheme.h
index c49d83feae..97e0f633a7 100644
--- a/src/plugins/platforms/cocoa/qcocoatheme.h
+++ b/src/plugins/platforms/cocoa/qcocoatheme.h
@@ -44,6 +44,7 @@ public:
static const char *name;
+ void requestColorScheme(Qt::ColorScheme scheme) override;
void handleSystemThemeChange();
#ifndef QT_NO_SHORTCUT
diff --git a/src/plugins/platforms/cocoa/qcocoatheme.mm b/src/plugins/platforms/cocoa/qcocoatheme.mm
index f4fbfadbe4..d9135c76c8 100644
--- a/src/plugins/platforms/cocoa/qcocoatheme.mm
+++ b/src/plugins/platforms/cocoa/qcocoatheme.mm
@@ -478,6 +478,23 @@ Qt::ColorScheme QCocoaTheme::colorScheme() const
return m_colorScheme;
}
+void QCocoaTheme::requestColorScheme(Qt::ColorScheme scheme)
+{
+ NSAppearance *appearance = nil;
+ switch (scheme) {
+ case Qt::ColorScheme::Dark:
+ appearance = [NSAppearance appearanceNamed:NSAppearanceNameDarkAqua];
+ break;
+ case Qt::ColorScheme::Light:
+ appearance = [NSAppearance appearanceNamed:NSAppearanceNameAqua];
+ break;
+ case Qt::ColorScheme::Unknown:
+ break;
+ }
+ if (appearance != NSApp.effectiveAppearance)
+ NSApplication.sharedApplication.appearance = appearance;
+}
+
/*
Update the theme's color scheme based on the current appearance.
diff --git a/src/plugins/platforms/cocoa/qcocoawindow.mm b/src/plugins/platforms/cocoa/qcocoawindow.mm
index 4a245a0f8a..d2c9bb0196 100644
--- a/src/plugins/platforms/cocoa/qcocoawindow.mm
+++ b/src/plugins/platforms/cocoa/qcocoawindow.mm
@@ -514,7 +514,7 @@ NSInteger QCocoaWindow::windowLevel(Qt::WindowFlags flags)
auto *nsWindow = transientCocoaWindow->nativeWindow();
// We only upgrade the window level for "special" windows, to work
- // around Qt Designer parenting the designer windows to the widget
+ // around Qt Widgets Designer parenting the designer windows to the widget
// palette window (QTBUG-31779). This should be fixed in designer.
if (type != Qt::Window)
windowLevel = qMax(windowLevel, nsWindow.level);
diff --git a/src/plugins/platforms/eglfs/CMakeLists.txt b/src/plugins/platforms/eglfs/CMakeLists.txt
index a0a6116a45..cb4b5d1eb9 100644
--- a/src/plugins/platforms/eglfs/CMakeLists.txt
+++ b/src/plugins/platforms/eglfs/CMakeLists.txt
@@ -92,7 +92,7 @@ endif()
qt_internal_add_plugin(QEglFSIntegrationPlugin
OUTPUT_NAME qeglfs
PLUGIN_TYPE platforms
- DEFAULT_IF ${QT_QPA_DEFAULT_PLATFORM} MATCHES eglfs
+ DEFAULT_IF "eglfs" IN_LIST QT_QPA_PLATFORMS
SOURCES
qeglfsmain.cpp
DEFINES
diff --git a/src/plugins/platforms/ios/CMakeLists.txt b/src/plugins/platforms/ios/CMakeLists.txt
index 4cc3efc91e..51c1b52cf3 100644
--- a/src/plugins/platforms/ios/CMakeLists.txt
+++ b/src/plugins/platforms/ios/CMakeLists.txt
@@ -5,10 +5,23 @@
## QIOSIntegrationPlugin Plugin:
#####################################################################
+if(VISIONOS)
+ include(SwiftIntegration.cmake)
+
+ qt_install(TARGETS QIOSIntegrationPluginSwift
+ EXPORT "${INSTALL_CMAKE_NAMESPACE}QIOSIntegrationPluginTargets"
+ DESTINATION "${INSTALL_LIBDIR}"
+ )
+ qt_internal_add_targets_to_additional_targets_export_file(
+ TARGETS QIOSIntegrationPluginSwift
+ EXPORT_NAME_PREFIX "${INSTALL_CMAKE_NAMESPACE}QIOSIntegrationPlugin"
+ )
+endif()
+
qt_internal_add_plugin(QIOSIntegrationPlugin
OUTPUT_NAME qios
STATIC # Force static, even in shared builds
- DEFAULT_IF ${QT_QPA_DEFAULT_PLATFORM} MATCHES ios
+ DEFAULT_IF "ios" IN_LIST QT_QPA_PLATFORMS
PLUGIN_TYPE platforms
SOURCES
plugin.mm
@@ -86,3 +99,7 @@ qt_internal_extend_target(QIOSIntegrationPlugin CONDITION NOT (TVOS OR VISIONOS)
)
add_subdirectory(optional)
+
+if(VISIONOS)
+ target_link_libraries(QIOSIntegrationPlugin PRIVATE QIOSIntegrationPluginSwift)
+endif()
diff --git a/src/plugins/platforms/ios/SwiftIntegration.cmake b/src/plugins/platforms/ios/SwiftIntegration.cmake
new file mode 100644
index 0000000000..d52edb3ad2
--- /dev/null
+++ b/src/plugins/platforms/ios/SwiftIntegration.cmake
@@ -0,0 +1,78 @@
+# Copyright (C) 2024 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
+set(CMAKE_Swift_COMPILER_TARGET arm64-apple-xros)
+if($CACHE{CMAKE_OSX_SYSROOT} MATCHES "^[a-z]+simulator$")
+ set(CMAKE_Swift_COMPILER_TARGET "${CMAKE_Swift_COMPILER_TARGET}-simulator")
+endif()
+
+cmake_policy(SET CMP0157 NEW)
+enable_language(Swift)
+
+# Verify that we have a new enough compiler
+if("${CMAKE_Swift_COMPILER_VERSION}" VERSION_LESS 5.9)
+ message(FATAL_ERROR "Swift 5.9 required for C++ interoperability")
+endif()
+
+get_target_property(QT_CORE_INCLUDES Qt6::Core INTERFACE_INCLUDE_DIRECTORIES)
+get_target_property(QT_GUI_INCLUDES Qt6::Gui INTERFACE_INCLUDE_DIRECTORIES)
+get_target_property(QT_CORE_PRIVATE_INCLUDES Qt6::CorePrivate INTERFACE_INCLUDE_DIRECTORIES)
+get_target_property(QT_GUI_PRIVATE_INCLUDES Qt6::GuiPrivate INTERFACE_INCLUDE_DIRECTORIES)
+
+set(target QIOSIntegrationPluginSwift)
+# Swift library
+set(SWIFT_SOURCES
+ "${CMAKE_CURRENT_SOURCE_DIR}/qiosapplication.swift"
+)
+add_library(${target} STATIC ${SWIFT_SOURCES})
+set_target_properties(${target} PROPERTIES
+ Swift_MODULE_NAME ${target})
+target_include_directories(${target} PUBLIC
+ "${CMAKE_CURRENT_SOURCE_DIR}"
+ "${QT_CORE_INCLUDES}"
+ "${QT_GUI_INCLUDES}"
+ "${QT_CORE_PRIVATE_INCLUDES}"
+ "${QT_GUI_PRIVATE_INCLUDES}"
+
+)
+target_compile_options(${target} PUBLIC
+ $<$<COMPILE_LANGUAGE:Swift>:-cxx-interoperability-mode=default>
+ $<$<COMPILE_LANGUAGE:Swift>:-Xcc -std=c++17>)
+
+# Swift to C++ bridging header
+set(SWIFT_BRIDGING_HEADER "${CMAKE_CURRENT_BINARY_DIR}/qiosswiftintegration.h")
+list(TRANSFORM QT_CORE_INCLUDES PREPEND "-I")
+list(TRANSFORM QT_GUI_INCLUDES PREPEND "-I")
+list(TRANSFORM QT_CORE_PRIVATE_INCLUDES PREPEND "-I")
+list(TRANSFORM QT_GUI_PRIVATE_INCLUDES PREPEND "-I")
+add_custom_command(
+ COMMAND
+ ${CMAKE_Swift_COMPILER} -frontend -typecheck
+ ${SWIFT_SOURCES}
+ -I ${CMAKE_CURRENT_SOURCE_DIR}
+ ${QT_CORE_INCLUDES}
+ ${QT_GUI_INCLUDES}
+ ${QT_CORE_PRIVATE_INCLUDES}
+ ${QT_GUI_PRIVATE_INCLUDES}
+ -sdk ${CMAKE_OSX_SYSROOT}
+ -module-name ${target}
+ -cxx-interoperability-mode=default
+ -Xcc -std=c++17
+ -emit-clang-header-path "${SWIFT_BRIDGING_HEADER}"
+ -target ${CMAKE_Swift_COMPILER_TARGET}
+ OUTPUT
+ "${SWIFT_BRIDGING_HEADER}"
+ DEPENDS
+ ${SWIFT_SOURCES}
+ )
+
+set(header_target "${target}Header")
+add_custom_target(${header_target}
+ DEPENDS "${SWIFT_BRIDGING_HEADER}"
+)
+# Make sure the "'__bridge_transfer' casts have no effect when not using ARC"
+# warning doesn't break warnings-are-error builds.
+target_compile_options(${target} INTERFACE
+ -Wno-error=arc-bridge-casts-disallowed-in-nonarc)
+
+add_dependencies(${target} ${header_target})
diff --git a/src/plugins/platforms/ios/module.modulemap b/src/plugins/platforms/ios/module.modulemap
new file mode 100644
index 0000000000..af42b3e1f5
--- /dev/null
+++ b/src/plugins/platforms/ios/module.modulemap
@@ -0,0 +1,4 @@
+module QIOSIntegrationPlugin {
+ header "qiosapplicationdelegate.h"
+ header "qiosintegration.h"
+}
diff --git a/src/plugins/platforms/ios/qiosapplication.swift b/src/plugins/platforms/ios/qiosapplication.swift
new file mode 100644
index 0000000000..6f75ebd0b5
--- /dev/null
+++ b/src/plugins/platforms/ios/qiosapplication.swift
@@ -0,0 +1,82 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import SwiftUI
+import CompositorServices
+import QIOSIntegrationPlugin
+import RealityKit
+
+struct QIOSSwiftApplication: App {
+ @UIApplicationDelegateAdaptor private var appDelegate: QIOSApplicationDelegate
+
+ var body: some SwiftUI.Scene {
+ WindowGroup() {
+ ImmersiveSpaceControlView()
+ }
+
+ ImmersiveSpace(id: "QIOSImmersiveSpace") {
+ CompositorLayer(configuration: QIOSLayerConfiguration()) { layerRenderer in
+ QIOSIntegration.instance().renderCompositorLayer(layerRenderer)
+ }
+ }
+ // CompositorLayer immersive spaces are always full, and should not need
+ // to set the immersion style, but lacking this we get a warning in the
+ // console about not being able to "configure an immersive space with
+ // selected style 'AutomaticImmersionStyle' since it is not in the list
+ // of supported styles for this type of content: 'FullImmersionStyle'."
+ .immersionStyle(selection: .constant(.full), in: .full)
+ }
+}
+
+public struct QIOSLayerConfiguration: CompositorLayerConfiguration {
+ public func makeConfiguration(capabilities: LayerRenderer.Capabilities,
+ configuration: inout LayerRenderer.Configuration) {
+ // Use reflection to pull out underlying C handles
+ // FIXME: Use proper bridging APIs when available
+ let capabilitiesMirror = Mirror(reflecting: capabilities)
+ let configurationMirror = Mirror(reflecting: configuration)
+ QIOSIntegration.instance().configureCompositorLayer(
+ capabilitiesMirror.descendant("c_capabilities") as? cp_layer_renderer_capabilities_t,
+ configurationMirror.descendant("box", "value") as? cp_layer_renderer_configuration_t
+ )
+ }
+}
+
+public func runSwiftAppMain() {
+ QIOSSwiftApplication.main()
+}
+
+public class ImmersiveState: ObservableObject {
+ static let shared = ImmersiveState()
+ @Published var showImmersiveSpace: Bool = false
+}
+
+struct ImmersiveSpaceControlView: View {
+ @ObservedObject private var immersiveState = ImmersiveState.shared
+
+ @Environment(\.openImmersiveSpace) var openImmersiveSpace
+ @Environment(\.dismissImmersiveSpace) var dismissImmersiveSpace
+
+ var body: some View {
+ VStack {}
+ .onChange(of: immersiveState.showImmersiveSpace) { _, newValue in
+ Task {
+ if newValue {
+ await openImmersiveSpace(id: "QIOSImmersiveSpace")
+ } else {
+ await dismissImmersiveSpace()
+ }
+ }
+ }
+ }
+}
+
+public class ImmersiveSpaceManager : NSObject {
+ @objc public static func openImmersiveSpace() {
+ ImmersiveState.shared.showImmersiveSpace = true
+ }
+
+ @objc public static func dismissImmersiveSpace() {
+ ImmersiveState.shared.showImmersiveSpace = false
+ }
+}
diff --git a/src/plugins/platforms/ios/qiosapplicationdelegate.h b/src/plugins/platforms/ios/qiosapplicationdelegate.h
index 39bb9fdedb..7e12d64cbf 100644
--- a/src/plugins/platforms/ios/qiosapplicationdelegate.h
+++ b/src/plugins/platforms/ios/qiosapplicationdelegate.h
@@ -1,6 +1,9 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+#ifndef QIOSAPPLICATIONDELEGATE_H
+#define QIOSAPPLICATIONDELEGATE_H
+
#import <UIKit/UIKit.h>
#import <QtGui/QtGui>
@@ -8,3 +11,5 @@
@interface QIOSApplicationDelegate : UIResponder <UIApplicationDelegate>
@end
+
+#endif // QIOSAPPLICATIONDELEGATE_H
diff --git a/src/plugins/platforms/ios/qiosdocumentpickercontroller.mm b/src/plugins/platforms/ios/qiosdocumentpickercontroller.mm
index fca0432426..09e2f2f4c3 100644
--- a/src/plugins/platforms/ios/qiosdocumentpickercontroller.mm
+++ b/src/plugins/platforms/ios/qiosdocumentpickercontroller.mm
@@ -30,14 +30,14 @@
case QFileDialogOptions::AnyFile:
case QFileDialogOptions::ExistingFile:
case QFileDialogOptions::ExistingFiles:
- [docTypes addObject:[UTType typeWithIdentifier:(__bridge NSString *)UTTypeContent]];
- [docTypes addObject:[UTType typeWithIdentifier:(__bridge NSString *)UTTypeItem]];
- [docTypes addObject:[UTType typeWithIdentifier:(__bridge NSString *)UTTypeData]];
+ [docTypes addObject:UTTypeContent];
+ [docTypes addObject:UTTypeItem];
+ [docTypes addObject:UTTypeData];
break;
// Showing files is not supported in Directory mode in iOS
case QFileDialogOptions::Directory:
case QFileDialogOptions::DirectoryOnly:
- [docTypes addObject:[UTType typeWithIdentifier:(__bridge NSString *)UTTypeFolder]];
+ [docTypes addObject:UTTypeFolder];
break;
}
}
diff --git a/src/plugins/platforms/ios/qioseventdispatcher.h b/src/plugins/platforms/ios/qioseventdispatcher.h
index b40024ec19..5eee0556f5 100644
--- a/src/plugins/platforms/ios/qioseventdispatcher.h
+++ b/src/plugins/platforms/ios/qioseventdispatcher.h
@@ -16,6 +16,8 @@ public:
static QIOSEventDispatcher* create();
bool processPostedEvents() override;
+ static bool isQtApplication();
+
protected:
explicit QIOSEventDispatcher(QObject *parent = nullptr);
};
diff --git a/src/plugins/platforms/ios/qioseventdispatcher.mm b/src/plugins/platforms/ios/qioseventdispatcher.mm
index 24d9d88294..710a834bfd 100644
--- a/src/plugins/platforms/ios/qioseventdispatcher.mm
+++ b/src/plugins/platforms/ios/qioseventdispatcher.mm
@@ -5,6 +5,10 @@
#include "qiosapplicationdelegate.h"
#include "qiosglobal.h"
+#if defined(Q_OS_VISIONOS)
+#include "qiosswiftintegration.h"
+#endif
+
#include <QtCore/qprocessordetection.h>
#include <QtCore/private/qcoreapplication_p.h>
#include <QtCore/private/qthread_p.h>
@@ -173,12 +177,16 @@ namespace
QAppleLogActivity UIApplicationMain;
QAppleLogActivity applicationDidFinishLaunching;
} logActivity;
+
+ static bool s_isQtApplication = false;
}
using namespace QT_PREPEND_NAMESPACE(QtPrivate);
extern "C" int qt_main_wrapper(int argc, char *argv[])
{
+ s_isQtApplication = true;
+
@autoreleasepool {
size_t defaultStackSize = 512 * kBytesPerKiloByte; // Same as secondary threads
@@ -202,8 +210,16 @@ extern "C" int qt_main_wrapper(int argc, char *argv[])
logActivity.UIApplicationMain = QT_APPLE_LOG_ACTIVITY(
lcEventDispatcher().isDebugEnabled(), "UIApplicationMain").enter();
+#if defined(Q_OS_VISIONOS)
+ Q_UNUSED(argc);
+ Q_UNUSED(argv);
+ qCDebug(lcEventDispatcher) << "Starting Swift app";
+ QIOSIntegrationPluginSwift::runSwiftAppMain();
+ Q_UNREACHABLE();
+#else
qCDebug(lcEventDispatcher) << "Running UIApplicationMain";
return UIApplicationMain(argc, argv, nil, NSStringFromClass([QIOSApplicationDelegate class]));
+#endif
}
}
@@ -424,6 +440,11 @@ QIOSEventDispatcher::QIOSEventDispatcher(QObject *parent)
QWindowSystemInterface::setSynchronousWindowSystemEvents(true);
}
+bool QIOSEventDispatcher::isQtApplication()
+{
+ return s_isQtApplication;
+}
+
/*!
Override of the CoreFoundation posted events runloop source callback
so that we can send window system (QPA) events in addition to sending
diff --git a/src/plugins/platforms/ios/qiosglobal.mm b/src/plugins/platforms/ios/qiosglobal.mm
index 25ccf2961b..1722e09aaa 100644
--- a/src/plugins/platforms/ios/qiosglobal.mm
+++ b/src/plugins/platforms/ios/qiosglobal.mm
@@ -5,6 +5,8 @@
#include "qiosapplicationdelegate.h"
#include "qiosviewcontroller.h"
#include "qiosscreen.h"
+#include "quiwindow.h"
+#include "qioseventdispatcher.h"
#include <QtCore/private/qcore_mac_p.h>
@@ -17,17 +19,13 @@ Q_LOGGING_CATEGORY(lcQpaWindowScene, "qt.qpa.window.scene");
bool isQtApplication()
{
- if (qt_apple_isApplicationExtension())
- return false;
-
// Returns \c true if the plugin is in full control of the whole application. This means
// that we control the application delegate and the top view controller, and can take
// actions that impacts all parts of the application. The opposite means that we are
// embedded inside a native iOS application, and should be more focused on playing along
// with native UIControls, and less inclined to change structures that lies outside the
// scope of our QWindows/UIViews.
- static bool isQt = ([qt_apple_sharedApplication().delegate isKindOfClass:[QIOSApplicationDelegate class]]);
- return isQt;
+ return QIOSEventDispatcher::isQtApplication();
}
bool isRunningOnVisionOS()
@@ -126,9 +124,15 @@ UIView *rootViewForScreen(QScreen *screen)
Q_UNUSED(iosScreen);
#endif
- UIWindow *uiWindow = windowScene.keyWindow;
- if (!uiWindow && windowScene.windows.count)
- uiWindow = windowScene.windows[0];
+ UIWindow *uiWindow = qt_objc_cast<QUIWindow*>(windowScene.keyWindow);
+ if (!uiWindow) {
+ for (UIWindow *win in windowScene.windows) {
+ if (qt_objc_cast<QUIWindow*>(win)) {
+ uiWindow = win;
+ break;
+ }
+ }
+ }
return uiWindow.rootViewController.view;
}
diff --git a/src/plugins/platforms/ios/qiosintegration.h b/src/plugins/platforms/ios/qiosintegration.h
index 2c7d33cc94..53f64c1748 100644
--- a/src/plugins/platforms/ios/qiosintegration.h
+++ b/src/plugins/platforms/ios/qiosintegration.h
@@ -16,11 +16,24 @@
#include "qiostextinputoverlay.h"
#endif
+#if defined(Q_OS_VISIONOS)
+#include <swift/bridging>
+#endif
+
QT_BEGIN_NAMESPACE
+using namespace QNativeInterface;
+
class QIOSServices;
-class QIOSIntegration : public QPlatformNativeInterface, public QPlatformIntegration
+class
+#if defined(Q_OS_VISIONOS)
+ SWIFT_IMMORTAL_REFERENCE
+#endif
+QIOSIntegration : public QPlatformNativeInterface, public QPlatformIntegration
+#if defined(Q_OS_VISIONOS)
+ , public QVisionOSApplication
+#endif
{
Q_OBJECT
public:
@@ -77,6 +90,17 @@ public:
QIOSApplicationState applicationState;
+#if defined(Q_OS_VISIONOS)
+ void openImmersiveSpace() override;
+ void dismissImmersiveSpace() override;
+
+ using CompositorLayer = QVisionOSApplication::ImmersiveSpaceCompositorLayer;
+ void setImmersiveSpaceCompositorLayer(CompositorLayer *layer) override;
+
+ void configureCompositorLayer(cp_layer_renderer_capabilities_t, cp_layer_renderer_configuration_t);
+ void renderCompositorLayer(cp_layer_renderer_t);
+#endif
+
private:
QPlatformFontDatabase *m_fontDatabase;
#if QT_CONFIG(clipboard)
@@ -90,6 +114,10 @@ private:
#if !defined(Q_OS_TVOS) && !defined(Q_OS_VISIONOS)
QIOSTextInputOverlay m_textInputOverlay;
#endif
+
+#if defined(Q_OS_VISIONOS)
+ CompositorLayer *m_immersiveSpaceCompositorLayer = nullptr;
+#endif
};
QT_END_NAMESPACE
diff --git a/src/plugins/platforms/ios/qiosintegration.mm b/src/plugins/platforms/ios/qiosintegration.mm
index 7cd21f83f6..2c32957c03 100644
--- a/src/plugins/platforms/ios/qiosintegration.mm
+++ b/src/plugins/platforms/ios/qiosintegration.mm
@@ -17,6 +17,10 @@
#include "qiosservices.h"
#include "qiosoptionalplugininterface.h"
+#if defined(Q_OS_VISIONOS)
+#include "qiosswiftintegration.h"
+#endif
+
#include <QtGui/qpointingdevice.h>
#include <QtGui/private/qguiapplication_p.h>
#include <QtGui/private/qrhibackingstore_p.h>
@@ -296,6 +300,39 @@ void QIOSIntegration::setApplicationBadge(qint64 number)
// ---------------------------------------------------------
+#if defined(Q_OS_VISIONOS)
+void QIOSIntegration::openImmersiveSpace()
+{
+ [ImmersiveSpaceManager openImmersiveSpace];
+}
+
+void QIOSIntegration::dismissImmersiveSpace()
+{
+ [ImmersiveSpaceManager dismissImmersiveSpace];
+}
+
+void QIOSIntegration::setImmersiveSpaceCompositorLayer(CompositorLayer *layer)
+{
+ m_immersiveSpaceCompositorLayer = layer;
+}
+
+void QIOSIntegration::configureCompositorLayer(cp_layer_renderer_capabilities_t capabilities,
+ cp_layer_renderer_configuration_t configuration)
+{
+ if (m_immersiveSpaceCompositorLayer)
+ m_immersiveSpaceCompositorLayer->configure(capabilities, configuration);
+}
+
+void QIOSIntegration::renderCompositorLayer(cp_layer_renderer_t renderer)
+{
+ if (m_immersiveSpaceCompositorLayer)
+ m_immersiveSpaceCompositorLayer->render(renderer);
+}
+
+#endif
+
+// ---------------------------------------------------------
+
void *QIOSIntegration::nativeResourceForWindow(const QByteArray &resource, QWindow *window)
{
if (!window || !window->handle())
diff --git a/src/plugins/platforms/ios/qiostextinputoverlay.mm b/src/plugins/platforms/ios/qiostextinputoverlay.mm
index 01046334a1..83170c1851 100644
--- a/src/plugins/platforms/ios/qiostextinputoverlay.mm
+++ b/src/plugins/platforms/ios/qiostextinputoverlay.mm
@@ -434,7 +434,7 @@ static void executeBlockWithoutAnimation(Block block)
if (enabled) {
_focusView = [reinterpret_cast<UIView *>(qApp->focusWindow()->winId()) retain];
- _desktopView = [qt_apple_sharedApplication().keyWindow.rootViewController.view retain];
+ _desktopView = [presentationWindow(nullptr).rootViewController.view retain];
Q_ASSERT(_focusView && _desktopView && _desktopView.superview);
[_desktopView addGestureRecognizer:self];
} else {
diff --git a/src/plugins/platforms/ios/qiostheme.h b/src/plugins/platforms/ios/qiostheme.h
index f0a404a61a..70e2c37ff1 100644
--- a/src/plugins/platforms/ios/qiostheme.h
+++ b/src/plugins/platforms/ios/qiostheme.h
@@ -4,6 +4,8 @@
#ifndef QIOSTHEME_H
#define QIOSTHEME_H
+#import <UIKit/UIKit.h>
+
#include <QtCore/QHash>
#include <QtGui/QPalette>
#include <qpa/qplatformtheme.h>
@@ -22,6 +24,7 @@ public:
QVariant themeHint(ThemeHint hint) const override;
Qt::ColorScheme colorScheme() const override;
+ void requestColorScheme(Qt::ColorScheme scheme) override;
#if !defined(Q_OS_TVOS) && !defined(Q_OS_VISIONOS)
QPlatformMenuItem* createPlatformMenuItem() const override;
@@ -37,9 +40,11 @@ public:
static const char *name;
static void initializeSystemPalette();
+ static void applyTheme(UIWindow *window);
private:
static QPalette s_systemPalette;
+ static inline Qt::ColorScheme s_colorSchemeOverride = Qt::ColorScheme::Unknown;
QMacNotificationObserver m_contentSizeCategoryObserver;
};
diff --git a/src/plugins/platforms/ios/qiostheme.mm b/src/plugins/platforms/ios/qiostheme.mm
index 3853de9cf1..0b420a875a 100644
--- a/src/plugins/platforms/ios/qiostheme.mm
+++ b/src/plugins/platforms/ios/qiostheme.mm
@@ -154,6 +154,9 @@ Qt::ColorScheme QIOSTheme::colorScheme() const
// the OS reports itself as always being in dark mode.
return Qt::ColorScheme::Dark;
#else
+ if (s_colorSchemeOverride != Qt::ColorScheme::Unknown)
+ return s_colorSchemeOverride;
+
// Set the appearance based on the QUIWindow
// Fallback to the UIScreen if no window is created yet
UIUserInterfaceStyle appearance = UIScreen.mainScreen.traitCollection.userInterfaceStyle;
@@ -171,6 +174,38 @@ Qt::ColorScheme QIOSTheme::colorScheme() const
#endif
}
+void QIOSTheme::requestColorScheme(Qt::ColorScheme scheme)
+{
+#if defined(Q_OS_VISIONOS)
+ Q_UNUSED(scheme);
+#else
+ s_colorSchemeOverride = scheme;
+
+ const NSArray<UIWindow *> *windows = qt_apple_sharedApplication().windows;
+ for (UIWindow *window in windows) {
+ // don't apply a theme to windows we don't own
+ if (qt_objc_cast<QUIWindow*>(window))
+ applyTheme(window);
+ }
+#endif
+}
+
+void QIOSTheme::applyTheme(UIWindow *window)
+{
+ const UIUserInterfaceStyle style = []{
+ switch (s_colorSchemeOverride) {
+ case Qt::ColorScheme::Dark:
+ return UIUserInterfaceStyleDark;
+ case Qt::ColorScheme::Light:
+ return UIUserInterfaceStyleLight;
+ case Qt::ColorScheme::Unknown:
+ return UIUserInterfaceStyleUnspecified;
+ }
+ }();
+
+ window.overrideUserInterfaceStyle = style;
+}
+
const QFont *QIOSTheme::font(Font type) const
{
const auto *platformIntegration = QGuiApplicationPrivate::platformIntegration();
diff --git a/src/plugins/platforms/ios/qioswindow.mm b/src/plugins/platforms/ios/qioswindow.mm
index 6a1080e238..f461a5f55b 100644
--- a/src/plugins/platforms/ios/qioswindow.mm
+++ b/src/plugins/platforms/ios/qioswindow.mm
@@ -55,8 +55,10 @@ QIOSWindow::QIOSWindow(QWindow *window, WId nativeHandle)
connect(qGuiApp, &QGuiApplication::applicationStateChanged, this, &QIOSWindow::applicationStateChanged);
- if (QPlatformWindow::parent())
- setParent(QPlatformWindow::parent());
+ // Always set parent, even if we don't have a parent window,
+ // as we use setParent to reparent top levels into our desktop
+ // manager view.
+ setParent(QPlatformWindow::parent());
if (!isForeignWindow()) {
// Resolve default window geometry in case it was not set before creating the
diff --git a/src/plugins/platforms/ios/quiwindow.mm b/src/plugins/platforms/ios/quiwindow.mm
index 7c910b6d9e..783e243e10 100644
--- a/src/plugins/platforms/ios/quiwindow.mm
+++ b/src/plugins/platforms/ios/quiwindow.mm
@@ -22,6 +22,15 @@
return self;
}
+- (instancetype)initWithWindowScene:(UIWindowScene *)windowScene
+{
+ if ((self = [super initWithWindowScene:windowScene]))
+ self->_sendingEvent = NO;
+
+ QIOSTheme::applyTheme(self);
+ return self;
+}
+
- (void)sendEvent:(UIEvent *)event
{
QScopedValueRollback<BOOL> sendingEvent(self->_sendingEvent, YES);
diff --git a/src/plugins/platforms/linuxfb/CMakeLists.txt b/src/plugins/platforms/linuxfb/CMakeLists.txt
index 9f75f53828..ba18cea50c 100644
--- a/src/plugins/platforms/linuxfb/CMakeLists.txt
+++ b/src/plugins/platforms/linuxfb/CMakeLists.txt
@@ -8,7 +8,7 @@
qt_internal_add_plugin(QLinuxFbIntegrationPlugin
OUTPUT_NAME qlinuxfb
PLUGIN_TYPE platforms
- DEFAULT_IF ${QT_QPA_DEFAULT_PLATFORM} MATCHES linuxfb
+ DEFAULT_IF "linuxfb" IN_LIST QT_QPA_PLATFORMS
SOURCES
main.cpp
qlinuxfbintegration.cpp qlinuxfbintegration.h
diff --git a/src/plugins/platforms/minimal/CMakeLists.txt b/src/plugins/platforms/minimal/CMakeLists.txt
index f3683deccf..18d8828134 100644
--- a/src/plugins/platforms/minimal/CMakeLists.txt
+++ b/src/plugins/platforms/minimal/CMakeLists.txt
@@ -10,7 +10,7 @@ qt_find_package(WrapFreetype PROVIDED_TARGETS WrapFreetype::WrapFreetype)
qt_internal_add_plugin(QMinimalIntegrationPlugin
OUTPUT_NAME qminimal
PLUGIN_TYPE platforms
- DEFAULT_IF ${QT_QPA_DEFAULT_PLATFORM} MATCHES minimal
+ DEFAULT_IF "minimal" IN_LIST QT_QPA_PLATFORMS
SOURCES
main.cpp
qminimalbackingstore.cpp qminimalbackingstore.h
diff --git a/src/plugins/platforms/minimalegl/CMakeLists.txt b/src/plugins/platforms/minimalegl/CMakeLists.txt
index a6ec8be781..b93f325b8f 100644
--- a/src/plugins/platforms/minimalegl/CMakeLists.txt
+++ b/src/plugins/platforms/minimalegl/CMakeLists.txt
@@ -10,7 +10,7 @@ qt_find_package(EGL)
qt_internal_add_plugin(QMinimalEglIntegrationPlugin
OUTPUT_NAME qminimalegl
PLUGIN_TYPE platforms
- DEFAULT_IF ${QT_QPA_DEFAULT_PLATFORM} MATCHES minimalegl
+ DEFAULT_IF "minimalegl" IN_LIST QT_QPA_PLATFORMS
SOURCES
main.cpp
qminimaleglintegration.cpp qminimaleglintegration.h
diff --git a/src/plugins/platforms/offscreen/CMakeLists.txt b/src/plugins/platforms/offscreen/CMakeLists.txt
index 09ad9a384d..907c2c9cc6 100644
--- a/src/plugins/platforms/offscreen/CMakeLists.txt
+++ b/src/plugins/platforms/offscreen/CMakeLists.txt
@@ -8,7 +8,7 @@
qt_internal_add_plugin(QOffscreenIntegrationPlugin
OUTPUT_NAME qoffscreen
PLUGIN_TYPE platforms
- DEFAULT_IF ${QT_QPA_DEFAULT_PLATFORM} MATCHES offscreen
+ DEFAULT_IF "offscreen" IN_LIST QT_QPA_PLATFORMS
SOURCES
main.cpp
qoffscreencommon.cpp qoffscreencommon.h
diff --git a/src/plugins/platforms/qnx/CMakeLists.txt b/src/plugins/platforms/qnx/CMakeLists.txt
index 9fb412d8a4..0f9deaa00b 100644
--- a/src/plugins/platforms/qnx/CMakeLists.txt
+++ b/src/plugins/platforms/qnx/CMakeLists.txt
@@ -8,7 +8,7 @@
qt_internal_add_plugin(QQnxIntegrationPlugin
OUTPUT_NAME qqnx
PLUGIN_TYPE platforms
- DEFAULT_IF ${QT_QPA_DEFAULT_PLATFORM} MATCHES qnx
+ DEFAULT_IF "qnx" IN_LIST QT_QPA_PLATFORMS
SOURCES
main.cpp main.h
qqnxabstractcover.h
diff --git a/src/plugins/platforms/qnx/qqnxabstractnavigator.cpp b/src/plugins/platforms/qnx/qqnxabstractnavigator.cpp
index 05389d3ea6..9b1107b7ec 100644
--- a/src/plugins/platforms/qnx/qqnxabstractnavigator.cpp
+++ b/src/plugins/platforms/qnx/qqnxabstractnavigator.cpp
@@ -6,14 +6,10 @@
#include <QDebug>
#include <QUrl>
-#if defined(QQNXNAVIGATOR_DEBUG)
-#define qNavigatorDebug qDebug
-#else
-#define qNavigatorDebug QT_NO_QDEBUG_MACRO
-#endif
-
QT_BEGIN_NAMESPACE
+Q_LOGGING_CATEGORY(lcQpaQnxNavigator, "qt.qpa.qnx.navigator");
+
QQnxAbstractNavigator::QQnxAbstractNavigator(QObject *parent)
: QObject(parent)
{
@@ -32,7 +28,7 @@ bool QQnxAbstractNavigator::invokeUrl(const QUrl &url)
// which is not recognized by the navigator anymore
const bool result = requestInvokeUrl(url.toString().toUtf8());
- qNavigatorDebug() << "url=" << url << "result=" << result;
+ qCDebug(lcQpaQnxNavigator) << Q_FUNC_INFO << "url =" << url << "result =" << result;
return result;
}
diff --git a/src/plugins/platforms/qnx/qqnxabstractnavigator.h b/src/plugins/platforms/qnx/qqnxabstractnavigator.h
index b4e4dd9bf8..fc92592307 100644
--- a/src/plugins/platforms/qnx/qqnxabstractnavigator.h
+++ b/src/plugins/platforms/qnx/qqnxabstractnavigator.h
@@ -5,9 +5,12 @@
#define QQNXABSTRACTNAVIGATOR_H
#include <QObject>
+#include <QtCore/QLoggingCategory>
QT_BEGIN_NAMESPACE
+Q_DECLARE_LOGGING_CATEGORY(lcQpaQnxNavigator);
+
class QUrl;
class QQnxAbstractNavigator : public QObject
diff --git a/src/plugins/platforms/qnx/qqnxbuffer.cpp b/src/plugins/platforms/qnx/qqnxbuffer.cpp
index 1ea9a8b0d3..4d3b5256b2 100644
--- a/src/plugins/platforms/qnx/qqnxbuffer.cpp
+++ b/src/plugins/platforms/qnx/qqnxbuffer.cpp
@@ -10,24 +10,20 @@
#include <errno.h>
#include <sys/mman.h>
-#if defined(QQNXBUFFER_DEBUG)
-#define qBufferDebug qDebug
-#else
-#define qBufferDebug QT_NO_QDEBUG_MACRO
-#endif
-
QT_BEGIN_NAMESPACE
+Q_LOGGING_CATEGORY(lcQpaScreenBuffer, "qt.qpa.screen.buffer");
+
QQnxBuffer::QQnxBuffer()
: m_buffer(0)
{
- qBufferDebug("empty");
+ qCDebug(lcQpaScreenBuffer) << Q_FUNC_INFO << "Empty";
}
QQnxBuffer::QQnxBuffer(screen_buffer_t buffer)
: m_buffer(buffer)
{
- qBufferDebug("normal");
+ qCDebug(lcQpaScreenBuffer) << Q_FUNC_INFO << "Normal";
// Get size of buffer
int size[2];
@@ -77,7 +73,7 @@ QQnxBuffer::QQnxBuffer(screen_buffer_t buffer)
imageFormat = QImage::Format_ARGB32_Premultiplied;
break;
default:
- qFatal("QQNX: unsupported buffer format, format=%d", screenFormat);
+ qFatal(lcQpaScreenBuffer, "QQNX: unsupported buffer format, format=%d", screenFormat);
}
// wrap buffer in an image
@@ -88,27 +84,27 @@ QQnxBuffer::QQnxBuffer(const QQnxBuffer &other)
: m_buffer(other.m_buffer),
m_image(other.m_image)
{
- qBufferDebug("copy");
+ qCDebug(lcQpaScreenBuffer) << Q_FUNC_INFO << "Copy";
}
QQnxBuffer::~QQnxBuffer()
{
- qBufferDebug();
+ qCDebug(lcQpaScreenBuffer) << Q_FUNC_INFO;
}
void QQnxBuffer::invalidateInCache()
{
- qBufferDebug();
+ qCDebug(lcQpaScreenBuffer) << Q_FUNC_INFO;
// Verify native buffer exists
if (Q_UNLIKELY(!m_buffer))
- qFatal("QQNX: can't invalidate cache for null buffer");
+ qFatal(lcQpaScreenBuffer, "QQNX: can't invalidate cache for null buffer");
// Evict buffer's data from cache
errno = 0;
int result = msync(m_image.bits(), m_image.height() * m_image.bytesPerLine(), MS_INVALIDATE | MS_CACHE_ONLY);
if (Q_UNLIKELY(result != 0))
- qFatal("QQNX: failed to invalidate cache, errno=%d", errno);
+ qFatal(lcQpaScreenBuffer, "QQNX: failed to invalidate cache, errno=%d", errno);
}
QT_END_NAMESPACE
diff --git a/src/plugins/platforms/qnx/qqnxbuffer.h b/src/plugins/platforms/qnx/qqnxbuffer.h
index b8f1443ad1..b456eb2a62 100644
--- a/src/plugins/platforms/qnx/qqnxbuffer.h
+++ b/src/plugins/platforms/qnx/qqnxbuffer.h
@@ -5,11 +5,14 @@
#define QQNXBUFFER_H
#include <QtGui/QImage>
+#include <QtCore/QLoggingCategory>
#include <screen/screen.h>
QT_BEGIN_NAMESPACE
+Q_DECLARE_LOGGING_CATEGORY(lcQpaScreenBuffer)
+
class QQnxBuffer
{
public:
diff --git a/src/plugins/platforms/qnx/qqnxbuttoneventnotifier.cpp b/src/plugins/platforms/qnx/qqnxbuttoneventnotifier.cpp
index 6497367579..788cddea87 100644
--- a/src/plugins/platforms/qnx/qqnxbuttoneventnotifier.cpp
+++ b/src/plugins/platforms/qnx/qqnxbuttoneventnotifier.cpp
@@ -13,14 +13,10 @@
#include <QtCore/QSocketNotifier>
#include <QtCore/private/qcore_unix_p.h>
-#if defined(QQNXBUTTON_DEBUG)
-#define qButtonDebug qDebug
-#else
-#define qButtonDebug QT_NO_QDEBUG_MACRO
-#endif
-
QT_BEGIN_NAMESPACE
+Q_LOGGING_CATEGORY(lcQpaInputHwButton, "qt.qpa.input.hwbutton");
+
const char *QQnxButtonEventNotifier::ppsPath = "/pps/system/buttons/status";
const size_t QQnxButtonEventNotifier::ppsBufferSize = 256;
@@ -47,7 +43,7 @@ QQnxButtonEventNotifier::~QQnxButtonEventNotifier()
void QQnxButtonEventNotifier::start()
{
- qButtonDebug("starting hardware button event processing");
+ qCDebug(lcQpaInputHwButton) << "Starting hardware button event processing";
if (m_fd != -1)
return;
@@ -64,7 +60,7 @@ void QQnxButtonEventNotifier::start()
m_readNotifier = new QSocketNotifier(m_fd, QSocketNotifier::Read);
QObject::connect(m_readNotifier, SIGNAL(activated(QSocketDescriptor)), this, SLOT(updateButtonStates()));
- qButtonDebug("successfully connected to Navigator. fd = %d", m_fd);
+ qCDebug(lcQpaInputHwButton, "successfully connected to Navigator. fd = %d", m_fd);
}
void QQnxButtonEventNotifier::updateButtonStates()
@@ -75,7 +71,8 @@ void QQnxButtonEventNotifier::updateButtonStates()
// Attempt to read pps data
errno = 0;
int bytes = qt_safe_read(m_fd, buffer, ppsBufferSize - 1);
- qButtonDebug() << "Read" << bytes << "bytes of data";
+ qCDebug(lcQpaInputHwButton) << "Read" << bytes << "bytes of data";
+
if (bytes == -1) {
qWarning("QQNX: failed to read hardware buttons pps object, errno=%d", errno);
return;
@@ -88,7 +85,7 @@ void QQnxButtonEventNotifier::updateButtonStates()
// Ensure data is null terminated
buffer[bytes] = '\0';
- qButtonDebug("received PPS message:\n%s", buffer);
+ qCDebug(lcQpaInputHwButton, "Received PPS message:\n%s", buffer);
// Process received message
QByteArray ppsData = QByteArray::fromRawData(buffer, bytes);
@@ -104,7 +101,8 @@ void QQnxButtonEventNotifier::updateButtonStates()
// If state has changed, update our state and inject a keypress event
if (m_state[buttonId] != newState) {
- qButtonDebug() << "Hardware button event: button =" << key << "state =" << fields.value(key);
+ qCDebug(lcQpaInputHwButton) << "Hardware button event: button =" << key << "state =" << fields.value(key);
+
m_state[buttonId] = newState;
// Is it a key press or key release event?
@@ -129,7 +127,7 @@ void QQnxButtonEventNotifier::updateButtonStates()
break;
default:
- qButtonDebug("Unknown hardware button");
+ qCDebug(lcQpaInputHwButton) << "Unknown hardware button";
continue;
}
@@ -170,7 +168,7 @@ bool QQnxButtonEventNotifier::parsePPS(const QByteArray &ppsData, QHash<QByteArr
// tokenize current attribute
const QByteArray &attr = lines.at(i);
- qButtonDebug() << "attr=" << attr;
+ qCDebug(lcQpaInputHwButton) << Q_FUNC_INFO << "attr =" << attr;
int doubleColon = attr.indexOf(QByteArrayLiteral("::"));
if (doubleColon == -1) {
diff --git a/src/plugins/platforms/qnx/qqnxbuttoneventnotifier.h b/src/plugins/platforms/qnx/qqnxbuttoneventnotifier.h
index 81ccf64415..476119ae27 100644
--- a/src/plugins/platforms/qnx/qqnxbuttoneventnotifier.h
+++ b/src/plugins/platforms/qnx/qqnxbuttoneventnotifier.h
@@ -5,9 +5,12 @@
#define QQNXBUTTONSEVENTNOTIFIER_H
#include <QObject>
+#include <QtCore/QLoggingCategory>
QT_BEGIN_NAMESPACE
+Q_DECLARE_LOGGING_CATEGORY(lcQpaInputHwButton);
+
class QSocketNotifier;
class QQnxButtonEventNotifier : public QObject
diff --git a/src/plugins/platforms/qnx/qqnxclipboard.cpp b/src/plugins/platforms/qnx/qqnxclipboard.cpp
index 8e42148c12..3f27ec8069 100644
--- a/src/plugins/platforms/qnx/qqnxclipboard.cpp
+++ b/src/plugins/platforms/qnx/qqnxclipboard.cpp
@@ -17,14 +17,10 @@
#include <clipboard/clipboard.h>
#include <errno.h>
-#if defined(QQNXCLIPBOARD_DEBUG)
-#define qClipboardDebug qDebug
-#else
-#define qClipboardDebug QT_NO_QDEBUG_MACRO
-#endif
-
QT_BEGIN_NAMESPACE
+Q_LOGGING_CATEGORY(lcQpaClipboard, "qt.qpa.clipboard");
+
// null terminated array
static const char *typeList[] = {"text/html", "text/plain", "image/png", "image/jpeg", "application/x-color", 0};
@@ -66,13 +62,13 @@ public:
void addFormatToCheck(const QString &format) {
m_formatsToCheck << format;
- qClipboardDebug() << "formats=" << m_formatsToCheck;
+ qCDebug(lcQpaClipboard) << "formats=" << m_formatsToCheck;
}
bool hasFormat(const QString &mimetype) const override
{
const bool result = is_clipboard_format_present(mimetype.toUtf8().constData()) == 0;
- qClipboardDebug() << "mimetype=" << mimetype << "result=" << result;
+ qCDebug(lcQpaClipboard) << "mimetype=" << mimetype << "result=" << result;
return result;
}
@@ -85,7 +81,7 @@ public:
result << format;
}
- qClipboardDebug() << "result=" << result;
+ qCDebug(lcQpaClipboard) << "result=" << result;
return result;
}
@@ -109,7 +105,7 @@ public:
protected:
QVariant retrieveData(const QString &mimetype, QMetaType preferredType) const override
{
- qClipboardDebug() << "mimetype=" << mimetype << "preferredType=" << preferredType.name();
+ qCDebug(lcQpaClipboard) << "mimetype=" << mimetype << "preferredType=" << preferredType.name();
if (is_clipboard_format_present(mimetype.toUtf8().constData()) != 0)
return QMimeData::retrieveData(mimetype, preferredType);
@@ -121,7 +117,7 @@ private Q_SLOTS:
void releaseOwnership()
{
if (m_userMimeData) {
- qClipboardDebug() << "user data formats=" << m_userMimeData->formats() << "system formats=" << formats();
+ qCDebug(lcQpaClipboard) << "user data formats=" << m_userMimeData->formats() << "system formats=" << formats();
delete m_userMimeData;
m_userMimeData = 0;
m_clipboard->emitChanged(QClipboard::Clipboard);
@@ -167,7 +163,7 @@ void QQnxClipboard::setMimeData(QMimeData *data, QClipboard::Mode mode)
}
const QStringList formats = data->formats();
- qClipboardDebug() << "formats=" << formats;
+ qCDebug(lcQpaClipboard) << "formats=" << formats;
Q_FOREACH (const QString &format, formats) {
const QByteArray buf = data->data(format);
@@ -176,7 +172,7 @@ void QQnxClipboard::setMimeData(QMimeData *data, QClipboard::Mode mode)
continue;
int ret = set_clipboard_data(format.toUtf8().data(), buf.size(), buf.data());
- qClipboardDebug() << "set " << format << "to clipboard, size=" << buf.size() << ";ret=" << ret;
+ qCDebug(lcQpaClipboard) << "set " << format << "to clipboard, size=" << buf.size() << ";ret=" << ret;
if (ret)
m_mimeData->addFormatToCheck(format);
}
diff --git a/src/plugins/platforms/qnx/qqnxclipboard.h b/src/plugins/platforms/qnx/qqnxclipboard.h
index adc70b158a..66f862f0dc 100644
--- a/src/plugins/platforms/qnx/qqnxclipboard.h
+++ b/src/plugins/platforms/qnx/qqnxclipboard.h
@@ -11,6 +11,8 @@
QT_BEGIN_NAMESPACE
+Q_DECLARE_LOGGING_CATEGORY(lcQpaClipboard);
+
class QQnxClipboard : public QPlatformClipboard
{
public:
diff --git a/src/plugins/platforms/qnx/qqnxcursor.cpp b/src/plugins/platforms/qnx/qqnxcursor.cpp
index 03c4e16401..7c55dd79b3 100644
--- a/src/plugins/platforms/qnx/qqnxcursor.cpp
+++ b/src/plugins/platforms/qnx/qqnxcursor.cpp
@@ -5,14 +5,10 @@
#include <QtCore/QDebug>
-#if defined(QQNXCURSOR_DEBUG)
-#define qCursorDebug qDebug
-#else
-#define qCursorDebug QT_NO_QDEBUG_MACRO
-#endif
-
QT_BEGIN_NAMESPACE
+Q_LOGGING_CATEGORY(lcQpaQnx, "qt.qpa.qnx");
+
QQnxCursor::QQnxCursor()
{
}
@@ -27,13 +23,13 @@ void QQnxCursor::changeCursor(QCursor *windowCursor, QWindow *window)
void QQnxCursor::setPos(const QPoint &pos)
{
- qCursorDebug() << "QQnxCursor::setPos -" << pos;
+ qCDebug(lcQpaQnx) << "QQnxCursor::setPos -" << pos;
m_pos = pos;
}
QPoint QQnxCursor::pos() const
{
- qCursorDebug() << "QQnxCursor::pos -" << m_pos;
+ qCDebug(lcQpaQnx) << "QQnxCursor::pos -" << m_pos;
return m_pos;
}
diff --git a/src/plugins/platforms/qnx/qqnxcursor.h b/src/plugins/platforms/qnx/qqnxcursor.h
index 6592e0a5ee..707bdbaaa2 100644
--- a/src/plugins/platforms/qnx/qqnxcursor.h
+++ b/src/plugins/platforms/qnx/qqnxcursor.h
@@ -5,9 +5,12 @@
#define QQNXCURSOR_H
#include <qpa/qplatformcursor.h>
+#include <QtCore/QLoggingCategory>
QT_BEGIN_NAMESPACE
+// Q_DECLARE_LOGGING_CATEGORY(lcQpaQnx);
+
class QQnxCursor : public QPlatformCursor
{
public:
diff --git a/src/plugins/platforms/qnx/qqnxeglwindow.cpp b/src/plugins/platforms/qnx/qqnxeglwindow.cpp
index ed53308216..854ad46c3d 100644
--- a/src/plugins/platforms/qnx/qqnxeglwindow.cpp
+++ b/src/plugins/platforms/qnx/qqnxeglwindow.cpp
@@ -10,14 +10,10 @@
#include <errno.h>
-#if defined(QQNXEGLWINDOW_DEBUG)
-#define qEglWindowDebug qDebug
-#else
-#define qEglWindowDebug QT_NO_QDEBUG_MACRO
-#endif
-
QT_BEGIN_NAMESPACE
+Q_LOGGING_CATEGORY(lcQpaWindowEgl, "qt.qpa.window.egl");
+
QQnxEglWindow::QQnxEglWindow(QWindow *window, screen_context_t context, bool needRootWindow) :
QQnxWindow(window, context, needRootWindow),
m_newSurfaceRequested(true),
@@ -96,8 +92,8 @@ void QQnxEglWindow::createEGLSurface(QQnxGLContext *context)
EGL_NONE
};
- qEglWindowDebug() << "Creating EGL surface from" << this << context
- << window()->surfaceType() << window()->type();
+ qCDebug(lcQpaWindowEgl) << "Creating EGL surface from" << this << context
+ << window()->surfaceType() << window()->type();
// Create EGL surface
EGLSurface eglSurface = eglCreateWindowSurface(
diff --git a/src/plugins/platforms/qnx/qqnxeglwindow.h b/src/plugins/platforms/qnx/qqnxeglwindow.h
index 909a901d3c..548d9be1ee 100644
--- a/src/plugins/platforms/qnx/qqnxeglwindow.h
+++ b/src/plugins/platforms/qnx/qqnxeglwindow.h
@@ -6,9 +6,12 @@
#include "qqnxwindow.h"
#include <QtCore/QMutex>
+#include <QtCore/QLoggingCategory>
QT_BEGIN_NAMESPACE
+Q_DECLARE_LOGGING_CATEGORY(lcQpaWindowEgl);
+
class QQnxGLContext;
class QQnxEglWindow : public QQnxWindow
diff --git a/src/plugins/platforms/qnx/qqnxglcontext.cpp b/src/plugins/platforms/qnx/qqnxglcontext.cpp
index 7b5b11b2e4..3d8fecf88b 100644
--- a/src/plugins/platforms/qnx/qqnxglcontext.cpp
+++ b/src/plugins/platforms/qnx/qqnxglcontext.cpp
@@ -14,14 +14,10 @@
#include <dlfcn.h>
-#if defined(QQNXGLCONTEXT_DEBUG)
-#define qGLContextDebug qDebug
-#else
-#define qGLContextDebug QT_NO_QDEBUG_MACRO
-#endif
-
QT_BEGIN_NAMESPACE
+Q_LOGGING_CATEGORY(lcQpaGLContext, "qt.qpa.glcontext");
+
static QEGLPlatformContext::Flags makeFlags()
{
QEGLPlatformContext::Flags result = {};
@@ -51,13 +47,13 @@ EGLSurface QQnxGLContext::eglSurfaceForPlatformSurface(QPlatformSurface *surface
bool QQnxGLContext::makeCurrent(QPlatformSurface *surface)
{
- qGLContextDebug();
+ qCDebug(lcQpaGLContext) << Q_FUNC_INFO;
return QEGLPlatformContext::makeCurrent(surface);
}
void QQnxGLContext::swapBuffers(QPlatformSurface *surface)
{
- qGLContextDebug();
+ qCDebug(lcQpaGLContext) << Q_FUNC_INFO;
QEGLPlatformContext::swapBuffers(surface);
diff --git a/src/plugins/platforms/qnx/qqnxglobal.cpp b/src/plugins/platforms/qnx/qqnxglobal.cpp
index 85642b44b3..9cfc328e8f 100644
--- a/src/plugins/platforms/qnx/qqnxglobal.cpp
+++ b/src/plugins/platforms/qnx/qqnxglobal.cpp
@@ -15,6 +15,8 @@ void qScreenCheckError(int rc, const char *funcInfo, const char *message, bool c
}
if (Q_UNLIKELY(rc)) {
+ qCDebug(lcQpaQnx, "%s - Screen: %s - Error: %s (%i)", funcInfo, message, strerror(errno), errno);
+
if (Q_UNLIKELY(critical))
qCritical("%s - Screen: %s - Error: %s (%i)", funcInfo, message, strerror(errno), errno);
else
diff --git a/src/plugins/platforms/qnx/qqnxinputcontext_imf.cpp b/src/plugins/platforms/qnx/qqnxinputcontext_imf.cpp
index a1063444d1..cd41ebb8c2 100644
--- a/src/plugins/platforms/qnx/qqnxinputcontext_imf.cpp
+++ b/src/plugins/platforms/qnx/qqnxinputcontext_imf.cpp
@@ -25,17 +25,7 @@
#include <process.h>
#include <sys/keycodes.h>
-#if defined(QQNXINPUTCONTEXT_IMF_EVENT_DEBUG)
-#define qInputContextIMFRequestDebug qDebug
-#else
-#define qInputContextIMFRequestDebug QT_NO_QDEBUG_MACRO
-#endif
-
-#if defined(QQNXINPUTCONTEXT_DEBUG)
-#define qInputContextDebug qDebug
-#else
-#define qInputContextDebug QT_NO_QDEBUG_MACRO
-#endif
+Q_LOGGING_CATEGORY(lcQpaInputMethods, "qt.qpa.input.methods");
static QQnxInputContext *sInputContextInstance;
static QColor sSelectedColor(0,0xb8,0,85);
@@ -161,7 +151,7 @@ static int32_t ic_begin_batch_edit(input_session_t *ic)
// See comment at beginning of namespace declaration for general information
static int32_t ic_commit_text(input_session_t *ic, spannable_string_t *text, int32_t new_cursor_position)
{
- qInputContextIMFRequestDebug();
+ qCDebug(lcQpaInputMethods) << Q_FUNC_INFO;
QQnxImfRequest event(ic, ImfCommitText);
event.ct.text = text;
@@ -176,7 +166,7 @@ static int32_t ic_commit_text(input_session_t *ic, spannable_string_t *text, int
// See comment at beginning of namespace declaration for general information
static int32_t ic_delete_surrounding_text(input_session_t *ic, int32_t left_length, int32_t right_length)
{
- qInputContextIMFRequestDebug();
+ qCDebug(lcQpaInputMethods) << Q_FUNC_INFO;
QQnxImfRequest event(ic, ImfDeleteSurroundingText);
event.dst.left_length = left_length;
@@ -200,7 +190,7 @@ static int32_t ic_end_batch_edit(input_session_t *ic)
// See comment at beginning of namespace declaration for general information
static int32_t ic_finish_composing_text(input_session_t *ic)
{
- qInputContextIMFRequestDebug();
+ qCDebug(lcQpaInputMethods) << Q_FUNC_INFO;
QQnxImfRequest event(ic, ImfFinishComposingText);
event.fct.result = -1;
@@ -213,7 +203,7 @@ static int32_t ic_finish_composing_text(input_session_t *ic)
// See comment at beginning of namespace declaration for general information
static int32_t ic_get_cursor_position(input_session_t *ic)
{
- qInputContextIMFRequestDebug();
+ qCDebug(lcQpaInputMethods) << Q_FUNC_INFO;
QQnxImfRequest event(ic, ImfGetCursorPosition);
event.gcp.result = -1;
@@ -226,7 +216,7 @@ static int32_t ic_get_cursor_position(input_session_t *ic)
// See comment at beginning of namespace declaration for general information
static spannable_string_t *ic_get_text_after_cursor(input_session_t *ic, int32_t n, int32_t flags)
{
- qInputContextIMFRequestDebug();
+ qCDebug(lcQpaInputMethods) << Q_FUNC_INFO;
QQnxImfRequest event(ic, ImfGetTextAfterCursor);
event.gtac.n = n;
@@ -241,7 +231,7 @@ static spannable_string_t *ic_get_text_after_cursor(input_session_t *ic, int32_t
// See comment at beginning of namespace declaration for general information
static spannable_string_t *ic_get_text_before_cursor(input_session_t *ic, int32_t n, int32_t flags)
{
- qInputContextIMFRequestDebug();
+ qCDebug(lcQpaInputMethods) << Q_FUNC_INFO;
QQnxImfRequest event(ic, ImfGetTextBeforeCursor);
event.gtac.n = n;
@@ -256,7 +246,7 @@ static spannable_string_t *ic_get_text_before_cursor(input_session_t *ic, int32_
// See comment at beginning of namespace declaration for general information
static int32_t ic_send_event(input_session_t *ic, event_t *event)
{
- qInputContextIMFRequestDebug();
+ qCDebug(lcQpaInputMethods) << Q_FUNC_INFO;
QQnxImfRequest imfEvent(ic, ImfSendEvent);
imfEvent.sae.event = event;
@@ -270,7 +260,7 @@ static int32_t ic_send_event(input_session_t *ic, event_t *event)
// See comment at beginning of namespace declaration for general information
static int32_t ic_send_async_event(input_session_t *ic, event_t *event)
{
- qInputContextIMFRequestDebug();
+ qCDebug(lcQpaInputMethods) << Q_FUNC_INFO;
// There's no difference from our point of view between ic_send_event & ic_send_async_event
QQnxImfRequest imfEvent(ic, ImfSendEvent);
@@ -285,7 +275,7 @@ static int32_t ic_send_async_event(input_session_t *ic, event_t *event)
// See comment at beginning of namespace declaration for general information
static int32_t ic_set_composing_region(input_session_t *ic, int32_t start, int32_t end)
{
- qInputContextIMFRequestDebug();
+ qCDebug(lcQpaInputMethods) << Q_FUNC_INFO;
QQnxImfRequest event(ic, ImfSetComposingRegion);
event.scr.start = start;
@@ -301,7 +291,7 @@ static int32_t ic_set_composing_region(input_session_t *ic, int32_t start, int32
// See comment at beginning of namespace declaration for general information
static int32_t ic_set_composing_text(input_session_t *ic, spannable_string_t *text, int32_t new_cursor_position)
{
- qInputContextIMFRequestDebug();
+ qCDebug(lcQpaInputMethods) << Q_FUNC_INFO;
QQnxImfRequest event(ic, ImfSetComposingText);
event.sct.text = text;
@@ -316,7 +306,7 @@ static int32_t ic_set_composing_text(input_session_t *ic, spannable_string_t *te
// See comment at beginning of namespace declaration for general information
static int32_t ic_is_text_selected(input_session_t* ic, int32_t* pIsSelected)
{
- qInputContextIMFRequestDebug();
+ qCDebug(lcQpaInputMethods) << Q_FUNC_INFO;
QQnxImfRequest event(ic, ImfIsTextSelected);
event.its.pIsSelected = pIsSelected;
@@ -330,7 +320,7 @@ static int32_t ic_is_text_selected(input_session_t* ic, int32_t* pIsSelected)
// See comment at beginning of namespace declaration for general information
static int32_t ic_is_all_text_selected(input_session_t* ic, int32_t* pIsSelected)
{
- qInputContextIMFRequestDebug();
+ qCDebug(lcQpaInputMethods) << Q_FUNC_INFO;
QQnxImfRequest event(ic, ImfIsAllTextSelected);
event.its.pIsSelected = pIsSelected;
@@ -466,7 +456,7 @@ initEvent(event_t *pEvent, const input_session_t *pSession, EventType eventType,
static spannable_string_t *toSpannableString(const QString &text)
{
- qInputContextDebug() << text;
+ qCDebug(lcQpaInputMethods) << Q_FUNC_INFO << "Text:" << text;
spannable_string_t *pString = static_cast<spannable_string_t *>(malloc(sizeof(spannable_string_t)));
pString->str = static_cast<wchar_t *>(malloc(sizeof(wchar_t) * text.length() + 1));
@@ -540,7 +530,7 @@ QQnxInputContext::QQnxInputContext(QQnxIntegration *integration, QQnxAbstractVir
m_integration(integration),
m_virtualKeyboard(keyboard)
{
- qInputContextDebug();
+ qCDebug(lcQpaInputMethods) << Q_FUNC_INFO;
if (!imfAvailable())
return;
@@ -563,7 +553,7 @@ QQnxInputContext::QQnxInputContext(QQnxIntegration *integration, QQnxAbstractVir
QQnxInputContext::~QQnxInputContext()
{
- qInputContextDebug();
+ qCDebug(lcQpaInputMethods) << Q_FUNC_INFO;
Q_ASSERT(sInputContextInstance == this);
sInputContextInstance = nullptr;
@@ -638,7 +628,7 @@ void QQnxInputContext::processImfEvent(QQnxImfRequest *imfEvent)
bool QQnxInputContext::filterEvent( const QEvent *event )
{
- qInputContextDebug() << event;
+ qCDebug(lcQpaInputMethods) << Q_FUNC_INFO << event;
switch (event->type()) {
case QEvent::CloseSoftwareInputPanel:
@@ -661,19 +651,19 @@ QRectF QQnxInputContext::keyboardRect() const
void QQnxInputContext::reset()
{
- qInputContextDebug();
+ qCDebug(lcQpaInputMethods) << Q_FUNC_INFO;
endComposition();
}
void QQnxInputContext::commit()
{
- qInputContextDebug();
+ qCDebug(lcQpaInputMethods) << Q_FUNC_INFO;
endComposition();
}
void QQnxInputContext::update(Qt::InputMethodQueries queries)
{
- qInputContextDebug() << queries;
+ qCDebug(lcQpaInputMethods) << Q_FUNC_INFO << "Queries:" << queries;
if (queries & Qt::ImCursorPosition) {
int lastCaret = m_caretPosition;
@@ -685,7 +675,8 @@ void QQnxInputContext::update(Qt::InputMethodQueries queries)
initEvent(&caretEvent.event, sInputSession, EVENT_CARET, CARET_POS_CHANGED, sizeof(caretEvent));
caretEvent.old_pos = lastCaret;
caretEvent.new_pos = m_caretPosition;
- qInputContextDebug("ictrl_dispatch_event caret changed %d %d", lastCaret, m_caretPosition);
+ qCDebug(lcQpaInputMethods, "ictrl_dispatch_event caret changed %d %d", lastCaret, m_caretPosition);
+
p_ictrl_dispatch_event(&caretEvent.event);
}
}
@@ -693,7 +684,7 @@ void QQnxInputContext::update(Qt::InputMethodQueries queries)
void QQnxInputContext::closeSession()
{
- qInputContextDebug();
+ qCDebug(lcQpaInputMethods) << Q_FUNC_INFO;
if (!imfAvailable())
return;
@@ -715,7 +706,7 @@ bool QQnxInputContext::openSession()
closeSession();
sInputSession = p_ictrl_open_session(&ic_funcs);
- qInputContextDebug();
+ qCDebug(lcQpaInputMethods) << Q_FUNC_INFO;
return sInputSession != 0;
}
@@ -739,7 +730,7 @@ bool QQnxInputContext::hasSelectedText()
bool QQnxInputContext::dispatchRequestSoftwareInputPanel()
{
- qInputContextDebug() << "requesting keyboard" << m_inputPanelVisible;
+ qCDebug(lcQpaInputMethods) << "Requesting keyboard" << m_inputPanelVisible;
m_virtualKeyboard.showKeyboard();
return true;
@@ -747,7 +738,7 @@ bool QQnxInputContext::dispatchRequestSoftwareInputPanel()
bool QQnxInputContext::dispatchCloseSoftwareInputPanel()
{
- qInputContextDebug() << "hiding keyboard" << m_inputPanelVisible;
+ qCDebug(lcQpaInputMethods) << "Hiding keyboard" << m_inputPanelVisible;
m_virtualKeyboard.hideKeyboard();
return true;
@@ -793,7 +784,7 @@ bool QQnxInputContext::dispatchFocusGainEvent(int inputHints)
focusEvent.style |= IMF_EMAIL_TYPE;
}
- qInputContextDebug() << "ictrl_dispatch_event focus gain style:" << focusEvent.style;
+ qCDebug(lcQpaInputMethods) << "ictrl_dispatch_event focus gain style:" << focusEvent.style;
p_ictrl_dispatch_event((event_t *)&focusEvent);
@@ -803,7 +794,7 @@ bool QQnxInputContext::dispatchFocusGainEvent(int inputHints)
void QQnxInputContext::dispatchFocusLossEvent()
{
if (hasSession()) {
- qInputContextDebug("ictrl_dispatch_event focus lost");
+ qCDebug(lcQpaInputMethods) << "ictrl_dispatch_event focus lost";
focus_event_t focusEvent;
initEvent(&focusEvent.event, sInputSession, EVENT_FOCUS, FOCUS_LOST, sizeof(focusEvent));
@@ -878,7 +869,7 @@ bool QQnxInputContext::handleKeyboardEvent(int flags, int sym, int mod, int scan
navigation_event_t navEvent;
initEvent(&navEvent.event, sInputSession, EVENT_NAVIGATION, key, sizeof(navEvent));
navEvent.magnitude = 1;
- qInputContextDebug("ictrl_dispatch_even navigation %d", key);
+ qCDebug(lcQpaInputMethods, "ictrl_dispatch_even navigation %d", key);
p_ictrl_dispatch_event(&navEvent.event);
}
} else {
@@ -891,7 +882,8 @@ bool QQnxInputContext::handleKeyboardEvent(int flags, int sym, int mod, int scan
keyEvent.sequence_id = sequenceId;
p_ictrl_dispatch_event(&keyEvent.event);
- qInputContextDebug("ictrl_dispatch_even key %d", key);
+ qCDebug(lcQpaInputMethods, "ictrl_dispatch_even key %d", key);
+
}
return true;
@@ -907,7 +899,7 @@ void QQnxInputContext::updateCursorPosition()
QCoreApplication::sendEvent(input, &query);
m_caretPosition = query.value(Qt::ImCursorPosition).toInt();
- qInputContextDebug("%d", m_caretPosition);
+ qCDebug(lcQpaInputMethods, "ictrl_dispatch_even key %d", key);
}
void QQnxInputContext::endComposition()
@@ -920,7 +912,7 @@ void QQnxInputContext::endComposition()
if (hasSession()) {
action_event_t actionEvent;
initEvent(&actionEvent.event, sInputSession, EVENT_ACTION, ACTION_END_COMPOSITION, sizeof(actionEvent));
- qInputContextDebug("ictrl_dispatch_even end composition");
+ qCDebug(lcQpaInputMethods, "ictrl_dispatch_even end composition");
p_ictrl_dispatch_event(&actionEvent.event);
}
}
@@ -937,7 +929,7 @@ void QQnxInputContext::updateComposition(spannable_string_t *text, int32_t new_c
m_composingText = QString::fromWCharArray(text->str, text->length);
m_isComposing = true;
- qInputContextDebug() << m_composingText << new_cursor_position;
+ qCDebug(lcQpaInputMethods) << "Text =" << m_composingText << "Cursor position =" << new_cursor_position;
QList<QInputMethodEvent::Attribute> attributes;
attributes.append(QInputMethodEvent::Attribute(QInputMethodEvent::Cursor,
@@ -967,7 +959,7 @@ void QQnxInputContext::updateComposition(spannable_string_t *text, int32_t new_c
format.setFontUnderline(true);
if (highlightColor.isValid())
format.setBackground(QBrush(highlightColor));
- qInputContextDebug() << " attrib: " << underline << highlightColor << text->spans[i].start << text->spans[i].end;
+ qCDebug(lcQpaInputMethods) << "attrib: " << underline << highlightColor << text->spans[i].start << text->spans[i].end;
attributes.append(QInputMethodEvent::Attribute(QInputMethodEvent::TextFormat, text->spans[i].start,
text->spans[i].end - text->spans[i].start + 1, QVariant(format)));
@@ -986,7 +978,7 @@ void QQnxInputContext::finishComposingText()
QObject *input = qGuiApp->focusObject();
if (input) {
- qInputContextDebug() << m_composingText;
+ qCDebug(lcQpaInputMethods) << Q_FUNC_INFO << "Text =" << m_composingText;
QInputMethodEvent event;
event.setCommitString(m_composingText);
@@ -1051,13 +1043,13 @@ int32_t QQnxInputContext::processEvent(event_t *event)
int32_t result = -1;
switch (event->event_type) {
case EVENT_SPELL_CHECK: {
- qInputContextDebug("EVENT_SPELL_CHECK");
+ qCDebug(lcQpaInputMethods) << "EVENT_SPELL_CHECK";
result = handleSpellCheck(reinterpret_cast<spell_check_event_t *>(event));
break;
}
case EVENT_NAVIGATION: {
- qInputContextDebug("EVENT_NAVIGATION");
+ qCDebug(lcQpaInputMethods) << "EVENT_NAVIGATION";
int key = event->event_id == NAVIGATE_UP ? KEYCODE_UP :
event->event_id == NAVIGATE_DOWN ? KEYCODE_DOWN :
@@ -1080,7 +1072,7 @@ int32_t QQnxInputContext::processEvent(event_t *event)
int flags = KEY_SYM_VALID | KEY_CAP_VALID;
if (event->event_id == IMF_KEY_DOWN)
flags |= KEY_DOWN;
- qInputContextDebug("EVENT_KEY %d %d", flags, keySym);
+ qCDebug(lcQpaInputMethods, "EVENT_KEY %d %d", flags, keySym);
QQnxScreenEventHandler::injectKeyboardEvent(flags, keySym, modifiers, 0, keyCap);
result = 0;
break;
@@ -1120,7 +1112,7 @@ int32_t QQnxInputContext::onCommitText(spannable_string_t *text, int32_t new_cur
int32_t QQnxInputContext::onDeleteSurroundingText(int32_t left_length, int32_t right_length)
{
- qInputContextDebug("L: %d R: %d", int(left_length), int(right_length));
+ qCDebug(lcQpaInputMethods, "L: %d R: %d", int(left_length), int(right_length));
QObject *input = qGuiApp->focusObject();
if (!input)
@@ -1151,7 +1143,7 @@ int32_t QQnxInputContext::onFinishComposingText()
int32_t QQnxInputContext::onGetCursorPosition()
{
- qInputContextDebug();
+ qCDebug(lcQpaInputMethods) << Q_FUNC_INFO;
QObject *input = qGuiApp->focusObject();
if (!input)
@@ -1165,7 +1157,7 @@ int32_t QQnxInputContext::onGetCursorPosition()
spannable_string_t *QQnxInputContext::onGetTextAfterCursor(int32_t n, int32_t flags)
{
Q_UNUSED(flags);
- qInputContextDebug();
+ qCDebug(lcQpaInputMethods) << Q_FUNC_INFO;
QObject *input = qGuiApp->focusObject();
if (!input)
@@ -1182,7 +1174,7 @@ spannable_string_t *QQnxInputContext::onGetTextAfterCursor(int32_t n, int32_t fl
spannable_string_t *QQnxInputContext::onGetTextBeforeCursor(int32_t n, int32_t flags)
{
Q_UNUSED(flags);
- qInputContextDebug();
+ qCDebug(lcQpaInputMethods) << Q_FUNC_INFO;
QObject *input = qGuiApp->focusObject();
if (!input)
@@ -1201,7 +1193,7 @@ spannable_string_t *QQnxInputContext::onGetTextBeforeCursor(int32_t n, int32_t f
int32_t QQnxInputContext::onSendEvent(event_t *event)
{
- qInputContextDebug();
+ qCDebug(lcQpaInputMethods) << Q_FUNC_INFO;
return processEvent(event);
}
@@ -1217,7 +1209,7 @@ int32_t QQnxInputContext::onSetComposingRegion(int32_t start, int32_t end)
QString text = query.value(Qt::ImSurroundingText).toString();
m_caretPosition = query.value(Qt::ImCursorPosition).toInt();
- qInputContextDebug() << text;
+ qCDebug(lcQpaInputMethods) << Q_FUNC_INFO << "Text =" << text;
m_isUpdatingText = true;
@@ -1260,7 +1252,7 @@ int32_t QQnxInputContext::onIsTextSelected(int32_t* pIsSelected)
{
*pIsSelected = hasSelectedText();
- qInputContextDebug() << *pIsSelected;
+ qCDebug(lcQpaInputMethods) << Q_FUNC_INFO << *pIsSelected;
return 0;
}
@@ -1276,20 +1268,20 @@ int32_t QQnxInputContext::onIsAllTextSelected(int32_t* pIsSelected)
*pIsSelected = query.value(Qt::ImSurroundingText).toString().length() == query.value(Qt::ImCurrentSelection).toString().length();
- qInputContextDebug() << *pIsSelected;
+ qCDebug(lcQpaInputMethods) << Q_FUNC_INFO << *pIsSelected;
return 0;
}
void QQnxInputContext::showInputPanel()
{
- qInputContextDebug();
+ qCDebug(lcQpaInputMethods) << Q_FUNC_INFO;
dispatchRequestSoftwareInputPanel();
}
void QQnxInputContext::hideInputPanel()
{
- qInputContextDebug();
+ qCDebug(lcQpaInputMethods) << Q_FUNC_INFO;
dispatchCloseSoftwareInputPanel();
}
@@ -1305,7 +1297,7 @@ QLocale QQnxInputContext::locale() const
void QQnxInputContext::keyboardVisibilityChanged(bool visible)
{
- qInputContextDebug() << "visible=" << visible;
+ qCDebug(lcQpaInputMethods) << Q_FUNC_INFO << "visible=" << visible;
if (m_inputPanelVisible != visible) {
m_inputPanelVisible = visible;
emitInputPanelVisibleChanged();
@@ -1314,7 +1306,7 @@ void QQnxInputContext::keyboardVisibilityChanged(bool visible)
void QQnxInputContext::keyboardLocaleChanged(const QLocale &locale)
{
- qInputContextDebug() << "locale=" << locale;
+ qCDebug(lcQpaInputMethods) << Q_FUNC_INFO << "locale=" << locale;
if (m_inputPanelLocale != locale) {
m_inputPanelLocale = locale;
emitLocaleChanged();
@@ -1323,7 +1315,7 @@ void QQnxInputContext::keyboardLocaleChanged(const QLocale &locale)
void QQnxInputContext::setHighlightColor(int index, const QColor &color)
{
- qInputContextDebug() << "setHighlightColor" << index << color << qGuiApp->focusObject();
+ qCDebug(lcQpaInputMethods) << "setHighlightColor" << index << color << qGuiApp->focusObject();
if (!sInputContextInstance)
return;
@@ -1342,7 +1334,7 @@ void QQnxInputContext::setHighlightColor(int index, const QColor &color)
void QQnxInputContext::setFocusObject(QObject *object)
{
- qInputContextDebug() << "input item=" << object;
+ qCDebug(lcQpaInputMethods) << Q_FUNC_INFO << "input item=" << object;
// Ensure the colors are reset if we've a change in focus object
setHighlightColor(-1, QColor());
@@ -1372,7 +1364,7 @@ void QQnxInputContext::setFocusObject(QObject *object)
bool QQnxInputContext::checkSpelling(const QString &text, void *context, void (*spellCheckDone)(void *context, const QString &text, const QList<int> &indices))
{
- qInputContextDebug() << "text" << text;
+ qCDebug(lcQpaInputMethods) << Q_FUNC_INFO << "Text =" << text;
if (!imfAvailable())
return false;
diff --git a/src/plugins/platforms/qnx/qqnxinputcontext_noimf.cpp b/src/plugins/platforms/qnx/qqnxinputcontext_noimf.cpp
index 2a4e5b509b..7789b2830a 100644
--- a/src/plugins/platforms/qnx/qqnxinputcontext_noimf.cpp
+++ b/src/plugins/platforms/qnx/qqnxinputcontext_noimf.cpp
@@ -10,14 +10,10 @@
#include <QtGui/QGuiApplication>
#include <QtGui/QInputMethodEvent>
-#if defined(QQNXINPUTCONTEXT_DEBUG)
-#define qInputContextDebug qDebug
-#else
-#define qInputContextDebug QT_NO_QDEBUG_MACRO
-#endif
-
QT_BEGIN_NAMESPACE
+Q_LOGGING_CATEGORY(lcQpaInputMethods, "qt.qpa.input.methods");
+
QQnxInputContext::QQnxInputContext(QQnxIntegration *integration, QQnxAbstractVirtualKeyboard &keyboard) :
QPlatformInputContext(),
m_inputPanelVisible(false),
@@ -58,13 +54,13 @@ bool QQnxInputContext::filterEvent( const QEvent *event )
if (event->type() == QEvent::CloseSoftwareInputPanel) {
m_virtualKeyboard.hideKeyboard();
- qInputContextDebug("hiding virtual keyboard");
+ qCDebug(lcQpaInputMethods) << "hiding virtual keyboard";
return false;
}
if (event->type() == QEvent::RequestSoftwareInputPanel) {
m_virtualKeyboard.showKeyboard();
- qInputContextDebug("requesting virtual keyboard");
+ qCDebug(lcQpaInputMethods) << "requesting virtual keyboard";
return false;
}
@@ -91,13 +87,13 @@ bool QQnxInputContext::handleKeyboardEvent(int flags, int sym, int mod, int scan
void QQnxInputContext::showInputPanel()
{
- qInputContextDebug();
+ qCDebug(lcQpaInputMethods) << Q_FUNC_INFO;
m_virtualKeyboard.showKeyboard();
}
void QQnxInputContext::hideInputPanel()
{
- qInputContextDebug();
+ qCDebug(lcQpaInputMethods) << Q_FUNC_INFO;
m_virtualKeyboard.hideKeyboard();
}
@@ -118,7 +114,7 @@ void QQnxInputContext::keyboardHeightChanged()
void QQnxInputContext::keyboardVisibilityChanged(bool visible)
{
- qInputContextDebug() << "visible=" << visible;
+ qCDebug(lcQpaInputMethods) << "visible=" << visible;
if (m_inputPanelVisible != visible) {
m_inputPanelVisible = visible;
emitInputPanelVisibleChanged();
@@ -127,7 +123,7 @@ void QQnxInputContext::keyboardVisibilityChanged(bool visible)
void QQnxInputContext::keyboardLocaleChanged(const QLocale &locale)
{
- qInputContextDebug() << "locale=" << locale;
+ qCDebug(lcQpaInputMethods) << "locale=" << locale;
if (m_inputPanelLocale != locale) {
m_inputPanelLocale = locale;
emitLocaleChanged();
@@ -136,7 +132,7 @@ void QQnxInputContext::keyboardLocaleChanged(const QLocale &locale)
void QQnxInputContext::setFocusObject(QObject *object)
{
- qInputContextDebug() << "input item=" << object;
+ qCDebug(lcQpaInputMethods) << "input item=" << object;;
if (!inputMethodAccepted()) {
if (m_inputPanelVisible)
diff --git a/src/plugins/platforms/qnx/qqnxintegration.cpp b/src/plugins/platforms/qnx/qqnxintegration.cpp
index 310d5d4c8a..b308c956f2 100644
--- a/src/plugins/platforms/qnx/qqnxintegration.cpp
+++ b/src/plugins/platforms/qnx/qqnxintegration.cpp
@@ -49,6 +49,7 @@
#include <qpa/qwindowsysteminterface.h>
#include <QtGui/private/qguiapplication_p.h>
+#include <QtGui/private/qrhibackingstore_p.h>
#if !defined(QT_NO_OPENGL)
#include "qqnxglcontext.h"
@@ -64,14 +65,10 @@
#include <QtCore/QFile>
#include <errno.h>
-#if defined(QQNXINTEGRATION_DEBUG)
-#define qIntegrationDebug qDebug
-#else
-#define qIntegrationDebug QT_NO_QDEBUG_MACRO
-#endif
-
QT_BEGIN_NAMESPACE
+// Q_LOGGING_CATEGORY(lcQpaQnx, "qt.qpa.qnx");
+
using namespace Qt::StringLiterals;
QQnxIntegration *QQnxIntegration::ms_instance;
@@ -144,7 +141,7 @@ QQnxIntegration::QQnxIntegration(const QStringList &paramList)
{
ms_instance = this;
m_options = parseOptions(paramList);
- qIntegrationDebug();
+ qCDebug(lcQpaQnx) << Q_FUNC_INFO;
// Open connection to QNX composition manager
if (screen_create_context(&m_screenContext, getContextCapabilities(paramList))) {
@@ -221,7 +218,7 @@ QQnxIntegration::QQnxIntegration(const QStringList &paramList)
QQnxIntegration::~QQnxIntegration()
{
- qIntegrationDebug("platform plugin shutdown begin");
+ qCDebug(lcQpaQnx) << "Platform plugin shutdown begin";
delete m_nativeInterface;
#if QT_CONFIG(draganddrop)
@@ -278,12 +275,12 @@ QQnxIntegration::~QQnxIntegration()
ms_instance = nullptr;
- qIntegrationDebug("platform plugin shutdown end");
+ qCDebug(lcQpaQnx) << "Platform plugin shutdown end";
}
bool QQnxIntegration::hasCapability(QPlatformIntegration::Capability cap) const
{
- qIntegrationDebug();
+ qCDebug(lcQpaQnx) << Q_FUNC_INFO;
switch (cap) {
case MultipleWindows:
case ForeignWindows:
@@ -314,7 +311,7 @@ QPlatformWindow *QQnxIntegration::createForeignWindow(QWindow *window, WId nativ
QPlatformWindow *QQnxIntegration::createPlatformWindow(QWindow *window) const
{
- qIntegrationDebug();
+ qCDebug(lcQpaQnx) << Q_FUNC_INFO;
QSurface::SurfaceType surfaceType = window->surfaceType();
const bool needRootWindow = options() & RootWindow;
switch (surfaceType) {
@@ -332,14 +329,25 @@ QPlatformWindow *QQnxIntegration::createPlatformWindow(QWindow *window) const
QPlatformBackingStore *QQnxIntegration::createPlatformBackingStore(QWindow *window) const
{
- qIntegrationDebug();
- return new QQnxRasterBackingStore(window);
+ QSurface::SurfaceType surfaceType = window->surfaceType();
+ qCDebug(lcQpaQnx) << Q_FUNC_INFO << surfaceType;
+ switch (surfaceType) {
+ case QSurface::RasterSurface:
+ return new QQnxRasterBackingStore(window);
+#if !defined(QT_NO_OPENGL)
+ // Return a QRhiBackingStore for non-raster surface windows
+ case QSurface::OpenGLSurface:
+ return new QRhiBackingStore(window);
+#endif
+ default:
+ return nullptr;
+ }
}
#if !defined(QT_NO_OPENGL)
QPlatformOpenGLContext *QQnxIntegration::createPlatformOpenGLContext(QOpenGLContext *context) const
{
- qIntegrationDebug();
+ qCDebug(lcQpaQnx) << Q_FUNC_INFO;
// Get color channel sizes from window format
QSurfaceFormat format = context->format();
@@ -398,7 +406,7 @@ QPlatformOpenGLContext *QQnxIntegration::createPlatformOpenGLContext(QOpenGLCont
#if QT_CONFIG(qqnx_pps)
QPlatformInputContext *QQnxIntegration::inputContext() const
{
- qIntegrationDebug();
+ qCDebug(lcQpaQnx) << Q_FUNC_INFO;
if (m_qpaInputContext)
return m_qpaInputContext;
return m_inputContext;
@@ -407,7 +415,7 @@ QPlatformInputContext *QQnxIntegration::inputContext() const
void QQnxIntegration::moveToScreen(QWindow *window, int screen)
{
- qIntegrationDebug() << "w =" << window << ", s =" << screen;
+ qCDebug(lcQpaQnx) << Q_FUNC_INFO << "w =" << window << ", s =" << screen;
// get platform window used by widget
QQnxWindow *platformWindow = static_cast<QQnxWindow *>(window->handle());
@@ -421,7 +429,7 @@ void QQnxIntegration::moveToScreen(QWindow *window, int screen)
QAbstractEventDispatcher *QQnxIntegration::createEventDispatcher() const
{
- qIntegrationDebug();
+ qCDebug(lcQpaQnx) << Q_FUNC_INFO;
// We transfer ownersip of the event-dispatcher to QtCoreApplication
QAbstractEventDispatcher *eventDispatcher = m_eventDispatcher;
@@ -438,7 +446,7 @@ QPlatformNativeInterface *QQnxIntegration::nativeInterface() const
#if !defined(QT_NO_CLIPBOARD)
QPlatformClipboard *QQnxIntegration::clipboard() const
{
- qIntegrationDebug();
+ qCDebug(lcQpaQnx) << Q_FUNC_INFO;
#if QT_CONFIG(qqnx_pps)
if (!m_clipboard)
@@ -457,7 +465,7 @@ QPlatformDrag *QQnxIntegration::drag() const
QVariant QQnxIntegration::styleHint(QPlatformIntegration::StyleHint hint) const
{
- qIntegrationDebug();
+ qCDebug(lcQpaQnx) << Q_FUNC_INFO;
if ((hint == ShowIsFullScreen) && (m_options & FullScreenApplication))
return true;
@@ -471,7 +479,7 @@ QPlatformServices * QQnxIntegration::services() const
QWindow *QQnxIntegration::window(screen_window_t qnxWindow) const
{
- qIntegrationDebug();
+ qCDebug(lcQpaQnx) << Q_FUNC_INFO;
QMutexLocker locker(&m_windowMapperMutex);
Q_UNUSED(locker);
return m_windowMapper.value(qnxWindow, 0);
@@ -479,7 +487,7 @@ QWindow *QQnxIntegration::window(screen_window_t qnxWindow) const
void QQnxIntegration::addWindow(screen_window_t qnxWindow, QWindow *window)
{
- qIntegrationDebug();
+ qCDebug(lcQpaQnx) << Q_FUNC_INFO;
QMutexLocker locker(&m_windowMapperMutex);
Q_UNUSED(locker);
m_windowMapper.insert(qnxWindow, window);
@@ -487,7 +495,7 @@ void QQnxIntegration::addWindow(screen_window_t qnxWindow, QWindow *window)
void QQnxIntegration::removeWindow(screen_window_t qnxWindow)
{
- qIntegrationDebug();
+ qCDebug(lcQpaQnx) << Q_FUNC_INFO;
QMutexLocker locker(&m_windowMapperMutex);
Q_UNUSED(locker);
m_windowMapper.remove(qnxWindow);
@@ -596,7 +604,7 @@ QList<screen_display_t *> QQnxIntegration::sortDisplays(screen_display_t *availa
void QQnxIntegration::createDisplays()
{
- qIntegrationDebug();
+ qCDebug(lcQpaQnx) << Q_FUNC_INFO;
// Query number of displays
int displayCount = 0;
int result = screen_get_context_property_iv(m_screenContext, SCREEN_PROPERTY_DISPLAY_COUNT,
@@ -626,11 +634,12 @@ void QQnxIntegration::createDisplays()
Q_SCREEN_CHECKERROR(result, "Failed to query display attachment");
if (!isAttached) {
- qIntegrationDebug("Skipping non-attached display %d", i);
+ qCDebug(lcQpaQnx) << "Skipping non-attached display " << i;
continue;
}
- qIntegrationDebug("Creating screen for display %d", i);
+ qCDebug(lcQpaQnx) << "Creating screen for display " << i;
+
createDisplay(*orderedDisplays[i], /*isPrimary=*/false);
} // of displays iteration
}
@@ -664,7 +673,8 @@ void QQnxIntegration::removeDisplay(QQnxScreen *screen)
void QQnxIntegration::destroyDisplays()
{
- qIntegrationDebug();
+ qCDebug(lcQpaQnx) << Q_FUNC_INFO;
+
Q_FOREACH (QQnxScreen *screen, m_screens) {
QWindowSystemInterface::handleScreenRemoved(screen);
}
@@ -715,7 +725,7 @@ bool QQnxIntegration::supportsNavigatorEvents() const
#if QT_CONFIG(opengl)
void QQnxIntegration::createEglDisplay()
{
- qIntegrationDebug();
+ qCDebug(lcQpaQnx) << Q_FUNC_INFO;
// Initialize connection to EGL
m_eglDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY);
@@ -729,7 +739,7 @@ void QQnxIntegration::createEglDisplay()
void QQnxIntegration::destroyEglDisplay()
{
- qIntegrationDebug();
+ qCDebug(lcQpaQnx) << Q_FUNC_INFO;
// Close connection to EGL
eglTerminate(m_eglDisplay);
diff --git a/src/plugins/platforms/qnx/qqnxintegration.h b/src/plugins/platforms/qnx/qqnxintegration.h
index f126300aed..8a78d54ceb 100644
--- a/src/plugins/platforms/qnx/qqnxintegration.h
+++ b/src/plugins/platforms/qnx/qqnxintegration.h
@@ -10,6 +10,7 @@
#include <QtCore/qmutex.h>
#include <screen/screen.h>
+#include <QtCore/QLoggingCategory>
#if QT_CONFIG(opengl)
#include <EGL/egl.h>
@@ -17,6 +18,9 @@
QT_BEGIN_NAMESPACE
+Q_DECLARE_LOGGING_CATEGORY(lcQpaQnx);
+Q_DECLARE_LOGGING_CATEGORY(lcQpaGLContext);
+
class QQnxScreenEventThread;
class QQnxFileDialogHelper;
class QQnxNativeInterface;
diff --git a/src/plugins/platforms/qnx/qqnxnavigatoreventhandler.cpp b/src/plugins/platforms/qnx/qqnxnavigatoreventhandler.cpp
index cee86a68a0..7580e560aa 100644
--- a/src/plugins/platforms/qnx/qqnxnavigatoreventhandler.cpp
+++ b/src/plugins/platforms/qnx/qqnxnavigatoreventhandler.cpp
@@ -10,11 +10,7 @@
#include <QGuiApplication>
#include <qpa/qwindowsysteminterface.h>
-#if defined(QQNXNAVIGATOREVENTHANDLER_DEBUG)
-#define qNavigatorEventHandlerDebug qDebug
-#else
-#define qNavigatorEventHandlerDebug QT_NO_QDEBUG_MACRO
-#endif
+Q_LOGGING_CATEGORY(lcQpaQnxNavigatorEvents, "qt.qpa.qnx.navigator.events");
QT_BEGIN_NAMESPACE
@@ -27,20 +23,20 @@ bool QQnxNavigatorEventHandler::handleOrientationCheck(int angle)
{
// reply to navigator that (any) orientation is acceptable
// TODO: check if top window flags prohibit orientation change
- qNavigatorEventHandlerDebug("angle=%d", angle);
+ qCDebug(lcQpaQnxNavigatorEvents, "angle=%d", angle);
return true;
}
void QQnxNavigatorEventHandler::handleOrientationChange(int angle)
{
// update screen geometry and reply to navigator that we're ready
- qNavigatorEventHandlerDebug("angle=%d", angle);
+ qCDebug(lcQpaQnxNavigatorEvents, "angle=%d", angle);
emit rotationChanged(angle);
}
void QQnxNavigatorEventHandler::handleSwipeDown()
{
- qNavigatorEventHandlerDebug();
+ qCDebug(lcQpaQnxNavigatorEvents) << Q_FUNC_INFO;
Q_EMIT swipeDown();
}
@@ -48,25 +44,25 @@ void QQnxNavigatorEventHandler::handleSwipeDown()
void QQnxNavigatorEventHandler::handleExit()
{
// shutdown everything
- qNavigatorEventHandlerDebug();
+ qCDebug(lcQpaQnxNavigatorEvents) << Q_FUNC_INFO;
QCoreApplication::quit();
}
void QQnxNavigatorEventHandler::handleWindowGroupActivated(const QByteArray &id)
{
- qNavigatorEventHandlerDebug() << id;
+ qCDebug(lcQpaQnxNavigatorEvents) << Q_FUNC_INFO << id;
Q_EMIT windowGroupActivated(id);
}
void QQnxNavigatorEventHandler::handleWindowGroupDeactivated(const QByteArray &id)
{
- qNavigatorEventHandlerDebug() << id;
+ qCDebug(lcQpaQnxNavigatorEvents) << Q_FUNC_INFO << id;
Q_EMIT windowGroupDeactivated(id);
}
void QQnxNavigatorEventHandler::handleWindowGroupStateChanged(const QByteArray &id, Qt::WindowState state)
{
- qNavigatorEventHandlerDebug() << id;
+ qCDebug(lcQpaQnxNavigatorEvents) << Q_FUNC_INFO << id;
Q_EMIT windowGroupStateChanged(id, state);
}
diff --git a/src/plugins/platforms/qnx/qqnxnavigatoreventhandler.h b/src/plugins/platforms/qnx/qqnxnavigatoreventhandler.h
index 342b6c3ef6..826c9a0ff9 100644
--- a/src/plugins/platforms/qnx/qqnxnavigatoreventhandler.h
+++ b/src/plugins/platforms/qnx/qqnxnavigatoreventhandler.h
@@ -5,9 +5,12 @@
#define QQNXNAVIGATOREVENTHANDLER_H
#include <QObject>
+#include <QtCore/QLoggingCategory>
QT_BEGIN_NAMESPACE
+Q_DECLARE_LOGGING_CATEGORY(lcQpaQnxNavigatorEvents);
+
class QQnxNavigatorEventHandler : public QObject
{
Q_OBJECT
diff --git a/src/plugins/platforms/qnx/qqnxnavigatoreventnotifier.cpp b/src/plugins/platforms/qnx/qqnxnavigatoreventnotifier.cpp
index 8024214e69..44c71dad52 100644
--- a/src/plugins/platforms/qnx/qqnxnavigatoreventnotifier.cpp
+++ b/src/plugins/platforms/qnx/qqnxnavigatoreventnotifier.cpp
@@ -17,14 +17,10 @@
#include <sys/types.h>
#include <sys/stat.h>
-#if defined(QQNXNAVIGATOREVENTNOTIFIER_DEBUG)
-#define qNavigatorEventNotifierDebug qDebug
-#else
-#define qNavigatorEventNotifierDebug QT_NO_QDEBUG_MACRO
-#endif
-
QT_BEGIN_NAMESPACE
+// Q_LOGGING_CATEGORY(lcQpaQnxNavigatorEvents, "qt.qpa.qnx.navigator.events");
+
const char *QQnxNavigatorEventNotifier::navigatorControlPath = "/pps/services/navigator/control";
const size_t QQnxNavigatorEventNotifier::ppsBufferSize = 4096;
@@ -44,18 +40,18 @@ QQnxNavigatorEventNotifier::~QQnxNavigatorEventNotifier()
if (m_fd != -1)
close(m_fd);
- qNavigatorEventNotifierDebug("navigator event notifier stopped");
+ qCDebug(lcQpaQnxNavigatorEvents) << "Navigator event notifier stopped";
}
void QQnxNavigatorEventNotifier::start()
{
- qNavigatorEventNotifierDebug("navigator event notifier started");
+ qCDebug(lcQpaQnxNavigatorEvents) << "Navigator event notifier started";
// open connection to navigator
errno = 0;
m_fd = open(navigatorControlPath, O_RDWR);
if (m_fd == -1) {
- qNavigatorEventNotifierDebug("failed to open navigator pps: %s", strerror(errno));
+ qCDebug(lcQpaQnxNavigatorEvents, "Failed to open navigator pps: %s", strerror(errno));
return;
}
@@ -65,7 +61,7 @@ void QQnxNavigatorEventNotifier::start()
void QQnxNavigatorEventNotifier::parsePPS(const QByteArray &ppsData, QByteArray &msg, QByteArray &dat, QByteArray &id)
{
- qNavigatorEventNotifierDebug() << "data=" << ppsData;
+ qCDebug(lcQpaQnxNavigatorEvents) << Q_FUNC_INFO << "data=" << ppsData;
// tokenize pps data into lines
QList<QByteArray> lines = ppsData.split('\n');
@@ -79,7 +75,7 @@ void QQnxNavigatorEventNotifier::parsePPS(const QByteArray &ppsData, QByteArray
// tokenize current attribute
const QByteArray &attr = lines.at(i);
- qNavigatorEventNotifierDebug() << "attr=" << attr;
+ qCDebug(lcQpaQnxNavigatorEvents) << Q_FUNC_INFO << "attr=" << attr;
int firstColon = attr.indexOf(':');
if (firstColon == -1) {
@@ -96,8 +92,7 @@ void QQnxNavigatorEventNotifier::parsePPS(const QByteArray &ppsData, QByteArray
QByteArray key = attr.left(firstColon);
QByteArray value = attr.mid(secondColon + 1);
- qNavigatorEventNotifierDebug() << "key=" << key;
- qNavigatorEventNotifierDebug() << "val=" << value;
+ qCDebug(lcQpaQnxNavigatorEvents) << Q_FUNC_INFO << "key =" << key << "value =" << value;
// save attribute value
if (key == "msg")
@@ -124,7 +119,7 @@ void QQnxNavigatorEventNotifier::replyPPS(const QByteArray &res, const QByteArra
}
ppsData += "\n";
- qNavigatorEventNotifierDebug() << "reply=" << ppsData;
+ qCDebug(lcQpaQnxNavigatorEvents) << Q_FUNC_INFO << "reply=" << ppsData;
// send pps message to navigator
errno = 0;
@@ -135,7 +130,7 @@ void QQnxNavigatorEventNotifier::replyPPS(const QByteArray &res, const QByteArra
void QQnxNavigatorEventNotifier::handleMessage(const QByteArray &msg, const QByteArray &dat, const QByteArray &id)
{
- qNavigatorEventNotifierDebug() << "msg=" << msg << ", dat=" << dat << ", id=" << id;
+ qCDebug(lcQpaQnxNavigatorEvents) << Q_FUNC_INFO << "msg=" << msg << ", dat=" << dat << ", id=" << id;
// check message type
if (msg == "orientationCheck") {
@@ -159,7 +154,7 @@ void QQnxNavigatorEventNotifier::handleMessage(const QByteArray &msg, const QByt
void QQnxNavigatorEventNotifier::readData()
{
- qNavigatorEventNotifierDebug("reading navigator data");
+ qCDebug(lcQpaQnxNavigatorEvents) << "Reading navigator data";
// allocate buffer for pps data
char buffer[ppsBufferSize];
diff --git a/src/plugins/platforms/qnx/qqnxnavigatorpps.cpp b/src/plugins/platforms/qnx/qqnxnavigatorpps.cpp
index c945f3e98a..3a2fee0afb 100644
--- a/src/plugins/platforms/qnx/qqnxnavigatorpps.cpp
+++ b/src/plugins/platforms/qnx/qqnxnavigatorpps.cpp
@@ -8,12 +8,6 @@
#include <QByteArray>
#include <private/qcore_unix_p.h>
-#if defined(QQNXNAVIGATOR_DEBUG)
-#define qNavigatorDebug qDebug
-#else
-#define qNavigatorDebug QT_NO_QDEBUG_MACRO
-#endif
-
QT_BEGIN_NAMESPACE
const char *QQnxNavigatorPps::navigatorControlPath = "/pps/services/navigator/control";
@@ -45,7 +39,7 @@ bool QQnxNavigatorPps::openPpsConnection()
return false;
}
- qNavigatorDebug("successfully connected to Navigator. fd=%d", m_fd);
+ qCDebug(lcQpaQnxNavigator) << "successfully connected to Navigator. fd=" << m_fd;
return true;
}
@@ -67,7 +61,7 @@ bool QQnxNavigatorPps::sendPpsMessage(const QByteArray &message, const QByteArra
ppsMessage += "\n";
- qNavigatorDebug() << "sending PPS message:\n" << ppsMessage;
+ qCDebug(lcQpaQnxNavigator) << "sending PPS message:\n" << ppsMessage;
// send pps message to navigator
errno = 0;
@@ -89,7 +83,7 @@ bool QQnxNavigatorPps::sendPpsMessage(const QByteArray &message, const QByteArra
// ensure data is null terminated
buffer[bytes] = '\0';
- qNavigatorDebug() << "received PPS message:\n" << buffer;
+ qCDebug(lcQpaQnxNavigator) << "received PPS message:\n" << buffer;
// process received message
QByteArray ppsData(buffer);
@@ -108,7 +102,7 @@ bool QQnxNavigatorPps::sendPpsMessage(const QByteArray &message, const QByteArra
void QQnxNavigatorPps::parsePPS(const QByteArray &ppsData, QHash<QByteArray, QByteArray> &messageFields)
{
- qNavigatorDebug() << "data=" << ppsData;
+ qCDebug(lcQpaQnxNavigator) << "data=" << ppsData;
// tokenize pps data into lines
QList<QByteArray> lines = ppsData.split('\n');
@@ -123,7 +117,7 @@ void QQnxNavigatorPps::parsePPS(const QByteArray &ppsData, QHash<QByteArray, QBy
// tokenize current attribute
const QByteArray &attr = lines.at(i);
- qNavigatorDebug() << "attr=" << attr;
+ qCDebug(lcQpaQnxNavigator) << "attr=" << attr;
int firstColon = attr.indexOf(':');
if (firstColon == -1) {
@@ -140,8 +134,7 @@ void QQnxNavigatorPps::parsePPS(const QByteArray &ppsData, QHash<QByteArray, QBy
QByteArray key = attr.left(firstColon);
QByteArray value = attr.mid(secondColon + 1);
- qNavigatorDebug() << "key=" << key;
- qNavigatorDebug() << "val=" << value;
+ qCDebug(lcQpaQnxNavigator) << "key=" << key << "value=" << value;
messageFields[key] = value;
}
}
diff --git a/src/plugins/platforms/qnx/qqnxnavigatorpps.h b/src/plugins/platforms/qnx/qqnxnavigatorpps.h
index cbe0f99621..889b9f62eb 100644
--- a/src/plugins/platforms/qnx/qqnxnavigatorpps.h
+++ b/src/plugins/platforms/qnx/qqnxnavigatorpps.h
@@ -5,9 +5,12 @@
#define QQNXNAVIGATORPPS_H
#include "qqnxabstractnavigator.h"
+#include <QtCore/QLoggingCategory>
QT_BEGIN_NAMESPACE
+Q_DECLARE_LOGGING_CATEGORY(lcQpaQnxNavigator);
+
template <typename K, typename V> class QHash;
class QQnxNavigatorPps : public QQnxAbstractNavigator
diff --git a/src/plugins/platforms/qnx/qqnxrasterbackingstore.cpp b/src/plugins/platforms/qnx/qqnxrasterbackingstore.cpp
index cf80e44f84..b94c056a79 100644
--- a/src/plugins/platforms/qnx/qqnxrasterbackingstore.cpp
+++ b/src/plugins/platforms/qnx/qqnxrasterbackingstore.cpp
@@ -10,12 +10,6 @@
#include <errno.h>
-#if defined(QQNXRASTERBACKINGSTORE_DEBUG)
-#define qRasterBackingStoreDebug qDebug
-#else
-#define qRasterBackingStoreDebug QT_NO_QDEBUG_MACRO
-#endif
-
QT_BEGIN_NAMESPACE
QQnxRasterBackingStore::QQnxRasterBackingStore(QWindow *window)
@@ -23,14 +17,14 @@ QQnxRasterBackingStore::QQnxRasterBackingStore(QWindow *window)
m_needsPosting(false),
m_scrolled(false)
{
- qRasterBackingStoreDebug() << "w =" << window;
+ qCDebug(lcQpaBackingStore) << Q_FUNC_INFO << "w =" << window;
m_window = window;
}
QQnxRasterBackingStore::~QQnxRasterBackingStore()
{
- qRasterBackingStoreDebug() << "w =" << window();
+ qCDebug(lcQpaBackingStore) << Q_FUNC_INFO << "w =" << window();
}
QPaintDevice *QQnxRasterBackingStore::paintDevice()
@@ -45,7 +39,7 @@ void QQnxRasterBackingStore::flush(QWindow *window, const QRegion &region, const
{
Q_UNUSED(offset);
- qRasterBackingStoreDebug() << "w =" << this->window();
+ qCDebug(lcQpaBackingStore) << Q_FUNC_INFO << "w =" << this->window();
// Sometimes this method is called even though there is nothing to be
// flushed (posted in "screen" parlance), for instance, after an expose
@@ -67,7 +61,7 @@ void QQnxRasterBackingStore::resize(const QSize &size, const QRegion &staticCont
{
Q_UNUSED(size);
Q_UNUSED(staticContents);
- qRasterBackingStoreDebug() << "w =" << window() << ", s =" << size;
+ qCDebug(lcQpaBackingStore) << Q_FUNC_INFO << "w =" << window() << ", s =" << size;
// NOTE: defer resizing window buffers until next paint as
// resize() can be called multiple times before a paint occurs
@@ -75,7 +69,7 @@ void QQnxRasterBackingStore::resize(const QSize &size, const QRegion &staticCont
bool QQnxRasterBackingStore::scroll(const QRegion &area, int dx, int dy)
{
- qRasterBackingStoreDebug() << "w =" << window();
+ qCDebug(lcQpaBackingStore) << Q_FUNC_INFO << "w =" << window();
m_needsPosting = true;
@@ -91,7 +85,7 @@ void QQnxRasterBackingStore::beginPaint(const QRegion &region)
{
Q_UNUSED(region);
- qRasterBackingStoreDebug() << "w =" << window();
+ qCDebug(lcQpaBackingStore) << Q_FUNC_INFO << "w =" << window();
m_needsPosting = true;
platformWindow()->adjustBufferSize();
@@ -119,7 +113,7 @@ void QQnxRasterBackingStore::beginPaint(const QRegion &region)
void QQnxRasterBackingStore::endPaint()
{
- qRasterBackingStoreDebug() << "w =" << window();
+ qCDebug(lcQpaBackingStore) << Q_FUNC_INFO << "w =" << window();
}
QQnxRasterWindow *QQnxRasterBackingStore::platformWindow() const
diff --git a/src/plugins/platforms/qnx/qqnxrasterwindow.cpp b/src/plugins/platforms/qnx/qqnxrasterwindow.cpp
index fb7a1d3fd3..303c9e7c06 100644
--- a/src/plugins/platforms/qnx/qqnxrasterwindow.cpp
+++ b/src/plugins/platforms/qnx/qqnxrasterwindow.cpp
@@ -10,12 +10,6 @@
#include <errno.h>
-#if defined(QQNXRASTERWINDOW_DEBUG)
-#define qRasterWindowDebug qDebug
-#else
-#define qRasterWindowDebug QT_NO_QDEBUG_MACRO
-#endif
-
QT_BEGIN_NAMESPACE
QQnxRasterWindow::QQnxRasterWindow(QWindow *window, screen_context_t context, bool needRootWindow) :
@@ -61,7 +55,7 @@ void QQnxRasterWindow::post(const QRegion &dirty)
// Check if render buffer exists and something was rendered
if (m_currentBufferIndex != -1 && !dirty.isEmpty()) {
- qRasterWindowDebug() << "window =" << window();
+ qCDebug(lcQpaWindow) << Q_FUNC_INFO << "window = " << window();
QQnxBuffer &currentBuffer = m_buffers[m_currentBufferIndex];
// Copy unmodified region from old render buffer to new render buffer;
@@ -94,14 +88,14 @@ void QQnxRasterWindow::post(const QRegion &dirty)
void QQnxRasterWindow::scroll(const QRegion &region, int dx, int dy, bool flush)
{
- qRasterWindowDebug() << "window =" << window();
+ qCDebug(lcQpaWindow) << Q_FUNC_INFO << "window = " << window();
blitPreviousToCurrent(region, dx, dy, flush);
m_scrolled += region;
}
QQnxBuffer &QQnxRasterWindow::renderBuffer()
{
- qRasterWindowDebug() << "window =" << window();
+ qCDebug(lcQpaWindow) << Q_FUNC_INFO << "window = " << window();
// Check if render buffer is invalid
if (m_currentBufferIndex == -1) {
@@ -162,7 +156,7 @@ void QQnxRasterWindow::resetBuffers()
void QQnxRasterWindow::blitPreviousToCurrent(const QRegion &region, int dx, int dy, bool flush)
{
- qRasterWindowDebug() << "window =" << window();
+ qCDebug(lcQpaWindow) << Q_FUNC_INFO << "window = " << window();
// Abort if previous buffer is invalid or if nothing to copy
if (m_previousBufferIndex == -1 || region.isEmpty())
diff --git a/src/plugins/platforms/qnx/qqnxscreen.cpp b/src/plugins/platforms/qnx/qqnxscreen.cpp
index 9d689da991..f2c3b3847d 100644
--- a/src/plugins/platforms/qnx/qqnxscreen.cpp
+++ b/src/plugins/platforms/qnx/qqnxscreen.cpp
@@ -15,12 +15,6 @@
#include <errno.h>
-#if defined(QQNXSCREEN_DEBUG)
-#define qScreenDebug qDebug
-#else
-#define qScreenDebug QT_NO_QDEBUG_MACRO
-#endif
-
#if defined(QQNX_PHYSICAL_SCREEN_WIDTH) && QQNX_PHYSICAL_SCREEN_WIDTH > 0 \
&& defined(QQNX_PHYSICAL_SCREEN_HEIGHT) && QQNX_PHYSICAL_SCREEN_HEIGHT > 0
#define QQNX_PHYSICAL_SCREEN_SIZE_DEFINED
@@ -34,6 +28,8 @@ static const int MAX_UNDERLAY_ZORDER = -1;
QT_BEGIN_NAMESPACE
+Q_LOGGING_CATEGORY(lcQpaScreen, "qt.qpa.screen");
+
static QSize determineScreenSize(screen_display_t display, bool primaryScreen) {
int val[2];
@@ -46,9 +42,9 @@ static QSize determineScreenSize(screen_display_t display, bool primaryScreen) {
if (val[0] > 0 && val[1] > 0)
return QSize(val[0], val[1]);
- qScreenDebug("QQnxScreen: screen_get_display_property_iv() reported an invalid "
- "physical screen size (%dx%d). Falling back to QQNX_PHYSICAL_SCREEN_SIZE "
- "environment variable.", val[0], val[1]);
+ qCDebug(lcQpaScreen, "QQnxScreen: screen_get_display_property_iv() reported an invalid "
+ "physical screen size (%dx%d). Falling back to QQNX_PHYSICAL_SCREEN_SIZE "
+ "environment variable.", val[0], val[1]);
const QString envPhySizeStr = qgetenv("QQNX_PHYSICAL_SCREEN_SIZE");
if (!envPhySizeStr.isEmpty()) {
@@ -90,7 +86,7 @@ QQnxScreen::QQnxScreen(screen_context_t screenContext, screen_display_t display,
m_coverWindow(0),
m_cursor(new QQnxCursor())
{
- qScreenDebug();
+ qCDebug(lcQpaScreen) << Q_FUNC_INFO;
// Cache initial orientation of this display
int result = screen_get_display_property_iv(m_display, SCREEN_PROPERTY_ROTATION,
&m_initialRotation);
@@ -127,7 +123,7 @@ QQnxScreen::QQnxScreen(screen_context_t screenContext, screen_display_t display,
QQnxScreen::~QQnxScreen()
{
- qScreenDebug();
+ qCDebug(lcQpaScreen) << Q_FUNC_INFO;
Q_FOREACH (QQnxWindow *childWindow, m_childWindows)
childWindow->setScreen(0);
@@ -236,7 +232,7 @@ QPixmap QQnxScreen::grabWindow(WId window, int x, int y, int width, int height)
static int defaultDepth()
{
- qScreenDebug();
+ qCDebug(lcQpaScreen) << Q_FUNC_INFO;
static int defaultDepth = 0;
if (defaultDepth == 0) {
// check if display depth was specified in environment variable;
@@ -250,7 +246,7 @@ static int defaultDepth()
QRect QQnxScreen::availableGeometry() const
{
- qScreenDebug();
+ qCDebug(lcQpaScreen) << Q_FUNC_INFO;
// available geometry = total geometry - keyboard
return QRect(m_currentGeometry.x(), m_currentGeometry.y(),
m_currentGeometry.width(), m_currentGeometry.height() - m_keyboardHeight);
@@ -270,12 +266,12 @@ qreal QQnxScreen::refreshRate() const
qWarning("QQnxScreen: Failed to query screen mode. Using default value of 60Hz");
return 60.0;
}
- qScreenDebug("screen mode:\n"
- " width = %u\n"
- " height = %u\n"
- " refresh = %u\n"
- " interlaced = %u",
- uint(displayMode.width), uint(displayMode.height), uint(displayMode.refresh), uint(displayMode.interlaced));
+ qCDebug(lcQpaScreen, "screen mode:\n"
+ " width = %u\n"
+ " height = %u\n"
+ " refresh = %u\n"
+ " interlaced = %u",
+ uint(displayMode.width), uint(displayMode.height), uint(displayMode.refresh), uint(displayMode.interlaced));
return static_cast<qreal>(displayMode.refresh);
}
@@ -309,7 +305,7 @@ Qt::ScreenOrientation QQnxScreen::orientation() const
else
orient = Qt::InvertedLandscapeOrientation;
}
- qScreenDebug() << "orientation =" << orient;
+ qCDebug(lcQpaScreen) << Q_FUNC_INFO << "Orientation =" << orient;
return orient;
}
@@ -333,7 +329,7 @@ static bool isOrthogonal(int angle1, int angle2)
void QQnxScreen::setRotation(int rotation)
{
- qScreenDebug("orientation = %d", rotation);
+ qCDebug(lcQpaScreen) << Q_FUNC_INFO << "orientation =" << rotation;
// Check if rotation changed
// We only want to rotate if we are the primary screen
if (m_currentRotation != rotation && isPrimaryScreen()) {
@@ -354,7 +350,7 @@ void QQnxScreen::setRotation(int rotation)
// Resize root window if we've rotated 90 or 270 from previous orientation
if (isOrthogonal(m_currentRotation, rotation)) {
- qScreenDebug() << "resize, size =" << m_currentGeometry.size();
+ qCDebug(lcQpaScreen) << Q_FUNC_INFO << "resize, size =" << m_currentGeometry.size();
if (rootWindow())
rootWindow()->setGeometry(QRect(QPoint(0,0), m_currentGeometry.size()));
@@ -501,7 +497,7 @@ QQnxWindow *QQnxScreen::findWindow(screen_window_t windowHandle) const
void QQnxScreen::addWindow(QQnxWindow *window)
{
- qScreenDebug() << "window =" << window;
+ qCDebug(lcQpaScreen) << Q_FUNC_INFO << "Window =" << window;
if (m_childWindows.contains(window))
return;
@@ -524,7 +520,7 @@ void QQnxScreen::addWindow(QQnxWindow *window)
void QQnxScreen::removeWindow(QQnxWindow *window)
{
- qScreenDebug() << "window =" << window;
+ qCDebug(lcQpaScreen) << Q_FUNC_INFO << "Window =" << window;
if (window != m_coverWindow) {
const int numWindowsRemoved = m_childWindows.removeAll(window);
@@ -539,7 +535,7 @@ void QQnxScreen::removeWindow(QQnxWindow *window)
void QQnxScreen::raiseWindow(QQnxWindow *window)
{
- qScreenDebug() << "window =" << window;
+ qCDebug(lcQpaScreen) << Q_FUNC_INFO << "Window =" << window;
if (window != m_coverWindow) {
removeWindow(window);
@@ -549,7 +545,7 @@ void QQnxScreen::raiseWindow(QQnxWindow *window)
void QQnxScreen::lowerWindow(QQnxWindow *window)
{
- qScreenDebug() << "window =" << window;
+ qCDebug(lcQpaScreen) << Q_FUNC_INFO << "Window =" << window;
if (window != m_coverWindow) {
removeWindow(window);
@@ -559,7 +555,7 @@ void QQnxScreen::lowerWindow(QQnxWindow *window)
void QQnxScreen::updateHierarchy()
{
- qScreenDebug();
+ qCDebug(lcQpaScreen) << Q_FUNC_INFO;
QList<QQnxWindow*>::const_iterator it;
int result;
@@ -709,7 +705,7 @@ void QQnxScreen::windowClosed(void *window)
void QQnxScreen::windowGroupStateChanged(const QByteArray &id, Qt::WindowState state)
{
- qScreenDebug();
+ qCDebug(lcQpaScreen) << Q_FUNC_INFO;
if (!rootWindow() || id != rootWindow()->groupName())
return;
@@ -724,7 +720,7 @@ void QQnxScreen::windowGroupStateChanged(const QByteArray &id, Qt::WindowState s
void QQnxScreen::activateWindowGroup(const QByteArray &id)
{
- qScreenDebug();
+ qCDebug(lcQpaScreen) << Q_FUNC_INFO;
if (!rootWindow() || id != rootWindow()->groupName())
return;
@@ -743,7 +739,7 @@ void QQnxScreen::activateWindowGroup(const QByteArray &id)
void QQnxScreen::deactivateWindowGroup(const QByteArray &id)
{
- qScreenDebug();
+ qCDebug(lcQpaScreen) << Q_FUNC_INFO;
if (!rootWindow() || id != rootWindow()->groupName())
return;
diff --git a/src/plugins/platforms/qnx/qqnxscreen.h b/src/plugins/platforms/qnx/qqnxscreen.h
index f988b42ab4..17b282bdc1 100644
--- a/src/plugins/platforms/qnx/qqnxscreen.h
+++ b/src/plugins/platforms/qnx/qqnxscreen.h
@@ -30,6 +30,10 @@ const int SCREEN_PROPERTY_SYM = SCREEN_PROPERTY_KEY_SYM;
QT_BEGIN_NAMESPACE
+Q_DECLARE_LOGGING_CATEGORY(lcQpaScreen);
+Q_DECLARE_LOGGING_CATEGORY(lcQpaScreenEvents);
+Q_DECLARE_LOGGING_CATEGORY(lcQpaScreenBuffer);
+
class QQnxWindow;
class QQnxScreen : public QObject, public QPlatformScreen
diff --git a/src/plugins/platforms/qnx/qqnxscreeneventhandler.cpp b/src/plugins/platforms/qnx/qqnxscreeneventhandler.cpp
index e93a38f62c..6d923bc3a8 100644
--- a/src/plugins/platforms/qnx/qqnxscreeneventhandler.cpp
+++ b/src/plugins/platforms/qnx/qqnxscreeneventhandler.cpp
@@ -19,11 +19,7 @@
#include <errno.h>
#include <sys/keycodes.h>
-#if defined(QQNXSCREENEVENT_DEBUG)
-#define qScreenEventDebug qDebug
-#else
-#define qScreenEventDebug QT_NO_QDEBUG_MACRO
-#endif
+Q_LOGGING_CATEGORY(lcQpaScreenEvents, "qt.qpa.screen.events");
static int qtKey(int virtualKey, QChar::Category category)
{
@@ -197,7 +193,7 @@ bool QQnxScreenEventHandler::handleEvent(screen_event_t event, int qnxType)
default:
// event ignored
- qScreenEventDebug("unknown event %d", qnxType);
+ qCDebug(lcQpaScreenEvents) << Q_FUNC_INFO << "Unknown event" << qnxType;
return false;
}
@@ -235,7 +231,7 @@ void QQnxScreenEventHandler::injectKeyboardEvent(int flags, int sym, int modifie
QWindowSystemInterface::handleExtendedKeyEvent(QGuiApplication::focusWindow(), type, key, qtMod,
scan, virtualKey, modifiers, keyStr, flags & KEY_REPEAT);
- qScreenEventDebug() << "Qt key t=" << type << ", k=" << key << ", s=" << keyStr;
+ qCDebug(lcQpaScreenEvents) << "Qt key t=" << type << ", k=" << key << ", s=" << keyStr;
}
void QQnxScreenEventHandler::setScreenEventThread(QQnxScreenEventThread *eventThread)
@@ -364,12 +360,12 @@ void QQnxScreenEventHandler::handlePointerEvent(screen_event_t event)
if (wOld) {
QWindowSystemInterface::handleLeaveEvent(wOld);
- qScreenEventDebug() << "Qt leave, w=" << wOld;
+ qCDebug(lcQpaScreenEvents) << "Qt leave, w=" << wOld;
}
if (w) {
QWindowSystemInterface::handleEnterEvent(w);
- qScreenEventDebug() << "Qt enter, w=" << w;
+ qCDebug(lcQpaScreenEvents) << "Qt enter, w=" << w;
}
}
@@ -412,8 +408,8 @@ void QQnxScreenEventHandler::handlePointerEvent(screen_event_t event)
QWindowSystemInterface::handleMouseEvent(w, timestamp, m_mouseDevice, localPoint,
globalPoint, buttons, Qt::NoButton,
QEvent::MouseMove);
- qScreenEventDebug() << "Qt mouse move, w=" << w << ", (" << localPoint.x() << ","
- << localPoint.y() << "), b=" << static_cast<int>(buttons);
+ qCDebug(lcQpaScreenEvents) << "Qt mouse move, w=" << w << ", (" << localPoint.x() << ","
+ << localPoint.y() << "), b=" << static_cast<int>(buttons);
}
if (m_lastButtonState != buttons) {
@@ -428,8 +424,8 @@ void QQnxScreenEventHandler::handlePointerEvent(screen_event_t event)
QWindowSystemInterface::handleMouseEvent(w, timestamp, m_mouseDevice,
localPoint, globalPoint, buttons,
button, QEvent::MouseButtonRelease);
- qScreenEventDebug() << "Qt mouse release, w=" << w << ", (" << localPoint.x()
- << "," << localPoint.y() << "), b=" << button;
+ qCDebug(lcQpaScreenEvents) << "Qt mouse release, w=" << w << ", (" << localPoint.x()
+ << "," << localPoint.y() << "), b=" << button;
}
}
@@ -443,8 +439,8 @@ void QQnxScreenEventHandler::handlePointerEvent(screen_event_t event)
QWindowSystemInterface::handleMouseEvent(w, timestamp, m_mouseDevice,
localPoint, globalPoint, buttons,
button, QEvent::MouseButtonPress);
- qScreenEventDebug() << "Qt mouse press, w=" << w << ", (" << localPoint.x()
- << "," << localPoint.y() << "), b=" << button;
+ qCDebug(lcQpaScreenEvents) << "Qt mouse press, w=" << w << ", (" << localPoint.x()
+ << "," << localPoint.y() << "), b=" << button;
}
}
}
@@ -455,7 +451,7 @@ void QQnxScreenEventHandler::handlePointerEvent(screen_event_t event)
QPoint angleDelta(0, wheelDelta);
QWindowSystemInterface::handleWheelEvent(w, timestamp, m_mouseDevice, localPoint,
globalPoint, QPoint(), angleDelta);
- qScreenEventDebug() << "Qt wheel, w=" << w << ", (" << localPoint.x() << ","
+ qCDebug(lcQpaScreenEvents) << "Qt wheel, w=" << w << ", (" << localPoint.x() << ","
<< localPoint.y() << "), d=" << static_cast<int>(wheelDelta);
}
}
@@ -513,12 +509,12 @@ void QQnxScreenEventHandler::handleTouchEvent(screen_event_t event, int qnxType)
if (wOld) {
QWindowSystemInterface::handleLeaveEvent(wOld);
- qScreenEventDebug() << "Qt leave, w=" << wOld;
+ qCDebug(lcQpaScreenEvents) << "Qt leave, w=" << wOld;
}
if (w) {
QWindowSystemInterface::handleEnterEvent(w);
- qScreenEventDebug() << "Qt enter, w=" << w;
+ qCDebug(lcQpaScreenEvents) << "Qt enter, w=" << w;
}
}
m_lastMouseWindow = qnxWindow;
@@ -585,9 +581,9 @@ void QQnxScreenEventHandler::handleTouchEvent(screen_event_t event, int qnxType)
// inject event into Qt
QWindowSystemInterface::handleTouchEvent(w, m_touchDevice, pointList);
- qScreenEventDebug() << "Qt touch, w =" << w
- << ", p=" << m_touchPoints[touchId].area.topLeft()
- << ", t=" << type;
+ qCDebug(lcQpaScreenEvents) << "Qt touch, w =" << w
+ << ", p=" << m_touchPoints[touchId].area.topLeft()
+ << ", t=" << type;
}
}
}
@@ -631,7 +627,8 @@ void QQnxScreenEventHandler::handleDisplayEvent(screen_event_t event)
return;
}
- qScreenEventDebug() << "display attachment is now:" << isAttached;
+ qCDebug(lcQpaScreenEvents) << "display attachment is now:" << isAttached;
+
QQnxScreen *screen = m_qnxIntegration->screenForNative(nativeDisplay);
if (!screen) {
@@ -641,7 +638,7 @@ void QQnxScreenEventHandler::handleDisplayEvent(screen_event_t event)
if (val[0] == 0 && val[1] == 0) //If screen size is invalid, wait for the next event
return;
- qScreenEventDebug("creating new QQnxScreen for newly attached display");
+ qCDebug(lcQpaScreenEvents) << "Creating new QQnxScreen for newly attached display";
m_qnxIntegration->createDisplay(nativeDisplay, false /* not primary, we assume */);
}
} else if (!isAttached) {
@@ -654,7 +651,7 @@ void QQnxScreenEventHandler::handleDisplayEvent(screen_event_t event)
if (!screen->isPrimaryScreen()) {
// libscreen display is deactivated, let's remove the QQnxScreen / QScreen
- qScreenEventDebug("removing display");
+ qCDebug(lcQpaScreenEvents) << "Removing display";
m_qnxIntegration->removeDisplay(screen);
}
}
@@ -691,7 +688,7 @@ void QQnxScreenEventHandler::handlePropertyEvent(screen_event_t event)
break;
default:
// event ignored
- qScreenEventDebug() << "Ignore property event for property: " << property;
+ qCDebug(lcQpaScreenEvents) << "Ignore property event for property: " << property;
}
}
@@ -734,7 +731,7 @@ void QQnxScreenEventHandler::handleGeometryPropertyEvent(screen_window_t window)
QWindowSystemInterface::handleGeometryChange(qtWindow, rect);
}
- qScreenEventDebug() << qtWindow << "moved to" << rect;
+ qCDebug(lcQpaScreenEvents) << qtWindow << "moved to" << rect;
}
void QQnxScreenEventHandler::timerEvent(QTimerEvent *event)
diff --git a/src/plugins/platforms/qnx/qqnxscreeneventhandler.h b/src/plugins/platforms/qnx/qqnxscreeneventhandler.h
index d27186af9e..ba3579aaf7 100644
--- a/src/plugins/platforms/qnx/qqnxscreeneventhandler.h
+++ b/src/plugins/platforms/qnx/qqnxscreeneventhandler.h
@@ -5,11 +5,14 @@
#define QQNXSCREENEVENTHANDLER_H
#include <qpa/qwindowsysteminterface.h>
+#include <QtCore/QLoggingCategory>
#include <screen/screen.h>
QT_BEGIN_NAMESPACE
+Q_DECLARE_LOGGING_CATEGORY(lcQpaScreenEvents);
+
class QQnxIntegration;
class QQnxScreenEventFilter;
class QQnxScreenEventThread;
diff --git a/src/plugins/platforms/qnx/qqnxscreeneventthread.cpp b/src/plugins/platforms/qnx/qqnxscreeneventthread.cpp
index 69997e4ba0..6b4ffc3962 100644
--- a/src/plugins/platforms/qnx/qqnxscreeneventthread.cpp
+++ b/src/plugins/platforms/qnx/qqnxscreeneventthread.cpp
@@ -14,12 +14,6 @@
#include <cctype>
-#if defined(QQNXSCREENEVENTTHREAD_DEBUG)
-#define qScreenEventThreadDebug qDebug
-#else
-#define qScreenEventThreadDebug QT_NO_QDEBUG_MACRO
-#endif
-
static const int c_screenCode = _PULSE_CODE_MINAVAIL + 0;
static const int c_armCode = _PULSE_CODE_MINAVAIL + 1;
static const int c_quitCode = _PULSE_CODE_MINAVAIL + 2;
@@ -74,7 +68,7 @@ QQnxScreenEventThread::~QQnxScreenEventThread()
void QQnxScreenEventThread::run()
{
- qScreenEventThreadDebug("screen event thread started");
+ qCDebug(lcQpaScreenEvents) << "Screen event thread started";
while (1) {
struct _pulse msg;
@@ -90,7 +84,7 @@ void QQnxScreenEventThread::run()
qWarning() << "MsgReceive error" << strerror(errno);
}
- qScreenEventThreadDebug("screen event thread stopped");
+ qCDebug(lcQpaScreenEvents) << "Screen event thread stopped";
}
void QQnxScreenEventThread::armEventsPending(int count)
@@ -134,10 +128,10 @@ void QQnxScreenEventThread::shutdown()
{
MsgSendPulse(m_connectionId, SIGEV_PULSE_PRIO_INHERIT, c_quitCode, 0);
- qScreenEventThreadDebug("screen event thread shutdown begin");
+ qCDebug(lcQpaScreenEvents) << "Screen event thread shutdown begin";
// block until thread terminates
wait();
- qScreenEventThreadDebug("screen event thread shutdown end");
+ qCDebug(lcQpaScreenEvents) << "Screen event thread shutdown end";
}
diff --git a/src/plugins/platforms/qnx/qqnxvirtualkeyboardpps.cpp b/src/plugins/platforms/qnx/qqnxvirtualkeyboardpps.cpp
index b4650de315..5eceacef95 100644
--- a/src/plugins/platforms/qnx/qqnxvirtualkeyboardpps.cpp
+++ b/src/plugins/platforms/qnx/qqnxvirtualkeyboardpps.cpp
@@ -18,14 +18,10 @@
#include <sys/types.h>
#include <unistd.h>
-#if defined(QQNXVIRTUALKEYBOARD_DEBUG)
-#define qVirtualKeyboardDebug qDebug
-#else
-#define qVirtualKeyboardDebug QT_NO_QDEBUG_MACRO
-#endif
-
QT_BEGIN_NAMESPACE
+Q_LOGGING_CATEGORY(lcQpaQnxVirtualKeyboard, "qt.qpa.qnx.virtualkeyboard");
+
const char *QQnxVirtualKeyboardPps::ms_PPSPath = "/pps/services/input/control";
const size_t QQnxVirtualKeyboardPps::ms_bufferSize = 2048;
@@ -45,7 +41,7 @@ QQnxVirtualKeyboardPps::~QQnxVirtualKeyboardPps()
void QQnxVirtualKeyboardPps::start()
{
- qVirtualKeyboardDebug("starting keyboard event processing");
+ qCDebug(lcQpaQnxVirtualKeyboard) << "Starting keyboard event processing";
if (!connect())
return;
}
@@ -90,8 +86,8 @@ bool QQnxVirtualKeyboardPps::connect()
m_fd = ::open(ms_PPSPath, O_RDWR);
if (m_fd == -1)
{
- qVirtualKeyboardDebug() << "Unable to open" << ms_PPSPath
- << ':' << strerror(errno);
+ qCDebug(lcQpaQnxVirtualKeyboard) << "Unable to open" << ms_PPSPath
+ << ':' << strerror(errno);
close();
return false;
}
@@ -128,7 +124,7 @@ void QQnxVirtualKeyboardPps::ppsDataReady()
{
qint64 nread = qt_safe_read(m_fd, m_buffer, ms_bufferSize - 1);
- qVirtualKeyboardDebug("keyboardMessage size: %lld", nread);
+ qCDebug(lcQpaQnxVirtualKeyboard, "keyboardMessage size: %lld", nread);
if (nread < 0){
connect(); // reconnect
return;
@@ -167,7 +163,7 @@ void QQnxVirtualKeyboardPps::ppsDataReady()
else if (strcmp(value, "info") == 0)
handleKeyboardInfoMessage();
else if (strcmp(value, "connect") == 0)
- qVirtualKeyboardDebug("Unhandled command 'connect'");
+ qCDebug(lcQpaQnxVirtualKeyboard, "Unhandled command 'connect'");
else
qCritical("QQnxVirtualKeyboard: Unexpected keyboard PPS msg value: %s", value ? value : "[null]");
} else if (pps_decoder_get_string(m_decoder, "res", &value) == PPS_DECODER_OK) {
@@ -194,12 +190,12 @@ void QQnxVirtualKeyboardPps::handleKeyboardInfoMessage()
}
setHeight(newHeight);
- qVirtualKeyboardDebug("size=%d", newHeight);
+ qCDebug(lcQpaQnxVirtualKeyboard, "size=%d", newHeight);
}
bool QQnxVirtualKeyboardPps::showKeyboard()
{
- qVirtualKeyboardDebug();
+ qCDebug(lcQpaQnxVirtualKeyboard) << Q_FUNC_INFO;
if (!prepareToSend())
return false;
@@ -221,7 +217,7 @@ bool QQnxVirtualKeyboardPps::showKeyboard()
bool QQnxVirtualKeyboardPps::hideKeyboard()
{
- qVirtualKeyboardDebug();
+ qCDebug(lcQpaQnxVirtualKeyboard) << Q_FUNC_INFO;
if (!prepareToSend())
return false;
diff --git a/src/plugins/platforms/qnx/qqnxvirtualkeyboardpps.h b/src/plugins/platforms/qnx/qqnxvirtualkeyboardpps.h
index 88af284db3..8f1d390760 100644
--- a/src/plugins/platforms/qnx/qqnxvirtualkeyboardpps.h
+++ b/src/plugins/platforms/qnx/qqnxvirtualkeyboardpps.h
@@ -5,11 +5,13 @@
#define VIRTUALKEYBOARDPPS_H
#include "qqnxabstractvirtualkeyboard.h"
+#include <QtCore/QLoggingCategory>
#include <sys/pps.h>
QT_BEGIN_NAMESPACE
+Q_DECLARE_LOGGING_CATEGORY(lcQpaQnxVirtualKeyboard);
class QSocketNotifier;
diff --git a/src/plugins/platforms/qnx/qqnxwindow.cpp b/src/plugins/platforms/qnx/qqnxwindow.cpp
index eed45227f6..3384932a8b 100644
--- a/src/plugins/platforms/qnx/qqnxwindow.cpp
+++ b/src/plugins/platforms/qnx/qqnxwindow.cpp
@@ -21,14 +21,10 @@
#include <errno.h>
-#if defined(QQNXWINDOW_DEBUG)
-#define qWindowDebug qDebug
-#else
-#define qWindowDebug QT_NO_QDEBUG_MACRO
-#endif
-
QT_BEGIN_NAMESPACE
+Q_LOGGING_CATEGORY(lcQpaWindow, "qt.qpa.window");
+
#define DECLARE_DEBUG_VAR(variable) \
static bool debug_ ## variable() \
{ static bool value = qgetenv("QNX_SCREEN_DEBUG").contains(QT_STRINGIFY(variable)); return value; }
@@ -125,7 +121,7 @@ QQnxWindow::QQnxWindow(QWindow *window, screen_context_t context, bool needRootW
m_windowState(Qt::WindowNoState),
m_firstActivateHandled(false)
{
- qWindowDebug() << "window =" << window << ", size =" << window->size();
+ qCDebug(lcQpaWindow) << "window =" << window << ", size =" << window->size();
QQnxScreen *platformScreen = static_cast<QQnxScreen *>(window->screen()->handle());
@@ -195,13 +191,13 @@ QQnxWindow::QQnxWindow(QWindow *window, screen_context_t context, bool needRootW
bool ok = false;
int pipeline = pipelineValue.toInt(&ok);
if (ok) {
- qWindowDebug() << "Set pipeline value to" << pipeline;
+ qCDebug(lcQpaWindow) << "Set pipeline value to" << pipeline;
Q_SCREEN_CHECKERROR(
screen_set_window_property_iv(m_window, SCREEN_PROPERTY_PIPELINE, &pipeline),
"Failed to set window pipeline");
} else {
- qWindowDebug() << "Invalid pipeline value:" << pipelineValue;
+ qCDebug(lcQpaWindow) << "Invalid pipeline value:" << pipelineValue;
}
}
@@ -231,7 +227,7 @@ QQnxWindow::QQnxWindow(QWindow *window, screen_context_t context, bool needRootW
if (debug > 0) {
Q_SCREEN_CHECKERROR(screen_set_window_property_iv(nativeHandle(), SCREEN_PROPERTY_DEBUG, &debug),
"Could not set SCREEN_PROPERTY_DEBUG");
- qWindowDebug() << "window SCREEN_PROPERTY_DEBUG= " << debug;
+ qCDebug(lcQpaWindow) << "window SCREEN_PROPERTY_DEBUG= " << debug;
}
}
@@ -248,7 +244,7 @@ QQnxWindow::QQnxWindow(QWindow *window, screen_context_t context, screen_window_
, m_parentGroupName(256, 0)
, m_isTopLevel(false)
{
- qWindowDebug() << "window =" << window << ", size =" << window->size();
+ qCDebug(lcQpaWindow) << "window =" << window << ", size =" << window->size();
collectWindowGroup();
@@ -269,7 +265,7 @@ QQnxWindow::QQnxWindow(QWindow *window, screen_context_t context, screen_window_
QQnxWindow::~QQnxWindow()
{
- qWindowDebug() << "window =" << window();
+ qCDebug(lcQpaWindow) << "window =" << window();
// Qt should have already deleted the children before deleting the parent.
Q_ASSERT(m_childWindows.size() == 0);
@@ -305,9 +301,9 @@ void QQnxWindow::setGeometry(const QRect &rect)
void QQnxWindow::setGeometryHelper(const QRect &rect)
{
- qWindowDebug() << "window =" << window()
- << ", (" << rect.x() << "," << rect.y()
- << "," << rect.width() << "," << rect.height() << ")";
+ qCDebug(lcQpaWindow) << "window =" << window()
+ << ", (" << rect.x() << "," << rect.y()
+ << "," << rect.width() << "," << rect.height() << ")";
// Call base class method
QPlatformWindow::setGeometry(rect);
@@ -335,7 +331,7 @@ void QQnxWindow::setGeometryHelper(const QRect &rect)
void QQnxWindow::setVisible(bool visible)
{
- qWindowDebug() << "window =" << window() << "visible =" << visible;
+ qCDebug(lcQpaWindow) << "window =" << window() << "visible =" << visible;
if (m_visible == visible || window()->type() == Qt::Desktop)
return;
@@ -374,7 +370,7 @@ void QQnxWindow::setVisible(bool visible)
void QQnxWindow::updateVisibility(bool parentVisible)
{
- qWindowDebug() << "parentVisible =" << parentVisible << "window =" << window();
+ qCDebug(lcQpaWindow) << "parentVisible =" << parentVisible << "window =" << window();
// Set window visibility
int val = (m_visible && parentVisible) ? 1 : 0;
Q_SCREEN_CHECKERROR(screen_set_window_property_iv(m_window, SCREEN_PROPERTY_VISIBLE, &val),
@@ -386,7 +382,7 @@ void QQnxWindow::updateVisibility(bool parentVisible)
void QQnxWindow::setOpacity(qreal level)
{
- qWindowDebug() << "window =" << window() << "opacity =" << level;
+ qCDebug(lcQpaWindow) << "window =" << window() << "opacity =" << level;
// Set window global alpha
int val = (int)(level * 255);
Q_SCREEN_CHECKERROR(screen_set_window_property_iv(m_window, SCREEN_PROPERTY_GLOBAL_ALPHA, &val),
@@ -397,7 +393,7 @@ void QQnxWindow::setOpacity(qreal level)
void QQnxWindow::setExposed(bool exposed)
{
- qWindowDebug() << "window =" << window() << "expose =" << exposed;
+ qCDebug(lcQpaWindow) << "window =" << window() << "expose =" << exposed;
if (m_exposed != exposed) {
m_exposed = exposed;
@@ -412,7 +408,7 @@ bool QQnxWindow::isExposed() const
void QQnxWindow::setBufferSize(const QSize &size)
{
- qWindowDebug() << "window =" << window() << "size =" << size;
+ qCDebug(lcQpaWindow) << "window =" << window() << "size =" << size;
// libscreen fails when creating empty buffers
const QSize nonEmptySize = size.isEmpty() ? QSize(1, 1) : size;
@@ -479,7 +475,7 @@ void QQnxWindow::setBufferSize(const QSize &size)
void QQnxWindow::setScreen(QQnxScreen *platformScreen)
{
- qWindowDebug() << "window =" << window() << "platformScreen =" << platformScreen;
+ qCDebug(lcQpaWindow) << "window =" << window() << "platformScreen =" << platformScreen;
if (platformScreen == 0) { // The screen has been destroyed
m_screen = 0;
@@ -493,7 +489,7 @@ void QQnxWindow::setScreen(QQnxScreen *platformScreen)
return;
if (m_screen) {
- qWindowDebug("Moving window to different screen");
+ qCDebug(lcQpaWindow) << "Moving window to different screen";
m_screen->removeWindow(this);
if ((QQnxIntegration::instance()->options() & QQnxIntegration::RootWindow)) {
@@ -524,7 +520,7 @@ void QQnxWindow::setScreen(QQnxScreen *platformScreen)
void QQnxWindow::removeFromParent()
{
- qWindowDebug() << "window =" << window();
+ qCDebug(lcQpaWindow) << Q_FUNC_INFO << "window =" << window();
// Remove from old Hierarchy position
if (m_parentWindow) {
if (Q_UNLIKELY(!m_parentWindow->m_childWindows.removeAll(this)))
@@ -538,7 +534,7 @@ void QQnxWindow::removeFromParent()
void QQnxWindow::setParent(const QPlatformWindow *window)
{
- qWindowDebug() << "window =" << this->window() << "platformWindow =" << window;
+ qCDebug(lcQpaWindow) << "window =" << this->window() << "platformWindow =" << window;
// Cast away the const, we need to modify the hierarchy.
QQnxWindow* const newParent = static_cast<QQnxWindow*>(const_cast<QPlatformWindow*>(window));
@@ -570,7 +566,7 @@ void QQnxWindow::setParent(const QPlatformWindow *window)
void QQnxWindow::raise()
{
- qWindowDebug() << "window =" << window();
+ qCDebug(lcQpaWindow) << Q_FUNC_INFO << "window =" << window();
if (m_parentWindow) {
m_parentWindow->m_childWindows.removeAll(this);
@@ -584,7 +580,7 @@ void QQnxWindow::raise()
void QQnxWindow::lower()
{
- qWindowDebug() << "window =" << window();
+ qCDebug(lcQpaWindow) << Q_FUNC_INFO << "window =" << window();
if (m_parentWindow) {
m_parentWindow->m_childWindows.removeAll(this);
@@ -698,7 +694,7 @@ void QQnxWindow::setFocus(screen_window_t newFocusWindow)
void QQnxWindow::setWindowState(Qt::WindowStates state)
{
- qWindowDebug() << "state =" << state;
+ qCDebug(lcQpaWindow) << Q_FUNC_INFO << "state =" << state;
// Prevent two calls with Qt::WindowFullScreen from changing m_unmaximizedGeometry
if (m_windowState == state)
@@ -713,7 +709,7 @@ void QQnxWindow::setWindowState(Qt::WindowStates state)
void QQnxWindow::propagateSizeHints()
{
// nothing to do; silence base class warning
- qWindowDebug("ignored");
+ // qWindowDebug("ignored");
}
QPlatformScreen *QQnxWindow::screen() const
@@ -742,7 +738,7 @@ void QQnxWindow::minimize()
void QQnxWindow::setRotation(int rotation)
{
- qWindowDebug() << "angle =" << rotation;
+ qCDebug(lcQpaWindow) << Q_FUNC_INFO << "angle =" << rotation;
Q_SCREEN_CHECKERROR(
screen_set_window_property_iv(m_window, SCREEN_PROPERTY_ROTATION, &rotation),
"Failed to set window rotation");
@@ -818,7 +814,7 @@ void QQnxWindow::joinWindowGroup(const QByteArray &groupName)
{
bool changed = false;
- qWindowDebug() << "group:" << groupName;
+ qCDebug(lcQpaWindow) << Q_FUNC_INFO << "group:" << groupName;
// screen has this annoying habit of generating a CLOSE/CREATE when the owner context of
// the parent group moves a foreign window to another group that it also owns. The
diff --git a/src/plugins/platforms/qnx/qqnxwindow.h b/src/plugins/platforms/qnx/qqnxwindow.h
index d302f22415..013ea342e4 100644
--- a/src/plugins/platforms/qnx/qqnxwindow.h
+++ b/src/plugins/platforms/qnx/qqnxwindow.h
@@ -8,6 +8,7 @@
#include "qqnxabstractcover.h"
#include <QtCore/QScopedPointer>
+#include <QtCore/QLoggingCategory>
#if !defined(QT_NO_OPENGL)
#include <EGL/egl.h>
@@ -17,6 +18,8 @@
QT_BEGIN_NAMESPACE
+Q_DECLARE_LOGGING_CATEGORY(lcQpaWindow);
+
// all surfaces double buffered
#define MAX_BUFFER_COUNT 2
diff --git a/src/plugins/platforms/vkkhrdisplay/CMakeLists.txt b/src/plugins/platforms/vkkhrdisplay/CMakeLists.txt
index 719e5c45e6..406487f1e9 100644
--- a/src/plugins/platforms/vkkhrdisplay/CMakeLists.txt
+++ b/src/plugins/platforms/vkkhrdisplay/CMakeLists.txt
@@ -6,7 +6,7 @@ qt_find_package(WrapFreetype PROVIDED_TARGETS WrapFreetype::WrapFreetype)
qt_internal_add_plugin(QVkKhrDisplayIntegrationPlugin
OUTPUT_NAME qvkkhrdisplay
PLUGIN_TYPE platforms
- DEFAULT_IF ${QT_QPA_DEFAULT_PLATFORM} MATCHES vkkhrdisplay
+ DEFAULT_IF "vkkhrdisplay" IN_LIST QT_QPA_PLATFORMS
SOURCES
main.cpp
qvkkhrdisplayintegration.cpp qvkkhrdisplayintegration.h
diff --git a/src/plugins/platforms/vnc/CMakeLists.txt b/src/plugins/platforms/vnc/CMakeLists.txt
index 25cb399bd0..34370807ae 100644
--- a/src/plugins/platforms/vnc/CMakeLists.txt
+++ b/src/plugins/platforms/vnc/CMakeLists.txt
@@ -8,7 +8,7 @@
qt_internal_add_plugin(QVncIntegrationPlugin
OUTPUT_NAME qvnc
PLUGIN_TYPE platforms
- DEFAULT_IF ${QT_QPA_DEFAULT_PLATFORM} MATCHES vnc
+ DEFAULT_IF "vnc" IN_LIST QT_QPA_PLATFORMS
SOURCES
main.cpp
qvnc.cpp qvnc_p.h
diff --git a/src/plugins/platforms/wasm/CMakeLists.txt b/src/plugins/platforms/wasm/CMakeLists.txt
index 185b921a4f..775946aaf9 100644
--- a/src/plugins/platforms/wasm/CMakeLists.txt
+++ b/src/plugins/platforms/wasm/CMakeLists.txt
@@ -7,9 +7,8 @@
qt_internal_add_plugin(QWasmIntegrationPlugin
OUTPUT_NAME qwasm
- DEFAULT_IF ${QT_QPA_DEFAULT_PLATFORM} MATCHES wasm
+ DEFAULT_IF "wasm" IN_LIST QT_QPA_PLATFORMS
PLUGIN_TYPE platforms
- STATIC
SOURCES
main.cpp
qwasmaccessibility.cpp qwasmaccessibility.h
diff --git a/src/plugins/platforms/wasm/qwasmscreen.cpp b/src/plugins/platforms/wasm/qwasmscreen.cpp
index ddf8140c48..0490b2bfe0 100644
--- a/src/plugins/platforms/wasm/qwasmscreen.cpp
+++ b/src/plugins/platforms/wasm/qwasmscreen.cpp
@@ -361,10 +361,14 @@ QList<QWasmWindow *> QWasmScreen::allWindows()
{
QList<QWasmWindow *> windows;
for (auto *child : childStack()) {
- QWindowList list = child->window()->findChildren<QWindow *>(Qt::FindChildrenRecursively);
- std::transform(
- list.begin(), list.end(), std::back_inserter(windows),
- [](const QWindow *window) { return static_cast<QWasmWindow *>(window->handle()); });
+ const QWindowList list = child->window()->findChildren<QWindow *>(Qt::FindChildrenRecursively);
+ for (auto child : list) {
+ auto handle = child->handle();
+ if (handle) {
+ auto wnd = static_cast<QWasmWindow *>(handle);
+ windows.push_back(wnd);
+ }
+ }
windows.push_back(child);
}
return windows;
diff --git a/src/plugins/platforms/windows/CMakeLists.txt b/src/plugins/platforms/windows/CMakeLists.txt
index 4b92317978..8cd84e208b 100644
--- a/src/plugins/platforms/windows/CMakeLists.txt
+++ b/src/plugins/platforms/windows/CMakeLists.txt
@@ -8,7 +8,7 @@
qt_internal_add_plugin(QWindowsIntegrationPlugin
OUTPUT_NAME qwindows
PLUGIN_TYPE platforms
- DEFAULT_IF ${QT_QPA_DEFAULT_PLATFORM} MATCHES windows
+ DEFAULT_IF "windows" IN_LIST QT_QPA_PLATFORMS
SOURCES
main.cpp
qtwindowsglobal.h
diff --git a/src/plugins/platforms/windows/qwindowscontext.cpp b/src/plugins/platforms/windows/qwindowscontext.cpp
index de65a2171d..8c0261d568 100644
--- a/src/plugins/platforms/windows/qwindowscontext.cpp
+++ b/src/plugins/platforms/windows/qwindowscontext.cpp
@@ -201,6 +201,8 @@ QWindowsContext::~QWindowsContext()
if (d->m_powerDummyWindow)
DestroyWindow(d->m_powerDummyWindow);
+ d->m_screenManager.destroyWindow();
+
unregisterWindowClasses();
if (d->m_oleInitializeResult == S_OK || d->m_oleInitializeResult == S_FALSE) {
#ifdef QT_USE_FACTORY_CACHE_REGISTRATION
diff --git a/src/plugins/platforms/windows/qwindowsscreen.cpp b/src/plugins/platforms/windows/qwindowsscreen.cpp
index a50f9fd4b0..1f22fb4f60 100644
--- a/src/plugins/platforms/windows/qwindowsscreen.cpp
+++ b/src/plugins/platforms/windows/qwindowsscreen.cpp
@@ -698,11 +698,15 @@ void QWindowsScreenManager::initialize()
handleScreenChanges();
}
-QWindowsScreenManager::~QWindowsScreenManager()
+void QWindowsScreenManager::destroyWindow()
{
+ qCDebug(lcQpaScreen) << "Destroying display change observer" << m_displayChangeObserver;
DestroyWindow(m_displayChangeObserver);
+ m_displayChangeObserver = nullptr;
}
+QWindowsScreenManager::~QWindowsScreenManager() = default;
+
bool QWindowsScreenManager::isSingleScreen()
{
return QWindowsContext::instance()->screenManager().screens().size() < 2;
diff --git a/src/plugins/platforms/windows/qwindowsscreen.h b/src/plugins/platforms/windows/qwindowsscreen.h
index 0467ab2a0c..ea6a29efe3 100644
--- a/src/plugins/platforms/windows/qwindowsscreen.h
+++ b/src/plugins/platforms/windows/qwindowsscreen.h
@@ -105,6 +105,7 @@ public:
QWindowsScreenManager();
void initialize();
+ void destroyWindow();
~QWindowsScreenManager();
void clearScreens();
diff --git a/src/plugins/platforms/windows/qwindowstheme.cpp b/src/plugins/platforms/windows/qwindowstheme.cpp
index 488935e02d..b6017c7692 100644
--- a/src/plugins/platforms/windows/qwindowstheme.cpp
+++ b/src/plugins/platforms/windows/qwindowstheme.cpp
@@ -259,7 +259,7 @@ static QColor placeHolderColor(QColor textColor)
This is used when the theme is light mode, and when the theme is dark but the
application doesn't support dark mode. In the latter case, we need to check.
*/
-static void populateLightSystemBasePalette(QPalette &result)
+void QWindowsTheme::populateLightSystemBasePalette(QPalette &result)
{
const QColor background = getSysColor(COLOR_BTNFACE);
const QColor textColor = getSysColor(COLOR_WINDOWTEXT);
@@ -299,8 +299,11 @@ static void populateLightSystemBasePalette(QPalette &result)
result.setColor(QPalette::Midlight, result.button().color().lighter(110));
}
-static void populateDarkSystemBasePalette(QPalette &result)
+void QWindowsTheme::populateDarkSystemBasePalette(QPalette &result)
{
+ QColor foreground, background,
+ accent, accentDark, accentDarker, accentDarkest,
+ accentLight, accentLighter, accentLightest;
#if QT_CONFIG(cpp_winrt)
using namespace winrt::Windows::UI::ViewManagement;
const auto settings = UISettings();
@@ -308,32 +311,37 @@ static void populateDarkSystemBasePalette(QPalette &result)
// We have to craft a palette from these colors. The settings.UIElementColor(UIElementType) API
// returns the old system colors, not the dark mode colors. If the background is black (which it
// usually), then override it with a dark gray instead so that we can go up and down the lightness.
- const QColor foreground = getSysColor(settings.GetColorValue(UIColorType::Foreground));
- const QColor background = [&settings]() -> QColor {
- auto systemBackground = getSysColor(settings.GetColorValue(UIColorType::Background));
- if (systemBackground == Qt::black)
- systemBackground = QColor(0x1E, 0x1E, 0x1E);
- return systemBackground;
- }();
-
- const QColor accent = getSysColor(settings.GetColorValue(UIColorType::Accent));
- const QColor accentDark = getSysColor(settings.GetColorValue(UIColorType::AccentDark1));
- const QColor accentDarker = getSysColor(settings.GetColorValue(UIColorType::AccentDark2));
- const QColor accentDarkest = getSysColor(settings.GetColorValue(UIColorType::AccentDark3));
- const QColor accentLight = getSysColor(settings.GetColorValue(UIColorType::AccentLight1));
- const QColor accentLighter = getSysColor(settings.GetColorValue(UIColorType::AccentLight2));
- const QColor accentLightest = getSysColor(settings.GetColorValue(UIColorType::AccentLight3));
-#else
- const QColor foreground = Qt::white;
- const QColor background = QColor(0x1E, 0x1E, 0x1E);
- const QColor accent = qt_accentColor(AccentColorNormal);
- const QColor accentDark = accent.darker(120);
- const QColor accentDarker = accentDark.darker(120);
- const QColor accentDarkest = accentDarker.darker(120);
- const QColor accentLight = accent.lighter(120);
- const QColor accentLighter = accentLight.lighter(120);
- const QColor accentLightest = accentLighter.lighter(120);
+ if (QWindowsTheme::queryColorScheme() == Qt::ColorScheme::Dark) {
+ // the system is actually running in dark mode, so UISettings will give us dark colors
+ foreground = getSysColor(settings.GetColorValue(UIColorType::Foreground));
+ background = [&settings]() -> QColor {
+ auto systemBackground = getSysColor(settings.GetColorValue(UIColorType::Background));
+ if (systemBackground == Qt::black)
+ systemBackground = QColor(0x1E, 0x1E, 0x1E);
+ return systemBackground;
+ }();
+
+ accent = getSysColor(settings.GetColorValue(UIColorType::Accent));
+ accentDark = getSysColor(settings.GetColorValue(UIColorType::AccentDark1));
+ accentDarker = getSysColor(settings.GetColorValue(UIColorType::AccentDark2));
+ accentDarkest = getSysColor(settings.GetColorValue(UIColorType::AccentDark3));
+ accentLight = getSysColor(settings.GetColorValue(UIColorType::AccentLight1));
+ accentLighter = getSysColor(settings.GetColorValue(UIColorType::AccentLight2));
+ accentLightest = getSysColor(settings.GetColorValue(UIColorType::AccentLight3));
+ } else
#endif
+ {
+ // If the system is running in light mode, then we need to make up our own dark palette
+ foreground = Qt::white;
+ background = QColor(0x1E, 0x1E, 0x1E);
+ accent = qt_accentColor(AccentColorNormal);
+ accentDark = accent.darker(120);
+ accentDarker = accentDark.darker(120);
+ accentDarkest = accentDarker.darker(120);
+ accentLight = accent.lighter(120);
+ accentLighter = accentLight.lighter(120);
+ accentLightest = accentLighter.lighter(120);
+ }
const QColor linkColor = accent;
const QColor buttonColor = background.lighter(200);
@@ -452,7 +460,7 @@ QWindowsTheme *QWindowsTheme::m_instance = nullptr;
QWindowsTheme::QWindowsTheme()
{
m_instance = this;
- s_darkMode = QWindowsTheme::queryDarkMode();
+ s_colorScheme = QWindowsTheme::queryColorScheme();
std::fill(m_fonts, m_fonts + NFonts, nullptr);
std::fill(m_palettes, m_palettes + NPalettes, nullptr);
refresh();
@@ -542,27 +550,42 @@ QVariant QWindowsTheme::themeHint(ThemeHint hint) const
Qt::ColorScheme QWindowsTheme::colorScheme() const
{
+ return QWindowsTheme::effectiveColorScheme();
+}
+
+Qt::ColorScheme QWindowsTheme::effectiveColorScheme()
+{
if (queryHighContrast())
return Qt::ColorScheme::Unknown;
- return s_darkMode ? Qt::ColorScheme::Dark : Qt::ColorScheme::Light;
+ if (s_colorSchemeOverride != Qt::ColorScheme::Unknown)
+ return s_colorSchemeOverride;
+ if (s_colorScheme != Qt::ColorScheme::Unknown)
+ return s_colorScheme;
+ return queryColorScheme();
+}
+
+void QWindowsTheme::requestColorScheme(Qt::ColorScheme scheme)
+{
+ s_colorSchemeOverride = scheme;
+ handleSettingsChanged();
}
void QWindowsTheme::handleSettingsChanged()
{
- const bool darkMode = QWindowsTheme::queryDarkMode();
- const bool darkModeChanged = darkMode != QWindowsTheme::s_darkMode;
- s_darkMode = darkMode;
+ const auto oldColorScheme = s_colorScheme;
+ s_colorScheme = Qt::ColorScheme::Unknown; // make effectiveColorScheme() query registry
+ const auto newColorScheme = effectiveColorScheme();
+ const bool colorSchemeChanged = newColorScheme != oldColorScheme;
+ s_colorScheme = newColorScheme;
auto integration = QWindowsIntegration::instance();
integration->updateApplicationBadge();
if (integration->darkModeHandling().testFlag(QWindowsApplication::DarkModeStyle)) {
QWindowsTheme::instance()->refresh();
- QWindowSystemInterface::handleThemeChange();
+ QWindowSystemInterface::handleThemeChange<QWindowSystemInterface::SynchronousDelivery>();
}
- if (darkModeChanged) {
- if (integration->darkModeHandling().testFlag(QWindowsApplication::DarkModeWindowFrames)) {
- for (QWindowsWindow *w : std::as_const(QWindowsContext::instance()->windows()))
- w->setDarkBorder(s_darkMode);
- }
+ if (colorSchemeChanged) {
+ for (QWindowsWindow *w : std::as_const(QWindowsContext::instance()->windows()))
+ w->setDarkBorder(s_colorScheme == Qt::ColorScheme::Dark);
}
}
@@ -577,10 +600,10 @@ void QWindowsTheme::refreshPalettes()
if (!QGuiApplication::desktopSettingsAware())
return;
const bool light =
- !s_darkMode
+ effectiveColorScheme() != Qt::ColorScheme::Dark
|| !QWindowsIntegration::instance()->darkModeHandling().testFlag(QWindowsApplication::DarkModeStyle);
clearPalettes();
- m_palettes[SystemPalette] = new QPalette(QWindowsTheme::systemPalette(light ? Qt::ColorScheme::Light : Qt::ColorScheme::Dark));
+ m_palettes[SystemPalette] = new QPalette(QWindowsTheme::systemPalette(s_colorScheme));
m_palettes[ToolTipPalette] = new QPalette(toolTipPalette(*m_palettes[SystemPalette], light));
m_palettes[MenuPalette] = new QPalette(menuPalette(*m_palettes[SystemPalette], light));
m_palettes[MenuBarPalette] = menuBarPalette(*m_palettes[MenuPalette], light);
@@ -598,15 +621,15 @@ QPalette QWindowsTheme::systemPalette(Qt::ColorScheme colorScheme)
QPalette result = standardPalette();
switch (colorScheme) {
+ case Qt::ColorScheme::Unknown:
+ // when a high-contrast theme is active or when we fail to read, assume light
+ Q_FALLTHROUGH();
case Qt::ColorScheme::Light:
populateLightSystemBasePalette(result);
break;
case Qt::ColorScheme::Dark:
populateDarkSystemBasePalette(result);
break;
- default:
- qFatal("Unknown color scheme");
- break;
}
if (result.window() != result.base()) {
@@ -1112,14 +1135,14 @@ bool QWindowsTheme::useNativeMenus()
return result;
}
-bool QWindowsTheme::queryDarkMode()
+Qt::ColorScheme QWindowsTheme::queryColorScheme()
{
- if (queryHighContrast()) {
- return false;
- }
+ if (queryHighContrast())
+ return Qt::ColorScheme::Unknown;
+
const auto setting = QWinRegistryKey(HKEY_CURRENT_USER, LR"(Software\Microsoft\Windows\CurrentVersion\Themes\Personalize)")
.dwordValue(L"AppsUseLightTheme");
- return setting.second && setting.first == 0;
+ return setting.second && setting.first == 0 ? Qt::ColorScheme::Dark : Qt::ColorScheme::Light;
}
bool QWindowsTheme::queryHighContrast()
diff --git a/src/plugins/platforms/windows/qwindowstheme.h b/src/plugins/platforms/windows/qwindowstheme.h
index 6f444d8408..96ae6197b4 100644
--- a/src/plugins/platforms/windows/qwindowstheme.h
+++ b/src/plugins/platforms/windows/qwindowstheme.h
@@ -32,6 +32,8 @@ public:
QVariant themeHint(ThemeHint) const override;
Qt::ColorScheme colorScheme() const override;
+ void requestColorScheme(Qt::ColorScheme scheme) override;
+ Qt::ColorScheme requestedColorScheme() const { return s_colorSchemeOverride; }
static void handleSettingsChanged();
@@ -56,8 +58,6 @@ public:
void showPlatformMenuBar() override;
static bool useNativeMenus();
- static bool queryDarkMode();
- static bool queryHighContrast();
void refreshFonts();
void refresh();
@@ -72,8 +72,17 @@ private:
void clearFonts();
void refreshIconPixmapSizes();
+ static void populateLightSystemBasePalette(QPalette &result);
+ static void populateDarkSystemBasePalette(QPalette &result);
+
+ static Qt::ColorScheme queryColorScheme();
+ static Qt::ColorScheme effectiveColorScheme();
+ static bool queryHighContrast();
+
static QWindowsTheme *m_instance;
- static inline bool s_darkMode = false;
+ static inline Qt::ColorScheme s_colorScheme = Qt::ColorScheme::Unknown;
+ static inline Qt::ColorScheme s_colorSchemeOverride = Qt::ColorScheme::Unknown;
+
QPalette *m_palettes[NPalettes];
QFont *m_fonts[NFonts];
QList<QSize> m_fileIconSizes;
diff --git a/src/plugins/platforms/windows/qwindowswindow.cpp b/src/plugins/platforms/windows/qwindowswindow.cpp
index 03c5d149a6..ee0b88ba54 100644
--- a/src/plugins/platforms/windows/qwindowswindow.cpp
+++ b/src/plugins/platforms/windows/qwindowswindow.cpp
@@ -850,10 +850,17 @@ static inline bool shouldApplyDarkFrame(const QWindow *w)
{
if (!w->isTopLevel() || w->flags().testFlag(Qt::FramelessWindowHint))
return false;
- // the application has explicitly opted out of dark frames
+
+ // the user of the application has explicitly opted out of dark frames
if (!QWindowsIntegration::instance()->darkModeHandling().testFlag(QWindowsApplication::DarkModeWindowFrames))
return false;
+ // the application explicitly overrides the color scheme
+ if (const auto requestedColorScheme = QWindowsTheme::instance()->requestedColorScheme();
+ requestedColorScheme != Qt::ColorScheme::Unknown) {
+ return requestedColorScheme == Qt::ColorScheme::Dark;
+ }
+
// if the application supports a dark border, and the palette is dark (window background color
// is darker than the text), then turn dark-border support on, otherwise use a light border.
auto *dWindow = QWindowPrivate::get(const_cast<QWindow*>(w));
@@ -3286,17 +3293,6 @@ enum : WORD {
DwmwaUseImmersiveDarkModeBefore20h1 = 19
};
-static bool queryDarkBorder(HWND hwnd)
-{
- BOOL result = FALSE;
- const bool ok =
- SUCCEEDED(DwmGetWindowAttribute(hwnd, DwmwaUseImmersiveDarkMode, &result, sizeof(result)))
- || SUCCEEDED(DwmGetWindowAttribute(hwnd, DwmwaUseImmersiveDarkModeBefore20h1, &result, sizeof(result)));
- if (!ok)
- qCWarning(lcQpaWindow, "%s: Unable to retrieve dark window border setting.", __FUNCTION__);
- return result == TRUE;
-}
-
bool QWindowsWindow::setDarkBorderToWindow(HWND hwnd, bool d)
{
const BOOL darkBorder = d ? TRUE : FALSE;
@@ -3312,8 +3308,6 @@ void QWindowsWindow::setDarkBorder(bool d)
{
// respect explicit opt-out and incompatible palettes or styles
d = d && shouldApplyDarkFrame(window());
- if (queryDarkBorder(m_data.hwnd) == d)
- return;
setDarkBorderToWindow(m_data.hwnd, d);
}
diff --git a/src/plugins/platforms/windows/uiautomation/qwindowsuiaaccessibility.cpp b/src/plugins/platforms/windows/uiautomation/qwindowsuiaaccessibility.cpp
index 1abb412ccd..5892493281 100644
--- a/src/plugins/platforms/windows/uiautomation/qwindowsuiaaccessibility.cpp
+++ b/src/plugins/platforms/windows/uiautomation/qwindowsuiaaccessibility.cpp
@@ -127,6 +127,9 @@ void QWindowsUiaAccessibility::notifyAccessibilityUpdate(QAccessibleEvent *event
return;
switch (event->type()) {
+ case QAccessible::Announcement:
+ QWindowsUiaMainProvider::raiseNotification(static_cast<QAccessibleAnnouncementEvent *>(event));
+ break;
case QAccessible::Focus:
QWindowsUiaMainProvider::notifyFocusChange(event);
break;
diff --git a/src/plugins/platforms/windows/uiautomation/qwindowsuiamainprovider.cpp b/src/plugins/platforms/windows/uiautomation/qwindowsuiamainprovider.cpp
index 95ddbcced6..b8f2d0eadd 100644
--- a/src/plugins/platforms/windows/uiautomation/qwindowsuiamainprovider.cpp
+++ b/src/plugins/platforms/windows/uiautomation/qwindowsuiamainprovider.cpp
@@ -204,6 +204,24 @@ void QWindowsUiaMainProvider::notifyTextChange(QAccessibleEvent *event)
}
}
+void QWindowsUiaMainProvider::raiseNotification(QAccessibleAnnouncementEvent *event)
+{
+ if (QAccessibleInterface *accessible = event->accessibleInterface()) {
+ if (QWindowsUiaMainProvider *provider = providerForAccessible(accessible)) {
+ BSTR message = bStrFromQString(event->message());
+ QAccessible::AnnouncementPriority prio = event->priority();
+ NotificationProcessing processing = (prio == QAccessible::AnnouncementPriority::Assertive)
+ ? NotificationProcessing_ImportantAll
+ : NotificationProcessing_All;
+ BSTR activityId = bStrFromQString(QString::fromLatin1(""));
+ UiaRaiseNotificationEvent(provider, NotificationKind_Other, processing, message, activityId);
+
+ ::SysFreeString(message);
+ ::SysFreeString(activityId);
+ }
+ }
+}
+
HRESULT STDMETHODCALLTYPE QWindowsUiaMainProvider::QueryInterface(REFIID iid, LPVOID *iface)
{
HRESULT result = QComObject::QueryInterface(iid, iface);
diff --git a/src/plugins/platforms/windows/uiautomation/qwindowsuiamainprovider.h b/src/plugins/platforms/windows/uiautomation/qwindowsuiamainprovider.h
index 99db0ed318..b24f4a6cc3 100644
--- a/src/plugins/platforms/windows/uiautomation/qwindowsuiamainprovider.h
+++ b/src/plugins/platforms/windows/uiautomation/qwindowsuiamainprovider.h
@@ -34,6 +34,7 @@ public:
static void notifyNameChange(QAccessibleEvent *event);
static void notifySelectionChange(QAccessibleEvent *event);
static void notifyTextChange(QAccessibleEvent *event);
+ static void raiseNotification(QAccessibleAnnouncementEvent *event);
// IUnknown
HRESULT STDMETHODCALLTYPE QueryInterface(REFIID id, LPVOID *iface) override;
diff --git a/src/plugins/platforms/windows/uiautomation/qwindowsuiautomation.cpp b/src/plugins/platforms/windows/uiautomation/qwindowsuiautomation.cpp
index 1593a07202..6954a881d0 100644
--- a/src/plugins/platforms/windows/uiautomation/qwindowsuiautomation.cpp
+++ b/src/plugins/platforms/windows/uiautomation/qwindowsuiautomation.cpp
@@ -69,6 +69,14 @@ HRESULT WINAPI UiaRaiseAutomationEvent(IRawElementProviderSimple *pProvider, EVE
return func.invoke(pProvider, id);
}
+HRESULT WINAPI UiaRaiseNotificationEvent(
+ IRawElementProviderSimple *pProvider, NotificationKind notificationKind,
+ NotificationProcessing notificationProcessing, BSTR displayString, BSTR activityId)
+{
+ static auto func = winapi_func("uiautomationcore", FN(UiaRaiseNotificationEvent));
+ return func.invoke(pProvider, notificationKind, notificationProcessing, displayString, activityId);
+}
+
#endif // defined(__MINGW32__) || defined(__MINGW64__)
#endif // QT_CONFIG(accessibility)
diff --git a/src/plugins/platforms/xcb/CMakeLists.txt b/src/plugins/platforms/xcb/CMakeLists.txt
index e8fb442dd4..96758e7181 100644
--- a/src/plugins/platforms/xcb/CMakeLists.txt
+++ b/src/plugins/platforms/xcb/CMakeLists.txt
@@ -164,7 +164,7 @@ endif()
qt_internal_add_plugin(QXcbIntegrationPlugin
OUTPUT_NAME qxcb
PLUGIN_TYPE platforms
- DEFAULT_IF ${QT_QPA_DEFAULT_PLATFORM} MATCHES xcb
+ DEFAULT_IF "xcb" IN_LIST QT_QPA_PLATFORMS
SOURCES
qxcbmain.cpp
DEFINES
diff --git a/src/plugins/sqldrivers/ibase/qsql_ibase.cpp b/src/plugins/sqldrivers/ibase/qsql_ibase.cpp
index f7abb8718d..f6eed5e227 100644
--- a/src/plugins/sqldrivers/ibase/qsql_ibase.cpp
+++ b/src/plugins/sqldrivers/ibase/qsql_ibase.cpp
@@ -406,6 +406,42 @@ protected:
int size() override;
int numRowsAffected() override;
QSqlRecord record() const override;
+
+ template<typename T>
+ QVariant applyScale(T val, int scale) const
+ {
+ if (scale >= 0)
+ return QVariant(val);
+
+ switch (numericalPrecisionPolicy()) {
+ case QSql::LowPrecisionInt32:
+ return QVariant(qint32(val * pow(10.0, scale)));
+ case QSql::LowPrecisionInt64:
+ return QVariant(qint64(val * pow(10.0, scale)));
+ case QSql::LowPrecisionDouble:
+ return QVariant(double(val * pow(10.0, scale)));
+ case QSql::HighPrecision: {
+ const bool negative = val < 0;
+ QString number;
+ if constexpr (std::is_signed_v<T> || negative)
+ number = QString::number(qAbs(val));
+ else
+ number = QString::number(val);
+ auto len = number.size();
+ scale *= -1;
+ if (scale >= len) {
+ number = QString(scale - len + 1, u'0') + number;
+ len = number.size();
+ }
+ const auto sepPos = len - scale;
+ number = number.left(sepPos) + u'.' + number.mid(sepPos);
+ if (negative)
+ number = u'-' + number;
+ return QVariant(number);
+ }
+ }
+ return QVariant(val);
+ }
};
class QIBaseResultPrivate: public QSqlCachedResultPrivate
@@ -1239,27 +1275,27 @@ bool QIBaseResult::gotoNext(QSqlCachedResult::ValueCache& row, int rowIdx)
// pascal strings - a short with a length information followed by the data
row[idx] = QString::fromUtf8(buf + sizeof(short), *(short*)buf);
break;
- case SQL_INT64:
- if (d->sqlda->sqlvar[i].sqlscale < 0)
- row[idx] = *(qint64*)buf * pow(10.0, d->sqlda->sqlvar[i].sqlscale);
- else
- row[idx] = QVariant(*(qint64*)buf);
+ case SQL_INT64: {
+ Q_ASSERT(d->sqlda->sqlvar[i].sqllen == sizeof(qint64));
+ const auto val = *(qint64 *)buf;
+ const auto scale = d->sqlda->sqlvar[i].sqlscale;
+ row[idx] = applyScale(val, scale);
break;
+ }
case SQL_LONG:
- if (d->sqlda->sqlvar[i].sqllen == 4)
- if (d->sqlda->sqlvar[i].sqlscale < 0)
- row[idx] = QVariant(*(qint32*)buf * pow(10.0, d->sqlda->sqlvar[i].sqlscale));
- else
- row[idx] = QVariant(*(qint32*)buf);
- else
+ if (d->sqlda->sqlvar[i].sqllen == 4) {
+ const auto val = *(qint32 *)buf;
+ const auto scale = d->sqlda->sqlvar[i].sqlscale;
+ row[idx] = applyScale(val, scale);
+ } else
row[idx] = QVariant(*(qint64*)buf);
break;
- case SQL_SHORT:
- if (d->sqlda->sqlvar[i].sqlscale < 0)
- row[idx] = QVariant(long((*(short*)buf)) * pow(10.0, d->sqlda->sqlvar[i].sqlscale));
- else
- row[idx] = QVariant(int((*(short*)buf)));
+ case SQL_SHORT: {
+ const auto val = *(short *)buf;
+ const auto scale = d->sqlda->sqlvar[i].sqlscale;
+ row[idx] = applyScale(val, scale);
break;
+ }
case SQL_FLOAT:
row[idx] = QVariant(double((*(float*)buf)));
break;
@@ -1299,27 +1335,6 @@ bool QIBaseResult::gotoNext(QSqlCachedResult::ValueCache& row, int rowIdx)
row[idx] = QVariant();
break;
}
- if (d->sqlda->sqlvar[i].sqlscale < 0) {
- QVariant v = row[idx];
- switch(numericalPrecisionPolicy()) {
- case QSql::LowPrecisionInt32:
- if (v.convert(QMetaType(QMetaType::Int)))
- row[idx]=v;
- break;
- case QSql::LowPrecisionInt64:
- if (v.convert(QMetaType(QMetaType::LongLong)))
- row[idx]=v;
- break;
- case QSql::LowPrecisionDouble:
- if (v.convert(QMetaType(QMetaType::Double)))
- row[idx]=v;
- break;
- case QSql::HighPrecision:
- if (v.convert(QMetaType(QMetaType::QString)))
- row[idx]=v;
- break;
- }
- }
}
return true;
diff --git a/src/plugins/styles/modernwindows/qwindows11style.cpp b/src/plugins/styles/modernwindows/qwindows11style.cpp
index daca801609..8b886396f8 100644
--- a/src/plugins/styles/modernwindows/qwindows11style.cpp
+++ b/src/plugins/styles/modernwindows/qwindows11style.cpp
@@ -434,12 +434,21 @@ void QWindows11Style::drawComplexControl(ComplexControl control, const QStyleOpt
#if QT_CONFIG(combobox)
case CC_ComboBox:
if (const QStyleOptionComboBox *combobox = qstyleoption_cast<const QStyleOptionComboBox *>(option)) {
- QBrush fillColor = state & State_MouseOver && !(state & State_HasFocus) ? QBrush(WINUI3Colors[colorSchemeIndex][subtleHighlightColor]) : option->palette.brush(QPalette::Base);
+ QBrush fillColor = combobox->palette.brush(QPalette::Base);
QRectF rect = option->rect.adjusted(2,2,-2,-2);
painter->setBrush(fillColor);
painter->setPen(Qt::NoPen);
painter->drawRoundedRect(rect, secondLevelRoundingRadius, secondLevelRoundingRadius);
+ // In case the QComboBox is hovered overdraw the background with a alpha mask to
+ // highlight the QComboBox.
+ if (state & State_MouseOver) {
+ fillColor = QBrush(WINUI3Colors[colorSchemeIndex][subtleHighlightColor]);
+ painter->setBrush(fillColor);
+ painter->setPen(Qt::NoPen);
+ painter->drawRoundedRect(rect, secondLevelRoundingRadius, secondLevelRoundingRadius);
+ }
+
rect.adjust(0.5,0.5,-0.5,-0.5);
painter->setBrush(Qt::NoBrush);
painter->setPen(highContrastTheme == true ? combobox->palette.buttonText().color() : WINUI3Colors[colorSchemeIndex][frameColorLight]);
@@ -905,12 +914,20 @@ void QWindows11Style::drawPrimitive(PrimitiveElement element, const QStyleOption
if (widget && widget->objectName() == "qt_spinbox_lineedit")
break;
if (const auto *panel = qstyleoption_cast<const QStyleOptionFrame *>(option)) {
- QBrush fillColor = state & State_MouseOver && !(state & State_HasFocus) ? QBrush(WINUI3Colors[colorSchemeIndex][subtleHighlightColor]) : option->palette.brush(QPalette::Base);
- painter->setBrush(fillColor);
- painter->setPen(Qt::NoPen);
QRectF frameRect = option->rect;
frameRect.adjust(0.5,0.5,-0.5,-0.5);
+ QBrush fillColor = option->palette.brush(QPalette::Base);
+ painter->setBrush(fillColor);
+ painter->setPen(Qt::NoPen);
painter->drawRoundedRect(frameRect, secondLevelRoundingRadius, secondLevelRoundingRadius);
+ // In case the QLineEdit is hovered overdraw the background with a alpha mask to
+ // highlight the QLineEdit.
+ if (state & State_MouseOver && !(state & State_HasFocus)) {
+ fillColor = QBrush(WINUI3Colors[colorSchemeIndex][subtleHighlightColor]);
+ painter->setBrush(fillColor);
+ painter->setPen(Qt::NoPen);
+ painter->drawRoundedRect(frameRect, secondLevelRoundingRadius, secondLevelRoundingRadius);
+ }
if (panel->lineWidth > 0)
proxy()->drawPrimitive(PE_FrameLineEdit, panel, painter, widget);
}
@@ -1104,7 +1121,7 @@ void QWindows11Style::drawControl(ControlElement element, const QStyleOption *op
rect.translate(shiftX, shiftY);
painter->setFont(toolbutton->font);
const QString text = d->toolButtonElideText(toolbutton, rect, alignment);
- if (toolbutton->state & State_Raised)
+ if (toolbutton->state & State_Raised || toolbutton->palette.isBrushSet(QPalette::Current, QPalette::ButtonText))
painter->setPen(QPen(toolbutton->palette.buttonText().color()));
else
painter->setPen(QPen(WINUI3Colors[colorSchemeIndex][controlTextSecondary]));
@@ -1158,7 +1175,7 @@ void QWindows11Style::drawControl(ControlElement element, const QStyleOption *op
}
tr.translate(shiftX, shiftY);
const QString text = d->toolButtonElideText(toolbutton, tr, alignment);
- if (toolbutton->state & State_Raised)
+ if (toolbutton->state & State_Raised || toolbutton->palette.isBrushSet(QPalette::Current, QPalette::ButtonText))
painter->setPen(QPen(toolbutton->palette.buttonText().color()));
else
painter->setPen(QPen(WINUI3Colors[colorSchemeIndex][controlTextSecondary]));
@@ -1207,75 +1224,72 @@ void QWindows11Style::drawControl(ControlElement element, const QStyleOption *op
break;
case QStyle::CE_ProgressBarGroove:{
if (const QStyleOptionProgressBar* progbaropt = qstyleoption_cast<const QStyleOptionProgressBar*>(option)) {
- if (const QProgressBar* bar = qobject_cast<const QProgressBar*>(widget)) {
- QRect rect = subElementRect(SE_ProgressBarContents, progbaropt, widget);
- QPointF center = rect.center();
- if (bar->orientation() & Qt::Horizontal) {
- rect.setHeight(1);
- rect.moveTop(center.y());
- } else {
- rect.setWidth(1);
- rect.moveLeft(center.x());
- }
- painter->setPen(Qt::NoPen);
- painter->setBrush(Qt::gray);
- painter->drawRect(rect);
+ QRect rect = subElementRect(SE_ProgressBarContents, progbaropt, widget);
+ QPointF center = rect.center();
+ if (progbaropt->state & QStyle::State_Horizontal) {
+ rect.setHeight(1);
+ rect.moveTop(center.y());
+ } else {
+ rect.setWidth(1);
+ rect.moveLeft(center.x());
}
+ painter->setPen(Qt::NoPen);
+ painter->setBrush(Qt::gray);
+ painter->drawRect(rect);
}
break;
}
case QStyle::CE_ProgressBarContents:
if (const QStyleOptionProgressBar* progbaropt = qstyleoption_cast<const QStyleOptionProgressBar*>(option)) {
- if (const QProgressBar* bar = qobject_cast<const QProgressBar*>(widget)) {
- const qreal progressBarThickness = 3;
- const qreal progressBarHalfThickness = progressBarThickness / 2.0;
- QRectF rect = subElementRect(SE_ProgressBarContents, progbaropt, widget);
- QRectF originalRect = rect;
- QPointF center = rect.center();
- bool isIndeterminate = progbaropt->maximum == 0 && progbaropt->minimum == 0;
- float fillPercentage = 0;
- const qreal offset = (bar->orientation() == Qt::Horizontal && int(rect.height()) % 2 == 0)
- || (bar->orientation() == Qt::Vertical && int(rect.width()) % 2 == 0) ? 0.5 : 0.0;
-
- if (!isIndeterminate) {
- fillPercentage = ((float(progbaropt->progress) - float(progbaropt->minimum)) / (float(progbaropt->maximum) - float(progbaropt->minimum)));
- if (bar->orientation() == Qt::Horizontal) {
- rect.setHeight(progressBarThickness);
- rect.moveTop(center.y() - progressBarHalfThickness - offset);
- rect.setWidth(rect.width() * fillPercentage);
- } else {
- float oldHeight = rect.height();
- rect.setWidth(progressBarThickness);
- rect.moveLeft(center.x() - progressBarHalfThickness - offset);
- rect.moveTop(oldHeight * (1.0f - fillPercentage));
- rect.setHeight(oldHeight * fillPercentage);
- }
+ const qreal progressBarThickness = 3;
+ const qreal progressBarHalfThickness = progressBarThickness / 2.0;
+ QRectF rect = subElementRect(SE_ProgressBarContents, progbaropt, widget);
+ QRectF originalRect = rect;
+ QPointF center = rect.center();
+ bool isIndeterminate = progbaropt->maximum == 0 && progbaropt->minimum == 0;
+ float fillPercentage = 0;
+ const Qt::Orientation orientation = (progbaropt->state & QStyle::State_Horizontal) ? Qt::Horizontal : Qt::Vertical;
+ const qreal offset = (orientation == Qt::Horizontal && int(rect.height()) % 2 == 0)
+ || (orientation == Qt::Vertical && int(rect.width()) % 2 == 0) ? 0.5 : 0.0;
+
+ if (!isIndeterminate) {
+ fillPercentage = ((float(progbaropt->progress) - float(progbaropt->minimum)) / (float(progbaropt->maximum) - float(progbaropt->minimum)));
+ if (orientation == Qt::Horizontal) {
+ rect.setHeight(progressBarThickness);
+ rect.moveTop(center.y() - progressBarHalfThickness - offset);
+ rect.setWidth(rect.width() * fillPercentage);
} else {
- auto elapsedTime = std::chrono::time_point_cast<std::chrono::milliseconds>(std::chrono::system_clock::now());
- fillPercentage = (elapsedTime.time_since_epoch().count() % 5000)/(5000.0f*0.75);
- if (bar->orientation() == Qt::Horizontal) {
- float barBegin = qMin(qMax(fillPercentage-0.25,0.0) * rect.width(), float(rect.width()));
- float barEnd = qMin(fillPercentage * rect.width(), float(rect.width()));
- rect = QRect(QPoint(rect.left() + barBegin, rect.top()), QPoint(rect.left() + barEnd, rect.bottom()));
- rect.setHeight(progressBarThickness);
- rect.moveTop(center.y() - progressBarHalfThickness - offset);
- } else {
- float barBegin = qMin(qMax(fillPercentage-0.25,0.0) * rect.height(), float(rect.height()));
- float barEnd = qMin(fillPercentage * rect.height(), float(rect.height()));
- rect = QRect(QPoint(rect.left(), rect.bottom() - barEnd), QPoint(rect.right(), rect.bottom() - barBegin));
- rect.setWidth(progressBarThickness);
- rect.moveLeft(center.x() - progressBarHalfThickness - offset);
- }
- const_cast<QWidget*>(widget)->update();
+ float oldHeight = rect.height();
+ rect.setWidth(progressBarThickness);
+ rect.moveLeft(center.x() - progressBarHalfThickness - offset);
+ rect.moveTop(oldHeight * (1.0f - fillPercentage));
+ rect.setHeight(oldHeight * fillPercentage);
}
- if (progbaropt->invertedAppearance && bar->orientation() == Qt::Horizontal)
- rect.moveLeft(originalRect.width() * (1.0 - fillPercentage));
- else if (progbaropt->invertedAppearance && bar->orientation() == Qt::Vertical)
- rect.moveBottom(originalRect.height() * fillPercentage);
- painter->setPen(Qt::NoPen);
- painter->setBrush(progbaropt->palette.accent());
- painter->drawRoundedRect(rect, secondLevelRoundingRadius, secondLevelRoundingRadius);
+ } else {
+ auto elapsedTime = std::chrono::time_point_cast<std::chrono::milliseconds>(std::chrono::system_clock::now());
+ fillPercentage = (elapsedTime.time_since_epoch().count() % 5000)/(5000.0f*0.75);
+ if (orientation == Qt::Horizontal) {
+ float barBegin = qMin(qMax(fillPercentage-0.25,0.0) * rect.width(), float(rect.width()));
+ float barEnd = qMin(fillPercentage * rect.width(), float(rect.width()));
+ rect = QRect(QPoint(rect.left() + barBegin, rect.top()), QPoint(rect.left() + barEnd, rect.bottom()));
+ rect.setHeight(progressBarThickness);
+ rect.moveTop(center.y() - progressBarHalfThickness - offset);
+ } else {
+ float barBegin = qMin(qMax(fillPercentage-0.25,0.0) * rect.height(), float(rect.height()));
+ float barEnd = qMin(fillPercentage * rect.height(), float(rect.height()));
+ rect = QRect(QPoint(rect.left(), rect.bottom() - barEnd), QPoint(rect.right(), rect.bottom() - barBegin));
+ rect.setWidth(progressBarThickness);
+ rect.moveLeft(center.x() - progressBarHalfThickness - offset);
+ }
+ const_cast<QWidget*>(widget)->update();
}
+ if (progbaropt->invertedAppearance && orientation == Qt::Horizontal)
+ rect.moveLeft(originalRect.width() * (1.0 - fillPercentage));
+ else if (progbaropt->invertedAppearance && orientation == Qt::Vertical)
+ rect.moveBottom(originalRect.height() * fillPercentage);
+ painter->setPen(Qt::NoPen);
+ painter->setBrush(progbaropt->palette.accent());
+ painter->drawRoundedRect(rect, secondLevelRoundingRadius, secondLevelRoundingRadius);
}
break;
case QStyle::CE_ProgressBarLabel:
@@ -1364,13 +1378,15 @@ void QWindows11Style::drawControl(ControlElement element, const QStyleOption *op
} else {
QRectF rect = btn->rect.marginsRemoved(QMargins(2,2,2,2));
painter->setPen(Qt::NoPen);
- if (flags & (State_Sunken))
- painter->setBrush(flags & State_On ? option->palette.accent().color().lighter(120) : WINUI3Colors[colorSchemeIndex][controlFillTertiary]);
- else if (flags & State_MouseOver)
- painter->setBrush(flags & State_On ? option->palette.accent().color().lighter(110) : WINUI3Colors[colorSchemeIndex][controlFillSecondary]);
- else
- painter->setBrush(flags & State_On ? option->palette.accent() : option->palette.button());
+ painter->setBrush(flags & State_On ? option->palette.accent() : option->palette.button());
painter->drawRoundedRect(rect, secondLevelRoundingRadius, secondLevelRoundingRadius);
+ if (flags.testFlags(State_Sunken | State_MouseOver)) {
+ if (flags & (State_Sunken))
+ painter->setBrush(flags & State_On ? option->palette.accent().color().lighter(120) : WINUI3Colors[colorSchemeIndex][controlFillTertiary]);
+ else if (flags & State_MouseOver)
+ painter->setBrush(flags & State_On ? option->palette.accent().color().lighter(110) : WINUI3Colors[colorSchemeIndex][controlFillSecondary]);
+ painter->drawRoundedRect(rect, secondLevelRoundingRadius, secondLevelRoundingRadius);
+ }
rect.adjust(0.5,0.5,-0.5,-0.5);
painter->setBrush(Qt::NoBrush);
@@ -1578,7 +1594,13 @@ void QWindows11Style::drawControl(ControlElement element, const QStyleOption *op
break;
case CE_HeaderSection: {
if (const QStyleOptionHeader *header = qstyleoption_cast<const QStyleOptionHeader *>(option)) {
+ painter->setPen(Qt::NoPen);
+ painter->setBrush(header->palette.button());
+ painter->drawRect(header->rect);
+
painter->setPen(highContrastTheme == true ? header->palette.buttonText().color() : WINUI3Colors[colorSchemeIndex][frameColorLight]);
+ painter->setBrush(Qt::NoBrush);
+
if (header->position == QStyleOptionHeader::OnlyOneSection) {
break;
}
@@ -1716,6 +1738,10 @@ int QWindows11Style::styleHint(StyleHint hint, const QStyleOption *opt,
return 0;
case QStyle::SH_ItemView_ShowDecorationSelected:
return 1;
+ case QStyle::SH_Slider_AbsoluteSetButtons:
+ return Qt::LeftButton;
+ case QStyle::SH_Slider_PageSetButtons:
+ return 0;
default:
return QWindowsVistaStyle::styleHint(hint, opt, widget, returnData);
}
@@ -2014,6 +2040,8 @@ void QWindows11Style::polish(QWidget* widget)
QLineEdit *le = cb->lineEdit();
le->setFrame(false);
}
+ } else if (widget->inherits("QAbstractButton") || widget->inherits("QToolButton")) {
+ widget->setAutoFillBackground(false);
} else if (qobject_cast<QGraphicsView *>(widget) && !qobject_cast<QTextEdit *>(widget)) {
QPalette pal = widget->palette();
pal.setColor(QPalette::Base, pal.window().color());
@@ -2049,48 +2077,35 @@ void QWindows11Style::unpolish(QWidget *widget)
The colors for Windows 11 are taken from the official WinUI3 Figma style at
https://www.figma.com/community/file/1159947337437047524
*/
+#define SET_IF_UNRESOLVED(GROUP, ROLE, VALUE) \
+ if (!result.isBrushSet(QPalette::Inactive, ROLE) || styleSheetChanged) \
+ result.setColor(GROUP, ROLE, VALUE)
+
static void populateLightSystemBasePalette(QPalette &result)
{
static QString oldStyleSheet;
const bool styleSheetChanged = oldStyleSheet != qApp->styleSheet();
- QPalette standardPalette = QApplication::palette();
const QColor textColor = QColor(0x00,0x00,0x00,0xE4);
-
const QColor btnFace = QColor(0xFF,0xFF,0xFF,0xB3);
const QColor btnHighlight = result.accent().color();
const QColor btnColor = result.button().color();
- if (standardPalette.color(QPalette::Highlight) == result.color(QPalette::Highlight) || styleSheetChanged)
- result.setColor(QPalette::Highlight, btnHighlight);
- if (standardPalette.color(QPalette::WindowText) == result.color(QPalette::WindowText) || styleSheetChanged)
- result.setColor(QPalette::WindowText, textColor);
- if (standardPalette.color(QPalette::Button) == result.color(QPalette::Button) || styleSheetChanged)
- result.setColor(QPalette::Button, btnFace);
- if (standardPalette.color(QPalette::Light) == result.color(QPalette::Light) || styleSheetChanged)
- result.setColor(QPalette::Light, btnColor.lighter(150));
- if (standardPalette.color(QPalette::Dark) == result.color(QPalette::Dark) || styleSheetChanged)
- result.setColor(QPalette::Dark, btnColor.darker(200));
- if (standardPalette.color(QPalette::Mid) == result.color(QPalette::Mid) || styleSheetChanged)
- result.setColor(QPalette::Mid, btnColor.darker(150));
- if (standardPalette.color(QPalette::Text) == result.color(QPalette::Text) || styleSheetChanged)
- result.setColor(QPalette::Text, textColor);
- if (standardPalette.color(QPalette::BrightText) != result.color(QPalette::BrightText) || styleSheetChanged)
- result.setColor(QPalette::BrightText, btnHighlight);
- if (standardPalette.color(QPalette::Base) == result.color(QPalette::Base) || styleSheetChanged)
- result.setColor(QPalette::Base, btnFace);
- if (standardPalette.color(QPalette::Window) == result.color(QPalette::Window) || styleSheetChanged)
- result.setColor(QPalette::Window, QColor(0xF3,0xF3,0xF3,0xFF));
- if (standardPalette.color(QPalette::ButtonText) == result.color(QPalette::ButtonText) || styleSheetChanged)
- result.setColor(QPalette::ButtonText, textColor);
- if (standardPalette.color(QPalette::Midlight) == result.color(QPalette::Midlight) || styleSheetChanged)
- result.setColor(QPalette::Midlight, btnColor.lighter(125));
- if (standardPalette.color(QPalette::Shadow) == result.color(QPalette::Shadow) || styleSheetChanged)
- result.setColor(QPalette::Shadow, Qt::black);
- if (standardPalette.color(QPalette::ToolTipBase) == result.color(QPalette::ToolTipBase) || styleSheetChanged)
- result.setColor(QPalette::ToolTipBase, result.window().color());
- if (standardPalette.color(QPalette::ToolTipText) == result.color(QPalette::ToolTipText) || styleSheetChanged)
- result.setColor(QPalette::ToolTipText, result.windowText().color());
+ SET_IF_UNRESOLVED(QPalette::All, QPalette::Highlight, btnHighlight);
+ SET_IF_UNRESOLVED(QPalette::All, QPalette::WindowText, textColor);
+ SET_IF_UNRESOLVED(QPalette::All, QPalette::Button, btnFace);
+ SET_IF_UNRESOLVED(QPalette::All, QPalette::Light, btnColor.lighter(150));
+ SET_IF_UNRESOLVED(QPalette::All, QPalette::Dark, btnColor.darker(200));
+ SET_IF_UNRESOLVED(QPalette::All, QPalette::Mid, btnColor.darker(150));
+ SET_IF_UNRESOLVED(QPalette::All, QPalette::Text, textColor);
+ SET_IF_UNRESOLVED(QPalette::All, QPalette::BrightText, btnHighlight);
+ SET_IF_UNRESOLVED(QPalette::All, QPalette::Base, btnFace);
+ SET_IF_UNRESOLVED(QPalette::All, QPalette::Window, QColor(0xF3,0xF3,0xF3,0xFF));
+ SET_IF_UNRESOLVED(QPalette::All, QPalette::ButtonText, textColor);
+ SET_IF_UNRESOLVED(QPalette::All, QPalette::Midlight, btnColor.lighter(125));
+ SET_IF_UNRESOLVED(QPalette::All, QPalette::Shadow, Qt::black);
+ SET_IF_UNRESOLVED(QPalette::All, QPalette::ToolTipBase, result.window().color());
+ SET_IF_UNRESOLVED(QPalette::All, QPalette::ToolTipText, result.windowText().color());
if (result.midlight() == result.button())
result.setColor(QPalette::Midlight, btnColor.lighter(110));
@@ -2100,35 +2115,30 @@ static void populateLightSystemBasePalette(QPalette &result)
/*!
\internal
*/
-void QWindows11Style::polish(QPalette& pal)
+void QWindows11Style::polish(QPalette& result)
{
- highContrastTheme = QGuiApplicationPrivate::colorScheme() == Qt::ColorScheme::Unknown;
- colorSchemeIndex = QGuiApplicationPrivate::colorScheme() == Qt::ColorScheme::Light ? 0 : 1;
+ highContrastTheme = QGuiApplication::styleHints()->colorScheme() == Qt::ColorScheme::Unknown;
+ colorSchemeIndex = QGuiApplication::styleHints()->colorScheme() == Qt::ColorScheme::Light ? 0 : 1;
if (!highContrastTheme && colorSchemeIndex == 0)
- populateLightSystemBasePalette(pal);
-
- if (standardPalette().color(QPalette::Inactive, QPalette::Button) == pal.color(QPalette::Inactive, QPalette::Button))
- pal.setColor(QPalette::Inactive, QPalette::Button, pal.button().color());
- if (standardPalette().color(QPalette::Inactive, QPalette::Window) == pal.color(QPalette::Inactive, QPalette::Window))
- pal.setColor(QPalette::Inactive, QPalette::Window, pal.window().color());
- if (standardPalette().color(QPalette::Inactive, QPalette::Light) == pal.color(QPalette::Inactive, QPalette::Light))
- pal.setColor(QPalette::Inactive, QPalette::Light, pal.light().color());
- if (standardPalette().color(QPalette::Inactive, QPalette::Dark) == pal.color(QPalette::Inactive, QPalette::Dark))
- pal.setColor(QPalette::Inactive, QPalette::Dark, pal.dark().color());
- if (standardPalette().color(QPalette::Inactive, QPalette::Accent) == pal.color(QPalette::Inactive, QPalette::Accent))
- pal.setColor(QPalette::Inactive, QPalette::Accent, pal.accent().color());
- if (standardPalette().color(QPalette::Inactive, QPalette::Highlight) == pal.color(QPalette::Inactive, QPalette::Highlight))
- pal.setColor(QPalette::Inactive, QPalette::Highlight, pal.highlight().color());
- if (standardPalette().color(QPalette::Inactive, QPalette::HighlightedText) == pal.color(QPalette::Inactive, QPalette::HighlightedText))
- pal.setColor(QPalette::Inactive, QPalette::HighlightedText, pal.highlightedText().color());
- if (standardPalette().color(QPalette::Inactive, QPalette::Text) == pal.color(QPalette::Inactive, QPalette::Text))
- pal.setColor(QPalette::Inactive, QPalette::Text, pal.text().color());
- if (standardPalette().color(QPalette::Inactive, QPalette::WindowText) == pal.color(QPalette::Inactive, QPalette::WindowText))
- pal.setColor(QPalette::Inactive, QPalette::WindowText, pal.windowText().color());
+ populateLightSystemBasePalette(result);
+
+ const bool styleSheetChanged = false; // so the macro works
+
+ SET_IF_UNRESOLVED(QPalette::Inactive, QPalette::Button, result.button().color());
+ SET_IF_UNRESOLVED(QPalette::Inactive, QPalette::Window, result.window().color());
+ SET_IF_UNRESOLVED(QPalette::Inactive, QPalette::Light, result.light().color());
+ SET_IF_UNRESOLVED(QPalette::Inactive, QPalette::Dark, result.dark().color());
+ SET_IF_UNRESOLVED(QPalette::Inactive, QPalette::Accent, result.accent().color());
+ SET_IF_UNRESOLVED(QPalette::Inactive, QPalette::Highlight, result.highlight().color());
+ SET_IF_UNRESOLVED(QPalette::Inactive, QPalette::HighlightedText, result.highlightedText().color());
+ SET_IF_UNRESOLVED(QPalette::Inactive, QPalette::Text, result.text().color());
+ SET_IF_UNRESOLVED(QPalette::Inactive, QPalette::WindowText, result.windowText().color());
if (highContrastTheme)
- pal.setColor(QPalette::Active, QPalette::HighlightedText, pal.windowText().color());
+ result.setColor(QPalette::Active, QPalette::HighlightedText, result.windowText().color());
}
+#undef SET_IF_UNRESOLVED
+
QT_END_NAMESPACE
diff --git a/src/plugins/styles/modernwindows/qwindowsvistastyle.cpp b/src/plugins/styles/modernwindows/qwindowsvistastyle.cpp
index 1df134b629..beee1d6f31 100644
--- a/src/plugins/styles/modernwindows/qwindowsvistastyle.cpp
+++ b/src/plugins/styles/modernwindows/qwindowsvistastyle.cpp
@@ -4755,7 +4755,7 @@ void QWindowsVistaStyle::polish(QPalette &pal)
{
Q_D(QWindowsVistaStyle);
- if (QGuiApplicationPrivate::colorScheme() == Qt::ColorScheme::Dark) {
+ if (QGuiApplication::styleHints()->colorScheme() == Qt::ColorScheme::Dark) {
// System runs in dark mode, but the Vista style cannot use a dark palette.
// Overwrite with the light system palette.
using QWindowsApplication = QNativeInterface::Private::QWindowsApplication;
diff --git a/src/testlib/doc/src/qttestlib-manual.qdoc b/src/testlib/doc/src/qttestlib-manual.qdoc
index 9f650496bb..1b6f534045 100644
--- a/src/testlib/doc/src/qttestlib-manual.qdoc
+++ b/src/testlib/doc/src/qttestlib-manual.qdoc
@@ -589,7 +589,5 @@
\li \l {Chapter 6: Skipping Tests with QSKIP}{Skipping Tests}
\endlist
- \note You can build and execute the tests from each chapter using the
- available source code, which is linked to at the end of each chapter.
*/
diff --git a/src/testlib/qcomparisontesthelper_p.h b/src/testlib/qcomparisontesthelper_p.h
index b422fc4049..afeb1088c4 100644
--- a/src/testlib/qcomparisontesthelper_p.h
+++ b/src/testlib/qcomparisontesthelper_p.h
@@ -167,7 +167,8 @@ void testAllComparisonOperatorsCompile()
Basic testing of equality operators.
The helper function tests {in}equality operators (== and !=) for the \a lhs
- operand of type \c {LeftType} and the \a rhs operand of type \c {RightType}.
+ operand of type \c {LeftType} and the \a rhs operand of type \c {RightType},
+ plus the reverse order of the operands.
The \a expectedEqual parameter is an expected result for \c {operator==()}.
@@ -187,10 +188,8 @@ void testEqualityOperators(LeftType lhs, RightType rhs, bool expectedEqual)
{
CHECK_RUNTIME_CREF(CHECK_RUNTIME_LR, lhs, rhs, ==, expectedEqual);
CHECK_RUNTIME_CREF(CHECK_RUNTIME_LR, lhs, rhs, !=, !expectedEqual);
- if constexpr (!std::is_same_v<LeftType, RightType>) {
- CHECK_RUNTIME_CREF(CHECK_RUNTIME_LR, rhs, lhs, ==, expectedEqual);
- CHECK_RUNTIME_CREF(CHECK_RUNTIME_LR, rhs, lhs, !=, !expectedEqual);
- }
+ CHECK_RUNTIME_CREF(CHECK_RUNTIME_LR, rhs, lhs, ==, expectedEqual);
+ CHECK_RUNTIME_CREF(CHECK_RUNTIME_LR, rhs, lhs, !=, !expectedEqual);
}
/*!
@@ -199,7 +198,8 @@ void testEqualityOperators(LeftType lhs, RightType rhs, bool expectedEqual)
The helper function tests all six relation and equality operators
(==, !=, <, >, <=, >=) for the \a lhs operand of type \c {LeftType} and
- the \a rhs operand of type \c {RightType}.
+ the \a rhs operand of type \c {RightType} and all six for the reverse
+ order of the operands.
If compiled in C++20 mode, also checks \c {operator<=>()} if that is
implemented.
@@ -283,7 +283,6 @@ void testAllComparisonOperators(LeftType lhs, RightType rhs, OrderingType expect
}
#endif
- if constexpr (!std::is_same_v<LeftType, RightType>) {
CHECK_RUNTIME_CREF(CHECK_RUNTIME_LR, rhs, lhs, ==,
!expectedUnordered && expectedEqual);
CHECK_RUNTIME_CREF(CHECK_RUNTIME_LR, rhs, lhs, !=,
@@ -319,7 +318,6 @@ void testAllComparisonOperators(LeftType lhs, RightType rhs, OrderingType expect
!expectedUnordered && (expectedEqual || expectedLess));
}
#endif
- }
}
#ifdef __cpp_lib_three_way_comparison
diff --git a/src/testlib/qsignalspy.cpp b/src/testlib/qsignalspy.cpp
index 5fd61d42dc..116ce87c3e 100644
--- a/src/testlib/qsignalspy.cpp
+++ b/src/testlib/qsignalspy.cpp
@@ -110,10 +110,6 @@ QT_BEGIN_NAMESPACE
Returns the normalized signal the spy is currently listening to.
*/
-/*! \fn int QSignalSpy::qt_metacall(QMetaObject::Call call, int id, void **a)
- \internal
-*/
-
/*! \fn bool QSignalSpy::wait(int timeout)
\since 5.0
@@ -220,7 +216,7 @@ QSignalSpy::ObjectSignal QSignalSpy::verify(const QObject *obj, QMetaMethod sign
return {};
}
-QList<int> QSignalSpy::makeArgs(const QMetaMethod &member, const QObject *obj)
+static QList<int> makeArgs(QMetaMethod member, const QObject *obj)
{
QList<int> result;
result.reserve(member.parameterCount());
@@ -244,22 +240,41 @@ QList<int> QSignalSpy::makeArgs(const QMetaMethod &member, const QObject *obj)
return result;
}
-void QSignalSpy::init(ObjectSignal os)
+class QSignalSpyPrivate : public QObject
+{
+ QSignalSpy * const q;
+public:
+ explicit QSignalSpyPrivate(QSignalSpy *qq) : q(qq) {}
+
+ int qt_metacall(QMetaObject::Call call, int methodId, void **a) override;
+};
+
+QSignalSpy::QSignalSpy(ObjectSignal os)
+ : sig(os.sig.methodSignature()),
+ args(os.obj ? makeArgs(os.sig, os.obj) : QList<int>{})
{
if (!os.obj)
return;
+ auto i = std::make_unique<QSignalSpyPrivate>(this);
+
const auto signalIndex = os.sig.methodIndex();
const auto slotIndex = QObject::staticMetaObject.methodCount();
if (!QMetaObject::connect(os.obj, signalIndex,
- this, slotIndex, Qt::DirectConnection)) {
+ i.get(), slotIndex, Qt::DirectConnection)) {
qWarning("QSignalSpy: QMetaObject::connect returned false. Unable to connect.");
return;
}
- sig = os.sig.methodSignature();
+ d_ptr = std::move(i);
}
+/*!
+ Destructor.
+*/
+QSignalSpy::~QSignalSpy()
+ = default;
+
void QSignalSpy::appendArgs(void **a)
{
QList<QVariant> list;
@@ -280,4 +295,23 @@ void QSignalSpy::appendArgs(void **a)
}
}
+/*!
+ \reimp
+ \internal
+*/
+int QSignalSpyPrivate::qt_metacall(QMetaObject::Call call, int methodId, void **a)
+{
+ methodId = QObject::qt_metacall(call, methodId, a);
+ if (methodId < 0)
+ return methodId;
+
+ if (call == QMetaObject::InvokeMetaMethod) {
+ if (methodId == 0) {
+ q->appendArgs(a);
+ }
+ --methodId;
+ }
+ return methodId;
+}
+
QT_END_NAMESPACE
diff --git a/src/testlib/qsignalspy.h b/src/testlib/qsignalspy.h
index f4c322ae71..b8df2a4deb 100644
--- a/src/testlib/qsignalspy.h
+++ b/src/testlib/qsignalspy.h
@@ -6,24 +6,26 @@
#include <QtCore/qbytearray.h>
#include <QtCore/qlist.h>
-#include <QtCore/qobject.h>
#include <QtCore/qmetaobject.h>
#include <QtTest/qtesteventloop.h>
#include <QtCore/qvariant.h>
#include <QtCore/qmutex.h>
+#include <memory>
+
QT_BEGIN_NAMESPACE
class QVariant;
-
-class QSignalSpy: public QObject, public QList<QList<QVariant> >
+class QSignalSpyPrivate;
+class QSignalSpy : public QList<QList<QVariant> >
{
struct ObjectSignal {
const QObject *obj;
QMetaMethod sig;
};
-
+ friend class QSignalSpyPrivate;
+ std::unique_ptr<QSignalSpyPrivate> d_ptr;
public:
explicit QSignalSpy(const QObject *obj, const char *aSignal)
: QSignalSpy(verify(obj, aSignal)) {}
@@ -37,8 +39,9 @@ public:
#endif // Q_QDOC
QSignalSpy(const QObject *obj, QMetaMethod signal)
: QSignalSpy(verify(obj, signal)) {}
+ Q_TESTLIB_EXPORT ~QSignalSpy();
- inline bool isValid() const { return !sig.isEmpty(); }
+ bool isValid() const noexcept { return d_ptr != nullptr; }
inline QByteArray signal() const { return sig; }
bool wait(int timeout)
@@ -46,37 +49,16 @@ public:
Q_TESTLIB_EXPORT bool wait(std::chrono::milliseconds timeout = std::chrono::seconds{5});
- int qt_metacall(QMetaObject::Call call, int methodId, void **a) override
- {
- methodId = QObject::qt_metacall(call, methodId, a);
- if (methodId < 0)
- return methodId;
-
- if (call == QMetaObject::InvokeMetaMethod) {
- if (methodId == 0) {
- appendArgs(a);
- }
- --methodId;
- }
- return methodId;
- }
-
private:
- explicit QSignalSpy(ObjectSignal os)
- : args(os.obj ? makeArgs(os.sig, os.obj) : QList<int>{})
- {
- init(os);
- }
- Q_TESTLIB_EXPORT void init(ObjectSignal os);
+ Q_TESTLIB_EXPORT explicit QSignalSpy(ObjectSignal os);
Q_TESTLIB_EXPORT static ObjectSignal verify(const QObject *obj, QMetaMethod signal);
Q_TESTLIB_EXPORT static ObjectSignal verify(const QObject *obj, const char *aSignal);
- Q_TESTLIB_EXPORT static QList<int> makeArgs(const QMetaMethod &member, const QObject *obj);
Q_TESTLIB_EXPORT void appendArgs(void **a);
// the full, normalized signal name
- QByteArray sig;
+ const QByteArray sig;
// holds the QMetaType types for the argument list of the signal
const QList<int> args;
diff --git a/src/testlib/qtestcase.cpp b/src/testlib/qtestcase.cpp
index 2ab55cac36..5648fedd63 100644
--- a/src/testlib/qtestcase.cpp
+++ b/src/testlib/qtestcase.cpp
@@ -2202,6 +2202,19 @@ void QTest::ignoreMessage(QtMsgType type, const QRegularExpression &messagePatte
#endif // QT_CONFIG(regularexpression)
/*!
+ \since 6.8
+ \overload failOnWarning()
+
+ Appends a test failure to the test log if any warning is output.
+
+ \sa failOnWarning()
+*/
+void QTest::failOnWarning()
+{
+ return QTestLog::failOnWarning();
+}
+
+/*!
\since 6.3
\overload failOnWarning()
@@ -2247,7 +2260,17 @@ void QTest::failOnWarning(const char *message)
\code
void FileTest::init()
{
- QTest::failOnWarning(QRegularExpression(".?"));
+ QTest::failOnWarning(
+ QRegularExpression("QFile::.*: File(.*) already open"));
+ }
+ \endcode
+
+ For the common case of failing on \e any warning pass no parameter:
+
+ \code
+ void FileTest::init()
+ {
+ QTest::failOnWarning();
}
\endcode
diff --git a/src/testlib/qtestcase.h b/src/testlib/qtestcase.h
index 0ae1a787e2..a855ace6a9 100644
--- a/src/testlib/qtestcase.h
+++ b/src/testlib/qtestcase.h
@@ -435,6 +435,7 @@ namespace QTest
#if QT_CONFIG(regularexpression)
Q_TESTLIB_EXPORT void ignoreMessage(QtMsgType type, const QRegularExpression &messagePattern);
#endif
+ Q_TESTLIB_EXPORT void failOnWarning();
Q_TESTLIB_EXPORT void failOnWarning(const char *message);
#if QT_CONFIG(regularexpression)
Q_TESTLIB_EXPORT void failOnWarning(const QRegularExpression &messagePattern);
diff --git a/src/testlib/qtestlog.cpp b/src/testlib/qtestlog.cpp
index 4b6df54e91..929ccb370b 100644
--- a/src/testlib/qtestlog.cpp
+++ b/src/testlib/qtestlog.cpp
@@ -629,6 +629,11 @@ void QTestLog::ignoreMessage(QtMsgType type, const QRegularExpression &expressio
}
#endif // QT_CONFIG(regularexpression)
+void QTestLog::failOnWarning()
+{
+ QTest::failOnWarningList.push_back({});
+}
+
void QTestLog::failOnWarning(const char *msg)
{
QTest::failOnWarningList.push_back(QString::fromUtf8(msg));
diff --git a/src/testlib/qtestlog_p.h b/src/testlib/qtestlog_p.h
index f9bbfa158d..ab5fb593d4 100644
--- a/src/testlib/qtestlog_p.h
+++ b/src/testlib/qtestlog_p.h
@@ -71,6 +71,7 @@ public:
#ifndef QT_NO_REGULAREXPRESSION
static void ignoreMessage(QtMsgType type, const QRegularExpression &expression);
#endif
+ static void failOnWarning();
static void failOnWarning(const char *msg);
#ifndef QT_NO_REGULAREXPRESSION
static void failOnWarning(const QRegularExpression &expression);
diff --git a/src/tools/androiddeployqt/main.cpp b/src/tools/androiddeployqt/main.cpp
index 6125b405b5..da5c2c8c77 100644
--- a/src/tools/androiddeployqt/main.cpp
+++ b/src/tools/androiddeployqt/main.cpp
@@ -44,15 +44,16 @@ static const bool mustReadOutputAnyway = true; // pclose seems to return the wro
static QStringList dependenciesForDepfile;
-FILE *openProcess(const QString &command)
+auto openProcess(const QString &command)
{
#if defined(Q_OS_WIN32)
QString processedCommand = u'\"' + command + u'\"';
#else
const QString& processedCommand = command;
#endif
-
- return popen(processedCommand.toLocal8Bit().constData(), QT_POPEN_READ);
+ struct Closer { void operator()(FILE *proc) const { if (proc) (void)pclose(proc); } };
+ using UP = std::unique_ptr<FILE, Closer>;
+ return UP{popen(processedCommand.toLocal8Bit().constData(), QT_POPEN_READ)};
}
struct QtDependency
@@ -104,6 +105,7 @@ struct Options
, installApk(false)
, uninstallApk(false)
, qmlImportScannerBinaryPath()
+ , buildAar(false)
{}
enum DeploymentMechanism
@@ -239,6 +241,7 @@ struct Options
// Override qml import scanner path
QString qmlImportScannerBinaryPath;
bool qmlSkipImportScanning = false;
+ bool buildAar;
};
static const QHash<QByteArray, QByteArray> elfArchitectures = {
@@ -310,23 +313,21 @@ QString fileArchitecture(const Options &options, const QString &path)
readElf = "%1 --needed-libs %2"_L1.arg(shellQuote(readElf), shellQuote(path));
- FILE *readElfCommand = openProcess(readElf);
+ auto readElfCommand = openProcess(readElf);
if (!readElfCommand) {
fprintf(stderr, "Cannot execute command %s\n", qPrintable(readElf));
return {};
}
char buffer[512];
- while (fgets(buffer, sizeof(buffer), readElfCommand) != nullptr) {
+ while (fgets(buffer, sizeof(buffer), readElfCommand.get()) != nullptr) {
QByteArray line = QByteArray::fromRawData(buffer, qstrlen(buffer));
line = line.trimmed();
if (line.startsWith("Arch: ")) {
auto it = elfArchitectures.find(line.mid(6));
- pclose(readElfCommand);
return it != elfArchitectures.constEnd() ? QString::fromLatin1(it.value()) : QString{};
}
}
- pclose(readElfCommand);
return {};
}
@@ -527,6 +528,8 @@ Options parseOptions()
options.protectedAuthenticationPath = true;
} else if (argument.compare("--aux-mode"_L1, Qt::CaseInsensitive) == 0) {
options.auxMode = true;
+ } else if (argument.compare("--build-aar"_L1, Qt::CaseInsensitive) == 0) {
+ options.buildAar = true;
} else if (argument.compare("--qml-importscanner-binary"_L1, Qt::CaseInsensitive) == 0) {
options.qmlImportScannerBinaryPath = arguments.at(++i).trimmed();
} else if (argument.compare("--no-rcc-bundle-cleanup"_L1,
@@ -538,6 +541,23 @@ Options parseOptions()
}
}
+ if (options.buildAar) {
+ if (options.installApk || options.uninstallApk) {
+ fprintf(stderr, "Warning: Skipping %s, AAR packages are not installable.\n",
+ options.uninstallApk ? "--reinstall" : "--install");
+ options.installApk = false;
+ options.uninstallApk = false;
+ }
+ if (options.buildAAB) {
+ fprintf(stderr, "Warning: Skipping -aab as --build-aar is present.\n");
+ options.buildAAB = false;
+ }
+ if (!options.keyStore.isEmpty()) {
+ fprintf(stderr, "Warning: Skipping --sign, signing AAR packages is not supported.\n");
+ options.keyStore.clear();
+ }
+ }
+
if (options.buildDirectory.isEmpty() && !options.depFilePath.isEmpty())
options.helpRequested = true;
@@ -645,6 +665,9 @@ Optional arguments:
--apk <path/where/to/copy/the/apk>: Path where to copy the built apk.
+ --build-aar: Build an AAR package. This option skips --aab, --install,
+ --reinstall, and --sign options if they are provided.
+
--qml-importscanner-binary <path/to/qmlimportscanner>: Override the
default qmlimportscanner binary path. By default the
qmlimportscanner binary is located using the Qt directory
@@ -732,18 +755,72 @@ bool copyFileIfNewer(const QString &sourceFileName,
return true;
}
-QString cleanPackageName(QString packageName)
+struct GradleBuildConfigs {
+ QString appNamespace;
+ bool setsLegacyPackaging = false;
+ bool usesIntegerCompileSdkVersion = false;
+};
+
+GradleBuildConfigs gradleBuildConfigs(const QString &path)
+{
+ GradleBuildConfigs configs;
+
+ QFile file(path);
+ if (!file.open(QIODevice::ReadOnly))
+ return configs;
+
+ auto isComment = [](const QByteArray &trimmed) {
+ return trimmed.startsWith("//") || trimmed.startsWith('*') || trimmed.startsWith("/*");
+ };
+
+ auto extractValue = [](const QByteArray &trimmed) {
+ int idx = trimmed.indexOf('=');
+
+ if (idx == -1)
+ idx = trimmed.indexOf(' ');
+
+ if (idx > -1)
+ return trimmed.mid(idx + 1).trimmed();
+
+ return QByteArray();
+ };
+
+ const auto lines = file.readAll().split('\n');
+ for (const auto &line : lines) {
+ const QByteArray trimmedLine = line.trimmed();
+ if (isComment(trimmedLine))
+ continue;
+ if (trimmedLine.contains("useLegacyPackaging")) {
+ configs.setsLegacyPackaging = true;
+ } else if (trimmedLine.contains("compileSdkVersion androidCompileSdkVersion.toInteger()")) {
+ configs.usesIntegerCompileSdkVersion = true;
+ } else if (trimmedLine.contains("namespace")) {
+ configs.appNamespace = QString::fromUtf8(extractValue(trimmedLine));
+ }
+ }
+
+ return configs;
+}
+
+QString cleanPackageName(QString packageName, bool *cleaned = nullptr)
{
auto isLegalChar = [] (QChar c) -> bool {
ushort ch = c.unicode();
return (ch >= '0' && ch <= '9') ||
(ch >= 'A' && ch <= 'Z') ||
(ch >= 'a' && ch <= 'z') ||
- ch == '.';
+ ch == '.' || ch == '_';
};
+
+ if (cleaned)
+ *cleaned = false;
+
for (QChar &c : packageName) {
- if (!isLegalChar(c))
+ if (!isLegalChar(c)) {
c = u'_';
+ if (cleaned)
+ *cleaned = true;
+ }
}
static QStringList keywords;
@@ -778,12 +855,16 @@ QString cleanPackageName(QString packageName)
QChar c = word[0];
if ((c >= u'0' && c <= u'9') || c == u'_') {
packageName.insert(index + 1, u'a');
+ if (cleaned)
+ *cleaned = true;
index = next + 1;
continue;
}
}
if (keywords.contains(word)) {
packageName.insert(next, "_"_L1);
+ if (cleaned)
+ *cleaned = true;
index = next + 1;
} else {
index = next;
@@ -813,18 +894,31 @@ QString detectLatestAndroidPlatform(const QString &sdkPath)
return latestPlatform.baseName();
}
-QString packageNameFromAndroidManifest(const QString &androidManifestPath)
+QString extractPackageName(Options *options)
{
- QFile androidManifestXml(androidManifestPath);
+ {
+ const QString gradleBuildFile = options->androidSourceDirectory + "/build.gradle"_L1;
+ QString packageName = gradleBuildConfigs(gradleBuildFile).appNamespace;
+
+ if (!packageName.isEmpty() && packageName != "androidPackageName"_L1)
+ return packageName;
+ }
+
+ QFile androidManifestXml(options->androidSourceDirectory + "/AndroidManifest.xml"_L1);
if (androidManifestXml.open(QIODevice::ReadOnly)) {
QXmlStreamReader reader(&androidManifestXml);
while (!reader.atEnd()) {
reader.readNext();
- if (reader.isStartElement() && reader.name() == "manifest"_L1)
- return cleanPackageName(reader.attributes().value("package"_L1).toString());
+ if (reader.isStartElement() && reader.name() == "manifest"_L1) {
+ QString packageName = reader.attributes().value("package"_L1).toString();
+ if (!packageName.isEmpty() && packageName != "org.qtproject.example"_L1)
+ return packageName;
+ break;
+ }
}
}
- return {};
+
+ return QString();
}
bool parseCmakeBoolean(const QJsonValue &value)
@@ -1222,6 +1316,24 @@ bool readInputFile(Options *options)
}
{
+ const QJsonValue androidPackageName = jsonObject.value("android-package-name"_L1);
+ const QString extractedPackageName = extractPackageName(options);
+ if (!extractedPackageName.isEmpty())
+ options->packageName = extractedPackageName;
+ else if (!androidPackageName.isUndefined())
+ options->packageName = androidPackageName.toString();
+ else
+ options->packageName = "org.qtproject.example.%1"_L1.arg(options->applicationBinary);
+
+ bool cleaned;
+ options->packageName = cleanPackageName(options->packageName, &cleaned);
+ if (cleaned) {
+ fprintf(stderr, "Warning: Package name contained illegal characters and was cleaned "
+ "to \"%s\"\n", qPrintable(options->packageName));
+ }
+ }
+
+ {
using ItFlag = QDirListing::IteratorFlag;
const QJsonValue deploymentDependencies = jsonObject.value("deployment-dependencies"_L1);
if (!deploymentDependencies.isUndefined()) {
@@ -1278,9 +1390,6 @@ bool readInputFile(Options *options)
options->isZstdCompressionEnabled = zstdCompressionFlag.toBool();
}
}
- options->packageName = packageNameFromAndroidManifest(options->androidSourceDirectory + "/AndroidManifest.xml"_L1);
- if (options->packageName.isEmpty())
- options->packageName = cleanPackageName("org.qtproject.example.%1"_L1.arg(options->applicationBinary));
return true;
}
@@ -1380,6 +1489,9 @@ bool copyAndroidTemplate(const Options &options)
if (!copyAndroidTemplate(options, "/src/android/templates"_L1))
return false;
+ if (options.buildAar)
+ return copyAndroidTemplate(options, "/src/android/templates_aar"_L1);
+
return true;
}
@@ -1735,13 +1847,7 @@ bool updateAndroidManifest(Options &options)
reader.readNext();
if (reader.isStartElement()) {
- if (reader.name() == "manifest"_L1) {
- if (!reader.attributes().hasAttribute("package"_L1)) {
- fprintf(stderr, "Invalid android manifest file: %s\n", qPrintable(androidManifestPath));
- return false;
- }
- options.packageName = reader.attributes().value("package"_L1).toString();
- } else if (reader.name() == "uses-sdk"_L1) {
+ if (reader.name() == "uses-sdk"_L1) {
if (reader.attributes().hasAttribute("android:minSdkVersion"_L1))
if (reader.attributes().value("android:minSdkVersion"_L1).toInt() < 23) {
fprintf(stderr, "Invalid minSdkVersion version, minSdkVersion must be >= 23\n");
@@ -2014,7 +2120,7 @@ QStringList getQtLibsFromElf(const Options &options, const QString &fileName)
readElf = "%1 --needed-libs %2"_L1.arg(shellQuote(readElf), shellQuote(fileName));
- FILE *readElfCommand = openProcess(readElf);
+ auto readElfCommand = openProcess(readElf);
if (!readElfCommand) {
fprintf(stderr, "Cannot execute command %s\n", qPrintable(readElf));
return QStringList();
@@ -2024,7 +2130,7 @@ QStringList getQtLibsFromElf(const Options &options, const QString &fileName)
bool readLibs = false;
char buffer[512];
- while (fgets(buffer, sizeof(buffer), readElfCommand) != nullptr) {
+ while (fgets(buffer, sizeof(buffer), readElfCommand.get()) != nullptr) {
QByteArray line = QByteArray::fromRawData(buffer, qstrlen(buffer));
QString library;
line = line.trimmed();
@@ -2034,7 +2140,6 @@ QStringList getQtLibsFromElf(const Options &options, const QString &fileName)
if (it == elfArchitectures.constEnd() || *it != options.currentArchitecture.toLatin1()) {
if (options.verbose)
fprintf(stdout, "Skipping \"%s\", architecture mismatch\n", qPrintable(fileName));
- pclose(readElfCommand);
return {};
}
}
@@ -2049,8 +2154,6 @@ QStringList getQtLibsFromElf(const Options &options, const QString &fileName)
ret += libraryName;
}
- pclose(readElfCommand);
-
return ret;
}
@@ -2188,7 +2291,7 @@ bool scanImports(Options *options, QSet<QString> *usedDependencies)
qmlImportScanner.toLocal8Bit().constData());
}
- FILE *qmlImportScannerCommand = popen(qmlImportScanner.toLocal8Bit().constData(), QT_POPEN_READ);
+ auto qmlImportScannerCommand = openProcess(qmlImportScanner);
if (qmlImportScannerCommand == 0) {
fprintf(stderr, "Couldn't run qmlimportscanner.\n");
return false;
@@ -2196,13 +2299,12 @@ bool scanImports(Options *options, QSet<QString> *usedDependencies)
QByteArray output;
char buffer[512];
- while (fgets(buffer, sizeof(buffer), qmlImportScannerCommand) != 0)
+ while (fgets(buffer, sizeof(buffer), qmlImportScannerCommand.get()) != nullptr)
output += QByteArray(buffer, qstrlen(buffer));
QJsonDocument jsonDocument = QJsonDocument::fromJson(output);
if (jsonDocument.isNull()) {
fprintf(stderr, "Invalid json output from qmlimportscanner.\n");
- pclose(qmlImportScannerCommand);
return false;
}
@@ -2211,7 +2313,6 @@ bool scanImports(Options *options, QSet<QString> *usedDependencies)
QJsonValue value = jsonArray.at(i);
if (!value.isObject()) {
fprintf(stderr, "Invalid format of qmlimportscanner output.\n");
- pclose(qmlImportScannerCommand);
return false;
}
@@ -2257,7 +2358,6 @@ bool scanImports(Options *options, QSet<QString> *usedDependencies)
if (importPathOfThisImport.isEmpty()) {
fprintf(stderr, "Import found outside of import paths: %s.\n", qPrintable(info.absoluteFilePath()));
- pclose(qmlImportScannerCommand);
return false;
}
@@ -2325,7 +2425,6 @@ bool scanImports(Options *options, QSet<QString> *usedDependencies)
}
}
- pclose(qmlImportScannerCommand);
return true;
}
@@ -2344,17 +2443,17 @@ bool runCommand(const Options &options, const QString &command)
if (options.verbose)
fprintf(stdout, "Running command '%s'\n", qPrintable(command));
- FILE *runCommand = openProcess(command);
+ auto runCommand = openProcess(command);
if (runCommand == nullptr) {
fprintf(stderr, "Cannot run command '%s'\n", qPrintable(command));
return false;
}
char buffer[4096];
- while (fgets(buffer, sizeof(buffer), runCommand) != nullptr) {
+ while (fgets(buffer, sizeof(buffer), runCommand.get()) != nullptr) {
if (options.verbose)
fprintf(stdout, "%s", buffer);
}
- pclose(runCommand);
+ runCommand.reset();
fflush(stdout);
fflush(stderr);
return true;
@@ -2505,7 +2604,8 @@ bool containsApplicationBinary(Options *options)
return true;
}
-FILE *runAdb(const Options &options, const QString &arguments)
+auto runAdb(const Options &options, const QString &arguments)
+ -> decltype(openProcess({}))
{
QString adb = execSuffixAppended(options.sdkPath + "/platform-tools/adb"_L1);
if (!QFile::exists(adb)) {
@@ -2521,7 +2621,7 @@ FILE *runAdb(const Options &options, const QString &arguments)
if (options.verbose)
fprintf(stdout, "Running command \"%s\"\n", adb.toLocal8Bit().constData());
- FILE *adbCommand = openProcess(adb);
+ auto adbCommand = openProcess(adb);
if (adbCommand == 0) {
fprintf(stderr, "Cannot start adb: %s\n", qPrintable(adb));
return 0;
@@ -2745,38 +2845,6 @@ void checkAndWarnGradleLongPaths(const QString &outputDirectory)
}
#endif
-struct GradleFlags {
- bool setsLegacyPackaging = false;
- bool usesIntegerCompileSdkVersion = false;
-};
-
-GradleFlags gradleBuildFlags(const QString &path)
-{
- GradleFlags flags;
-
- QFile file(path);
- if (!file.open(QIODevice::ReadOnly))
- return flags;
-
- auto isComment = [](const QByteArray &line) {
- const auto trimmed = line.trimmed();
- return trimmed.startsWith("//") || trimmed.startsWith('*') || trimmed.startsWith("/*");
- };
-
- const auto lines = file.readAll().split('\n');
- for (const auto &line : lines) {
- if (isComment(line))
- continue;
- if (line.contains("useLegacyPackaging")) {
- flags.setsLegacyPackaging = true;
- } else if (line.contains("compileSdkVersion androidCompileSdkVersion.toInteger()")) {
- flags.usesIntegerCompileSdkVersion = true;
- }
- }
-
- return flags;
-}
-
bool buildAndroidProject(const Options &options)
{
GradleProperties localProperties;
@@ -2789,8 +2857,8 @@ bool buildAndroidProject(const Options &options)
GradleProperties gradleProperties = readGradleProperties(gradlePropertiesPath);
const QString gradleBuildFilePath = options.outputDirectory + "build.gradle"_L1;
- GradleFlags gradleFlags = gradleBuildFlags(gradleBuildFilePath);
- if (!gradleFlags.setsLegacyPackaging)
+ GradleBuildConfigs gradleConfigs = gradleBuildConfigs(gradleBuildFilePath);
+ if (!gradleConfigs.setsLegacyPackaging)
gradleProperties["android.bundle.enableUncompressedNativeLibs"] = "false";
gradleProperties["buildDir"] = "build";
@@ -2809,7 +2877,7 @@ bool buildAndroidProject(const Options &options)
QByteArray sdkPlatformVersion;
// Provide the integer version only if build.gradle explicitly converts to Integer,
// to avoid regression to existing projects that build for sdk platform of form android-xx.
- if (gradleFlags.usesIntegerCompileSdkVersion) {
+ if (gradleConfigs.usesIntegerCompileSdkVersion) {
const QByteArray tmp = options.androidPlatform.split(u'-').last().toLocal8Bit();
bool ok;
tmp.toInt(&ok);
@@ -2824,6 +2892,7 @@ bool buildAndroidProject(const Options &options)
if (sdkPlatformVersion.isEmpty())
sdkPlatformVersion = options.androidPlatform.toLocal8Bit();
+ gradleProperties["androidPackageName"] = options.packageName.toLocal8Bit();
gradleProperties["androidCompileSdkVersion"] = sdkPlatformVersion;
gradleProperties["qtMinSdkVersion"] = options.minSdkVersion;
gradleProperties["qtTargetSdkVersion"] = options.targetSdkVersion;
@@ -2839,6 +2908,9 @@ bool buildAndroidProject(const Options &options)
abiList.append(it.key());
}
gradleProperties["qtTargetAbiList"] = abiList.toLocal8Bit();// armeabi-v7a or arm64-v8a or ...
+ gradleProperties["qtGradlePluginType"] = options.buildAar
+ ? "com.android.library"
+ : "com.android.application";
if (!mergeGradleProperties(gradlePropertiesPath, gradleProperties))
return false;
@@ -2864,19 +2936,19 @@ bool buildAndroidProject(const Options &options)
if (options.verbose)
commandLine += " --info"_L1;
- FILE *gradleCommand = openProcess(commandLine);
+ auto gradleCommand = openProcess(commandLine);
if (gradleCommand == 0) {
fprintf(stderr, "Cannot run gradle command: %s\n.", qPrintable(commandLine));
return false;
}
char buffer[512];
- while (fgets(buffer, sizeof(buffer), gradleCommand) != 0) {
+ while (fgets(buffer, sizeof(buffer), gradleCommand.get()) != nullptr) {
fprintf(stdout, "%s", buffer);
fflush(stdout);
}
- int errorCode = pclose(gradleCommand);
+ const int errorCode = pclose(gradleCommand.release());
if (errorCode != 0) {
fprintf(stderr, "Building the android package failed!\n");
if (!options.verbose)
@@ -2902,18 +2974,18 @@ bool uninstallApk(const Options &options)
fprintf(stdout, "Uninstalling old Android package %s if present.\n", qPrintable(options.packageName));
- FILE *adbCommand = runAdb(options, " uninstall "_L1 + shellQuote(options.packageName));
+ auto adbCommand = runAdb(options, " uninstall "_L1 + shellQuote(options.packageName));
if (adbCommand == 0)
return false;
if (options.verbose || mustReadOutputAnyway) {
char buffer[512];
- while (fgets(buffer, sizeof(buffer), adbCommand) != 0)
+ while (fgets(buffer, sizeof(buffer), adbCommand.get()) != nullptr)
if (options.verbose)
fprintf(stdout, "%s", buffer);
}
- int returnCode = pclose(adbCommand);
+ const int returnCode = pclose(adbCommand.release());
if (returnCode != 0) {
fprintf(stderr, "Warning: Uninstall failed!\n");
if (!options.verbose)
@@ -2926,39 +2998,43 @@ bool uninstallApk(const Options &options)
enum PackageType {
AAB,
+ AAR,
UnsignedAPK,
SignedAPK
};
-QString packagePath(const Options &options, PackageType pt)
-{
- QString path(options.outputDirectory);
- path += "/build/outputs/%1/"_L1.arg(pt >= UnsignedAPK ? QStringLiteral("apk") : QStringLiteral("bundle"));
- QString buildType(options.releasePackage ? "release/"_L1 : "debug/"_L1);
- if (QDir(path + buildType).exists())
- path += buildType;
- path += QDir(options.outputDirectory).dirName() + u'-';
- if (options.releasePackage) {
- path += "release-"_L1;
- if (pt >= UnsignedAPK) {
- if (pt == UnsignedAPK)
- path += "un"_L1;
- path += "signed.apk"_L1;
- } else {
- path.chop(1);
- path += ".aab"_L1;
- }
- } else {
- path += "debug"_L1;
- if (pt >= UnsignedAPK) {
- if (pt == SignedAPK)
- path += "-signed"_L1;
- path += ".apk"_L1;
- } else {
- path += ".aab"_L1;
- }
- }
- return path;
+QString packagePath(const Options &options, PackageType packageType)
+{
+ // The package type is always AAR if option.buildAar has been set
+ if (options.buildAar)
+ packageType = AAR;
+
+ static const QHash<PackageType, QLatin1StringView> packageTypeToPath{
+ { AAB, "bundle"_L1 }, { AAR, "aar"_L1 }, { UnsignedAPK, "apk"_L1 }, { SignedAPK, "apk"_L1 }
+ };
+ static const QHash<PackageType, QLatin1StringView> packageTypeToExtension{
+ { AAB, "aab"_L1 }, { AAR, "aar"_L1 }, { UnsignedAPK, "apk"_L1 }, { SignedAPK, "apk"_L1 }
+ };
+
+ const QString buildType(options.releasePackage ? "release"_L1 : "debug"_L1);
+ QString signedSuffix;
+ if (packageType == SignedAPK)
+ signedSuffix = "-signed"_L1;
+ else if (packageType == UnsignedAPK && options.releasePackage)
+ signedSuffix = "-unsigned"_L1;
+
+ QString dirPath(options.outputDirectory);
+ dirPath += "/build/outputs/%1/"_L1.arg(packageTypeToPath[packageType]);
+ if (QDir(dirPath + buildType).exists())
+ dirPath += buildType;
+
+ const QString fileName = "/%1-%2%3.%4"_L1.arg(
+ QDir(options.outputDirectory).dirName(),
+ buildType,
+ signedSuffix,
+ packageTypeToExtension[packageType]);
+
+ return dirPath + fileName;
}
bool installApk(const Options &options)
@@ -2971,20 +3047,20 @@ bool installApk(const Options &options)
if (options.verbose)
fprintf(stdout, "Installing Android package to device.\n");
- FILE *adbCommand = runAdb(options, " install -r "_L1
- + packagePath(options, options.keyStore.isEmpty() ? UnsignedAPK
- : SignedAPK));
+ auto adbCommand = runAdb(options, " install -r "_L1
+ + packagePath(options, options.keyStore.isEmpty() ? UnsignedAPK
+ : SignedAPK));
if (adbCommand == 0)
return false;
if (options.verbose || mustReadOutputAnyway) {
char buffer[512];
- while (fgets(buffer, sizeof(buffer), adbCommand) != 0)
+ while (fgets(buffer, sizeof(buffer), adbCommand.get()) != nullptr)
if (options.verbose)
fprintf(stdout, "%s", buffer);
}
- int returnCode = pclose(adbCommand);
+ const int returnCode = pclose(adbCommand.release());
if (returnCode != 0) {
fprintf(stderr, "Installing to device failed!\n");
if (!options.verbose)
@@ -3102,7 +3178,7 @@ bool signAAB(const Options &options)
QString command = jarSignerTool + " %1 %2"_L1.arg(shellQuote(file))
.arg(shellQuote(options.keyStoreAlias));
- FILE *jarSignerCommand = openProcess(command);
+ auto jarSignerCommand = openProcess(command);
if (jarSignerCommand == 0) {
fprintf(stderr, "Couldn't run jarsigner.\n");
return false;
@@ -3110,11 +3186,11 @@ bool signAAB(const Options &options)
if (options.verbose) {
char buffer[512];
- while (fgets(buffer, sizeof(buffer), jarSignerCommand) != 0)
+ while (fgets(buffer, sizeof(buffer), jarSignerCommand.get()) != nullptr)
fprintf(stdout, "%s", buffer);
}
- int errorCode = pclose(jarSignerCommand);
+ const int errorCode = pclose(jarSignerCommand.release());
if (errorCode != 0) {
fprintf(stderr, "jarsigner command failed.\n");
if (!options.verbose)
@@ -3142,17 +3218,17 @@ bool signPackage(const Options &options)
return false;
auto zipalignRunner = [](const QString &zipAlignCommandLine) {
- FILE *zipAlignCommand = openProcess(zipAlignCommandLine);
+ auto zipAlignCommand = openProcess(zipAlignCommandLine);
if (zipAlignCommand == 0) {
fprintf(stderr, "Couldn't run zipalign.\n");
return false;
}
char buffer[512];
- while (fgets(buffer, sizeof(buffer), zipAlignCommand) != 0)
+ while (fgets(buffer, sizeof(buffer), zipAlignCommand.get()) != nullptr)
fprintf(stdout, "%s", buffer);
- return pclose(zipAlignCommand) == 0;
+ return pclose(zipAlignCommand.release()) == 0;
};
const QString verifyZipAlignCommandLine =
@@ -3209,17 +3285,17 @@ bool signPackage(const Options &options)
apkSignCommand += " %1"_L1.arg(shellQuote(packagePath(options, SignedAPK)));
auto apkSignerRunner = [](const QString &command, bool verbose) {
- FILE *apkSigner = openProcess(command);
+ auto apkSigner = openProcess(command);
if (apkSigner == 0) {
fprintf(stderr, "Couldn't run apksigner.\n");
return false;
}
char buffer[512];
- while (fgets(buffer, sizeof(buffer), apkSigner) != 0)
+ while (fgets(buffer, sizeof(buffer), apkSigner.get()) != nullptr)
fprintf(stdout, "%s", buffer);
- int errorCode = pclose(apkSigner);
+ const int errorCode = pclose(apkSigner.release());
if (errorCode != 0) {
fprintf(stderr, "apksigner command failed.\n");
if (!verbose)
diff --git a/src/tools/moc/generator.cpp b/src/tools/moc/generator.cpp
index 02e9ef178a..1c6604a96e 100644
--- a/src/tools/moc/generator.cpp
+++ b/src/tools/moc/generator.cpp
@@ -833,7 +833,7 @@ void Generator::generateProperties()
//
if (cdef->propertyList.size())
- fprintf(out, "\n // properties: name, type, flags\n");
+ fprintf(out, "\n // properties: name, type, flags, notifyId, revision\n");
for (const PropertyDef &p : std::as_const(cdef->propertyList)) {
uint flags = Invalid;
if (!isBuiltinType(p.type))
diff --git a/src/tools/moc/main.cpp b/src/tools/moc/main.cpp
index bb51352519..89fb367ca7 100644
--- a/src/tools/moc/main.cpp
+++ b/src/tools/moc/main.cpp
@@ -19,7 +19,8 @@
#include <qcoreapplication.h>
#include <qcommandlineoption.h>
#include <qcommandlineparser.h>
-#include <qscopedpointer.h>
+
+#include <memory>
QT_BEGIN_NAMESPACE
@@ -58,10 +59,21 @@ void error(const char *msg = "Invalid argument")
fprintf(stderr, "moc: %s\n", msg);
}
-struct ScopedPointerFileCloser
+static auto openFileForWriting(const QString &name)
{
- static inline void cleanup(FILE *handle) { if (handle) fclose(handle); }
-};
+ struct Closer { void operator()(FILE *handle) const { fclose(handle); } };
+ using R = std::unique_ptr<FILE, Closer>;
+
+#ifdef _MSC_VER
+ FILE *file;
+ if (_wfopen_s(&file, reinterpret_cast<const wchar_t *>(name.utf16()), L"w") != 0)
+ return R{};
+ return R{file};
+#else
+ return R{fopen(QFile::encodeName(name).constData(), "w")};
+#endif
+}
+using File = decltype(openFileForWriting({}));
static inline bool hasNext(const Symbols &symbols, int i)
{ return (i < symbols.size()); }
@@ -178,7 +190,7 @@ int runMoc(int argc, char **argv)
QString filename;
QString output;
QFile in;
- FILE *out = nullptr;
+ File out;
// Note that moc isn't translated.
// If you use this code as an example for a translated app, make sure to translate the strings.
@@ -528,16 +540,12 @@ int runMoc(int argc, char **argv)
// 3. and output meta object code
- QScopedPointer<FILE, ScopedPointerFileCloser> jsonOutput;
+ File jsonOutput;
bool outputToFile = true;
if (output.size()) { // output file specified
-#if defined(_MSC_VER)
- if (_wfopen_s(&out, reinterpret_cast<const wchar_t *>(output.utf16()), L"w") != 0)
-#else
- out = fopen(QFile::encodeName(output).constData(), "w"); // create output file
+ out = openFileForWriting(output);
if (!out)
-#endif
{
const auto fopen_errno = errno;
fprintf(stderr, "moc: Cannot create %s. Error: %s\n",
@@ -548,37 +556,29 @@ int runMoc(int argc, char **argv)
if (parser.isSet(jsonOption)) {
const QString jsonOutputFileName = output + ".json"_L1;
- FILE *f;
-#if defined(_MSC_VER)
- if (_wfopen_s(&f, reinterpret_cast<const wchar_t *>(jsonOutputFileName.utf16()), L"w") != 0)
-#else
- f = fopen(QFile::encodeName(jsonOutputFileName).constData(), "w");
- if (!f)
-#endif
- {
+ jsonOutput = openFileForWriting(jsonOutputFileName);
+ if (!jsonOutput) {
const auto fopen_errno = errno;
fprintf(stderr, "moc: Cannot create JSON output file %s. Error: %s\n",
QFile::encodeName(jsonOutputFileName).constData(),
strerror(fopen_errno));
}
- jsonOutput.reset(f);
}
} else { // use stdout
- out = stdout;
+ out.reset(stdout);
outputToFile = false;
}
if (pp.preprocessOnly) {
- fprintf(out, "%s\n", composePreprocessorOutput(moc.symbols).constData());
+ fprintf(out.get(), "%s\n", composePreprocessorOutput(moc.symbols).constData());
} else {
if (moc.classList.isEmpty())
moc.note("No relevant classes found. No output generated.");
else
- moc.generate(out, jsonOutput.data());
+ moc.generate(out.get(), jsonOutput.get());
}
- if (output.size())
- fclose(out);
+ out.reset();
if (parser.isSet(depFileOption)) {
// 4. write a Make-style dependency file (can also be consumed by Ninja).
@@ -596,26 +596,17 @@ int runMoc(int argc, char **argv)
fprintf(stderr, "moc: Writing to stdout, but no depfile path specified.\n");
}
- QScopedPointer<FILE, ScopedPointerFileCloser> depFileHandle;
- FILE *depFileHandleRaw;
-#if defined(_MSC_VER)
- if (_wfopen_s(&depFileHandleRaw,
- reinterpret_cast<const wchar_t *>(depOutputFileName.utf16()), L"w") != 0)
-#else
- depFileHandleRaw = fopen(QFile::encodeName(depOutputFileName).constData(), "w");
- if (!depFileHandleRaw)
-#endif
- {
+ File depFileHandle = openFileForWriting(depOutputFileName);
+ if (!depFileHandle) {
const auto fopen_errno = errno;
fprintf(stderr, "moc: Cannot create dep output file '%s'. Error: %s\n",
QFile::encodeName(depOutputFileName).constData(),
strerror(fopen_errno));
}
- depFileHandle.reset(depFileHandleRaw);
- if (!depFileHandle.isNull()) {
+ if (depFileHandle) {
// First line is the path to the generated file.
- fprintf(depFileHandle.data(), "%s: ",
+ fprintf(depFileHandle.get(), "%s: ",
escapeAndEncodeDependencyPath(depRuleName).constData());
QByteArrayList dependencies;
@@ -647,7 +638,7 @@ int runMoc(int argc, char **argv)
// Join dependencies, output them, and output a final new line.
const auto dependenciesJoined = dependencies.join(QByteArrayLiteral(" \\\n "));
- fprintf(depFileHandle.data(), "%s\n", dependenciesJoined.constData());
+ fprintf(depFileHandle.get(), "%s\n", dependenciesJoined.constData());
}
}
diff --git a/src/tools/qdbusxml2cpp/qdbusxml2cpp.cpp b/src/tools/qdbusxml2cpp/qdbusxml2cpp.cpp
index 579604286c..d637854d2b 100644
--- a/src/tools/qdbusxml2cpp/qdbusxml2cpp.cpp
+++ b/src/tools/qdbusxml2cpp/qdbusxml2cpp.cpp
@@ -276,7 +276,7 @@ QTextStream &QDBusXmlToCpp::writeHeader(QTextStream &ts, bool changesWillBeLost)
{
ts << "/*\n"
" * This file was generated by " PROGRAMNAME " version " PROGRAMVERSION "\n"
- " * Command line was: " << commandLine << "\n"
+ " * Source file was " << QFileInfo(inputFile).fileName() << "\n"
" *\n"
" * " PROGRAMNAME " is " PROGRAMCOPYRIGHT "\n"
" *\n"
diff --git a/src/tools/qlalr/cppgenerator.cpp b/src/tools/qlalr/cppgenerator.cpp
index fd56de106d..f12917f0eb 100644
--- a/src/tools/qlalr/cppgenerator.cpp
+++ b/src/tools/qlalr/cppgenerator.cpp
@@ -1,5 +1,7 @@
+// REUSE-IgnoreStart
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+// REUSE-IgnoreEnd
#include "cppgenerator.h"
@@ -39,7 +41,7 @@ void generateList(const QList<int> &list, QTextStream &out)
}
}
-
+// REUSE-IgnoreStart
QString CppGenerator::copyrightHeader() const
{
return
@@ -47,6 +49,7 @@ QString CppGenerator::copyrightHeader() const
"// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0\n"
"\n"_L1;
}
+// REUSE-IgnoreEnd
QString CppGenerator::privateCopyrightHeader() const
{
diff --git a/src/tools/rcc/rcc.cpp b/src/tools/rcc/rcc.cpp
index 444e9c4ae5..0fdba3671d 100644
--- a/src/tools/rcc/rcc.cpp
+++ b/src/tools/rcc/rcc.cpp
@@ -21,7 +21,7 @@
# include <zstd.h>
#endif
-// Note: A copy of this file is used in Qt Designer (qttools/src/designer/src/lib/shared/rcc.cpp)
+// Note: A copy of this file is used in Qt Widgets Designer (qttools/src/designer/src/lib/shared/rcc.cpp)
QT_BEGIN_NAMESPACE
@@ -321,7 +321,7 @@ qint64 RCCFileInfo::writeDataBlob(RCCResourceLibrary &lib, qint64 offset,
// some info
if (text || pass1) {
lib.writeString(" // ");
- lib.writeByteArray(m_fileInfo.absoluteFilePath().toLocal8Bit());
+ lib.writeByteArray(m_fileInfo.fileName().toLocal8Bit());
lib.writeString("\n ");
}
diff --git a/src/tools/rcc/rcc.h b/src/tools/rcc/rcc.h
index 60af1c67cf..bfc3503da8 100644
--- a/src/tools/rcc/rcc.h
+++ b/src/tools/rcc/rcc.h
@@ -2,7 +2,7 @@
// Copyright (C) 2018 Intel Corporation.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
-// Note: A copy of this file is used in Qt Designer (qttools/src/designer/src/lib/shared/rcc_p.h)
+// Note: A copy of this file is used in Qt Widgets Designer (qttools/src/designer/src/lib/shared/rcc_p.h)
#ifndef RCC_H
#define RCC_H
diff --git a/src/tools/uic/cpp/cppwriteinitialization.cpp b/src/tools/uic/cpp/cppwriteinitialization.cpp
index 205d6a50a9..c9356a4111 100644
--- a/src/tools/uic/cpp/cppwriteinitialization.cpp
+++ b/src/tools/uic/cpp/cppwriteinitialization.cpp
@@ -2155,7 +2155,7 @@ QString WriteInitialization::pixCall(const DomProperty *p) const
s = p->elementPixmap()->text();
break;
default:
- qWarning("%s: Warning: Unknown icon format encountered. The ui-file was generated with a too-recent version of Designer.",
+ qWarning("%s: Warning: Unknown icon format encountered. The ui-file was generated with a too-recent version of Qt Widgets Designer.",
qPrintable(m_option.messagePrefix()));
return "QIcon()"_L1;
break;
diff --git a/src/tools/uic/uic.cpp b/src/tools/uic/uic.cpp
index fb0a37d21d..1b10e1d722 100644
--- a/src/tools/uic/uic.cpp
+++ b/src/tools/uic/uic.cpp
@@ -165,7 +165,7 @@ DomUI *Uic::parseUiFile(QXmlStreamReader &reader)
&& !ui) {
const double version = versionFromUiAttribute(reader);
if (version < 4.0) {
- const QString msg = QString::fromLatin1("uic: File generated with too old version of Qt Designer (%1)").arg(version);
+ const QString msg = QString::fromLatin1("uic: File generated with too old version of Qt Widgets Designer (%1)").arg(version);
fprintf(stderr, "%s\n", qPrintable(msg));
return nullptr;
}
@@ -202,7 +202,7 @@ bool Uic::write(QIODevice *in)
double version = ui->attributeVersion().toDouble();
if (version < 4.0) {
- fprintf(stderr, "uic: File generated with too old version of Qt Designer\n");
+ fprintf(stderr, "uic: File generated with too old version of Qt Widgets Designer\n");
return false;
}
diff --git a/src/tools/windeployqt/main.cpp b/src/tools/windeployqt/main.cpp
index 084345a4d8..dca9132e15 100644
--- a/src/tools/windeployqt/main.cpp
+++ b/src/tools/windeployqt/main.cpp
@@ -139,8 +139,10 @@ static Platform platformFromMkSpec(const QString &xSpec)
return WindowsDesktopClangMsvc;
if (xSpec.contains("arm"_L1))
return WindowsDesktopMsvcArm;
+ if (xSpec.contains("G++"_L1))
+ return WindowsDesktopMinGW;
- return xSpec.contains("g++"_L1) ? WindowsDesktopMinGW : WindowsDesktopMsvcIntel;
+ return WindowsDesktopMsvc;
}
return UnknownPlatform;
}
@@ -1928,6 +1930,24 @@ int main(int argc, char **argv)
}
options.platform = platformFromMkSpec(xSpec);
+ // We are on MSVC and not crosscompiling. We need the host arch
+ if (options.platform == WindowsDesktopMsvc) {
+ SYSTEM_INFO si;
+ GetSystemInfo(&si);
+ switch (si.wProcessorArchitecture) {
+ case PROCESSOR_ARCHITECTURE_INTEL:
+ case PROCESSOR_ARCHITECTURE_IA64:
+ case PROCESSOR_ARCHITECTURE_AMD64:
+ options.platform |= IntelBased;
+ break;
+ case PROCESSOR_ARCHITECTURE_ARM:
+ case PROCESSOR_ARCHITECTURE_ARM64:
+ options.platform |= ArmBased;
+ break;
+ default:
+ options.platform = UnknownPlatform;
+ }
+ }
if (options.platform == UnknownPlatform) {
std::wcerr << "Unsupported platform " << xSpec << '\n';
return 1;
diff --git a/src/widgets/Qt6WidgetsMacros.cmake b/src/widgets/Qt6WidgetsMacros.cmake
index b15d1fe81d..a7ff7af2f3 100644
--- a/src/widgets/Qt6WidgetsMacros.cmake
+++ b/src/widgets/Qt6WidgetsMacros.cmake
@@ -49,3 +49,293 @@ if(NOT QT_NO_CREATE_VERSIONLESS_FUNCTIONS)
set("${outfiles}" "${${outfiles}}" PARENT_SCOPE)
endfunction()
endif()
+
+function(qt6_add_ui target)
+ if(NOT TARGET ${target})
+ message(FATAL_ERROR "Target \"${target}\" does not exist.")
+ endif()
+
+ set(options)
+ set(oneValueArgs INCLUDE_PREFIX)
+ set(multiValueArgs SOURCES OPTIONS)
+
+ cmake_parse_arguments(arg "${options}" "${oneValueArgs}" "${multiValueArgs}"
+ ${ARGN})
+
+ set(sources ${arg_SOURCES})
+ set(ui_options ${arg_OPTIONS})
+ set(raw_include_prefix "${arg_INCLUDE_PREFIX}")
+
+ if("${sources}" STREQUAL "")
+ message(FATAL_ERROR "The \"SOURCES\" parameter is empty.")
+ endif()
+
+
+ # Disable AUTOUIC for the target explicitly to avoid the AUTOUIC
+ # error message when the AUTOUIC is enabled for the target.
+ set_target_properties(${target} PROPERTIES AUTOUIC OFF)
+
+ if(NOT "${raw_include_prefix}" STREQUAL "")
+ # generate dummy `_` folders from the given path
+ # e.g. if the given path is `../a/b/c`, then the generated path will be
+ # `_`. if the given path is ``../a/../b/c`, then the generated path will
+ # be `_/_`
+ function(_qt_internal_generate_dash_path_from_input input_path
+ out_generated_dash_path)
+ string(REGEX MATCHALL "\\.\\." upper_folder_list "${input_path}")
+
+ list(JOIN upper_folder_list "" upper_folder_list)
+ string(REGEX REPLACE "\\.\\." "_/" additional_path
+ "${upper_folder_list}")
+
+ # remove last / character
+ if(NOT "${additional_path}" STREQUAL "")
+ string(LENGTH "${additional_path}" additional_path_length)
+ math(EXPR additional_path_length "${additional_path_length} - 1")
+ string(SUBSTRING "${additional_path}" 0 ${additional_path_length}
+ additional_path)
+ endif()
+
+ set(${out_generated_dash_path} "${additional_path}" PARENT_SCOPE)
+ endfunction()
+ # NOTE: If previous folders are less than ../ folder count in
+ # ${raw_include_prefix}, relative path calculation will miscalculate,
+ # so we need to add dummy folders to calculate the relative path
+ # correctly
+ _qt_internal_generate_dash_path_from_input("${raw_include_prefix}"
+ dummy_path_for_relative_calculation)
+ if(NOT "${dummy_path_for_relative_calculation}" STREQUAL "")
+ set(dummy_path_for_relative_calculation
+ "/${dummy_path_for_relative_calculation}")
+ set(raw_include_prefix_to_compare
+ "${dummy_path_for_relative_calculation}/${raw_include_prefix}")
+ else()
+ set(dummy_path_for_relative_calculation "/")
+ set(raw_include_prefix_to_compare "/${raw_include_prefix}")
+ endif()
+ # NOTE: This relative path calculation could be done just by using the
+ # below code
+ # cmake_path(RELATIVE_PATH normalized_include_prefix "${CMAKE_CURRENT_SOURCE_DIR}"
+ # but due to the backward compatibility, we need to use
+ # file(RELATIVE_PATH) and ../ folder calculation the with
+ # _qt_internal_generate_dash_path_from_input() function
+ if(WIN32)
+ set(dummy_path_for_relative_calculation
+ "${CMAKE_CURRENT_SOURCE_DIR}/${dummy_path_for_relative_calculation}")
+ set(raw_include_prefix_to_compare
+ "${CMAKE_CURRENT_SOURCE_DIR}/${raw_include_prefix_to_compare}")
+ string(REGEX REPLACE "//" "/" dummy_path_for_relative_calculation
+ "${dummy_path_for_relative_calculation}")
+ string(REGEX REPLACE "//" "/" raw_include_prefix_to_compare
+ "${raw_include_prefix_to_compare}")
+ endif()
+
+ file(RELATIVE_PATH normalized_include_prefix
+ "${dummy_path_for_relative_calculation}"
+ "${raw_include_prefix_to_compare}")
+
+ # if normalized_include_prefix ends with `/` remove it
+ string(REGEX REPLACE "/$" "" normalized_include_prefix
+ "${normalized_include_prefix}")
+
+ _qt_internal_generate_dash_path_from_input("${normalized_include_prefix}"
+ additional_path)
+ endif()
+
+ if(ui_options MATCHES "\\$<CONFIG")
+ set(is_ui_options_contains_config true)
+ endif()
+ get_property(is_multi GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG)
+ if(is_multi AND is_ui_options_contains_config)
+ set(include_folder "$<CONFIG>")
+ endif()
+
+ set(include_folder_to_add "${include_folder}")
+ if(NOT "${additional_path}" STREQUAL "")
+ set(include_folder_to_add "${include_folder_to_add}/${additional_path}")
+ endif()
+
+ set(prefix_info_file_cmake
+ "${CMAKE_CURRENT_BINARY_DIR}/${target}_autogen/prefix_info.cmake")
+ if(EXISTS ${prefix_info_file_cmake})
+ include(${prefix_info_file_cmake})
+ set(prefix_info_file_cmake_exists true)
+ else()
+ set(prefix_info_file_cmake_exists false)
+ endif()
+
+ foreach(source_file ${sources})
+ get_filename_component(outfile "${source_file}" NAME_WE)
+ get_filename_component(infile "${source_file}" ABSOLUTE)
+ string(SHA1 infile_hash "${target}${infile}")
+ string(SUBSTRING "${infile_hash}" 0 6 short_hash)
+ set(file_ui_folder "${CMAKE_CURRENT_BINARY_DIR}/.qt/${short_hash}")
+ target_include_directories(${target} SYSTEM PRIVATE
+ "${file_ui_folder}/${include_folder_to_add}")
+
+ set(target_include_folder_with_add_path
+ "${file_ui_folder}/${include_folder}")
+ # Add additional path to include folder if it is not empty
+ if(NOT "${additional_path}" STREQUAL "")
+ set(target_include_folder_with_add_path
+ "${target_include_folder_with_add_path}/${additional_path}")
+ endif()
+
+ set(inc_dir_to_create "${target_include_folder_with_add_path}")
+ if(NOT "${raw_include_prefix}" STREQUAL "")
+ set(inc_dir_to_create
+ "${target_include_folder_with_add_path}/${raw_include_prefix}")
+ endif()
+
+ set(output_directory "${target_include_folder_with_add_path}")
+ if(NOT "${normalized_include_prefix}" STREQUAL "")
+ set(output_directory
+ "${output_directory}/${normalized_include_prefix}")
+ endif()
+
+ if(NOT EXISTS "${infile}")
+ message(FATAL_ERROR "${infile} does not exist.")
+ endif()
+ set_source_files_properties(${infile} PROPERTIES SKIP_AUTOUIC ON)
+
+ macro(_qt_internal_set_output_file_properties file)
+ set_source_files_properties(${file} PROPERTIES
+ SKIP_AUTOMOC TRUE
+ SKIP_AUTOUIC TRUE
+ SKIP_LINTING TRUE)
+ endmacro()
+
+ set(outfile "${output_directory}/ui_${outfile}.h")
+ # Before CMake 3.27, there is a bug for using $<CONFIG> in a generated
+ # file with Ninja Multi-Config generator. To avoid this issue, we need
+ # to add the generated file for each configuration.
+ # Explicitly set the output file properties for each configuration
+ # to avoid Policy CMP0071 warnings.
+ # See https://gitlab.kitware.com/cmake/cmake/-/issues/24848
+ # Xcode generator cannot handle $<CONFIG> in the output file name.
+ # it changes $<CONFIG> with NOCONFIG. That's why we need to add the
+ # generated file for each configuration for Xcode generator as well.
+ if(is_multi AND outfile MATCHES "\\$<CONFIG>"
+ AND CMAKE_VERSION VERSION_LESS "3.27"
+ OR CMAKE_GENERATOR MATCHES "Xcode")
+ foreach(config ${CMAKE_CONFIGURATION_TYPES})
+ string(REPLACE "$<CONFIG>" "${config}" outfile_with_config
+ "${outfile}")
+ _qt_internal_set_output_file_properties(
+ ${outfile_with_config})
+ target_sources(${target} PRIVATE ${outfile_with_config})
+ endforeach()
+ else()
+ _qt_internal_set_output_file_properties(${outfile})
+ target_sources(${target} PRIVATE ${outfile})
+ endif()
+ # remove double slashes
+ string(REGEX REPLACE "//" "/" outfile "${outfile}")
+
+ # Note: If INCLUDE_PREFIX is changed without changing the corresponding
+ # include path in the source file and Ninja generator is used, this
+ # casues the double build issue. To avoid this issue, we need to
+ # remove the output file in configure step if the include_prefix is
+ # changed.
+ if(CMAKE_GENERATOR MATCHES "Ninja")
+ if(prefix_info_file_cmake_exists)
+ if(NOT "${${short_hash}_prefix}" STREQUAL
+ "${normalized_include_prefix}")
+ file(REMOVE_RECURSE "${file_ui_folder}")
+ list(APPEND prefix_info_list
+ "set(${short_hash}_prefix \"${normalized_include_prefix}\")")
+ elseif(NOT DEFINED ${short_hash}_prefix)
+ list(APPEND prefix_info_list
+ "set(${short_hash}_prefix \"${normalized_include_prefix}\")")
+ endif()
+ else()
+ set(prefix_info_list "")
+ list(APPEND prefix_info_list
+ "set(${short_hash}_prefix \"${normalized_include_prefix}\")")
+ endif()
+ file(MAKE_DIRECTORY ${file_ui_folder})
+ endif()
+
+ if(CMAKE_VERSION VERSION_GREATER_EQUAL "3.17")
+ set(remove_command rm -rf)
+ else()
+ set(remove_command "remove_directory")
+ endif()
+
+ add_custom_command(OUTPUT ${outfile}
+ DEPENDS ${QT_CMAKE_EXPORT_NAMESPACE}::uic
+ COMMAND ${CMAKE_COMMAND} -E ${remove_command} "${file_ui_folder}/${include_folder}"
+ COMMAND ${CMAKE_COMMAND} -E make_directory ${inc_dir_to_create}
+ COMMAND ${QT_CMAKE_EXPORT_NAMESPACE}::uic ${ui_options} -o
+ ${outfile} ${infile}
+ COMMAND_EXPAND_LISTS
+ MAIN_DEPENDENCY ${infile} VERBATIM)
+ endforeach()
+
+
+ get_target_property(is_guard_on ${target} _qt_ui_property_check_guard)
+ if(NOT is_guard_on)
+ # Ninja fails when a newline is used. That's why message is
+ # divided into parts.
+ set(error_message_1
+ "Error: The target \"${target}\" has \"AUTOUIC\" enabled.")
+ set(error_message_2
+ "Error: Please disable \"AUTOUIC\" for the target \"${target}\".")
+
+ set(ui_property_check_dummy_file
+ "${CMAKE_CURRENT_BINARY_DIR}/${target}_autogen/ui_property_check_timestamp")
+
+ add_custom_command(OUTPUT ${ui_property_check_dummy_file}
+ COMMAND ${CMAKE_COMMAND} -E make_directory
+ "${CMAKE_CURRENT_BINARY_DIR}/${target}_autogen"
+ COMMAND ${CMAKE_COMMAND} -E touch ${ui_property_check_dummy_file}
+ COMMAND
+ "$<$<BOOL:$<TARGET_PROPERTY:${target},AUTOUIC>>:${CMAKE_COMMAND}\
+;-E;echo;${error_message_1}>"
+ COMMAND
+ "$<$<BOOL:$<TARGET_PROPERTY:${target},AUTOUIC>>:${CMAKE_COMMAND}\
+;-E;echo;${error_message_2}>"
+ # Remove the dummy file so that the error message is shown until
+ # the AUTOUIC is disabled. Otherwise, the error message is shown
+ # only once when the AUTOUIC is enabled with Visual Studio generator.
+ COMMAND
+ "$<$<BOOL:$<TARGET_PROPERTY:${target},AUTOUIC>>:${CMAKE_COMMAND}\
+;-E;remove;${ui_property_check_dummy_file}>"
+ COMMAND
+ "$<$<BOOL:$<TARGET_PROPERTY:${target},AUTOUIC>>:${CMAKE_COMMAND}\
+;-E;false>"
+ COMMAND_EXPAND_LISTS
+ VERBATIM)
+
+ # When AUTOUIC is enabled, AUTOUIC runs before ${target}_ui_property_check
+ # target. So we need to add ${target}_ui_property_check as a dependency
+ # to ${target} to make sure that AUTOUIC runs after ${target}_ui_property_check
+ if (NOT QT_NO_MIX_UI_AUTOUIC_CHECK)
+ set_target_properties(${target} PROPERTIES
+ _qt_ui_property_check_guard ON)
+ add_custom_target(${target}_ui_property_check
+ ALL DEPENDS ${ui_property_check_dummy_file})
+ _qt_internal_assign_to_internal_targets_folder(
+ ${target}_ui_property_check)
+ add_dependencies(${target} ${target}_ui_property_check)
+ endif()
+ endif()
+
+ # write prefix info file
+ if(CMAKE_GENERATOR MATCHES "Ninja")
+ list(PREPEND prefix_info_list "include_guard()")
+ string(REPLACE ";" "\n" prefix_info_list "${prefix_info_list}")
+ file(WRITE "${prefix_info_file_cmake}" "${prefix_info_list}")
+ endif()
+endfunction()
+
+if(NOT QT_NO_CREATE_VERSIONLESS_FUNCTIONS)
+ function(qt_add_ui)
+ if(QT_DEFAULT_MAJOR_VERSION EQUAL 6)
+ qt6_add_ui(${ARGN})
+ else()
+ message(FATAL_ERROR "qt_add_ui() is only available in Qt 6.")
+ endif()
+ endfunction()
+endif()
+
diff --git a/src/widgets/doc/snippets/cmake-macros/examples.cmake b/src/widgets/doc/snippets/cmake-macros/examples.cmake
index 3c58509fdf..7d0023aed4 100644
--- a/src/widgets/doc/snippets/cmake-macros/examples.cmake
+++ b/src/widgets/doc/snippets/cmake-macros/examples.cmake
@@ -6,3 +6,30 @@ set(SOURCES mainwindow.cpp main.cpp)
qt_wrap_ui(SOURCES mainwindow.ui)
qt_add_executable(myapp ${SOURCES})
#! [qt_wrap_ui]
+
+#! [qt6_add_ui_1]
+qt_add_executable(myapp mainwindow.cpp main.cpp)
+qt6_add_ui(myapp SOURCES mainwindow.ui)
+#! [qt6_add_ui_1]
+
+#! [qt6_add_ui_2]
+qt_add_executable(myapp mainwindow.cpp main.cpp)
+qt6_add_ui(myapp INCLUDE_PREFIX "src/files" SOURCES mainwindow.ui)
+#! [qt6_add_ui_2]
+
+#! [qt6_add_ui_3]
+qt_add_executable(myapp widget1.cpp widget2.cpp main.cpp)
+qt6_add_ui(myapp INCLUDE_PREFIX "src/files" SOURCES widget1.ui widget2.ui)
+#! [qt6_add_ui_3]
+
+#! [qt6_add_ui_4]
+qt_add_executable(myapp widget1.cpp widget2.cpp main.cpp)
+qt6_add_ui(myapp INCLUDE_PREFIX "src/files"
+ SOURCES "my_ui_files_1/widget1.ui" "my_ui_files_2/widget2.ui")
+#! [qt6_add_ui_4]
+
+#! [qt6_add_ui_5]
+qt_add_executable(myapp widget1.cpp widget2.cpp main.cpp)
+qt6_add_ui(myapp INCLUDE_PREFIX "src/files_1" SOURCES "my_ui_files/widget1.ui")
+qt6_add_ui(myapp INCLUDE_PREFIX "src/files_2" SOURCES "my_ui_files/widget2.ui")
+#! [qt6_add_ui_5]
diff --git a/src/widgets/doc/snippets/cmake-macros/examples.cpp b/src/widgets/doc/snippets/cmake-macros/examples.cpp
new file mode 100644
index 0000000000..683f8453e4
--- /dev/null
+++ b/src/widgets/doc/snippets/cmake-macros/examples.cpp
@@ -0,0 +1,26 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+
+//! [qt6_add_ui_1]
+#include "mainwindow.h"
+#include "ui_mainwindow.h"
+//! [qt6_add_ui_1]
+
+//! [qt6_add_ui_2]
+#include "mainwindow.h"
+#include "src/files/ui_mainwindow.h"
+//! [qt6_add_ui_2]
+
+//! [qt6_add_ui_3_1]
+#include "src/files/ui_widget1.h"
+//! [qt6_add_ui_3_1]
+
+//! [qt6_add_ui_3_2]
+#include "src/files/ui_widget2.h"
+//! [qt6_add_ui_3_2]
+
+//! [qt6_add_ui_5]
+#include "src/files_1/ui_widget1.h"
+#include "src/files_2/ui_widget2.h"
+//! [qt6_add_ui_5]
diff --git a/src/widgets/doc/src/cmake-macros.qdoc b/src/widgets/doc/src/cmake-macros.qdoc
index 13bdcca67b..906a18b2b0 100644
--- a/src/widgets/doc/src/cmake-macros.qdoc
+++ b/src/widgets/doc/src/cmake-macros.qdoc
@@ -30,6 +30,10 @@ directory. The paths of the generated header files are added to \c{<VAR>}.
\note This is a low-level macro. See the \l{CMake AUTOUIC Documentation} for a
more convenient way to process \c{.ui} files with \c{uic}.
+Since 6.8:
+\note \l{qt6_add_ui}{qt_add_ui} is the recommended way to process \c{.ui}
+files.
+
\section1 Options
You can set additional \c{OPTIONS} that should be added to the \c{uic} calls.
@@ -39,3 +43,132 @@ You can find possible options in the \l{uic}{uic documentation}.
\snippet cmake-macros/examples.cmake qt_wrap_ui
*/
+
+/*!
+\page qt-add-ui.html
+\ingroup cmake-macros-qtwidgets
+
+\title qt_add_ui
+\keyword qt6_add_ui
+
+\summary {Adds .ui files to a target.}
+
+\include cmake-find-package-widgets.qdocinc
+
+\section1 Synopsis
+
+\badcode
+qt_add_ui(<TARGET>
+ SOURCES file1.ui [file2.ui ...]
+ [INCLUDE_PREFIX <PREFIX>]
+ [OPTIONS ...])
+\endcode
+
+\versionlessCMakeCommandsNote qt6_add_ui()
+
+\cmakecommandsince 6.8
+
+\section1 Description
+
+Creates rules for calling the \l{uic}{User Interface Compiler (uic)} on the
+\c{.ui} files. For each input file, a header file is generated in the build
+directory. The generated header files are added to sources of the target.
+
+\section1 Arguments
+
+\section2 TARGET
+
+The \c{TARGET} argument specifies the CMake target to which the generated header
+files will be added.
+
+\section2 SOURCES
+
+The \c{SOURCES} argument specifies the list of \c{.ui} files to process.
+
+\section2 INCLUDE_PREFIX
+
+\c INCLUDE_PREFIX specifies the include prefix for the generated header files.
+Use the same include prefix as in the \c{#include} directive in the source
+files. If \c{ui_<basename>.h} is included without a prefix, then this argument
+can be omitted.
+
+\section2 OPTIONS
+
+You can set additional \c{OPTIONS} that should be added to the \c{uic} calls.
+You can find possible options in the \l{uic}{uic documentation}.
+
+\section1 Examples
+
+\section2 Without INCLUDE_PREFIX
+
+In the following snippet, the \c{mainwindow.cpp} file includes
+\c{ui_mainwindow.h} and \c{mainwindow.h}.
+
+\snippet cmake-macros/examples.cpp qt6_add_ui_1
+
+\c{CMakeLists.txt} is implemented as follows and it calls
+\l{qt6_add_ui}{qt_add_ui} to add \c{ui_mainwindow.h} to the \c{myapp} target.
+
+\snippet cmake-macros/examples.cmake qt6_add_ui_1
+
+In the above example, \c{ui_mainwindow.h} is included without a prefix. So the
+\c{INCLUDE_PREFIX} argument is not specified.
+
+\section2 With INCLUDE_PREFIX
+
+\snippet cmake-macros/examples.cpp qt6_add_ui_2
+
+In the above snippet, \c{mainwindow.cpp} includes \c{ui_mainwindow.h} with a
+prefix.
+
+\snippet cmake-macros/examples.cmake qt6_add_ui_2
+
+Since \c{ui_mainwindow.h} is included with a prefix, the \c{INCLUDE_PREFIX}
+argument is specified as \c{src/files} in the above example.
+
+\section2 Multiple .ui files
+
+In the following snippets, both \c{widget1.cpp} and \c{widget2.cpp} include
+\c{ui_widget1.h} and \c{ui_widget2.h} respectively.
+
+\c{widget1.cpp}:
+
+\snippet cmake-macros/examples.cpp qt6_add_ui_3_1
+
+\c{widget2.cpp}:
+
+\snippet cmake-macros/examples.cpp qt6_add_ui_3_2
+
+Both \c{ui_widget1.h} and \c{ui_widget2.h} are included with the same prefix
+
+\snippet cmake-macros/examples.cmake qt6_add_ui_3
+
+In this case, the \c{INCLUDE_PREFIX} argument can be specified as \c{src/files}
+for both files in the above snippet.
+
+\section1 When to prefer \l{qt6_add_ui}{qt_add_ui} over \c{AUTOUIC}?
+
+\l{qt6_add_ui}{qt_add_ui} has some advantages over \c{AUTOUIC}:
+
+\list
+\li \l{qt6_add_ui}{qt_add_ui} ensures that the \c{.ui} files are generated
+correctly during the first build, for the \c{Ninja} and \c{Ninja Multi-Config}
+generators.
+
+\li \l{qt6_add_ui}{qt_add_ui} guarantees that the generated \c{.h} files are
+not leaked outside the build directory.
+
+\li Since \l{qt6_add_ui}{qt_add_ui} does not scan source files, it provides a
+faster build than \c{AUTOUIC}.
+
+\endlist
+
+\section1 When to prefer \l{qt6_add_ui}{qt_add_ui} over \c{qt_wrap_ui}?
+
+\l{qt6_add_ui}{qt_add_ui} has the \c{INCLUDE_PREFIX} argument, which can be used to
+specify the include prefix for the generated header files.
+
+\note It is not recommended to use both \l{qt6_add_ui}{qt_add_ui} and
+\c{AUTOUIC} for the same target.
+
+*/
diff --git a/src/widgets/kernel/qapplication.cpp b/src/widgets/kernel/qapplication.cpp
index de7d93ce20..a1392e10dc 100644
--- a/src/widgets/kernel/qapplication.cpp
+++ b/src/widgets/kernel/qapplication.cpp
@@ -1094,6 +1094,12 @@ QPalette QApplicationPrivate::basePalette() const
if (const QPalette *themePalette = platformTheme() ? platformTheme()->palette() : nullptr)
palette = themePalette->resolve(palette);
+ // This palette now is Qt-generated, so reset the resolve mask. This allows
+ // QStyle::polish implementations to respect palettes that are user provided,
+ // by checking if the palette has a brush set for a color that the style might
+ // otherwise overwrite.
+ palette.setResolveMask(0);
+
// Finish off by letting the application style polish the palette. This will
// not result in the polished palette becoming a user-set palette, as the
// resulting base palette is only used as a fallback, with the resolve mask
diff --git a/src/widgets/kernel/qwidget.cpp b/src/widgets/kernel/qwidget.cpp
index b302b32b88..ead633cda3 100644
--- a/src/widgets/kernel/qwidget.cpp
+++ b/src/widgets/kernel/qwidget.cpp
@@ -5160,6 +5160,7 @@ void QWidget::render(QPainter *painter, const QPoint &targetOffset,
const QRegion oldSystemClip = enginePriv->systemClip;
const QRegion oldBaseClip = enginePriv->baseSystemClip;
const QRegion oldSystemViewport = enginePriv->systemViewport;
+ const Qt::LayoutDirection oldLayoutDirection = painter->layoutDirection();
// This ensures that all painting triggered by render() is clipped to the current engine clip.
if (painter->hasClipping()) {
@@ -5168,6 +5169,7 @@ void QWidget::render(QPainter *painter, const QPoint &targetOffset,
} else {
enginePriv->setSystemViewport(oldSystemClip);
}
+ painter->setLayoutDirection(layoutDirection());
d->render(target, targetOffset, toBePainted, renderFlags);
@@ -5175,6 +5177,7 @@ void QWidget::render(QPainter *painter, const QPoint &targetOffset,
enginePriv->baseSystemClip = oldBaseClip;
enginePriv->setSystemTransformAndViewport(oldTransform, oldSystemViewport);
enginePriv->systemStateChanged();
+ painter->setLayoutDirection(oldLayoutDirection);
// Restore shared painter.
d->setSharedPainter(oldPainter);
@@ -7691,11 +7694,15 @@ QMargins QWidgetPrivate::safeAreaMargins() const
return QMargins();
// Or, if one of our ancestors are in a layout that does not have WA_LayoutOnEntireRect
- // set, then we know that the layout has already taken care of placing us inside the
- // safe area, by taking the contents rect of its parent widget into account.
+ // set, and the widget respects the safe area, then we know that the layout has already
+ // taken care of placing us inside the safe area, by taking the contents rect of its
+ // parent widget into account.
const QWidget *assumedSafeWidget = nullptr;
for (const QWidget *w = q; w != nativeWidget; w = w->parentWidget()) {
QWidget *parentWidget = w->parentWidget();
+ if (!parentWidget->testAttribute(Qt::WA_ContentsMarginsRespectsSafeArea))
+ continue; // Layout can't help us
+
if (parentWidget->testAttribute(Qt::WA_LayoutOnEntireRect))
continue; // Layout not going to help us
@@ -13614,7 +13621,8 @@ bool QWidgetPrivate::isInFocusChain() const
when the focus chain has been initialized. A newly constructed widget is considered to have
a consistent focus chain, while not being part of a focus chain.
- This method returns \c true early, if a widget is pointing to itself.
+ The method always returns \c true, when the logging category "qt.widgets.focus" is disabled.
+ When it is enabled, the method returns \c true early, if a widget is pointing to itself.
It returns \c false, if one of the following is detected:
\list
\li nullptr found in a previous/next pointer.
@@ -13631,6 +13639,10 @@ bool QWidgetPrivate::isInFocusChain() const
bool QWidgetPrivate::isFocusChainConsistent() const
{
Q_Q(const QWidget);
+ const bool skip = !QLoggingCategory("qt.widgets.focus").isDebugEnabled();
+ if (skip)
+ return true;
+
if (!isInFocusChain())
return true;
diff --git a/src/widgets/kernel/qwidgetwindow.cpp b/src/widgets/kernel/qwidgetwindow.cpp
index f51baba88b..03dde9ca69 100644
--- a/src/widgets/kernel/qwidgetwindow.cpp
+++ b/src/widgets/kernel/qwidgetwindow.cpp
@@ -77,6 +77,39 @@ public:
widget->focusWidget()->clearFocus();
}
+ void setFocusToTarget(FocusTarget target, Qt::FocusReason reason) override
+ {
+ Q_Q(QWidgetWindow);
+ QWidget *widget = q->widget();
+ if (!widget)
+ return;
+ QWidget *newFocusWidget = nullptr;
+
+ switch (target) {
+ case FocusTarget::First:
+ newFocusWidget = q->getFocusWidget(QWidgetWindow::FirstFocusWidget);
+ break;
+ case FocusTarget::Last:
+ newFocusWidget = q->getFocusWidget(QWidgetWindow::LastFocusWidget);
+ break;
+ case FocusTarget::Next: {
+ QWidget *focusWidget = widget->focusWidget() ? widget->focusWidget() : widget;
+ newFocusWidget = focusWidget->nextInFocusChain() ? focusWidget->nextInFocusChain() : focusWidget;
+ break;
+ }
+ case FocusTarget::Prev: {
+ QWidget *focusWidget = widget->focusWidget() ? widget->focusWidget() : widget;
+ newFocusWidget = focusWidget->previousInFocusChain() ? focusWidget->previousInFocusChain() : focusWidget;
+ break;
+ }
+ default:
+ break;
+ }
+
+ if (newFocusWidget)
+ newFocusWidget->setFocus(reason);
+ }
+
QRectF closestAcceptableGeometry(const QRectF &rect) const override;
void processSafeAreaMarginsChanged() override
@@ -472,9 +505,6 @@ void QWidgetWindow::handleNonClientAreaMouseEvent(QMouseEvent *e)
void QWidgetWindow::handleMouseEvent(QMouseEvent *event)
{
- static const QEvent::Type contextMenuTrigger =
- QGuiApplicationPrivate::platformTheme()->themeHint(QPlatformTheme::ContextMenuOnMouseRelease).toBool() ?
- QEvent::MouseButtonRelease : QEvent::MouseButtonPress;
if (QApplicationPrivate::inPopupMode()) {
QPointer<QWidget> activePopupWidget = QApplication::activePopupWidget();
QPointF mapped = event->position();
@@ -591,7 +621,7 @@ void QWidgetWindow::handleMouseEvent(QMouseEvent *event)
}
qt_replay_popup_mouse_event = false;
#ifndef QT_NO_CONTEXTMENU
- } else if (event->type() == contextMenuTrigger
+ } else if (event->type() == QGuiApplicationPrivate::contextMenuEventType()
&& event->button() == Qt::RightButton
&& (openPopupCount == oldOpenPopupCount)) {
QWidget *receiver = activePopupWidget;
@@ -604,7 +634,6 @@ void QWidgetWindow::handleMouseEvent(QMouseEvent *event)
QApplication::forwardEvent(receiver, &e, event);
}
#else
- Q_UNUSED(contextMenuTrigger);
Q_UNUSED(oldOpenPopupCount);
}
#endif
@@ -651,7 +680,8 @@ void QWidgetWindow::handleMouseEvent(QMouseEvent *event)
event->setAccepted(translated.isAccepted());
}
#ifndef QT_NO_CONTEXTMENU
- if (event->type() == contextMenuTrigger && event->button() == Qt::RightButton
+ if (event->type() == QGuiApplicationPrivate::contextMenuEventType()
+ && event->button() == Qt::RightButton
&& m_widget->rect().contains(event->position().toPoint())) {
QContextMenuEvent e(QContextMenuEvent::Mouse, mapped, event->globalPosition().toPoint(), event->modifiers());
QGuiApplication::forwardEvent(receiver, &e, event);
diff --git a/src/widgets/kernel/qwindowcontainer.cpp b/src/widgets/kernel/qwindowcontainer.cpp
index 2c374ac408..1aaf04af43 100644
--- a/src/widgets/kernel/qwindowcontainer.cpp
+++ b/src/widgets/kernel/qwindowcontainer.cpp
@@ -5,6 +5,7 @@
#include "qwidget_p.h"
#include "qwidgetwindow_p.h"
#include <QtGui/qwindow.h>
+#include <QtGui/private/qwindow_p.h>
#include <QtGui/private/qguiapplication_p.h>
#include <qpa/qplatformintegration.h>
#include <QDebug>
@@ -317,8 +318,17 @@ bool QWindowContainer::event(QEvent *e)
break;
case QEvent::FocusIn:
if (d->window->parent()) {
- if (QGuiApplication::focusWindow() != d->window)
+ if (QGuiApplication::focusWindow() != d->window) {
+ QFocusEvent *event = static_cast<QFocusEvent *>(e);
+ const auto reason = event->reason();
+ QWindowPrivate::FocusTarget target = QWindowPrivate::FocusTarget::Current;
+ if (reason == Qt::TabFocusReason)
+ target = QWindowPrivate::FocusTarget::First;
+ else if (reason == Qt::BacktabFocusReason)
+ target = QWindowPrivate::FocusTarget::Last;
+ qt_window_private(d->window)->setFocusToTarget(target, reason);
d->window->requestActivate();
+ }
}
break;
#if QT_CONFIG(draganddrop)
diff --git a/src/widgets/styles/qcommonstyle.cpp b/src/widgets/styles/qcommonstyle.cpp
index 418a0c2c5f..aab1192d50 100644
--- a/src/widgets/styles/qcommonstyle.cpp
+++ b/src/widgets/styles/qcommonstyle.cpp
@@ -761,57 +761,64 @@ void QCommonStyle::drawPrimitive(PrimitiveElement pe, const QStyleOption *opt, Q
% HexString<uint>(pe),
opt, QSize(size, size), dpr);
if (!QPixmapCache::find(pixmapName, &pixmap)) {
- const qreal border = size / 5.;
- const qreal sqsize = size;
- pixmap = styleCachePixmap(QSize(size, size), dpr);
- QPainter imagePainter(&pixmap);
- imagePainter.setRenderHint(QPainter::Antialiasing);
-
- QPolygonF poly;
+ // dpr scaling does not work well on such small pixel sizes, do it on our own
+ const int border = 1 * dpr;
+ const int sizeDpr = size * dpr;
+ int width = sizeDpr - 2 * border - 1;
+ int height = width / 2;
+ const int add = ((width & 1) == 1);
+ if (pe == PE_IndicatorArrowRight || pe == PE_IndicatorArrowLeft)
+ std::swap(width, height);
+ pixmap = styleCachePixmap(QSize(sizeDpr, sizeDpr), 1);
+
+ std::array<QPointF, 4> poly;
switch (pe) {
case PE_IndicatorArrowUp:
- poly = {QPointF(border, sqsize / 2), QPointF(sqsize / 2, border), QPointF(sqsize - border, sqsize / 2)};
+ poly = {QPointF(0, height), QPointF(width, height),
+ QPointF(width / 2 + add, 0), QPointF(width / 2, 0)};
break;
case PE_IndicatorArrowDown:
- poly = {QPointF(border, sqsize / 2), QPointF(sqsize / 2, sqsize - border), QPointF(sqsize - border, sqsize / 2)};
+ poly = {QPointF(0, 0), QPointF(width, 0),
+ QPointF(width / 2 + add, height), QPointF(width / 2, height)};
break;
case PE_IndicatorArrowRight:
- poly = {QPointF(sqsize - border, sqsize / 2), QPointF(sqsize / 2, border), QPointF(sqsize / 2, sqsize - border)};
+ poly = {QPointF(0, 0), QPointF(0, height),
+ QPointF(width, height / 2 + add), QPointF(width, height / 2)};
break;
case PE_IndicatorArrowLeft:
- poly = {QPointF(border, sqsize / 2), QPointF(sqsize / 2, border), QPointF(sqsize / 2, sqsize - border)};
+ poly = {QPointF(width, 0), QPointF(width, height),
+ QPointF(0, height / 2 + add), QPointF(0, height / 2)};
break;
default:
break;
}
- int bsx = 0;
- int bsy = 0;
-
+ QPainter imagePainter(&pixmap);
+ imagePainter.translate((sizeDpr - width) / 2, (sizeDpr - height) / 2);
if (opt->state & State_Sunken) {
- bsx = proxy()->pixelMetric(PM_ButtonShiftHorizontal, opt, widget);
- bsy = proxy()->pixelMetric(PM_ButtonShiftVertical, opt, widget);
+ const auto bsx = proxy()->pixelMetric(PM_ButtonShiftHorizontal, opt, widget);
+ const auto bsy = proxy()->pixelMetric(PM_ButtonShiftVertical, opt, widget);
+ imagePainter.translate(bsx, bsy);
}
-
- const QPointF boundsCenter = poly.boundingRect().center();
- const qreal sx = sqsize / 2 - boundsCenter.x();
- const qreal sy = sqsize / 2 - boundsCenter.y();
- imagePainter.translate(sx + bsx, sy + bsy);
imagePainter.setPen(opt->palette.buttonText().color());
imagePainter.setBrush(opt->palette.buttonText());
if (!(opt->state & State_Enabled)) {
- imagePainter.translate(1, 1);
+ const int ofs = qRound(1 * dpr);
+ imagePainter.translate(ofs, ofs);
imagePainter.setBrush(opt->palette.light().color());
imagePainter.setPen(opt->palette.light().color());
- imagePainter.drawPolygon(poly);
- imagePainter.translate(-1, -1);
+ imagePainter.drawPolygon(poly.data(), int(poly.size()));
+ imagePainter.drawPoints(poly.data(), int(poly.size()));
+ imagePainter.translate(-ofs, -ofs);
imagePainter.setBrush(opt->palette.mid().color());
imagePainter.setPen(opt->palette.mid().color());
}
-
- imagePainter.drawPolygon(poly);
+ imagePainter.drawPolygon(poly.data(), int(poly.size()));
+ // sometimes the corners are not drawn by drawPolygon for unknown reaons, so re-draw them again
+ imagePainter.drawPoints(poly.data(), int(poly.size()));
imagePainter.end();
+ pixmap.setDevicePixelRatio(dpr);
QPixmapCache::insert(pixmapName, pixmap);
}
int xOffset = r.x() + (r.width() - size)/2;
diff --git a/src/widgets/styles/qfusionstyle.cpp b/src/widgets/styles/qfusionstyle.cpp
index 34a0105b80..6b8fd979a9 100644
--- a/src/widgets/styles/qfusionstyle.cpp
+++ b/src/widgets/styles/qfusionstyle.cpp
@@ -3660,7 +3660,7 @@ QRect QFusionStyle::subElementRect(SubElement sr, const QStyleOption *opt, const
}
/*!
- \reimp
+ \internal
*/
QIcon QFusionStyle::iconFromTheme(StandardPixmap standardIcon) const
{
diff --git a/src/widgets/styles/qstylehelper.cpp b/src/widgets/styles/qstylehelper.cpp
index 02827de847..b4616b8c24 100644
--- a/src/widgets/styles/qstylehelper.cpp
+++ b/src/widgets/styles/qstylehelper.cpp
@@ -202,7 +202,7 @@ QPolygonF calcLines(const QStyleOptionSlider *dial)
qreal xc = width / 2 + 0.5;
qreal yc = height / 2 + 0.5;
const int ns = dial->tickInterval;
- if (!ns) // Invalid values may be set by Qt Designer.
+ if (!ns) // Invalid values may be set by Qt Widgets Designer.
return poly;
int notches = (dial->maximum + ns - 1 - dial->minimum) / ns;
if (notches <= 0)
diff --git a/src/widgets/styles/qstylesheetstyle_default.cpp b/src/widgets/styles/qstylesheetstyle_default.cpp
index e50e18f291..6356835ff4 100644
--- a/src/widgets/styles/qstylesheetstyle_default.cpp
+++ b/src/widgets/styles/qstylesheetstyle_default.cpp
@@ -122,7 +122,8 @@ StyleSheet QStyleSheetStyle::getDefaultStyleSheet() const
// pixmap based style doesn't support any features
bool styleIsPixmapBased = baseStyle()->inherits("QMacStyle")
- || baseStyle()->inherits("QWindowsVistaStyle");
+ || (baseStyle()->inherits("QWindowsVistaStyle")
+ && !baseStyle()->inherits("QWindows11Style"));
/*QLineEdit {
diff --git a/src/widgets/util/qcompleter.cpp b/src/widgets/util/qcompleter.cpp
index 394a968aad..f52321a3e1 100644
--- a/src/widgets/util/qcompleter.cpp
+++ b/src/widgets/util/qcompleter.cpp
@@ -1296,10 +1296,21 @@ bool QCompleter::eventFilter(QObject *o, QEvent *e)
{
Q_D(QCompleter);
- if (d->eatFocusOut && o == d->widget && e->type() == QEvent::FocusOut) {
- d->hiddenBecauseNoMatch = false;
- if (d->popup && d->popup->isVisible())
- return true;
+ if (o == d->widget) {
+ switch (e->type()) {
+ case QEvent::FocusOut:
+ if (d->eatFocusOut) {
+ d->hiddenBecauseNoMatch = false;
+ if (d->popup && d->popup->isVisible())
+ return true;
+ }
+ break;
+ case QEvent::Hide:
+ if (d->popup)
+ d->popup->hide();
+ default:
+ break;
+ }
}
if (o != d->popup)
diff --git a/src/widgets/widgets/qcalendarwidget.cpp b/src/widgets/widgets/qcalendarwidget.cpp
index 034127b4f3..0495b20422 100644
--- a/src/widgets/widgets/qcalendarwidget.cpp
+++ b/src/widgets/widgets/qcalendarwidget.cpp
@@ -2731,12 +2731,29 @@ bool QCalendarWidget::isGridVisible() const
return d->m_view->showGrid();
}
+/*!
+ \since 5.14
+ Report the calendar system in use by this widget.
+
+ \sa setCalendar()
+*/
+
QCalendar QCalendarWidget::calendar() const
{
Q_D(const QCalendarWidget);
return d->m_model->m_calendar;
}
+/*!
+ \since 5.14
+ Set \a c as the calendar system to be used by this widget.
+
+ The widget can use any supported calendar system.
+ By default, it uses the Gregorian calendar.
+
+ \sa calendar()
+*/
+
void QCalendarWidget::setCalendar(QCalendar c)
{
Q_D(QCalendarWidget);
diff --git a/src/widgets/widgets/qcheckbox.cpp b/src/widgets/widgets/qcheckbox.cpp
index 88cd603d70..3c03e9efa5 100644
--- a/src/widgets/widgets/qcheckbox.cpp
+++ b/src/widgets/widgets/qcheckbox.cpp
@@ -93,6 +93,11 @@ public:
\fn void QCheckBox::stateChanged(int state)
\deprecated [6.9] Use checkStateChanged(Qt::CheckState) instead.
+
+ This signal is emitted whenever the checkbox's state changes, i.e.,
+ whenever the user checks or unchecks it.
+
+ \a state contains the checkbox's new Qt::CheckState.
*/
/*!
diff --git a/src/widgets/widgets/qdatetimeedit.cpp b/src/widgets/widgets/qdatetimeedit.cpp
index 01e52b2fa6..a9b5babde5 100644
--- a/src/widgets/widgets/qdatetimeedit.cpp
+++ b/src/widgets/widgets/qdatetimeedit.cpp
@@ -303,6 +303,12 @@ void QDateTimeEdit::setTime(QTime time)
}
}
+/*!
+ \since 5.14
+ Report the calendar system in use by this widget.
+
+ \sa setCalendar()
+*/
QCalendar QDateTimeEdit::calendar() const
{
@@ -310,6 +316,16 @@ QCalendar QDateTimeEdit::calendar() const
return d->calendar;
}
+/*!
+ \since 5.14
+ Set \a calendar as the calendar system to be used by this widget.
+
+ The widget can use any supported calendar system.
+ By default, it uses the Gregorian calendar.
+
+ \sa calendar()
+*/
+
void QDateTimeEdit::setCalendar(QCalendar calendar)
{
Q_D(QDateTimeEdit);
@@ -2462,7 +2478,7 @@ int QDateTimeEditPrivate::absoluteIndex(QDateTimeEdit::Section s, int index) con
return NoSectionIndex;
}
-int QDateTimeEditPrivate::absoluteIndex(const SectionNode &s) const
+int QDateTimeEditPrivate::absoluteIndex(SectionNode s) const
{
return sectionNodes.indexOf(s);
}
diff --git a/src/widgets/widgets/qdatetimeedit_p.h b/src/widgets/widgets/qdatetimeedit_p.h
index 215ee75bfe..f93afd1519 100644
--- a/src/widgets/widgets/qdatetimeedit_p.h
+++ b/src/widgets/widgets/qdatetimeedit_p.h
@@ -67,7 +67,7 @@ public:
int cursorPosition() const override { return edit ? edit->cursorPosition() : -1; }
int absoluteIndex(QDateTimeEdit::Section s, int index) const;
- int absoluteIndex(const SectionNode &s) const;
+ int absoluteIndex(SectionNode s) const;
QDateTime stepBy(int index, int steps, bool test = false) const;
int sectionAt(int pos) const;
int closestSection(int index, bool forward) const;
diff --git a/src/widgets/widgets/qdialogbuttonbox.cpp b/src/widgets/widgets/qdialogbuttonbox.cpp
index 30ace89fa8..0b6a4df41a 100644
--- a/src/widgets/widgets/qdialogbuttonbox.cpp
+++ b/src/widgets/widgets/qdialogbuttonbox.cpp
@@ -374,7 +374,7 @@ QPushButton *QDialogButtonBoxPrivate::createButton(QDialogButtonBox::StandardBut
button->setIcon(style->standardIcon(QStyle::StandardPixmap(icon), nullptr, q));
if (style != QApplication::style()) // Propagate style
button->setStyle(style);
- standardButtonHash.insert(button, sbutton);
+ standardButtonMap.insert(button, sbutton);
QPlatformDialogHelper::ButtonRole role = QPlatformDialogHelper::buttonRole(static_cast<QPlatformDialogHelper::StandardButton>(sbutton));
if (Q_UNLIKELY(role == QPlatformDialogHelper::InvalidRole))
qWarning("QDialogButtonBox::createButton: Invalid ButtonRole, button not added");
@@ -426,10 +426,10 @@ void QDialogButtonBoxPrivate::createStandardButtons(QDialogButtonBox::StandardBu
void QDialogButtonBoxPrivate::retranslateStrings()
{
- for (auto &&[key, value] : std::as_const(standardButtonHash).asKeyValueRange()) {
- const QString text = QGuiApplicationPrivate::platformTheme()->standardButtonText(value);
+ for (const auto &it : std::as_const(standardButtonMap)) {
+ const QString text = QGuiApplicationPrivate::platformTheme()->standardButtonText(it.second);
if (!text.isEmpty())
- key->setText(text);
+ it.first->setText(text);
}
}
@@ -644,15 +644,15 @@ void QDialogButtonBox::clear()
Q_D(QDialogButtonBox);
// Remove the created standard buttons, they should be in the other lists, which will
// do the deletion
- d->standardButtonHash.clear();
+ d->standardButtonMap.clear();
for (int i = 0; i < NRoles; ++i) {
QList<QAbstractButton *> &list = d->buttonLists[i];
- while (list.size()) {
- QAbstractButton *button = list.takeAt(0);
+ for (auto button : std::as_const(list)) {
QObjectPrivate::disconnect(button, &QAbstractButton::destroyed,
d, &QDialogButtonBoxPrivate::handleButtonDestroyed);
delete button;
}
+ list.clear();
}
}
@@ -680,7 +680,11 @@ QList<QAbstractButton *> QDialogButtonBoxPrivate::visibleButtons() const
QList<QAbstractButton *> QDialogButtonBoxPrivate::allButtons() const
{
- return visibleButtons() << hiddenButtons.keys();
+ QList<QAbstractButton *> ret(visibleButtons());
+ ret.reserve(ret.size() + hiddenButtons.size());
+ for (const auto &it : hiddenButtons)
+ ret.push_back(it.first);
+ return ret;
}
/*!
@@ -718,9 +722,9 @@ void QDialogButtonBox::removeButton(QAbstractButton *button)
Removes \param button.
\param reason determines the behavior following the removal:
\list
- \li \c ManualRemove disconnects all signals and removes the button from standardButtonHash.
- \li \c HideEvent keeps connections alive, standard buttons remain in standardButtonHash.
- \li \c Destroyed removes the button from standardButtonHash. Signals remain untouched, because
+ \li \c ManualRemove disconnects all signals and removes the button from standardButtonMap.
+ \li \c HideEvent keeps connections alive, standard buttons remain in standardButtonMap.
+ \li \c Destroyed removes the button from standardButtonMap. Signals remain untouched, because
the button might already be only a QObject, the destructor of which handles disconnecting.
\endlist
*/
@@ -744,7 +748,7 @@ void QDialogButtonBoxPrivate::removeButton(QAbstractButton *button, RemoveReason
button->removeEventFilter(filter.get());
Q_FALLTHROUGH();
case RemoveReason::Destroyed:
- standardButtonHash.remove(reinterpret_cast<QPushButton *>(button));
+ standardButtonMap.remove(reinterpret_cast<QPushButton *>(button));
break;
case RemoveReason::HideEvent:
break;
@@ -818,8 +822,9 @@ void QDialogButtonBox::setStandardButtons(StandardButtons buttons)
{
Q_D(QDialogButtonBox);
// Clear out all the old standard buttons, then recreate them.
- qDeleteAll(d->standardButtonHash.keyBegin(), d->standardButtonHash.keyEnd());
- d->standardButtonHash.clear();
+ const auto oldButtons = d->standardButtonMap.keys();
+ d->standardButtonMap.clear();
+ qDeleteAll(oldButtons);
d->createStandardButtons(buttons);
}
@@ -828,11 +833,8 @@ QDialogButtonBox::StandardButtons QDialogButtonBox::standardButtons() const
{
Q_D(const QDialogButtonBox);
StandardButtons standardButtons = NoButton;
- QHash<QPushButton *, StandardButton>::const_iterator it = d->standardButtonHash.constBegin();
- while (it != d->standardButtonHash.constEnd()) {
- standardButtons |= it.value();
- ++it;
- }
+ for (const auto value : d->standardButtonMap.values())
+ standardButtons |= value;
return standardButtons;
}
@@ -845,7 +847,12 @@ QDialogButtonBox::StandardButtons QDialogButtonBox::standardButtons() const
QPushButton *QDialogButtonBox::button(StandardButton which) const
{
Q_D(const QDialogButtonBox);
- return d->standardButtonHash.key(which);
+
+ for (const auto &it : std::as_const(d->standardButtonMap)) {
+ if (it.second == which)
+ return it.first;
+ }
+ return nullptr;
}
/*!
@@ -857,7 +864,7 @@ QPushButton *QDialogButtonBox::button(StandardButton which) const
QDialogButtonBox::StandardButton QDialogButtonBox::standardButton(QAbstractButton *button) const
{
Q_D(const QDialogButtonBox);
- return d->standardButtonHash.value(static_cast<QPushButton *>(button));
+ return d->standardButtonMap.value(static_cast<QPushButton *>(button));
}
void QDialogButtonBoxPrivate::handleButtonClicked()
@@ -965,16 +972,13 @@ bool QDialogButtonBox::centerButtons() const
*/
void QDialogButtonBox::changeEvent(QEvent *event)
{
- typedef QHash<QPushButton *, QDialogButtonBox::StandardButton> StandardButtonHash;
-
Q_D(QDialogButtonBox);
switch (event->type()) {
case QEvent::StyleChange: // Propagate style
- if (!d->standardButtonHash.empty()) {
+ if (!d->standardButtonMap.empty()) {
QStyle *newStyle = style();
- const StandardButtonHash::iterator end = d->standardButtonHash.end();
- for (StandardButtonHash::iterator it = d->standardButtonHash.begin(); it != end; ++it)
- it.key()->setStyle(newStyle);
+ for (auto key : d->standardButtonMap.keys())
+ key->setStyle(newStyle);
}
#ifdef Q_OS_MAC
Q_FALLTHROUGH();
diff --git a/src/widgets/widgets/qdialogbuttonbox_p.h b/src/widgets/widgets/qdialogbuttonbox_p.h
index c3d7e03489..e439819c49 100644
--- a/src/widgets/widgets/qdialogbuttonbox_p.h
+++ b/src/widgets/widgets/qdialogbuttonbox_p.h
@@ -16,6 +16,7 @@
//
#include <private/qwidget_p.h>
+#include <private/qflatmap_p.h>
#include <qdialogbuttonbox.h>
QT_BEGIN_NAMESPACE
@@ -42,8 +43,8 @@ public:
QDialogButtonBoxPrivate(Qt::Orientation orient);
QList<QAbstractButton *> buttonLists[QDialogButtonBox::NRoles];
- QHash<QPushButton *, QDialogButtonBox::StandardButton> standardButtonHash;
- QHash<QAbstractButton *, QDialogButtonBox::ButtonRole> hiddenButtons;
+ QVarLengthFlatMap<QPushButton *, QDialogButtonBox::StandardButton, 8> standardButtonMap;
+ QVarLengthFlatMap<QAbstractButton *, QDialogButtonBox::ButtonRole, 8> hiddenButtons;
Qt::Orientation orientation;
QDialogButtonBox::ButtonLayout layoutPolicy;
diff --git a/src/widgets/widgets/qlineedit.cpp b/src/widgets/widgets/qlineedit.cpp
index 8909ac80d9..0db46bf175 100644
--- a/src/widgets/widgets/qlineedit.cpp
+++ b/src/widgets/widgets/qlineedit.cpp
@@ -2211,11 +2211,13 @@ QMenu *QLineEdit::createStandardContextMenu()
if (!isReadOnly()) {
action = popup->addAction(QLineEdit::tr("&Undo") + ACCEL_KEY(QKeySequence::Undo));
action->setEnabled(d->control->isUndoAvailable());
+ action->setObjectName(QStringLiteral("edit-undo"));
setActionIcon(action, QStringLiteral("edit-undo"));
connect(action, &QAction::triggered, this, &QLineEdit::undo);
action = popup->addAction(QLineEdit::tr("&Redo") + ACCEL_KEY(QKeySequence::Redo));
action->setEnabled(d->control->isRedoAvailable());
+ action->setObjectName(QStringLiteral("edit-redo"));
setActionIcon(action, QStringLiteral("edit-redo"));
connect(action, &QAction::triggered, this, &QLineEdit::redo);
@@ -2227,6 +2229,7 @@ QMenu *QLineEdit::createStandardContextMenu()
action = popup->addAction(QLineEdit::tr("Cu&t") + ACCEL_KEY(QKeySequence::Cut));
action->setEnabled(!d->control->isReadOnly() && d->control->hasSelectedText()
&& d->control->echoMode() == QLineEdit::Normal);
+ action->setObjectName(QStringLiteral("edit-cut"));
setActionIcon(action, QStringLiteral("edit-cut"));
connect(action, &QAction::triggered, this, &QLineEdit::cut);
}
@@ -2234,12 +2237,14 @@ QMenu *QLineEdit::createStandardContextMenu()
action = popup->addAction(QLineEdit::tr("&Copy") + ACCEL_KEY(QKeySequence::Copy));
action->setEnabled(d->control->hasSelectedText()
&& d->control->echoMode() == QLineEdit::Normal);
+ action->setObjectName(QStringLiteral("edit-copy"));
setActionIcon(action, QStringLiteral("edit-copy"));
connect(action, &QAction::triggered, this, &QLineEdit::copy);
if (!isReadOnly()) {
action = popup->addAction(QLineEdit::tr("&Paste") + ACCEL_KEY(QKeySequence::Paste));
action->setEnabled(!d->control->isReadOnly() && !QGuiApplication::clipboard()->text().isEmpty());
+ action->setObjectName(QStringLiteral("edit-paste"));
setActionIcon(action, QStringLiteral("edit-paste"));
connect(action, &QAction::triggered, this, &QLineEdit::paste);
}
@@ -2248,6 +2253,7 @@ QMenu *QLineEdit::createStandardContextMenu()
if (!isReadOnly()) {
action = popup->addAction(QLineEdit::tr("Delete"));
action->setEnabled(!d->control->isReadOnly() && !d->control->text().isEmpty() && d->control->hasSelectedText());
+ action->setObjectName(QStringLiteral("edit-delete"));
setActionIcon(action, QStringLiteral("edit-delete"));
connect(action, &QAction::triggered,
d->control, &QWidgetLineControl::_q_deleteSelected);
@@ -2258,6 +2264,7 @@ QMenu *QLineEdit::createStandardContextMenu()
action = popup->addAction(QLineEdit::tr("Select All") + ACCEL_KEY(QKeySequence::SelectAll));
action->setEnabled(!d->control->text().isEmpty() && !d->control->allSelected());
+ action->setObjectName(QStringLiteral("select-all"));
setActionIcon(action, QStringLiteral("edit-select-all"));
d->selectAllAction = action;
connect(action, &QAction::triggered, this, &QLineEdit::selectAll);
diff --git a/src/widgets/widgets/qmainwindowlayout.cpp b/src/widgets/widgets/qmainwindowlayout.cpp
index db17e50d5c..85c58fd70f 100644
--- a/src/widgets/widgets/qmainwindowlayout.cpp
+++ b/src/widgets/widgets/qmainwindowlayout.cpp
@@ -1931,6 +1931,11 @@ bool QMainWindowTabBar::contains(const QDockWidget *dockWidget) const
return false;
}
+// When a dock widget is removed from a floating tab,
+// Events need to be processed for the tab bar to realize that the dock widget is gone.
+// In this case count() counts the dock widget in transition and accesses dockAt
+// with an out-of-bounds index.
+// => return nullptr in contrast to other xxxxxAt() functions
QDockWidget *QMainWindowTabBar::dockAt(int index) const
{
QMainWindowTabBar *that = const_cast<QMainWindowTabBar *>(this);
@@ -1938,10 +1943,15 @@ QDockWidget *QMainWindowTabBar::dockAt(int index) const
QDockAreaLayoutInfo *info = mlayout->dockInfo(that);
if (!info)
return nullptr;
+
const int itemIndex = info->tabIndexToListIndex(index);
- Q_ASSERT(itemIndex >= 0 && itemIndex < info->item_list.count());
- const QDockAreaLayoutItem &item = info->item_list.at(itemIndex);
- return item.widgetItem ? qobject_cast<QDockWidget *>(item.widgetItem->widget()) : nullptr;
+ if (itemIndex >= 0) {
+ Q_ASSERT(itemIndex < info->item_list.count());
+ const QDockAreaLayoutItem &item = info->item_list.at(itemIndex);
+ return item.widgetItem ? qobject_cast<QDockWidget *>(item.widgetItem->widget()) : nullptr;
+ }
+
+ return nullptr;
}
void QMainWindowTabBar::mouseMoveEvent(QMouseEvent *e)